git-svn-id: https://svn.typo3.org/TYPO3v4/Core/trunk@1770 709f56b5-9817-0410-a4d7...
authorKasper Skårhøj <kasper@typo3.org>
Wed, 25 Oct 2006 10:27:07 +0000 (10:27 +0000)
committerKasper Skårhøj <kasper@typo3.org>
Wed, 25 Oct 2006 10:27:07 +0000 (10:27 +0000)
41 files changed:
ChangeLog
t3lib/class.t3lib_admin.php
t3lib/class.t3lib_basicfilefunc.php
t3lib/class.t3lib_befunc.php
t3lib/class.t3lib_div.php
t3lib/class.t3lib_extmgm.php
t3lib/class.t3lib_loadmodules.php
t3lib/class.t3lib_refindex.php
t3lib/class.t3lib_softrefproc.php
t3lib/class.t3lib_tcemain.php
t3lib/class.t3lib_userauthgroup.php
t3lib/config_default.php
t3lib/stddb/tables.sql
t3lib/stddb/tbl_be.php
typo3/alt_clickmenu.php
typo3/alt_main.php
typo3/class.alt_menu_functions.inc
typo3/init.php
typo3/mod/user/ws/index.php
typo3/sysext/belog/ext_emconf.php
typo3/sysext/belog/mod/conf.php
typo3/sysext/belog/mod/index.php
typo3/sysext/beuser/ext_emconf.php
typo3/sysext/beuser/ext_tables.php
typo3/sysext/beuser/mod/conf.php
typo3/sysext/beuser/mod/index.php
typo3/sysext/cms/tslib/class.tslib_fe.php
typo3/sysext/cms/tslib/class.tslib_pibase.php
typo3/sysext/impexp/app/conf.php
typo3/sysext/impexp/app/index.php
typo3/sysext/impexp/class.tx_impexp_clickmenu.php
typo3/sysext/impexp/ext_emconf.php
typo3/sysext/impexp/ext_tables.php
typo3/sysext/lang/locallang_csh_sysws.xml
typo3/sysext/lowlevel/class.tx_lowlevel_cleaner.php
typo3/sysext/lowlevel/class.tx_lowlevel_cleaner_core.php
typo3/sysext/lowlevel/ext_tables.php
typo3/sysext/version/class.tx_version_cm1.php
typo3/sysext/version/cm1/index.php
typo3/sysext/version/locallang.xml
typo3/template.php

index c6c2d53..9b34b66 100755 (executable)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2006-10-25  Kasper Skårhøj     <kasper2006@typo3.com>
+       * Added "alt_main_new.php" including various modifications around to support this.
+       * Added clean-up functions in extension "lowlevel" plus modified t3lib_refindex and t3lib_tcemain accordingly
+       * Added word index to sys_refindex.
+       * Added feature to workspace records so an element can still be edited even when passed to "review"; This allows an editor to notify that the element is ready but still make changes. Of course no more changes can be made as soon as the reviewer has pushed it to the next level.
+       * Added "mod.php" which is a module dispatch script that can potentially help us to get rid of the problem with local vs global extensions and their backend modules having different conf.php files. Documentation remains to be added to "Inside TYPO3".
+
 2006-10-14  Dmitry Dulepov  <dmitry@typo3.org>
 
        * Fixed bug 3199: SUing to myself?
index 1a3228b..20fdba7 100644 (file)
@@ -289,14 +289,14 @@ class t3lib_admin {
         *
         * @return      [type]          ...
         */
-       function genTreeStatus() {
+       function genTreeStatus($root=0) {
                $this->genTree_includeDeleted = TRUE;           // if set, genTree() includes deleted pages. This is default.
                $this->genTree_includeVersions = TRUE;          // if set, genTree() includes verisonized pages/records. This is default.
                $this->genTree_includeRecords = TRUE;           // if set, genTree() includes records from pages.
                $this->perms_clause = '';                                       // extra where-clauses for the tree-selection
                $this->genTree_makeHTML = 0;                            // if set, genTree() generates HTML, that visualizes the tree.
 
-               $this->genTree(537,'');
+               $this->genTree($root,'');
 
                return $this->recStats;
        }
index dc18db1..c8280c1 100644 (file)
@@ -80,7 +80,7 @@ require_once(PATH_t3lib.'class.t3lib_cs.php');
  */
 class t3lib_basicFileFunctions {
        var $getUniqueNamePrefix = '';  // Prefix which will be prepended the file when using the getUniqueName-function
-       var $maxNumber = 20;                    // This number decides the highest allowed appended number used on a filename before we use naming with unique strings
+       var $maxNumber = 99;                    // This number decides the highest allowed appended number used on a filename before we use naming with unique strings
        var $uniquePrecision = 6;               // This number decides how many characters out of a unique MD5-hash that is appended to a filename if getUniqueName is asked to find an available filename.
        var $maxInputNameLen = 60;              // This is the maximum length of names treated by cleanFileName()
        var $tempFN = '_temp_';                 // Temp-foldername. A folder in the root of one of the mounts with this name is regarded a TEMP-folder (used for upload from clipboard)
index 97dbc1b..83b1bce 100755 (executable)
@@ -958,6 +958,7 @@ class t3lib_BEfunc  {
 
        /**
         * Finds the Data Structure for a FlexForm field
+        * NOTE ON data structures for deleted records: This function may fail to deliver the data structure for a record for a few reasons: a) The data structure could be deleted (either with deleted-flagged or hard-deleted), b) the data structure is fetched using the ds_pointerField_searchParent in which case any deleted record on the route to the final location of the DS will make it fail. In theory, we can solve the problem in the case where records that are deleted-flagged keeps us from finding the DS - this is done at the markers ###NOTE_A### where we make sure to also select deleted records. However, we generally want the DS lookup to fail for deleted records since for the working website we expect a deleted-flagged record to be as inaccessible as one that is completely deleted from the DB. Any way we look at it, this may lead to integrity problems of the reference index and even lost files if attached. However, that is not really important considering that a single change to a data structure can instantly invalidate large amounts of the reference index which we do accept as a cost for the flexform features. Other than requiring a reference index update, deletion of/changes in data structure or the failure to look them up when completely deleting records may lead to lost files in the uploads/ folders since those are now without a proper reference.
         * Usage: 5
         *
         * @param       array           Field config array
@@ -965,10 +966,11 @@ class t3lib_BEfunc        {
         * @param       string          The table name
         * @param       string          Optional fieldname passed to hook object
         * @param       boolean         Boolean; If set, workspace overlay is applied to records. This is correct behaviour for all presentation and export, but NOT if you want a true reflection of how things are in the live workspace.
+        * @param       integer         SPECIAL CASES: Use this, if the DataStructure may come from a parent record and the INPUT row doesn't have a uid yet (hence, the pid cannot be looked up). Then it is necessary to supply a PID value to search recursively in for the DS (used from TCEmain)
         * @return      mixed           If array, the data structure was found and returned as an array. Otherwise (string) it is an error message.
         * @see t3lib_TCEforms::getSingleField_typeFlex()
         */
-       function getFlexFormDS($conf,$row,$table,$fieldName='',$WSOL=TRUE)      {
+       function getFlexFormDS($conf,$row,$table,$fieldName='',$WSOL=TRUE,$newRecordPidValue=0) {
                global $TYPO3_CONF_VARS;
 
                        // Get pointer field etc from TCA-config:
@@ -1002,7 +1004,7 @@ class t3lib_BEfunc        {
 
                                // Searching recursively back if 'ds_pointerField_searchParent' is defined (typ. a page rootline, or maybe a tree-table):
                        if ($ds_searchParentField && !$srcPointer)      {
-                               $rr = t3lib_BEfunc::getRecord($table,$row['uid'],'uid,'.$ds_searchParentField); // Get the "pid" field - we cannot know that it is in the input record!
+                               $rr = t3lib_BEfunc::getRecord($table,$row['uid'],'uid,'.$ds_searchParentField); // Get the "pid" field - we cannot know that it is in the input record! ###NOTE_A###
                                if ($WSOL)      {
                                        t3lib_BEfunc::workspaceOL($table,$rr);
                                        t3lib_BEfunc::fixVersioningPid($table,$rr,TRUE);        // Added "TRUE" 23/03/06 before 4.0. (Also to similar call below!).  Reason: When t3lib_refindex is scanning the system in Live workspace all Pages with FlexForms will not find their inherited datastructure. Thus all references from workspaces are removed! Setting TRUE means that versioning PID doesn't check workspace of the record. I can't see that this should give problems anywhere. See more information inside t3lib_refindex!
@@ -1010,11 +1012,13 @@ class t3lib_BEfunc      {
                                $uidAcc=array();        // Used to avoid looping, if any should happen.
                                $subFieldPointer = $conf['ds_pointerField_searchParent_subField'];
                                while(!$srcPointer)             {
+
                                        $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
                                                                        'uid,'.$ds_pointerField.','.$ds_searchParentField.($subFieldPointer?','.$subFieldPointer:''),
                                                                        $table,
-                                                                       'uid='.intval($rr[$ds_searchParentField]).t3lib_BEfunc::deleteClause($table)
+                                                                       'uid='.intval($newRecordPidValue ? $newRecordPidValue : $rr[$ds_searchParentField]).t3lib_BEfunc::deleteClause($table)  ###NOTE_A###
                                                                );
+                                       $newRecordPidValue = 0;
                                        $rr = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
 
                                                // break if no result from SQL db or if looping...
@@ -2035,11 +2039,11 @@ class t3lib_BEfunc      {
         *
         * @param       string          Table name, present in TCA
         * @param       string          Table prefix
+        * @param       array           Preset fields (must include prefix if that is used)
         * @return      string          List of fields.
         */
-       function getCommonSelectFields($table,$prefix='')       {
+       function getCommonSelectFields($table,$prefix='',$fields = array())     {
                global $TCA;
-               $fields = array();
                $fields[] = $prefix.'uid';
                $fields[] = $prefix.$TCA[$table]['ctrl']['label'];
 
@@ -2346,7 +2350,10 @@ class t3lib_BEfunc       {
                        }
                        $mainParams = t3lib_div::implodeArrayForUrl('',$mainParams);
 
-                       if (!$script) { $script=basename(PATH_thisScript); }
+                       if (!$script) { 
+                               $script = basename(PATH_thisScript);
+                               $mainParams.= (t3lib_div::_GET('M') ? '&M='.rawurlencode(t3lib_div::_GET('M')) : '');
+                       }
 
                        $options = array();
                        foreach($menuItems as $value => $label) {
@@ -2564,7 +2571,7 @@ class t3lib_BEfunc        {
 
        /**
         * Set preview keyword, eg:
-        *      $previewUrl = t3lib_div::getIndpEnv('TYPO3_SITE_URL').'?ADMCMD_prev='.t3lib_BEfunc::compilePreviewKeyword('id='.$pageId.'&L='.$language.'&ADMCMD_view=1&ADMCMD_editIcons=1&ADMCMD_previewWS='.$this->workspace, $GLOBALS['BE_USER']->user['uid'], 120);
+        *      $previewUrl = t3lib_div::getIndpEnv('TYPO3_SITE_URL').'index.php?ADMCMD_prev='.t3lib_BEfunc::compilePreviewKeyword('id='.$pageId.'&L='.$language.'&ADMCMD_view=1&ADMCMD_editIcons=1&ADMCMD_previewWS='.$this->workspace, $GLOBALS['BE_USER']->user['uid'], 120);
         *
         * todo for sys_preview:
         * - Add a comment which can be shown to previewer in frontend in some way (plus maybe ability to write back, take other action?)
@@ -3120,9 +3127,10 @@ class t3lib_BEfunc       {
         * @param       integer         Record uid for which to find versions.
         * @param       string          Field list to select
         * @param       integer         Workspace ID, if zero all versions regardless of workspace is found.
+        * @param       boolean         If set, deleted-flagged versions are included! (Only for clean-up script!)
         * @return      array           Array of versions of table/uid
         */
-       function selectVersionsOfRecord($table, $uid, $fields='*', $workspace=0)        {
+       function selectVersionsOfRecord($table, $uid, $fields='*', $workspace=0, $includeDeletedRecords=FALSE)  {
                global $TCA;
 
                if ($TCA[$table] && $TCA[$table]['ctrl']['versioningWS'])       {
@@ -3132,7 +3140,7 @@ class t3lib_BEfunc        {
                                $fields,
                                $table,
                                '((t3ver_oid='.intval($uid).($workspace!=0?' AND t3ver_wsid='.intval($workspace):'').') OR uid='.intval($uid).')'.
-                                       t3lib_BEfunc::deleteClause($table),
+                                       ($includeDeletedRecords ? '' : t3lib_BEfunc::deleteClause($table)),
                                '',
                                't3ver_id DESC'
                        );
index 60d584d..4297e4b 100755 (executable)
@@ -3200,7 +3200,7 @@ class t3lib_div {
                        $bInfo['BROWSER']= 'konqu';
                } elseif (strstr($useragent,'Opera'))   {
                        $bInfo['BROWSER']= 'opera';
-               } elseif (strstr($useragent,'MSIE 4') || strstr($useragent,'MSIE 5') || strstr($useragent,'MSIE 6'))    {
+               } elseif (strstr($useragent,'MSIE 4') || strstr($useragent,'MSIE 5') || strstr($useragent,'MSIE 6') || strstr($useragent,'MSIE 7'))     {
                        $bInfo['BROWSER']= 'msie';
                } elseif (strstr($useragent,'Mozilla/4') || strstr($useragent,'Mozilla/5'))     {
                        $bInfo['BROWSER']='net';
index 6518258..f9052bd 100644 (file)
@@ -371,6 +371,41 @@ class t3lib_extMgm {
        }
 
        /**
+        * Adds a module path to TBE_MODULES for used with the module dispatcher, mod.php
+        * Used only for modules that are not placed in the main/sub menu hierarchy by the traditional mechanism of addModule()
+        * Examples for this is context menu functionality (like import/export) which runs as an independent module through mod.php
+        * FOR USE IN ext_tables.php FILES
+        * Example:  t3lib_extMgm::addModulePath('xMOD_tximpexp',t3lib_extMgm::extPath($_EXTKEY).'app/');
+        *
+        * @param       string          $name is the name of the module, refer to conf.php of the module.
+        * @param       string          $path is the absolute path to the module directory inside of which "index.php" and "conf.php" is found.
+        * @return      void
+        */
+       function addModulePath($name,$path)     {
+               global $TBE_MODULES;
+
+               $TBE_MODULES['_PATHS'][$name] = $path;
+       }
+
+       /**
+        * Adding an application for the top menu. These are regular modules but is required to respond with Ajax content in case of certain parameters sent to them.
+        *
+        * @param       string          $name is the name of the module, refer to conf.php of the module.
+        * @param       string          $path is the absolute path to the module directory inside of which "index.php" and "conf.php" is found.
+        * @param       boolean         If set, the application is placed in the shortcut bar below the menu bar.
+        * @param       array           Options
+        * @return      void
+        */
+       function addTopApp($name,$path,$iconPane=FALSE,$options=array())        {
+               global $TBE_MODULES,$TYPO3_CONF_VARS;
+
+               $TYPO3_CONF_VARS['SC_OPTIONS']['GLOBAL']['topApps'][$iconPane?'icons':'menu'][$name] = $options;
+
+                       // Set path for TBE-modules:
+               $TBE_MODULES['_PATHS'][$name] = $path;
+       }
+
+       /**
         * Adds a "Function menu module" ('third level module') to an existing function menu for some other backend module
         * The arguments values are generally determined by which function menu this is supposed to interact with
         * See Inside TYPO3 for information on how to use this function.
index 27493fc..79a99bf 100644 (file)
@@ -435,7 +435,9 @@ class t3lib_loadModules {
                                }
 
                                        // Default script setup
-                               if ($MCONF['script'] && @file_exists($path.'/'.$MCONF['script']))       {
+                               if ($MCONF['script']==='_DISPATCH')     {
+                                       $modconf['script'] = 'mod.php?M='.rawurlencode($name);
+                               } elseif ($MCONF['script'] && @file_exists($path.'/'.$MCONF['script'])) {
                                        $modconf['script'] = $this->getRelativePath(PATH_typo3,$fullpath.'/'.$MCONF['script']);
                                } else {
                                        $modconf['script'] = 'dummy.php';
index 6bf3c21..83247be 100755 (executable)
  *
  *
  *
- *   76: class t3lib_refindex
- *   94:     function updateRefIndexTable($table,$uid,$testOnly=FALSE)
- *  165:     function generateRefIndexData($table,$uid)
- *  242:     function createEntryData($table,$uid,$field,$flexpointer,$deleted,$ref_table,$ref_uid,$ref_string='',$sort=-1,$softref_key='',$softref_id='')
- *  269:     function createEntryData_dbRels($table,$uid,$fieldname,$flexpointer,$deleted,$items)
- *  286:     function createEntryData_fileRels($table,$uid,$fieldname,$flexpointer,$deleted,$items)
- *  307:     function createEntryData_softreferences($table,$uid,$fieldname,$flexpointer,$deleted,$keys)
+ *   89: class t3lib_refindex
+ *  107:     function updateRefIndexTable($table,$uid,$testOnly=FALSE)
+ *  178:     function generateRefIndexData($table,$uid)
+ *  255:     function createEntryData($table,$uid,$field,$flexpointer,$deleted,$ref_table,$ref_uid,$ref_string='',$sort=-1,$softref_key='',$softref_id='')
+ *  282:     function createEntryData_dbRels($table,$uid,$fieldname,$flexpointer,$deleted,$items)
+ *  299:     function createEntryData_fileRels($table,$uid,$fieldname,$flexpointer,$deleted,$items)
+ *  320:     function createEntryData_softreferences($table,$uid,$fieldname,$flexpointer,$deleted,$keys)
  *
  *              SECTION: Get relations from table row
- *  362:     function getRelations($table,$row)
- *  459:     function getRelations_flexFormCallBack($dsArr, $dataValue, $PA, $structurePath, &$pObj)
- *  509:     function getRelations_procFiles($value, $conf, $uid)
- *  559:     function getRelations_procDB($value, $conf, $uid)
+ *  376:     function getRelations($table,$row,$onlyField='')
+ *  473:     function getRelations_flexFormCallBack($dsArr, $dataValue, $PA, $structurePath, &$pObj)
+ *  523:     function getRelations_procFiles($value, $conf, $uid)
+ *  573:     function getRelations_procDB($value, $conf, $uid)
+ *
+ *              SECTION: Setting values
+ *  616:     function setReferenceValue($hash,$newValue,$returnDataArray=FALSE)
+ *  699:     function setReferenceValue_dbRels($refRec,$itemArray,$newValue,&$dataArray,$flexpointer='')
+ *  737:     function setReferenceValue_fileRels($refRec,$itemArray,$newValue,&$dataArray,$flexpointer='')
+ *  775:     function setReferenceValue_softreferences($refRec,$softref,$newValue,&$dataArray,$flexpointer='')
  *
  *              SECTION: Helper functions
- *  596:     function isReferenceField($conf)
- *  606:     function destPathFromUploadFolder($folder)
- *  616:     function error($msg)
- *  627:     function updateIndex($testOnly,$cli_echo=FALSE)
+ *  822:     function isReferenceField($conf)
+ *  832:     function destPathFromUploadFolder($folder)
+ *  842:     function error($msg)
+ *  853:     function updateIndex($testOnly,$cli_echo=FALSE)
  *
- * TOTAL FUNCTIONS: 14
+ * TOTAL FUNCTIONS: 18
  * (This index is automatically created/updated by the extension "extdeveval")
  *
  */
 
 require_once(PATH_t3lib.'class.t3lib_tcemain.php');
 require_once(PATH_t3lib.'class.t3lib_flexformtools.php');
+require_once(PATH_typo3.'sysext/indexed_search/class.lexer.php');
 
 
 
 /**
  * Reference index processing and relation extraction
  *
- * NOTICE: When the reference index is updated for an offline version the results may not be correct. 
+ * NOTICE: When the reference index is updated for an offline version the results may not be correct.
  * First, lets assumed that the reference update happens in LIVE workspace (ALWAYS update from Live workspace if you analyse whole database!)
  * Secondly, lets assume that in a Draft workspace you have changed the data structure of a parent page record - this is (in TemplaVoila) inherited by subpages.
- * When in the LIVE workspace the data structure for the records/pages in the offline workspace will not be evaluated to the right one simply because the data structure is taken from a rootline traversal and in the Live workspace that will NOT include the changed DataSTructure! Thus the evaluation will be based on the Data Structure set in the Live workspace! 
+ * When in the LIVE workspace the data structure for the records/pages in the offline workspace will not be evaluated to the right one simply because the data structure is taken from a rootline traversal and in the Live workspace that will NOT include the changed DataSTructure! Thus the evaluation will be based on the Data Structure set in the Live workspace!
  * Somehow this scenario is rarely going to happen. Yet, it is an inconsistency and I see now practical way to handle it - other than simply ignoring maintaining the index for workspace records. Or we can say that the index is precise for all Live elements while glitches might happen in an offline workspace?
  * Anyway, I just wanted to document this finding - I don't think we can find a solution for it. And its very TemplaVoila specific.
  *
@@ -86,16 +93,20 @@ class t3lib_refindex {
        var $errorLog = array();
        var $WSOL = FALSE;
        var $relations = array();
+
+       var $words_strings = array();
+       var $words = array();
+       
        var $hashVersion = 1;   // Number which we can increase if a change in the code means we will have to force a re-generation of the index.
 
 
        /**
-        * Call this function to update the sys_refindex table for a record.
+        * Call this function to update the sys_refindex table for a record (even one just deleted)
         * NOTICE: Currently, references updated for a deleted-flagged record will not include those from within flexform fields in some cases where the data structure is defined by another record since the resolving process ignores deleted records! This will also result in bad cleaning up in tcemain I think... Anyway, thats the story of flexforms; as long as the DS can change, lots of references can get lost in no time.
         *
         * @param       string          Table name
         * @param       integer         UID of record
-        * @param       boolean         If set, nothing will be written to the index but the result value will still report statistics on what as added, deleted and kept. Can be used for mere analysis.
+        * @param       boolean         If set, nothing will be written to the index but the result value will still report statistics on what is added, deleted and kept. Can be used for mere analysis.
         * @return      array           Array with statistics about how many index records were added, deleted and not altered plus the complete reference set for the record.
         */
        function updateRefIndexTable($table,$uid,$testOnly=FALSE)       {
@@ -119,7 +130,7 @@ class t3lib_refindex {
                        '','','','hash'
                );
 
-                       // First, test to see if the record exists (being deleted also means it doesn't exist!)
+                       // First, test to see if the record exists (including deleted-flagged)
                if (t3lib_BEfunc::getRecordRaw($table,'uid='.intval($uid),'uid'))       {
 
                                // Then, get relations:
@@ -146,6 +157,9 @@ class t3lib_refindex {
 
                                $result['relations'] = $relations;
                        } else return FALSE;    // Weird mistake I would say...
+                       
+                               // Words:
+                       if (!$testOnly) $this->wordIndexing($table,$uid);
                }
 
                        // If any old are left, remove them:
@@ -177,6 +191,10 @@ class t3lib_refindex {
                        list($record) = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('*',$table,'uid='.intval($uid));
 
                        if (is_array($record))  {
+                               
+                                       // Initialize:
+                               $this->words_strings = array();
+                               $this->words = array();
 
                                        // Deleted:
                                $deleted = $TCA[$table]['ctrl']['delete'] ? ($record[$TCA[$table]['ctrl']['delete']]?1:0) : 0;
@@ -224,6 +242,14 @@ class t3lib_refindex {
                                        }
                                }
 
+                                       // Word indexing:
+                               t3lib_div::loadTCA($table);
+                               foreach($TCA[$table]['columns'] as $field => $conf)     {
+                                       if (t3lib_div::inList('input,text',$conf['config']['type']) && strcmp($record[$field],'') && !t3lib_div::testInt($record[$field])) {
+                                               $this->words_strings[$field] = $record[$field];
+                                       }
+                               }
+
                                return $this->relations;
                        }
                }
@@ -363,10 +389,11 @@ class t3lib_refindex {
         *
         * @param       string          Table name
         * @param       array           Row from table
+        * @param       string          Specific field to fetch for.
         * @return      array           Array with information about relations
         * @see export_addRecord()
         */
-       function getRelations($table,$row)      {
+       function getRelations($table,$row,$onlyField='')        {
                global $TCA;
 
                        // Load full table description
@@ -378,7 +405,7 @@ class t3lib_refindex {
 
                $outRow = array();
                foreach($row as $field => $value)       {
-                       if (!in_array($field,$nonFields) && is_array($TCA[$table]['columns'][$field]))  {
+                       if (!in_array($field,$nonFields) && is_array($TCA[$table]['columns'][$field]) && (!$onlyField || $onlyField===$field))  {
                                $conf = $TCA[$table]['columns'][$field]['config'];
 
                                        // Add files
@@ -403,6 +430,7 @@ class t3lib_refindex {
                                if ($conf['type']=='flex')      {
 
                                                // Get current value array:
+                                               // NOTICE: failure to resolve Data Structures can lead to integrity problems with the reference index. Please look up the note in the JavaDoc documentation for the function t3lib_BEfunc::getFlexFormDS() 
                                        $dataStructArray = t3lib_BEfunc::getFlexFormDS($conf, $row, $table,'',$this->WSOL);
                                        $currentValueArray = t3lib_div::xml2array($value);
 
@@ -541,13 +569,13 @@ class t3lib_refindex {
                        foreach($theFileValues as $file)        {
                                if (trim($file))        {
                                        $realFile = $dest.'/'.trim($file);
-                                       if (@is_file($realFile))        {
+#                                      if (@is_file($realFile))        {               // Now, the refernece index should NOT look if files exist - just faithfully include them if they are in the records!
                                                $newValueFiles[] = array(
                                                        'filename' => $file,
                                                        'ID' => md5($realFile),
                                                        'ID_absFile' => $realFile
                                                );      // the order should be preserved here because.. (?)
-                                       } else $this->error('Missing file: '.$realFile);
+#                                      } else $this->error('Missing file: '.$realFile);
                                }
                        }
 
@@ -594,6 +622,336 @@ class t3lib_refindex {
 
        /*******************************
         *
+        * Setting values
+        *
+        *******************************/
+
+       /**
+        * Setting the value of a reference or removing it completely.
+        * Usage: For lowlevel clean up operations!
+        * WARNING: With this you can set values that are not allowed in the database since it will bypass all checks for validity! Hence it is targetted at clean-up operations. Please use TCEmain in the usual ways if you wish to manipulate references.
+        * Since this interface allows updates to soft reference values (which TCEmain does not directly) you may like to use it for that as an exception to the warning above.
+        * Notice; If you want to remove multiple references from the same field, you MUST start with the one having the highest sorting number. If you don't the removal of a reference with a lower number will recreate an index in which the remaining references in that field has new hash-keys due to new sorting numbers - and you will get errors for the remaining operations which cannot find the hash you feed it!
+        * To ensure proper working only admin-BE_USERS in live workspace should use this function
+        *
+        * @param       string          32-byte hash string identifying the record from sys_refindex which you wish to change the value for
+        * @param       mixed           Value you wish to set for reference. If NULL, the reference is removed (unless a soft-reference in which case it can only be set to a blank string). If you wish to set a database reference, use the format "[table]:[uid]". Any other case, the input value is set as-is
+        * @param       boolean         Return $dataArray only, do not submit it to database.
+        * @return      string          If a return string, that carries an error message, otherwise false (=OK) (except if $returnDataArray is set!)
+        */
+       function setReferenceValue($hash,$newValue,$returnDataArray=FALSE)      {
+               
+               if ($GLOBALS['BE_USER']->workspace===0 && $GLOBALS['BE_USER']->isAdmin())       {
+
+                               // Get current index from Database:
+                       list($refRec) = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
+                               '*',
+                               'sys_refindex',
+                               'hash='.$GLOBALS['TYPO3_DB']->fullQuoteStr($hash,'sys_refindex')
+                       );
+
+                               // Check if reference existed.
+                       if (is_array($refRec))  {
+                               if ($GLOBALS['TCA'][$refRec['tablename']])      {
+
+                                               // Get that record from database:
+                                       list($record) = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('*',$refRec['tablename'],'uid='.intval($refRec['recuid']));
+
+                                       if (is_array($record))  {
+
+                                                       // Get all relations from record, filter with fieldname:
+                                               $dbrels = $this->getRelations($refRec['tablename'],$record,$refRec['field']);
+                                               if ($dat = $dbrels[$refRec['field']])   {
+
+                                                               // Initialize data array that is to be sent to TCEmain afterwards:
+                                                       $dataArray = array();
+
+                                                               // Based on type,
+                                                       switch((string)$dat['type'])    {
+                                                               case 'db':
+                                                                       $error = $this->setReferenceValue_dbRels($refRec,$dat['itemArray'],$newValue,$dataArray);
+                                                                       if ($error)     return $error;
+                                                               break;
+                                                               case 'file':
+                                                                       $this->setReferenceValue_fileRels($refRec,$dat['newValueFiles'],$newValue,$dataArray);
+                                                                       if ($error)     return $error;
+                                                               break;
+                                                               case 'flex':
+                                                                               // DB references:
+                                                                       if (is_array($dat['flexFormRels']['db'][$refRec['flexpointer']]))       {
+                                                                               $error = $this->setReferenceValue_dbRels($refRec,$dat['flexFormRels']['db'][$refRec['flexpointer']],$newValue,$dataArray,$refRec['flexpointer']);
+                                                                               if ($error)     return $error;
+                                                                       }
+                                                                               // File references
+                                                                       if (is_array($dat['flexFormRels']['file'][$refRec['flexpointer']]))     {
+                                                                               $this->setReferenceValue_fileRels($refRec,$dat['flexFormRels']['file'][$refRec['flexpointer']],$newValue,$dataArray,$refRec['flexpointer']);
+                                                                               if ($error)     return $error;
+                                                                       }
+                                                                               // Soft references in flexforms
+                                                                       if ($refRec['softref_key'] && is_array($dat['flexFormRels']['softrefs'][$refRec['flexpointer']]['keys'][$refRec['softref_key']]))       {
+                                                                               $error = $this->setReferenceValue_softreferences($refRec,$dat['flexFormRels']['softrefs'][$refRec['flexpointer']],$newValue,$dataArray,$refRec['flexpointer']);
+                                                                               if ($error)     return $error;
+                                                                       }
+                                                               break;
+                                                       }
+
+                                                               // Softreferences in the field:
+                                                       if ($refRec['softref_key'] && is_array($dat['softrefs']['keys'][$refRec['softref_key']]))       {
+                                                               $error = $this->setReferenceValue_softreferences($refRec,$dat['softrefs'],$newValue,$dataArray);
+                                                               if ($error)     return $error;
+
+                                                       }
+
+                                                               // Data Array, now ready to sent to TCEmain
+                                                       if ($returnDataArray)   {
+                                                               return $dataArray;
+                                                       } else {
+                                                                       // Execute CMD array:
+                                                               $tce = t3lib_div::makeInstance('t3lib_TCEmain');
+                                                               $tce->stripslashes_values = FALSE;
+                                                               $tce->dontProcessTransformations = TRUE;
+                                                               $tce->bypassWorkspaceRestrictions = TRUE;
+                                                               $tce->bypassFileHandling = TRUE;
+                                                               $tce->bypassAccessCheckForRecords = TRUE;       // Otherwise this cannot update things in deleted records...
+                                                       
+                                                               $tce->start($dataArray,array());        // check has been done previously that there is a backend user which is Admin and also in live workspace
+                                                               $tce->process_datamap();
+       
+                                                                       // Return errors if any:
+                                                               if (count($tce->errorLog))      {
+                                                                       return chr(10).'TCEmain:'.implode(chr(10).'TCEmain:',$tce->errorLog);
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               } else return 'ERROR: Tablename "'.$refRec['tablename'].'" was not in TCA!';
+                       } else return 'ERROR: No reference record with hash="'.$hash.'" was found!';
+               } else return 'ERROR: BE_USER object is not admin OR not in workspace 0 (Live)';
+       }
+
+       /**
+        * Setting a value for a reference for a DB field:
+        *
+        * @param       array           sys_refindex record
+        * @param       array           Array of references from that field
+        * @param       string          Value to substitute current value with (or NULL to unset it)
+        * @param       array           data array in which the new value is set (passed by reference)
+        * @param       string          Flexform pointer, if in a flex form field.
+        * @return      string          Error message if any, otherwise false = OK
+        */
+       function setReferenceValue_dbRels($refRec,$itemArray,$newValue,&$dataArray,$flexpointer='')     {
+               if (!strcmp($itemArray[$refRec['sorting']]['id'],$refRec['ref_uid']) && !strcmp($itemArray[$refRec['sorting']]['table'],$refRec['ref_table']))  {
+
+                               // Setting or removing value:
+                       if ($newValue===NULL)   {       // Remove value:
+                               unset($itemArray[$refRec['sorting']]);
+                       } else {
+                               list($itemArray[$refRec['sorting']]['table'],$itemArray[$refRec['sorting']]['id']) = explode(':',$newValue);
+                       }
+
+                               // Traverse and compile new list of records:
+                       $saveValue = array();
+                       foreach($itemArray as $pair)    {
+                               $saveValue[] = $pair['table'].'_'.$pair['id'];
+                       }
+
+                               // Set in data array:
+                       if ($flexpointer)       {
+                               $flexToolObj = t3lib_div::makeInstance('t3lib_flexformtools');
+                               $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']]['data'] = array();
+                               $flexToolObj->setArrayValueByPath(substr($flexpointer,0,-1),$dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']]['data'],implode(',',$saveValue));
+                       } else {
+                               $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']] = implode(',',$saveValue);
+                       }
+
+               } else return 'ERROR: table:id pair "'.$refRec['ref_table'].':'.$refRec['ref_uid'].'" did not match that of the record ("'.$itemArray[$refRec['sorting']]['table'].':'.$itemArray[$refRec['sorting']]['id'].'") in sorting index "'.$refRec['sorting'].'"';
+       }
+
+       /**
+        * Setting a value for a reference for a FILE field:
+        *
+        * @param       array           sys_refindex record
+        * @param       array           Array of references from that field
+        * @param       string          Value to substitute current value with (or NULL to unset it)
+        * @param       array           data array in which the new value is set (passed by reference)
+        * @param       string          Flexform pointer, if in a flex form field.
+        * @return      string          Error message if any, otherwise false = OK
+        */
+       function setReferenceValue_fileRels($refRec,$itemArray,$newValue,&$dataArray,$flexpointer='')   {
+               if (!strcmp(substr($itemArray[$refRec['sorting']]['ID_absFile'],strlen(PATH_site)),$refRec['ref_string']) && !strcmp('_FILE',$refRec['ref_table']))     {
+
+                               // Setting or removing value:
+                       if ($newValue===NULL)   {       // Remove value:
+                               unset($itemArray[$refRec['sorting']]);
+                       } else {
+                               $itemArray[$refRec['sorting']]['filename'] = $newValue;
+                       }
+
+                               // Traverse and compile new list of records:
+                       $saveValue = array();
+                       foreach($itemArray as $fileInfo)        {
+                               $saveValue[] = $fileInfo['filename'];
+                       }
+
+                               // Set in data array:
+                       if ($flexpointer)       {
+                               $flexToolObj = t3lib_div::makeInstance('t3lib_flexformtools');
+                               $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']]['data'] = array();
+                               $flexToolObj->setArrayValueByPath(substr($flexpointer,0,-1),$dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']]['data'],implode(',',$saveValue));
+                       } else {
+                               $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']] = implode(',',$saveValue);
+                       }
+
+               } else return 'ERROR: either "'.$refRec['ref_table'].'" was not "_FILE" or file PATH_site+"'.$refRec['ref_string'].'" did not match that of the record ("'.$itemArray[$refRec['sorting']]['ID_absFile'].'") in sorting index "'.$refRec['sorting'].'"';
+       }
+
+       /**
+        * Setting a value for a soft reference token
+        *
+        * @param       array           sys_refindex record
+        * @param       array           Array of soft reference occurencies
+        * @param       string          Value to substitute current value with
+        * @param       array           data array in which the new value is set (passed by reference)
+        * @param       string          Flexform pointer, if in a flex form field.
+        * @return      string          Error message if any, otherwise false = OK
+        */
+       function setReferenceValue_softreferences($refRec,$softref,$newValue,&$dataArray,$flexpointer='')       {
+               if (is_array($softref['keys'][$refRec['softref_key']][$refRec['softref_id']]))  {
+
+                               // Set new value:
+                       $softref['keys'][$refRec['softref_key']][$refRec['softref_id']]['subst']['tokenValue'] = ''.$newValue;
+
+                               // Traverse softreferences and replace in tokenized content to rebuild it with new value inside:
+                       foreach($softref['keys'] as $sfIndexes) {
+                               foreach($sfIndexes as $data)    {
+                                       $softref['tokenizedContent'] = str_replace('{softref:'.$data['subst']['tokenID'].'}', $data['subst']['tokenValue'], $softref['tokenizedContent']);
+                               }
+                       }
+
+                               // Set in data array:
+                       if (!strstr($softref['tokenizedContent'],'{softref:'))  {
+                               if ($flexpointer)       {
+                                       $flexToolObj = t3lib_div::makeInstance('t3lib_flexformtools');
+                                       $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']]['data'] = array();
+                                       $flexToolObj->setArrayValueByPath(substr($flexpointer,0,-1),$dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']]['data'],$softref['tokenizedContent']);
+                               } else {
+                                       $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']] = $softref['tokenizedContent'];
+                               }
+                       } else return 'ERROR: After substituting all found soft references there were still soft reference tokens in the text. (theoretically this does not have to be an error if the string "{softref:" happens to be in the field for another reason.)';
+               } else return 'ERROR: Soft reference parser key "'.$refRec['softref_key'].'" or the index "'.$refRec['softref_id'].'" was not found.';
+       }
+
+
+
+
+
+
+
+
+
+
+       /*******************************
+        *
+        * Indexing words
+        *
+        *******************************/
+       
+       /**
+        *
+        */
+       function wordIndexing($table,$uid)      {
+       
+               $lexer = t3lib_div::makeInstance('tx_indexedsearch_lexer');
+               $words = $lexer->split2Words(implode(' ',$this->words_strings));
+               foreach($words as $w) {
+                       $words[]=substr($w,0,3);
+               }
+               $words = array_unique($words);
+               $this->updateWordIndex($words,$table,$uid);
+       }
+
+       /**
+        * Update/Create word index for record
+        *
+        * @param       array           Word list array (words are values in array)
+        * @param       string          Table
+        * @param       integer         Rec uid
+        * @return      void
+        */
+       function updateWordIndex($words,$table,$uid) {
+
+                       // Submit words to 
+               $this->submitWords($words);
+               
+                       // Result id and remove relations:
+               $rid = t3lib_div::md5int($table.':'.$uid);
+               $GLOBALS['TYPO3_DB']->exec_DELETEquery('sys_refindex_rel', 'rid='.intval($rid));
+
+                       // Add relations:
+               foreach($words as $w)   {
+                       $insertFields = array(
+                               'rid' => $rid,
+                               'wid' => t3lib_div::md5int($w)
+                       );
+
+                       $GLOBALS['TYPO3_DB']->exec_INSERTquery('sys_refindex_rel', $insertFields);
+               }
+
+                       // Add result record:
+               $GLOBALS['TYPO3_DB']->exec_DELETEquery('sys_refindex_res', 'rid='.intval($rid));
+               $insertFields = array(
+                       'rid' => $rid,
+                       'tablename' => $table,
+                       'recuid' => $uid
+               );
+               $GLOBALS['TYPO3_DB']->exec_INSERTquery('sys_refindex_res', $insertFields);
+       }
+
+       /**
+        * Adds new words to db
+        *
+        * @param       array           Word List array (where each word has information about position etc).
+        * @return      void
+        */
+       function submitWords($wl) {
+
+               $hashArr = array();
+               foreach($wl as $w)      {
+                       $hashArr[] = t3lib_div::md5int($w);     
+               }
+               $wl = array_flip($wl);
+
+               if (count($hashArr))    {
+                       $cwl = implode(',',$hashArr);
+                       $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('baseword', 'sys_refindex_words', 'wid IN ('.$cwl.')');
+
+                       if($GLOBALS['TYPO3_DB']->sql_num_rows($res)!=count($wl)) {
+                               while($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
+                                       unset($wl[$row['baseword']]);
+                               }
+
+                               reset($wl);
+                               while(list($key,$val)=each($wl)) {
+                                       $insertFields = array(
+                                               'wid' => t3lib_div::md5int($key),
+                                               'baseword' => $key
+                                       );
+
+                                               // A duplicate-key error will occur here if a word is NOT unset in the unset() line. However as long as the words in $wl are NOT longer as 60 chars (the baseword varchar is 60 characters...) this is not a problem.
+                                       $GLOBALS['TYPO3_DB']->exec_INSERTquery('sys_refindex_words', $insertFields);
+                               }
+                       }
+               }
+       }
+
+
+
+
+
+
+
+       /*******************************
+        *
         * Helper functions
         *
         *******************************/
@@ -643,7 +1001,7 @@ class t3lib_refindex {
                $recCount=0;
                $tableCount=0;
 
-               $headerContent = $testOnly ? 'Reference Index TESTED (nothing written)' : 'Reference Index Updated';
+               $headerContent = $testOnly ? 'Reference Index being TESTED (nothing written)' : 'Reference Index being Updated';
                if ($cli_echo) echo
                                                '*******************************************'.chr(10).
                                                $headerContent.chr(10).
@@ -654,7 +1012,7 @@ class t3lib_refindex {
                        $tableNames[] = $tableName;
                        $tableCount++;
 
-                               // Traverse all non-deleted records in tables:
+                               // Traverse all records in tables, including deleted records:
                        $allRecs = $TYPO3_DB->exec_SELECTgetRows('uid',$tableName,'1=1');       //.t3lib_BEfunc::deleteClause($tableName)
                        $uidList = array(0);
                        foreach ($allRecs as $recdat)   {
@@ -697,7 +1055,7 @@ class t3lib_refindex {
                $bodyContent = $testedHowMuch.(count($errors)?implode(chr(10),$errors):'Index Integrity was perfect!');
                if ($cli_echo) echo $testedHowMuch.(count($errors)?'Updates: '.count($errors):'Index Integrity was perfect!').chr(10);
 
-               return array($headerContent,$bodyContent);
+               return array($headerContent,$bodyContent,count($errors));
        }
 }
 
index b403283..a5fa5fd 100755 (executable)
@@ -488,7 +488,7 @@ class t3lib_softrefproc {
        }
 
        /**
-        * Finding URLs in content, but only to notify about their existence. No substitution (could be done later via a parameter)
+        * Finding URLs in content
         *
         * @param       string          The input content to analyse
         * @param       array           Parameters set for the softref parser key in TCA/columns
@@ -725,7 +725,8 @@ class t3lib_softrefproc {
                        // Based on link type, maybe do more:
                switch ((string)$tLP['LINK_TYPE'])      {
                        case 'mailto':
-                                       // Mail addresses can be substituted manually:
+                       case 'url':
+                                       // Mail addresses and URLs can be substituted manually:
                                $elements[$tokenID.':'.$idx]['subst'] = array(
                                        'type' => 'string',
                                        'tokenID' => $tokenID,
@@ -734,12 +735,6 @@ class t3lib_softrefproc {
                                        // Output content will be the token instead:
                                $content = '{softref:'.$tokenID.'}';
                        break;
-                       case 'url':
-                                       // Nothing done, only for informational purposes. So return content right away:
-                               {
-                                       return $content;
-                               }
-                       break;
                        case 'file':
                                        // Process files found in fileadmin directory:
                                if (!$tLP['query'])     {       // We will not process files which has a query added to it. That will look like a script we don't want to move.
index a397efe..8a0eefa 100755 (executable)
@@ -244,6 +244,8 @@ class t3lib_TCEmain {
        var $neverHideAtCopy = FALSE;                   // Boolean. If set, then the 'hideAtCopy' flag for tables will be ignored.
        var $dontProcessTransformations = FALSE;        // Boolean: If set, then transformations are NOT performed on the input.
        var $bypassWorkspaceRestrictions = FALSE;       // Boolean: If true, workspace restrictions are bypassed on edit an create actions (process_datamap()). YOU MUST KNOW what you do if you use this feature!
+       var $bypassFileHandling = FALSE;                        // Boolean: If true, file handling of attached files (addition, deletion etc) is bypassed - the value is saved straight away. YOU MUST KNOW what you are doing with this feature!
+       var $bypassAccessCheckForRecords = FALSE;       // Boolean: If true, access check, check for deleted etc. for records is bypassed. YOU MUST KNOW what you are doing if you use this feature!
 
        var $copyWhichTables = '*';                             // String. Comma-list. This list of tables decides which tables will be copied. If empty then none will. If '*' then all will (that the user has permission to of course)
        var $generalComment = '';                               // General comment, eg. for staging in workspaces.
@@ -1363,152 +1365,157 @@ class t3lib_TCEmain   {
        /**
         * Handling files for group/select function
         *
-        * @param       [type]          $valueArray: ...
-        * @param       [type]          $tcaFieldConf: ...
-        * @param       [type]          $curValue: ...
-        * @param       [type]          $uploadedFileArray: ...
-        * @param       [type]          $status: ...
-        * @param       [type]          $table: ...
-        * @param       [type]          $id: ...
-        * @param       [type]          $recFID: ...
+        * @param       array           Array of incoming file references. Keys are numeric, values are files (basically, this is the exploded list of incoming files)
+        * @param       array           Configuration array from TCA of the field
+        * @param       string          Current value of the field
+        * @param       array           Array of uploaded files, if any
+        * @param       string          Status ("update" or ?)
+        * @param       string          tablename of record
+        * @param       integer         UID of record
+        * @param       string          Field identifier ([table:uid:field:....more for flexforms?]
         * @return      array           Modified value array
         * @see checkValue_group_select()
         */
        function checkValue_group_select_file($valueArray,$tcaFieldConf,$curValue,$uploadedFileArray,$status,$table,$id,$recFID)        {
 
-                       // If any files are uploaded:
-               if (is_array($uploadedFileArray) &&
-                       $uploadedFileArray['name'] &&
-                       strcmp($uploadedFileArray['tmp_name'],'none'))  {
-                               $valueArray[]=$uploadedFileArray['tmp_name'];
-                               $this->alternativeFileName[$uploadedFileArray['tmp_name']] = $uploadedFileArray['name'];
-               }
+               if (!$this->bypassFileHandling) {       // If filehandling should NOT be bypassed, do processing:
+                       
+                               // If any files are uploaded, add them to value array
+                       if (is_array($uploadedFileArray) &&
+                               $uploadedFileArray['name'] &&
+                               strcmp($uploadedFileArray['tmp_name'],'none'))  {
+                                       $valueArray[]=$uploadedFileArray['tmp_name'];
+                                       $this->alternativeFileName[$uploadedFileArray['tmp_name']] = $uploadedFileArray['name'];
+                       }
 
-                       // Creating fileFunc object.
-               if (!$this->fileFunc)   {
-                       $this->fileFunc = t3lib_div::makeInstance('t3lib_basicFileFunctions');
-                       $this->include_filefunctions=1;
+                               // Creating fileFunc object.
+                       if (!$this->fileFunc)   {
+                               $this->fileFunc = t3lib_div::makeInstance('t3lib_basicFileFunctions');
+                               $this->include_filefunctions=1;
+                       }
+                               // Setting permitted extensions.
+                       $all_files = Array();
+                       $all_files['webspace']['allow'] = $tcaFieldConf['allowed'];
+                       $all_files['webspace']['deny'] = $tcaFieldConf['disallowed'] ? $tcaFieldConf['disallowed'] : '*';
+                       $all_files['ftpspace'] = $all_files['webspace'];
+                       $this->fileFunc->init('', $all_files);
                }
-                       // Setting permitted extensions.
-               $all_files = Array();
-               $all_files['webspace']['allow'] = $tcaFieldConf['allowed'];
-               $all_files['webspace']['deny'] = $tcaFieldConf['disallowed'] ? $tcaFieldConf['disallowed'] : '*';
-               $all_files['ftpspace'] = $all_files['webspace'];
-               $this->fileFunc->init('', $all_files);
 
                        // If there is an upload folder defined:
                if ($tcaFieldConf['uploadfolder'])      {
-                               // For logging..
-                       $propArr = $this->getRecordProperties($table,$id);
+                       if (!$this->bypassFileHandling) {       // If filehandling should NOT be bypassed, do processing:
+                                       // For logging..
+                               $propArr = $this->getRecordProperties($table,$id);
 
-                               // Get destrination path:
-                       $dest = $this->destPathFromUploadFolder($tcaFieldConf['uploadfolder']);
+                                       // Get destrination path:
+                               $dest = $this->destPathFromUploadFolder($tcaFieldConf['uploadfolder']);
 
-                               // If we are updating:
-                       if ($status=='update')  {
+                                       // If we are updating:
+                               if ($status=='update')  {
 
-                                       // Traverse the input values and convert to absolute filenames in case the update happens to an autoVersionized record.
-                                       // Background: This is a horrible workaround! The problem is that when a record is auto-versionized the files of the record get copied and therefore get new names which is overridden with the names from the original record in the incoming data meaning both lost files and double-references!
-                                       // The only solution I could come up with (except removing support for managing files when autoversioning) was to convert all relative files to absolute names so they are copied again (and existing files deleted). This should keep references intact but means that some files are copied, then deleted after being copied _again_.
-                                       // Actually, the same problem applies to database references in case auto-versioning would include sub-records since in such a case references are remapped - and they would be overridden due to the same principle then.
-                                       // Illustration of the problem comes here:
-                                       // We have a record 123 with a file logo.gif. We open and edit the files header in a workspace. So a new version is automatically made.
-                                       // The versions uid is 456 and the file is copied to "logo_01.gif". But the form data that we sents was based on uid 123 and hence contains the filename "logo.gif" from the original.
-                                       // The file management code below will do two things: First it will blindly accept "logo.gif" as a file attached to the record (thus creating a double reference) and secondly it will find that "logo_01.gif" was not in the incoming filelist and therefore should be deleted.
-                                       // If we prefix the incoming file "logo.gif" with its absolute path it will be seen as a new file added. Thus it will be copied to "logo_02.gif". "logo_01.gif" will still be deleted but since the files are the same the difference is zero - only more processing and file copying for no reason. But it will work.
-                               if ($this->autoVersioningUpdate===TRUE) {
-                                       foreach($valueArray as $key => $theFile)        {
-                                               if ($theFile===basename($theFile))      {
-                                                       $valueArray[$key] = PATH_site.$tcaFieldConf['uploadfolder'].'/'.$theFile;
+                                               // Traverse the input values and convert to absolute filenames in case the update happens to an autoVersionized record.
+                                               // Background: This is a horrible workaround! The problem is that when a record is auto-versionized the files of the record get copied and therefore get new names which is overridden with the names from the original record in the incoming data meaning both lost files and double-references!
+                                               // The only solution I could come up with (except removing support for managing files when autoversioning) was to convert all relative files to absolute names so they are copied again (and existing files deleted). This should keep references intact but means that some files are copied, then deleted after being copied _again_.
+                                               // Actually, the same problem applies to database references in case auto-versioning would include sub-records since in such a case references are remapped - and they would be overridden due to the same principle then.
+                                               // Illustration of the problem comes here:
+                                               // We have a record 123 with a file logo.gif. We open and edit the files header in a workspace. So a new version is automatically made.
+                                               // The versions uid is 456 and the file is copied to "logo_01.gif". But the form data that we sents was based on uid 123 and hence contains the filename "logo.gif" from the original.
+                                               // The file management code below will do two things: First it will blindly accept "logo.gif" as a file attached to the record (thus creating a double reference) and secondly it will find that "logo_01.gif" was not in the incoming filelist and therefore should be deleted.
+                                               // If we prefix the incoming file "logo.gif" with its absolute path it will be seen as a new file added. Thus it will be copied to "logo_02.gif". "logo_01.gif" will still be deleted but since the files are the same the difference is zero - only more processing and file copying for no reason. But it will work.
+                                       if ($this->autoVersioningUpdate===TRUE) {
+                                               foreach($valueArray as $key => $theFile)        {
+                                                       if ($theFile===basename($theFile))      {       // If it is an already attached file...
+                                                               $valueArray[$key] = PATH_site.$tcaFieldConf['uploadfolder'].'/'.$theFile;
+                                                       }
                                                }
                                        }
-                               }
 
-                                       // Finding the CURRENT files listed, either from MM or from the current record.
-                               $theFileValues=array();
-                               if ($tcaFieldConf['MM'])        {       // If MM relations for the files also!
-                                       $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
-                                       $dbAnalysis->start('','files',$tcaFieldConf['MM'],$id);
-                                       reset($dbAnalysis->itemArray);
-                                       while (list($somekey,$someval)=each($dbAnalysis->itemArray))    {
-                                               if ($someval['id'])     {
-                                                       $theFileValues[]=$someval['id'];
+                                               // Finding the CURRENT files listed, either from MM or from the current record.
+                                       $theFileValues=array();
+                                       if ($tcaFieldConf['MM'])        {       // If MM relations for the files also!
+                                               $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
+                                               $dbAnalysis->start('','files',$tcaFieldConf['MM'],$id);
+                                               reset($dbAnalysis->itemArray);
+                                               while (list($somekey,$someval)=each($dbAnalysis->itemArray))    {
+                                                       if ($someval['id'])     {
+                                                               $theFileValues[]=$someval['id'];
+                                                       }
                                                }
+                                       } else {
+                                               $theFileValues=t3lib_div::trimExplode(',',$curValue,1);
                                        }
-                               } else {
-                                       $theFileValues=t3lib_div::trimExplode(',',$curValue,1);
-                               }
 
-                                       // DELETE files: If existing files were found, traverse those and register files for deletion which has been removed:
-                               if (count($theFileValues))      {
-                                               // Traverse the input values and for all input values which match an EXISTING value, remove the existing from $theFileValues array (this will result in an array of all the existing files which should be deleted!)
-                                       foreach($valueArray as $key => $theFile)        {
-                                               if ($theFile && !strstr(t3lib_div::fixWindowsFilePath($theFile),'/'))   {
-                                                       $theFileValues = t3lib_div::removeArrayEntryByValue($theFileValues,$theFile);
+                                               // DELETE files: If existing files were found, traverse those and register files for deletion which has been removed:
+                                       if (count($theFileValues))      {
+                                                       // Traverse the input values and for all input values which match an EXISTING value, remove the existing from $theFileValues array (this will result in an array of all the existing files which should be deleted!)
+                                               foreach($valueArray as $key => $theFile)        {
+                                                       if ($theFile && !strstr(t3lib_div::fixWindowsFilePath($theFile),'/'))   {
+                                                               $theFileValues = t3lib_div::removeArrayEntryByValue($theFileValues,$theFile);
+                                                       }
                                                }
-                                       }
 
-                                               // This array contains the filenames in the uploadfolder that should be deleted:
-                                       foreach($theFileValues as $key => $theFile)     {
-                                               $theFile = trim($theFile);
-                                               if (@is_file($dest.'/'.$theFile))       {
-                                                       $this->removeFilesStore[]=$dest.'/'.$theFile;
-                                               } elseif ($theFile) {
-                                                       $this->log($table,$id,5,0,1,"Could not delete file '%s' (does not exist). (%s)",10,array($dest.'/'.$theFile, $recFID),$propArr['event_pid']);
+                                                       // This array contains the filenames in the uploadfolder that should be deleted:
+                                               foreach($theFileValues as $key => $theFile)     {
+                                                       $theFile = trim($theFile);
+                                                       if (@is_file($dest.'/'.$theFile))       {
+                                                               $this->removeFilesStore[]=$dest.'/'.$theFile;
+                                                       } elseif ($theFile) {
+                                                               $this->log($table,$id,5,0,1,"Could not delete file '%s' (does not exist). (%s)",10,array($dest.'/'.$theFile, $recFID),$propArr['event_pid']);
+                                                       }
                                                }
                                        }
                                }
-                       }
 
-                               // Traverse the submitted values:
-                       foreach($valueArray as $key => $theFile)        {
-                                       // NEW FILES? If the value contains '/' it indicates, that the file is new and should be added to the uploadsdir (whether its absolute or relative does not matter here)
-                               if (strstr(t3lib_div::fixWindowsFilePath($theFile),'/'))        {
-                                               // Init:
-                                       $maxSize = intval($tcaFieldConf['max_size']);
-                                       $cmd='';
-                                       $theDestFile='';                // Must be cleared. Else a faulty fileref may be inserted if the below code returns an error!! (Change: 22/12/2000)
-
-                                               // Check various things before copying file:
-                                       if (@is_dir($dest) && (@is_file($theFile) || @is_uploaded_file($theFile)))      {               // File and destination must exist
-
-                                                       // Finding size. For safe_mode we have to rely on the size in the upload array if the file is uploaded.
-                                               if (is_uploaded_file($theFile) && $theFile==$uploadedFileArray['tmp_name'])     {
-                                                       $fileSize = $uploadedFileArray['size'];
+                                       // Traverse the submitted values:
+                               foreach($valueArray as $key => $theFile)        {
+                                               // NEW FILES? If the value contains '/' it indicates, that the file is new and should be added to the uploadsdir (whether its absolute or relative does not matter here)
+                                       if (strstr(t3lib_div::fixWindowsFilePath($theFile),'/'))        {
+                                                       // Init:
+                                               $maxSize = intval($tcaFieldConf['max_size']);
+                                               $cmd='';
+                                               $theDestFile='';                // Must be cleared. Else a faulty fileref may be inserted if the below code returns an error!
+
+                                                       // Check various things before copying file:
+                                               if (@is_dir($dest) && (@is_file($theFile) || @is_uploaded_file($theFile)))      {               // File and destination must exist
+
+                                                               // Finding size. For safe_mode we have to rely on the size in the upload array if the file is uploaded.
+                                                       if (is_uploaded_file($theFile) && $theFile==$uploadedFileArray['tmp_name'])     {
+                                                               $fileSize = $uploadedFileArray['size'];
+                                                       } else {
+                                                               $fileSize = filesize($theFile);
+                                                       }
+
+                                                       if (!$maxSize || $fileSize<=($maxSize*1024))    {       // Check file size:
+                                                                       // Prepare filename:
+                                                               $theEndFileName = isset($this->alternativeFileName[$theFile]) ? $this->alternativeFileName[$theFile] : $theFile;
+                                                               $fI = t3lib_div::split_fileref($theEndFileName);
+
+                                                                       // Check for allowed extension:
+                                                               if ($this->fileFunc->checkIfAllowed($fI['fileext'], $dest, $theEndFileName)) {
+                                                                       $theDestFile = $this->fileFunc->getUniqueName($this->fileFunc->cleanFileName($fI['file']), $dest);
+
+                                                                               // If we have a unique destination filename, then write the file:
+                                                                       if ($theDestFile)       {
+                                                                               t3lib_div::upload_copy_move($theFile,$theDestFile);
+                                                                               $this->copiedFileMap[$theFile] = $theDestFile;
+                                                                               clearstatcache();
+                                                                               if (!@is_file($theDestFile))    $this->log($table,$id,5,0,1,"Copying file '%s' failed!: The destination path (%s) may be write protected. Please make it write enabled!. (%s)",16,array($theFile, dirname($theDestFile), $recFID),$propArr['event_pid']);
+                                                                       } else $this->log($table,$id,5,0,1,"Copying file '%s' failed!: No destination file (%s) possible!. (%s)",11,array($theFile, $theDestFile, $recFID),$propArr['event_pid']);
+                                                               } else $this->log($table,$id,5,0,1,"Fileextension '%s' not allowed. (%s)",12,array($fI['fileext'], $recFID),$propArr['event_pid']);
+                                                       } else $this->log($table,$id,5,0,1,"Filesize (%s) of file '%s' exceeds limit (%s). (%s)",13,array(t3lib_div::formatSize($fileSize),$theFile,t3lib_div::formatSize($maxSize*1024),$recFID),$propArr['event_pid']);
+                                               } else $this->log($table,$id,5,0,1,'The destination (%s) or the source file (%s) does not exist. (%s)',14,array($dest, $theFile, $recFID),$propArr['event_pid']);
+
+                                                       // If the destination file was created, we will set the new filename in the value array, otherwise unset the entry in the value array!
+                                               if (@is_file($theDestFile))     {
+                                                       $info = t3lib_div::split_fileref($theDestFile);
+                                                       $valueArray[$key]=$info['file']; // The value is set to the new filename
                                                } else {
-                                                       $fileSize = filesize($theFile);
+                                                       unset($valueArray[$key]);       // The value is set to the new filename
                                                }
-
-                                               if (!$maxSize || $fileSize<=($maxSize*1024))    {       // Check file size:
-                                                               // Prepare filename:
-                                                       $theEndFileName = isset($this->alternativeFileName[$theFile]) ? $this->alternativeFileName[$theFile] : $theFile;
-                                                       $fI = t3lib_div::split_fileref($theEndFileName);
-
-                                                               // Check for allowed extension:
-                                                       if ($this->fileFunc->checkIfAllowed($fI['fileext'], $dest, $theEndFileName)) {
-                                                               $theDestFile = $this->fileFunc->getUniqueName($this->fileFunc->cleanFileName($fI['file']), $dest);
-
-                                                                       // If we have a unique destination filename, then write the file:
-                                                               if ($theDestFile)       {
-                                                                       t3lib_div::upload_copy_move($theFile,$theDestFile);
-                                                                       $this->copiedFileMap[$theFile] = $theDestFile;
-                                                                       clearstatcache();
-                                                                       if (!@is_file($theDestFile))    $this->log($table,$id,5,0,1,"Copying file '%s' failed!: The destination path (%s) may be write protected. Please make it write enabled!. (%s)",16,array($theFile, dirname($theDestFile), $recFID),$propArr['event_pid']);
-                                                               } else $this->log($table,$id,5,0,1,"Copying file '%s' failed!: No destination file (%s) possible!. (%s)",11,array($theFile, $theDestFile, $recFID),$propArr['event_pid']);
-                                                       } else $this->log($table,$id,5,0,1,"Fileextension '%s' not allowed. (%s)",12,array($fI['fileext'], $recFID),$propArr['event_pid']);
-                                               } else $this->log($table,$id,5,0,1,"Filesize (%s) of file '%s' exceeds limit (%s). (%s)",13,array(t3lib_div::formatSize($fileSize),$theFile,t3lib_div::formatSize($maxSize*1024),$recFID),$propArr['event_pid']);
-                                       } else $this->log($table,$id,5,0,1,'The destination (%s) or the source file (%s) does not exist. (%s)',14,array($dest, $theFile, $recFID),$propArr['event_pid']);
-
-                                               // If the destination file was created, we will set the new filename in the value array, otherwise unset the entry in the value array!
-                                       if (@is_file($theDestFile))     {
-                                               $info = t3lib_div::split_fileref($theDestFile);
-                                               $valueArray[$key]=$info['file']; // The value is set to the new filename
-                                       } else {
-                                               unset($valueArray[$key]);       // The value is set to the new filename
                                        }
                                }
                        }
-
+                       
                                // If MM relations for the files, we will set the relations as MM records and change the valuearray to contain a single entry with a count of the number of files!
                        if ($tcaFieldConf['MM'])        {
                                $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
@@ -1549,9 +1556,13 @@ class t3lib_TCEmain      {
 
                if (is_array($value))   {
 
+                               // This value is necessary for flex form processing to happen on flexform fields in page records when they are copied.
+                               // The problem is, that when copying a page, flexfrom XML comes along in the array for the new record - but since $this->checkValue_currentRecord does not have a uid or pid for that sake, the t3lib_BEfunc::getFlexFormDS() function returns no good DS. For new records we do know the expected PID so therefore we send that with this special parameter. Only active when larger than zero.
+                       $newRecordPidValue = $status=='new' ? $realPid : 0;
+
                                // Get current value array:
-                       $dataStructArray = t3lib_BEfunc::getFlexFormDS($tcaFieldConf,$this->checkValue_currentRecord,$table);
-#debug($this->checkValue_currentRecord);
+                       $dataStructArray = t3lib_BEfunc::getFlexFormDS($tcaFieldConf,$this->checkValue_currentRecord,$table,'',TRUE,$newRecordPidValue);
+
                        $currentValueArray = t3lib_div::xml2array($curValue);
                        if (!is_array($currentValueArray))      $currentValueArray = array();
                        if (is_array($currentValueArray['meta']['currentLangId']))              unset($currentValueArray['meta']['currentLangId']);     // Remove all old meta for languages...
@@ -3054,9 +3065,33 @@ class t3lib_TCEmain      {
                if ($table == 'pages')  {
                        $this->deletePages($uid, $noRecordCheck, $forceHardDelete);
                } else {
+                       $this->deleteVersionsForRecord($table,$uid,$forceHardDelete);
                        $this->deleteRecord($table, $uid, $noRecordCheck, $forceHardDelete);
                }
        }
+       
+       /**
+        * Delete versions for element from any table
+        *
+        * @param       string          Table name
+        * @param       integer         Record UID
+        * @param       boolean         If TRUE, the "deleted" flag is ignored if applicable for record and the record is deleted COMPLETELY!
+        * @return      void
+        */
+       function deleteVersionsForRecord($table, $uid, $forceHardDelete)        {
+               $versions = t3lib_BEfunc::selectVersionsOfRecord($table, $uid, 'uid,pid');
+               if (is_array($versions))        {
+                       foreach($versions as $verRec)   {
+                               if (!$verRec['_CURRENT_VERSION'])       {
+                                       if ($table == 'pages')  {
+                                               $this->deletePages($verRec['uid'], TRUE, $forceHardDelete);
+                                       } else {
+                                               $this->deleteRecord($table, $verRec['uid'], TRUE, $forceHardDelete);
+                                       }
+                               }
+                       }
+               }
+       }
 
        /**
         * Undelete a single record
@@ -3072,7 +3107,7 @@ class t3lib_TCEmain       {
        /**
         * Deleting/Undeleting a record
         * This function may not be used to delete pages-records unless the underlying records are already deleted
-        * Deletes a record regardless of versioning state (live of offline, doesn't matter, the uid decides)
+        * Deletes a record regardless of versioning state (live or offline, doesn't matter, the uid decides)
         * If both $noRecordCheck and $forceHardDelete are set it could even delete a "deleted"-flagged record!
         *
         * @param       string          Table name
@@ -3112,6 +3147,19 @@ class t3lib_TCEmain      {
                                        $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($uid), $updateFields);
                                } else {
 
+                                               // Fetches all fields with flexforms and look for files to delete:
+                                       t3lib_div::loadTCA($table);
+                                       foreach($TCA[$table]['columns'] as $fieldName => $cfg)  {
+                                               $conf = $cfg['config'];
+
+                                               switch($conf['type'])   {
+                                                       case 'flex':
+                                                               $flexObj = t3lib_div::makeInstance('t3lib_flexformtools');
+                                                               $flexObj->traverseFlexFormXMLData($table,$fieldName,t3lib_BEfunc::getRecordRaw($table,'uid='.intval($uid)),$this,'deleteRecord_flexFormCallBack');
+                                                       break;
+                                               }
+                                       }
+
                                                // Fetches all fields that holds references to files
                                        $fileFieldArr = $this->extFileFields($table);
                                        if (count($fileFieldArr))       {
@@ -3125,7 +3173,8 @@ class t3lib_TCEmain       {
                                                        $this->log($table,$uid,3,0,100,'Delete: Zero rows in result when trying to read filenames from record which should be deleted');
                                                }
                                        }
-
+                                               
+                                               // Delete the hard way...:
                                        $GLOBALS['TYPO3_DB']->exec_DELETEquery($table, 'uid='.intval($uid));
                                }
 
@@ -3159,6 +3208,28 @@ class t3lib_TCEmain      {
                        } else $this->log($table,$uid,3,0,1,'Attempt to delete record without delete-permissions');
                }
        }
+       
+       /**
+        * Call back function for deleting file relations for flexform fields in records which are being completely deleted.
+        */
+       function deleteRecord_flexFormCallBack($dsArr, $dataValue, $PA, $structurePath, &$pObj) {
+               
+                       // Use reference index object to find files in fields:
+               $refIndexObj = t3lib_div::makeInstance('t3lib_refindex');
+               $files = $refIndexObj->getRelations_procFiles($dataValue, $dsArr['TCEforms']['config'], $PA['uid']);
+               
+                       // Traverse files and delete them:
+               if (is_array($files))   {
+                       foreach($files as $dat) {
+                               if (@is_file($dat['ID_absFile']))       {
+                                       unlink ($dat['ID_absFile']);
+#echo 'DELETE FlexFormFile:'.$dat['ID_absFile'].chr(10);
+                               } else {
+                                       $this->log($table,0,3,0,100,"Delete: Referenced file '".$dat['ID_absFile']."' that was supposed to be deleted together with it's record didn't exist");
+                               }
+                       }
+               }
+       }
 
        /**
         * Used to delete page because it will check for branch below pages and unallowed tables on the page as well.
@@ -3205,10 +3276,12 @@ class t3lib_TCEmain     {
                                if ($table!='pages')    {
                                        $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'pid='.intval($uid).$this->deleteClause($table));
                                        while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres))     {
+                                               $this->deleteVersionsForRecord($table,$row['uid'],$forceHardDelete);
                                                $this->deleteRecord($table,$row['uid'], TRUE, $forceHardDelete);
                                        }
                                }
                        }
+                       $this->deleteVersionsForRecord('pages',$uid,$forceHardDelete);
                        $this->deleteRecord('pages',$uid, TRUE, $forceHardDelete);
                }
        }
@@ -3976,6 +4049,10 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
         */
        function doesRecordExist($table,$id,$perms)     {
                global $TCA;
+               
+               if ($this->bypassAccessCheckForRecords) {
+                       return is_array(t3lib_BEfunc::getRecordRaw($table,'uid='.intval($id),'uid'));
+               }
 
                $res = 0;
                $id = intval($id);
index d46f277..5c3ea8c 100755 (executable)
@@ -250,6 +250,13 @@ class t3lib_userAuthGroup extends t3lib_userAuth {
        function isInWebMount($id,$readPerms='',$exitOnError=0) {
                if (!$GLOBALS['TYPO3_CONF_VARS']['BE']['lockBeUserToDBmounts'] || $this->isAdmin())     return 1;
                $id = intval($id);
+               
+                       // Check if input id is an offline version page in which case we will map id to the online version:
+               $checkRec = t3lib_beFUnc::getRecord('pages',$id,'pid,t3ver_oid');
+               if ($checkRec['pid']==-1)       {
+                       $id = intval($checkRec['t3ver_oid']);
+               }
+               
                if (!$readPerms)        $readPerms = $this->getPagePermsClause(1);
                if ($id>0)      {
                        $wM = $this->returnWebmounts();
@@ -765,6 +772,7 @@ class t3lib_userAuthGroup extends t3lib_userAuth {
         * Checks if an element stage allows access for the user in the current workspace
         * In workspaces 0 (Live) and -1 (Default draft) access is always granted for any stage.
         * Admins are always allowed.
+        * An option for custom workspaces allows members to also edit when the stage is "Review"
         *
         * @param       integer         Stage id from an element: -1,0 = editing, 1 = reviewer, >1 = owner
         * @return      boolean         TRUE if user is allowed access
@@ -774,7 +782,8 @@ class t3lib_userAuthGroup extends t3lib_userAuth {
 
                if ($this->workspace>0) {
                        $stat = $this->checkWorkspaceCurrent();
-                       if (($stage<=0 && $stat['_ACCESS']==='member') ||
+                       $memberStageLimit = $this->workspaceRec['review_stage_edit'] ? 1 : 0;
+                       if (($stage<=$memberStageLimit && $stat['_ACCESS']==='member') ||
                                ($stage<=1 && $stat['_ACCESS']==='reviewer') ||
                                ($stat['_ACCESS']==='owner')) {
                                        return TRUE;    // OK for these criteria
index 6df2ad1..39e9530 100755 (executable)
@@ -233,7 +233,10 @@ $TYPO3_CONF_VARS = Array(
                                'email' => 't3lib/class.t3lib_softrefproc.php:&t3lib_softrefproc',
                                'url' => 't3lib/class.t3lib_softrefproc.php:&t3lib_softrefproc',
                        ),
-                       'softRefParser_GL' => array()   // Global soft reference parsers
+                       'softRefParser_GL' => array(),  // Global soft reference parsers
+                       'cliKeys' => array(     // Arrays of EXT:file and module name pairs. See EXT:lowlevel/ext_localconf.php for example.
+                       ),
+                       'topApps' => array()    // See t3lib_extMgm::addTopApp()
                ),
        ),
        'EXTCONF' => Array (            // Here you may add manually set configuration options for your extensions. Eg. $TYPO3_CONF_VARS['EXTCONF']['my_extension_key']['my_option'] = 'my_value';
index e9bf0e6..673f5c8 100755 (executable)
@@ -221,6 +221,7 @@ CREATE TABLE sys_workspace (
   unpublish_time int(11) DEFAULT '0' NOT NULL,
   freeze tinyint(3) DEFAULT '0' NOT NULL,
   live_edit tinyint(3) DEFAULT '0' NOT NULL,
+  review_stage_edit tinyint(3) DEFAULT '0' NOT NULL,
   vtypes tinyint(3) DEFAULT '0' NOT NULL,
   disable_autocreate tinyint(1) DEFAULT '0' NOT NULL,
   swap_modes tinyint(3) DEFAULT '0' NOT NULL,
@@ -288,6 +289,35 @@ CREATE TABLE sys_refindex (
 );
 
 #
+# Table structure for table ''
+#
+CREATE TABLE sys_refindex_words (
+  wid int(11) DEFAULT '0' NOT NULL,
+  baseword varchar(60) DEFAULT '' NOT NULL,
+  PRIMARY KEY (wid)
+);
+
+#
+# Table structure for table ''
+#
+CREATE TABLE sys_refindex_rel (
+  rid int(11) DEFAULT '0' NOT NULL,
+  wid int(11) DEFAULT '0' NOT NULL,
+  PRIMARY KEY (rid,wid)
+);
+
+
+#
+# Table structure for table ''
+#
+CREATE TABLE sys_refindex_res (
+  rid int(11) DEFAULT '0' NOT NULL,
+  tablename varchar(100) DEFAULT '' NOT NULL,
+  recuid int(11) DEFAULT '0' NOT NULL,
+  PRIMARY KEY (rid)
+);
+
+#
 # Table structure for table 'sys_log'
 #
 CREATE TABLE sys_log (
index a4e9f05..441b388 100755 (executable)
@@ -822,6 +822,13 @@ $TCA['sys_workspace'] = Array (
                                'default' => '0'
                        )
                ),
+               'review_stage_edit' => Array (
+                       'label' => 'Allow members to edit records in "Review" stage',
+                       'config' => Array (
+                               'type' => 'check',
+                               'default' => '0'
+                       )
+               ),
                'disable_autocreate' => Array (
                        'label' => 'Disable auto-versioning when editing',
                        'config' => Array (
@@ -841,7 +848,7 @@ $TCA['sys_workspace'] = Array (
                        )
                ),
                'vtypes' => Array (
-                       'label' => 'Disable Versioning Types (not for admins and owners):',
+                       'label' => 'Disable Versioning Types for members and reviewers:',
                        'config' => Array (
                                'type' => 'check',
                                'items' => Array (
@@ -874,7 +881,7 @@ $TCA['sys_workspace'] = Array (
                ),
        ),
        'types' => Array (
-               '0' => Array('showitem' => 'title,description,--div--;Users,adminusers,members,reviewers,stagechg_notification,--div--;Mountpoints,db_mountpoints,file_mountpoints,--div--;Publishing,publish_time,unpublish_time,--div--;Other,freeze,live_edit,disable_autocreate,swap_modes,vtypes,publish_access')
+               '0' => Array('showitem' => 'title,description,--div--;Users,adminusers,members,reviewers,stagechg_notification,--div--;Mountpoints,db_mountpoints,file_mountpoints,--div--;Publishing,publish_time,unpublish_time,--div--;Other,freeze,live_edit,review_stage_edit,disable_autocreate,swap_modes,vtypes,publish_access')
        )
 );
 
index 9118edc..926c32a 100755 (executable)
@@ -409,11 +409,12 @@ class clickMenu {
         * @param       string          The URL relative to TYPO3_mainDir
         * @param       string          The return_url-parameter
         * @param       boolean         If set, the "hideCM()" will be called
+        * @param       string          If set, gives alternative location to load in (for example top frame or somewhere else)
         * @return      string          JavaScript for an onClick event.
         */
-       function urlRefForCM($url,$retUrl='',$hideCM=1) {
+       function urlRefForCM($url,$retUrl='',$hideCM=1,$overrideLoc='') {
                $loc='top.content'.($this->listFrame && !$this->alwaysContentFrame ?'.list_frame':'');
-               $editOnClick='var docRef=(top.content.list_frame)?top.content.list_frame:'.$loc.'; docRef.location.href=top.TS.PATH_typo3+\''.$url.'\''.
+               $editOnClick= ($overrideLoc ? 'var docRef='.$overrideLoc : 'var docRef=(top.content.list_frame)?top.content.list_frame:'.$loc).'; docRef.location.href=top.TS.PATH_typo3+\''.$url.'\''.
                        ($retUrl?"+'&".$retUrl."='+top.rawurlencode(".$this->frameLocation('docRef.document').')':'').';'.
                        ($hideCM?'return hideCM();':'');
                return $editOnClick;
index 02d8d16..77bb191 100755 (executable)
@@ -215,7 +215,7 @@ class SC_alt_main {
        function launchView(table,uid,bP)       {       //
                var backPath= bP ? bP : "";
                var thePreviewWindow="";
-               thePreviewWindow = window.open(TS.PATH_typo3+"show_item.php?table="+escape(table)+"&uid="+escape(uid),"ShowItem"+TS.uniqueID,"height=300,width=410,status=0,menubar=0,resizable=0,location=0,directories=0,scrollbars=1,toolbar=0");
+               thePreviewWindow = window.open(TS.PATH_typo3+"show_item.php?table="+escape(table)+"&uid="+escape(uid),"ShowItem"+TS.uniqueID,"height=400,width=550,status=0,menubar=0,resizable=0,location=0,directories=0,scrollbars=1,toolbar=0");
                if (thePreviewWindow && thePreviewWindow.focus) {
                        thePreviewWindow.focus();
                }
index f852051..e86f451 100755 (executable)
@@ -103,6 +103,7 @@ class alt_menu_functions {
                $final='';
                $menuCode='';
                $descrCode='';
+               $collection=array();
                $menuCode_sub='';
                $selectItems=array();
                $mIcons=array();
@@ -177,12 +178,19 @@ class alt_menu_functions {
                        $moduleKey = $moduleName.'_tab';
                        $moduleCSSId = 'ID_'.t3lib_div::md5int($moduleName);
 
+                       $collection[$moduleKey] = array(
+                               'moduleName' => $moduleName,
+                               'title' => $LANG->moduleLabels['tabs'][$moduleKey],
+                               'onclick' => 'top.goToModule(\''.$moduleName.'\');',
+                       );
+
                                // Creating image icon
                        $image = @getimagesize($this->mIconFile($LANG->moduleLabels['tabs_images'][$moduleKey],$backPath));
                        $imageCode='';
                        $descr3_title = $LANG->moduleLabels['tabs'][$moduleKey].' ';
                        if ($image)     {
                                $Ifilename = $this->mIconFilename($LANG->moduleLabels['tabs_images'][$moduleKey],$backPath);
+                               $collection[$moduleKey]['icon'] = array($Ifilename, $image[3]); 
                                $imageCode = '<img src="'.$Ifilename.'" '.$image[3].' alt="" />';
                                $descr3_imageCode = '<img src="'.$Ifilename.'" '.$image[3].' title="'.htmlspecialchars($descr3_title).'" alt="" />';
                        } else {
@@ -237,6 +245,7 @@ class alt_menu_functions {
                        $subCode='';
                        if (is_array($moduleInfo['sub']))       {
                                reset($moduleInfo['sub']);
+                               $collection[$moduleKey]['subitems'] = array();
                                $c=0;
                                while(list($subName,$subInfo)=each($moduleInfo['sub'])) {
                                        if ($c==0)              {
@@ -256,12 +265,19 @@ class alt_menu_functions {
                                        $subKey = $moduleName.'_'.$subName.'_tab';
                                        $moduleCSSId = 'ID_'.t3lib_div::md5int($moduleName.'_'.$subName);
 
+                                       $collection[$moduleKey]['subitems'][$subKey] = array(
+                                               'moduleName' => $moduleName.'_'.$subName,
+                                               'title' => $LANG->moduleLabels['tabs'][$subKey],
+                                               'onclick' => 'top.goToModule(\''.$moduleName.'_'.$subName.'\');',
+                                       );
+
                                                // Creating image icon
                                        $image = @getimagesize($this->mIconFile($LANG->moduleLabels['tabs_images'][$subKey],$backPath));
                                        $imageCode='';
                                        $descr3_title = $LANG->moduleLabels['tabs'][$subKey].': '.$LANG->moduleLabels['labels'][$subKey.'label'];
                                        if ($image)     {
                                                $Ifilename = $this->mIconFilename($LANG->moduleLabels['tabs_images'][$subKey],$backPath);
+                                               $collection[$moduleKey]['subitems'][$subKey]['icon'] = array($Ifilename, $image[3]); 
                                                $imageCode = '<img src="'.$Ifilename.'" '.$image[3].' title="'.htmlspecialchars($LANG->moduleLabels['labels'][$subKey.'label']).'" alt="" />';
                                                $descr3_imageCode = '<img src="'.$Ifilename.'" '.$image[3].' title="'.htmlspecialchars($descr3_title).'" alt="" />';
                                        } else {
@@ -459,6 +475,11 @@ class alt_menu_functions {
                        $final = chr(10).implode(chr(10),$mJScmds).chr(10);
                }
 
+                       // Output for TOPMENU BAR drop downs (but basically this is an array with which you can do many interesting things...)
+               if ($descr==5)  {
+                       $final = $collection;
+               }
+
                        // Return result:
                return $final;
        }
@@ -569,6 +590,7 @@ class alt_menu_functions {
                if ($TYPO3_CONF_VARS['EXT']['extCache'])        {
                        $title = $LANG->sL('LLL:EXT:lang/locallang_core.php:rm.clearCache_allTypo3Conf');
                        $functions[]=array(
+                               'id' => 'temp_CACHED',
                                'title' => $title,
                                'href' => $backPath.'tce_db.php?vC='.$BE_USER->veriCode().'&redirect='.rawurlencode(t3lib_div::getIndpEnv('TYPO3_REQUEST_SCRIPT')).'&cacheCmd=temp_CACHED',
                                'icon' => '<img'.t3lib_iconWorks::skinImg($backPath,'gfx/clear_cache_files_in_typo3c.gif','width="21" height="18"').' title="'.htmlspecialchars($title).'" alt="" />'
@@ -578,6 +600,7 @@ class alt_menu_functions {
                        // Clear all page cache
                $title = $LANG->sL('LLL:EXT:lang/locallang_core.php:rm.clearCache_all');
                $functions[]=array(
+                       'id' => 'all',
                        'title' => $title,
                        'href' => $backPath.'tce_db.php?vC='.$BE_USER->veriCode().'&redirect='.rawurlencode(t3lib_div::getIndpEnv('TYPO3_REQUEST_SCRIPT')).'&cacheCmd=all',
                        'icon' => '<img'.t3lib_iconWorks::skinImg($backPath,'gfx/clear_all_cache.gif','width="21" height="18"').' title="'.htmlspecialchars($title).'" alt="" />'
index 27b512a..28de2c3 100755 (executable)
@@ -157,6 +157,38 @@ $CLIENT = t3lib_div::clientInfo();                                 // $CLIENT includes information about the
 $PARSETIME_START = t3lib_div::milliseconds();          // Is set to the system time in milliseconds. This could be used to output script parsetime in the end of the script
 
 
+
+
+// *************************
+// CLI dispatch processing
+// *************************
+if (defined('TYPO3_cliMode') && TYPO3_cliMode´&& basename(PATH_thisScript)=='cli_dispatch.phpsh')     {
+               // First, take out the first argument (cli-key)
+       $temp_cliScriptPath = array_shift($_SERVER['argv']);
+       $temp_cliKey = array_shift($_SERVER['argv']);
+       array_unshift($_SERVER['argv'],$temp_cliScriptPath);
+
+               // If cli_key was found in configuration, then set up the cliInclude path and module name:
+       if ($temp_cliKey)       {
+               if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['GLOBAL']['cliKeys'][$temp_cliKey]))        {
+                       define('TYPO3_cliInclude', t3lib_div::getFileAbsFileName($TYPO3_CONF_VARS['SC_OPTIONS']['GLOBAL']['cliKeys'][$temp_cliKey][0]));
+                       $MCONF['name'] = $TYPO3_CONF_VARS['SC_OPTIONS']['GLOBAL']['cliKeys'][$temp_cliKey][1];
+               } else {
+                       echo "The supplied 'cliKey' was not valid. Please use one of the available from this list:\n\n";
+                       print_r(array_keys($TYPO3_CONF_VARS['SC_OPTIONS']['GLOBAL']['cliKeys']));
+                       echo "\n";
+                       exit;
+               }
+       } else {
+               echo "Please supply a 'cliKey' as first argument. The following are available:\n\n";
+               print_r($TYPO3_CONF_VARS['SC_OPTIONS']['GLOBAL']['cliKeys']);
+               echo "\n";
+               exit;
+       }
+}
+
+
+
 // *********************
 // Libraries included
 // *********************
@@ -279,6 +311,8 @@ if (TYPO3_extTableDef_script)       {
        include (PATH_typo3conf.TYPO3_extTableDef_script);
 }
 
+
+
 // *******************************
 // BackEnd User authentication
 // *******************************
index 8612471..7d0e468 100755 (executable)
@@ -1684,8 +1684,8 @@ class SC_mod_user_ws_index extends t3lib_SCbase {
                                else {
                                        $tag0 = $tag1 = '';
                                }
-                               $content_array[] = t3lib_iconWorks::getIconImage('be_users', $uid, $GLOBALS['BACK_PATH'], ' align="middle" alt="UID: ' . $uid . '"') .
-                                                                       $tag0 . $user['username'] . $tag1;
+                               $content_array[] = $this->doc->wrapClickMenuOnIcon(t3lib_iconWorks::getIconImage('be_users', $uid, $GLOBALS['BACK_PATH'], ' align="middle" alt="UID: ' . $uid . '"'), 'be_users', $uid, 2).
+                                       $tag0 . $user['username'] . $tag1;
                        }
                }
                return implode('<br />', $content_array);
@@ -1723,7 +1723,7 @@ class SC_mod_user_ws_index extends t3lib_SCbase {
                                                else {
                                                        $tag0 = $tag1 = '';
                                                }
-                                               $content_array[] = t3lib_iconWorks::getIconImage($table, $this->be_user_Array[$id], $GLOBALS['BACK_PATH'], ' align="middle" alt="UID: ' . $id . '"') .
+                                               $content_array[] = $this->doc->wrapClickMenuOnIcon(t3lib_iconWorks::getIconImage($table, $this->be_user_Array[$id], $GLOBALS['BACK_PATH'], ' align="middle" alt="UID: ' . $id . '"'), $table, $id, 2) .
                                                                                        $tag0 . $this->be_user_Array_full[$id]['username'] . $tag1;
                                        }
                                        else {
@@ -1731,7 +1731,7 @@ class SC_mod_user_ws_index extends t3lib_SCbase {
                                                if (false === $groups) {
                                                        $groups = t3lib_BEfunc::getGroupNames();
                                                }
-                                               $content_array[] = t3lib_iconWorks::getIconImage($table, $groups[$id], $GLOBALS['BACK_PATH'], ' align="middle" alt="UID: ' . $id . '"') .
+                                               $content_array[] = $this->doc->wrapClickMenuOnIcon(t3lib_iconWorks::getIconImage($table, $groups[$id], $GLOBALS['BACK_PATH'], ' align="middle" alt="UID: ' . $id . '"'), $table, $id, 2) .
                                                                                        $groups[$id]['title'];
                                        }
                                }
index 491fdda..3dda394 100755 (executable)
@@ -20,7 +20,7 @@ $EM_CONF[$_EXTKEY] = Array (
        'loadOrder' => '',
        'TYPO3_version' => '3.7.0-',
        'PHP_version' => '0.0.1-0.0.1',
-       'module' => 'mod',
+       'module' => '',
        'state' => 'stable',
        'internal' => 0,
        'uploadfolder' => 0,
index 0950744..c357d8c 100755 (executable)
@@ -1,11 +1,11 @@
 <?php
-define('TYPO3_MOD_PATH', 'sysext/belog/mod/');
-$BACK_PATH='../../../';
+#define('TYPO3_MOD_PATH', 'sysext/belog/mod/');
+#$BACK_PATH='../../../';
 
 $MLANG['default']['tabs_images']['tab'] = 'log.gif';
 $MLANG['default']['ll_ref']='LLL:EXT:belog/mod/locallang_mod.php';
 
-$MCONF['script']='index.php';
+$MCONF['script']='_DISPATCH';
 $MCONF['access']='admin';
 $MCONF['name']='tools_log';
 ?>
\ No newline at end of file
index 74b670b..6fba760 100755 (executable)
  */
 
 
-unset($MCONF);
-require ('conf.php');
-require ($BACK_PATH.'init.php');
-require ($BACK_PATH.'template.php');
+#unset($MCONF);
+#require ('conf.php');
+#require ($BACK_PATH.'init.php');
+#require ($BACK_PATH.'template.php');
 $LANG->includeLLFile('EXT:belog/mod/locallang.php');
 require_once (PATH_t3lib.'class.t3lib_bedisplaylog.php');
 require_once (PATH_t3lib.'class.t3lib_pagetree.php');
index a14b46e..ad3a0c4 100755 (executable)
@@ -20,7 +20,7 @@ $EM_CONF[$_EXTKEY] = Array (
        'loadOrder' => '',
        'TYPO3_version' => '3.7.0-',
        'PHP_version' => '0.0.1-0.0.1',
-       'module' => 'mod',
+       'module' => '',
        'state' => 'stable',
        'internal' => 0,
        'uploadfolder' => 0,
index 7e6d00f..c846648 100755 (executable)
@@ -1,5 +1,12 @@
 <?php
 if (!defined ('TYPO3_MODE'))   die ('Access denied.');
 
-if (TYPO3_MODE=='BE')  t3lib_extMgm::addModule('tools','beuser','top',t3lib_extMgm::extPath($_EXTKEY).'mod/');
+if (TYPO3_MODE=='BE')  {
+       t3lib_extMgm::addModule('tools','beuser','top',t3lib_extMgm::extPath($_EXTKEY).'mod/');
+
+       $GLOBALS['TBE_MODULES_EXT']['xMOD_alt_clickmenu']['extendCMclasses'][] = array(
+               'name' => 'tx_beuser',
+               'path' => t3lib_extMgm::extPath($_EXTKEY).'class.tx_beuser.php'
+       );
+}
 ?>
\ No newline at end of file
index d76ebaa..8ba923d 100755 (executable)
@@ -1,11 +1,11 @@
 <?php
-define('TYPO3_MOD_PATH', 'sysext/beuser/mod/');
-$BACK_PATH='../../../';
+#define('TYPO3_MOD_PATH', 'sysext/beuser/mod/');
+#$BACK_PATH='../../../';
 
 $MLANG['default']['tabs_images']['tab'] = 'beuser.gif';
 $MLANG['default']['ll_ref']='LLL:EXT:beuser/mod/locallang_mod.php';
 
-$MCONF['script']='index.php';
+$MCONF['script']='_DISPATCH';
 $MCONF['access']='admin';
 $MCONF['name']='tools_beuser';
 $MCONF['workspaces']='online';
index 53e8f99..1f57a05 100755 (executable)
  *
  */
 
-unset($MCONF);
-require ('conf.php');
-require ($BACK_PATH.'init.php');
-require ($BACK_PATH.'template.php');
+#unset($MCONF);
+#require ('conf.php');
+#require ($BACK_PATH.'init.php');
+#require ($BACK_PATH.'template.php');
 require_once (PATH_t3lib.'class.t3lib_basicfilefunc.php');
 require_once (PATH_t3lib.'class.t3lib_browsetree.php');
 require_once (PATH_t3lib.'class.t3lib_foldertree.php');
@@ -1268,7 +1268,7 @@ class SC_mod_tools_be_user_index {
                        $outTable = '<table border="0" cellpadding="1" cellspacing="1"><tr class="bgColor5"><td>'.t3lib_iconWorks::getIconImage('be_users',$tempBE_USER->user,$GLOBALS['BACK_PATH'],'class="absmiddle" title="'.$tempBE_USER->user['uid'].'"').$tempBE_USER->user['username'].'</td>';
                        $outTable.= '<td>'.$tempBE_USER->user['realName'].($tempBE_USER->user['email'] ? ', <a href="mailto:'.$tempBE_USER->user['email'].'">'.$tempBE_USER->user['email'].'</a>' : '').'</td>';
                        $outTable.= '<td>'.$this->elementLinks('be_users',$tempBE_USER->user).'</td></tr></table>';
-                       $outTable.= '<strong><a href="index.php">&lt; Back to overview</a></strong><br />';
+                       $outTable.= '<strong><a href="'.htmlspecialchars($MCONF['_']).'">&lt; Back to overview</a></strong><br />';
 
                        $outTable.= '<br /><table border="0" cellpadding="2" cellspacing="1">'.implode('',$lines).'</table>';
                        $content.= $this->doc->section('User info',$outTable,0,1);
@@ -1400,7 +1400,7 @@ class SC_mod_tools_be_user_index {
         * @return      [type]          ...
         */
        function linkUser($str,$rec)    {
-               return '<a href="index.php?be_user_uid='.$rec['uid'].'">'.$str.'</a>';
+               return '<a href="'.htmlspecialchars($MCONF['_']).'&be_user_uid='.$rec['uid'].'">'.$str.'</a>';
        }
 
        /**
index fc92940..63c4286 100755 (executable)
        /**
         * Looking for a ADMCMD_prev code, looks it up if found and returns configuration data.
         * Background: From the backend a request to the frontend to show a page, possibly with workspace preview can be "recorded" and associated with a keyword. When the frontend is requested with this keyword the associated request parameters are restored from the database AND the backend user is loaded - only for that request.
-        * The main point is that a special URL valid for a limited time, eg. http://localhost/typo3site/?ADMCMD_prev=035d9bf938bd23cb657735f68a8cedbf will open up for a preview that doesn't require login. Thus its useful for sending an email.
+        * The main point is that a special URL valid for a limited time, eg. http://localhost/typo3site/index.php?ADMCMD_prev=035d9bf938bd23cb657735f68a8cedbf will open up for a preview that doesn't require login. Thus its useful for sending an email.
         * This can also be used to generate previews of hidden pages, start/endtimes, usergroups and those other settings from the Admin Panel - just not implemented yet.
         *
         * @return      array           Preview configuration array from sys_preview record.
                                // - Make sure to remove fe/be cookies (temporarily); BE already done in ADMCMD_preview_postInit()
                        if (is_array($previewData))     {
                                if (!count(t3lib_div::_POST())) {
-                                       if (t3lib_div::getIndpEnv('TYPO3_SITE_URL').'?ADMCMD_prev='.$inputCode === t3lib_div::getIndpEnv('TYPO3_REQUEST_URL'))  {
+                                       if (t3lib_div::getIndpEnv('TYPO3_SITE_URL').'index.php?ADMCMD_prev='.$inputCode === t3lib_div::getIndpEnv('TYPO3_REQUEST_URL')) {
 
                                                        // Unserialize configuration:
                                                $previewConfig = unserialize($previewData['config']);
 
                                                        // Return preview keyword configuration:
                                                return $previewConfig;
-                                       } else die(htmlspecialchars('Request URL did not match "'.t3lib_div::getIndpEnv('TYPO3_SITE_URL').'?ADMCMD_prev='.$inputCode.'"'));     // This check is to prevent people from setting additional GET vars via realurl or other URL path based ways of passing parameters.
+                                       } else die(htmlspecialchars('Request URL did not match "'.t3lib_div::getIndpEnv('TYPO3_SITE_URL').'index.php?ADMCMD_prev='.$inputCode.'"'));    // This check is to prevent people from setting additional GET vars via realurl or other URL path based ways of passing parameters.
                                } else die('POST requests are incompatible with keyword preview.');
                        } else die('ADMCMD command could not be executed! (No keyword configuration found)');
                }
index d9a6834..931caef 100755 (executable)
 
 
 
-
-
-
-
-
-
-
-
-
-
-
 /**
  * Base class for frontend plugins
  * Most modern frontend plugins are extension classes of this one.
index 62a9d0d..d5227f0 100755 (executable)
@@ -1,6 +1,6 @@
 <?php
-define('TYPO3_MOD_PATH', 'sysext/impexp/app/');
-$BACK_PATH='../../../';
+#define('TYPO3_MOD_PATH', 'sysext/impexp/app/');
+#$BACK_PATH='../../../';
 
 $MCONF['name']='xMOD_tximpexp';        // xMOD_[modulename][optional: '_something']
 ?>
index 323b663..59c2b3b 100755 (executable)
  */
 
 
-unset($MCONF);
-require ('conf.php');
-require ($BACK_PATH.'init.php');
-require ($BACK_PATH.'template.php');
+#unset($MCONF);
+#require ('conf.php');
+#require ($BACK_PATH.'init.php');
+#require ($BACK_PATH.'template.php');
 $LANG->includeLLFile('EXT:impexp/app/locallang.php');
 require_once (PATH_t3lib.'class.t3lib_scbase.php');
 require_once (t3lib_extMgm::extPath('impexp').'class.tx_impexp.php');
@@ -309,7 +309,7 @@ class SC_mod_tools_log_index extends t3lib_SCbase {
                        script_ended = 1;
                        if (top.fsMod) top.fsMod.recentIds["web"] = '.intval($this->id).';
                ');
-               $this->doc->form = '<form action="index.php" method="post" enctype="'.$GLOBALS['TYPO3_CONF_VARS']['SYS']['form_enctype'].'"><input type="hidden" name="id" value="'.$this->id.'" />';
+               $this->doc->form = '<form action="'.htmlspecialchars($GLOBALS['MCONF']['_']).'" method="post" enctype="'.$GLOBALS['TYPO3_CONF_VARS']['SYS']['form_enctype'].'"><input type="hidden" name="id" value="'.$this->id.'" />';
 
                $this->content.= $this->doc->startPage($LANG->getLL('title'));
                $this->content.= $this->doc->header($LANG->getLL('title'));
@@ -1257,7 +1257,7 @@ class SC_mod_tools_log_index extends t3lib_SCbase {
                                                unset($passParams['import_mode']);
                                                unset($passParams['import_file']);
 
-                                               $thisScriptUrl = t3lib_div::getIndpEnv('REQUEST_URI').'?id='.$this->id.t3lib_div::implodeArrayForUrl('tx_impexp',$passParams);
+                                               $thisScriptUrl = t3lib_div::getIndpEnv('REQUEST_URI').'?M=xMOD_tximpexp&id='.$this->id.t3lib_div::implodeArrayForUrl('tx_impexp',$passParams);
                                                $emURL = $this->doc->backPath.'mod/tools/em/index.php?CMD[requestInstallExtensions]='.implode(',',$extKeysToInstall).'&returnUrl='.rawurlencode($thisScriptUrl);
                                                $extensionInstallationMessage = 'Before you can install this T3D file you need to install the extensions "'.implode('", "',$extKeysToInstall).'". Clicking Import will first take you to the Extension Manager so these dependencies can be resolved.';
                                        } 
index 34c947f..a63ade0 100755 (executable)
@@ -84,7 +84,7 @@ class tx_impexp_clickmenu {
 
                        $LL = $this->includeLL();
 
-                       $url = t3lib_extMgm::extRelPath('impexp').'app/index.php?tx_impexp[action]=export';
+                       $url = 'mod.php?M=xMOD_tximpexp&tx_impexp[action]=export';
                        if ($table=='pages')    {
                                $url.='&tx_impexp[pagetree][id]='.$uid;
                                $url.='&tx_impexp[pagetree][levels]=0';
@@ -101,7 +101,7 @@ class tx_impexp_clickmenu {
                        );
 
                        if ($table=='pages')    {
-                               $url = t3lib_extMgm::extRelPath('impexp').'app/index.php?id='.$uid.'&table='.$table.'&tx_impexp[action]=import';
+                               $url = 'mod.php?M=xMOD_tximpexp&id='.$uid.'&table='.$table.'&tx_impexp[action]=import';
                                $localItems[] = $backRef->linkItem(
                                        $GLOBALS['LANG']->makeEntities($GLOBALS['LANG']->getLLL('import',$LL)),
                                        $backRef->excludeIcon('<img'.t3lib_iconWorks::skinImg($backRef->backPath,t3lib_extMgm::extRelPath('impexp').'import.gif','width="18" height="16"').' alt="" />'),
index aff48da..046c7d0 100755 (executable)
@@ -20,7 +20,7 @@ $EM_CONF[$_EXTKEY] = Array (
        'loadOrder' => '',
        'TYPO3_version' => '3.7.0-',
        'PHP_version' => '0.0.1-0.0.1',
-       'module' => 'app',
+       'module' => '',
        'state' => 'beta',
        'internal' => 0,
        'uploadfolder' => 0,
index 0bdd97c..426984d 100755 (executable)
@@ -6,6 +6,7 @@ if (TYPO3_MODE=='BE')   {
                'name' => 'tx_impexp_clickmenu',
                'path' => t3lib_extMgm::extPath($_EXTKEY).'class.tx_impexp_clickmenu.php'
        );
+       t3lib_extMgm::addModulePath('xMOD_tximpexp',t3lib_extMgm::extPath($_EXTKEY).'app/');
 
        t3lib_extMgm::insertModuleFunction(
                'user_task',
index 41a1490..8c54a8f 100755 (executable)
@@ -34,6 +34,7 @@
                        <label index="_unpublish_time.seeAlso">sys_workspace:publish_time,sys_workspace:swap_modes</label>
                        <label index="freeze.description">If set, no editing is allowed inside workspace.</label>
                        <label index="live_edit.description">If set, records from tables where versioning is not enabled can still be edited &quot;live&quot; inside the workspace.</label>
+                       <label index="review_stage_edit.description">If set, records raised to Review stage can still be edited by workspace members. This allows late changes to be made while waiting for the review. Only when the stage is raised to Publish the record is completely locked for editing for members and reviewers.</label>
                        <label index="disable_autocreate.description">If set, records are not automatically created as new versions when edited in the workspace. A new version must be manually created first.</label>
                        <label index="swap_modes.description">Defines modes for publishing as a &quot;swap&quot; action where the online content is moved into the workspace in exchange for the workspace content.</label>
                        <label index="swap_modes.details">By default swapping is allowed. Thereby two versions can be published &quot;in and out&quot; of a workspace multiple times, always substituting each other. If this is not desired, it can be disabled with &quot;Disable Swap-Into-Workspace&quot;.
index ab4fb31..8ee7e37 100644 (file)
@@ -48,8 +48,9 @@
 
 require_once(PATH_t3lib.'class.t3lib_extobjbase.php');
 require_once(t3lib_extMgm::extPath('lowlevel').'class.tx_lowlevel_cleaner_core.php');
+require_once (PATH_t3lib.'class.t3lib_refindex.php');
 
-
+die('Not developed...');
 
 
 
@@ -115,6 +116,7 @@ class tx_lowlevel_cleaner extends t3lib_extobjbase {
                        $content.= $this->pObj->doc->spacer(5);
                        $content.= $this->createMenu().'<hr/>';
                        $content.= $this->moduleContent();
+                       $content.= $this->quickDBlookUp();
                } else {
                        $content.= $this->pObj->doc->spacer(5);
                        $content.= 'Only access for admin users, sorry.';
@@ -180,6 +182,60 @@ debug($contentTreeData);
 */
                return $output;
        }
+       
+       function quickDBlookUp()        {
+               $output = 'Enter [table]:[uid]:[fieldlist (optional)] <input name="table_uid" value="'.htmlspecialchars(t3lib_div::_POST('table_uid')).'" />';
+               $output.='<input type="submit" name="_" value="REFRESH" /><br/>';
+               
+                       // Show record:
+               if (t3lib_div::_POST('table_uid'))      {
+                       list($table,$uid,$fieldName) = t3lib_div::trimExplode(':',t3lib_div::_POST('table_uid'),1);
+                       if ($GLOBALS['TCA'][$table])    {
+                               $rec = t3lib_BEfunc::getRecordRaw($table,'uid='.intval($uid),$fieldName?$fieldName:'*');
+
+                               if (count($rec))        {
+                                       if (t3lib_div::_POST('_EDIT'))  {
+                                               $output.='<hr/>Edit:<br/><br/>';
+                                               foreach($rec as $field => $value)       {
+                                                       $output.= htmlspecialchars($field).'<br/><input name="record['.$table.']['.$uid.']['.$field.']" value="'.htmlspecialchars($value).'" /><br/>';
+                                               }
+                                               $output.='<input type="submit" name="_SAVE" value="SAVE" />';
+                                       } elseif (t3lib_div::_POST('_SAVE'))    {
+                                               $incomingData = t3lib_div::_POST('record');
+                                               $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table,'uid='.intval($uid),$incomingData[$table][$uid]);
+                                               $output.='<br/>Updated '.$table.':'.$uid.'...';
+                                               $this->updateRefIndex($table,$uid);
+                                       } else if (t3lib_div::_POST('_DELETE')) {
+                                               $GLOBALS['TYPO3_DB']->exec_DELETEquery($table,'uid='.intval($uid));
+                                               $output.='<br/>Deleted '.$table.':'.$uid.'...';
+                                               $this->updateRefIndex($table,$uid);
+                                       } else {
+                                               $output.='<input type="submit" name="_EDIT" value="EDIT" />';
+                                               $output.='<input type="submit" name="_DELETE" value="DELETE" onclick="return confirm(\'Are you sure you wish to delete?\');" />';
+                                               $output.=t3lib_div::view_array($rec);
+                                               $output.=md5(implode($rec));
+                                       }
+                               } else {
+                                       $output.='No record existed!';
+                               }
+                       }
+               }
+                               
+               return $output;
+       }
+
+
+       /**
+        * Update Reference Index (sys_refindex) for a record
+        *
+        * @param       string          Table name
+        * @param       integer         Record UID
+        * @return      void
+        */
+       function updateRefIndex($table,$id)     {
+               $refIndexObj = t3lib_div::makeInstance('t3lib_refindex');
+               $result = $refIndexObj->updateRefIndexTable($table,$id);
+       }
 }
 
 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/lowlevel/class.tx_lowlevel_cleaner.php'])     {
index f89f811..7e14474 100644 (file)
  *
  *
  *
- *   71: class tx_lowlevel_cleaner_core
+ *   71: class tx_lowlevel_cleaner_core extends t3lib_cli
+ *   88:     function tx_lowlevel_cleaner_core()
  *
- *              SECTION: Analyse functions
- *   94:     function missing_files_analyze()
- *  161:     function missing_relations_analyze($filter='')
- *  258:     function double_files_analyze()
- *  348:     function RTEmagic_files_analyze()
- *  437:     function clean_lost_files_analyze()
- *  525:     function orphan_records_analyze()
+ *              SECTION: CLI functionality
+ *  134:     function cli_main($argv)
+ *  193:     function cli_referenceIndexCheck()
+ *  228:     function cli_noExecutionCheck($matchString)
+ *  251:     function cli_printInfo($header,$res)
  *
- *              SECTION: Helper functions
- *  572:     function html_printInfo($header,$res,$silent=FALSE,$detailLevel=0)
+ *              SECTION: Page tree traversal
+ *  331:     function genTree($rootID,$depth=1000,$echoLevel=0,$callBack='')
+ *  369:     function genTree_traverse($rootID,$depth,$echoLevel=0,$callBack='',$versionSwapmode='',$rootIsVersion=0,$accumulatedPath='')
  *
- *              SECTION: CLI functionality
- *  642:     function cli_main($argv)
- *  693:     function cli_printInfo($header,$res,$silent=FALSE,$detailLevel=0)
+ *              SECTION: Helper functions
+ *  554:     function infoStr($rec)
  *
- * TOTAL FUNCTIONS: 9
+ * TOTAL FUNCTIONS: 8
  * (This index is automatically created/updated by the extension "extdeveval")
  *
  */
 
 
 require_once(PATH_t3lib.'class.t3lib_admin.php');
+require_once(PATH_t3lib.'class.t3lib_cli.php');
 
 
 
@@ -68,515 +68,205 @@ require_once(PATH_t3lib.'class.t3lib_admin.php');
  * @package TYPO3
  * @subpackage tx_lowlevel
  */
-class tx_lowlevel_cleaner_core {
-
-       var $label_infoString = 'The list of records is organized as [table]:[uid]:[field]:[flexpointer]:[softref_key]';
-
-
-
-
+class tx_lowlevel_cleaner_core extends t3lib_cli {
 
+       var $genTree_traverseDeleted = TRUE;
+       var $genTree_traverseVersions = TRUE;
 
 
 
+       var $label_infoString = 'The list of records is organized as [table]:[uid]:[field]:[flexpointer]:[softref_key]';
+       var $pagetreePlugins = array();
+       var $cleanerModules = array();
 
-       /**************************
-        *
-        * Analyse functions
-        *
-        *************************/
 
        /**
-        * Find missing files
+        * Constructor
         *
-        * @return      array
+        * @return      void
         */
-       function missing_files_analyze() {
-               global $TYPO3_DB;
-
-               $listExplain = ' Shows the relative filename of missing file as header and under a list of record fields in which the references are found. '.$this->label_infoString;
-
-                       // Initialize result array:
-               $resultArray = array(
-                       'message' => '
-Objective: Find all file references from non-deleted records pointing to a missing (non-existing) file.
-
-Assumptions:
-- a perfect integrity of the reference index table (always update the reference index table before using this tool!)
-- relevant soft reference parsers applied everywhere file references are used inline
-
-Files may be missing for these reasons (except software bugs):
-- someone manually deleted the file inside fileadmin/ or another user maintained folder. If the reference was a soft reference (opposite to a TCEmain managed file relation from "group" type fields), technically it is not an error although it might be a mistake that someone did so.
-- someone manually deleted the file inside the uploads/ folder (typically containing managed files) which is an error since no user interaction should take place there.
-
-NOTICE: Uses the Reference Index Table (sys_refindex) for analysis. Update it before use!',
-                       'headers' => array(
-                               'managedFilesMissing' => array('List of missing files managed by TCEmain', $listExplain, 3),
-                               'softrefFilesMissing' => array('List of missing files registered as a soft reference', $listExplain, 3),
-                               'warnings' => array('Warnings, if any','',2)
-                       ),
-                       'managedFilesMissing' => array(),
-                       'softrefFilesMissing' => array(),
-                       'warnings' => array()
-               );
-
-                       // Select all files in the reference table
-               $recs = $TYPO3_DB->exec_SELECTgetRows(
-                       '*',
-                       'sys_refindex',
-                       'ref_table='.$TYPO3_DB->fullQuoteStr('_FILE', 'sys_refindex').
-                               ' AND deleted=0'        // Check only for records which are not deleted (we don't care about missing files in deleted-flagged records)
-               );
-
-                       // Traverse the files and put into a large table:
-               if (is_array($recs)) {
-                       foreach($recs as $rec)  {
-
-                                       // Compile info string for location of reference:
-                               $infoString = $rec['tablename'].':'.$rec['recuid'].':'.$rec['field'].':'.$rec['flexpointer'].':'.$rec['softref_key'];
-
-                                       // Handle missing file:
-                               if (!@is_file(PATH_site.$rec['ref_string']))    {
-
-                                       if ((string)$rec['softref_key']=='')    {
-                                               $resultArrayIndex = 'managedFilesMissing';
-                                       } else {
-                                               $resultArrayIndex = 'softrefFilesMissing';
-                                       }
-
-                                       $resultArray[$resultArrayIndex][$rec['ref_string']][$rec['hash']] = $infoString;
-                               }
-                       }
-               }
-
-               return $resultArray;
+       function tx_lowlevel_cleaner_core()     {
+
+                       // Running parent class constructor
+               parent::t3lib_cli();
+
+               $this->cleanerModules = (array)$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['lowlevel']['cleanerModules'];
+
+                       // Adding options to help archive:
+               $this->cli_options[] = array('-r', 'Execute this tool, otherwise help is shown');
+               $this->cli_options[] = array('-v level', 'Verbosity level 0-3', "The value of level can be:\n  0 = all output\n  1 = info and greater (default)\n  2 = warnings and greater\n  3 = errors");
+               $this->cli_options[] = array('--refindex mode', 'Mode for reference index handling for operations that require a clean reference index ("update"/"ignore")', 'Options are "check" (default), "update" and "ignore". By default, the reference index is checked before running analysis that require a clean index. If the check fails, the analysis is not run. You can choose to bypass this completely (using value "ignore") or ask to have the index updated right away before the analysis (using value "update")');
+               $this->cli_options[] = array('--AUTOFIX', 'Repairs errors that can be automatically fixed.', 'Only add this option after having run the test without it so you know what will happen when you add this option!');
+               $this->cli_options[] = array('--dryrun', 'With --AUTOFIX it will only simulate a repair process','You may like to use this to see what the --AUTOFIX option will be doing. It will output the whole process like if a fix really occurred but nothing is in fact happening');
+               $this->cli_options[] = array('--YES', 'Implicit YES to all questions','Use this with EXTREME care. The option "-i" is not affected by this option.');
+               $this->cli_options[] = array('-i', 'Interactive','Will ask you before running the AUTOFIX on each element.');
+               $this->cli_options[] = array('--filterRegex expr', 'Define an expression for preg_match() that must match the element ID in order to auto repair it','The element ID is the string in quotation marks when the text \'Cleaning ... in "ELEMENT ID"\'. "expr" is the expression for preg_match(). To match for example "Nature3.JPG" and "Holiday3.JPG" you can use "/.*3.JPG/". To match for example "Image.jpg" and "Image.JPG" you can use "/.*.jpg/i". Try a --dryrun first to see what the matches are!');
+               $this->cli_options[] = array('--showhowto', 'Displays HOWTO file for cleaner script.');
+
+                       // Setting help texts:
+               $this->cli_help['name'] = 'lowlevel_cleaner -- Analysis and clean-up tools for TYPO3 installations';
+               $this->cli_help['synopsis'] = 'toolkey ###OPTIONS###';
+               $this->cli_help['description'] = "Dispatches to various analysis and clean-up tools which can plug into the API of this script. Typically you can run tests that will take longer than the usual max execution time of PHP. Such tasks could be checking for orphan records in the page tree or flushing all published versions in the system. For the complete list of options, please explore each of the 'toolkey' keywords below:\n\n  ".implode("\n  ",array_keys($this->cleanerModules));
+               $this->cli_help['examples'] = "/.../cli_dispatch.phpsh lowlevel_cleaner missing_files -s -r\nThis will show you missing files in the TYPO3 system and only report back if errors were found.";
+               $this->cli_help['author'] = "Kasper Skaarhoej, (c) 2006";
        }
 
-       /**
-        * Missing relations to database records
-        *
-        * @param       string          Filter selection, options: "softref", "managed", "" (all)
-        * @return      array
-        */
-       function missing_relations_analyze($filter='') {
-               global $TYPO3_DB;
-
-               $listExplain = ' Shows the missing record as header and underneath a list of record fields in which the references are found. '.$this->label_infoString;
-
-                       // Initialize result array:
-               $resultArray = array(
-                       'message' => '
-Objective: Find all record references pointing to a missing (non-existing or deleted-flagged) record.
-Assumptions:
-- a perfect integrity of the reference index table (always update the reference index table before using this tool!)
-- all database references to check are integers greater than zero
-- does not check if a referenced record is inside an offline branch, another workspace etc. which could make the reference useless in reality or otherwise question integrity
-Records may be missing for these reasons (except software bugs):
-- someone deleted the record which is technically not an error although it might be a mistake that someone did so.
-- after flushing published versions and/or deleted-flagged records a number of new missing references might appear; those were pointing to records just flushed.
-NOTICE: Uses the Reference Index Table (sys_refindex) for analysis. Update it before use!',
-                       'headers' => array(
-                               'offlineVersionRecords' => array('Offline version records','These records are offline versions having a pid=-1 and references should never occur directly to their uids.'.$listExplain,3),
-                               'deletedRecords' => array('Deleted-flagged records','These records are deleted with a flag but references are still pointing at them. Keeping the references is useful if you undelete the referenced records later, otherwise the references are lost completely when the deleted records are flushed at some point.'.$listExplain,2),
-                               'nonExistingRecords' => array('Non-existing records to which there are references','These references can safely be removed since there is no record found in the database at all.'.$listExplain,3),     // 3 = error
-                               'uniqueReferencesToTables' => array('Unique references to various tables','For each listed table, this shows how many different records had references pointing to them. More references to the same record counts only 1, hence it is the number of unique referenced records you see. The count includes both valid and invalid references.',1), // 1 = info
-                               'warnings' => array('Warnings picked up','',2)          // 2 = warning
-                       ),
-                       'offlineVersionRecords' => array(),
-                       'deletedRecords' => array(),
-                       'nonExistingRecords' => array(),
-                       'uniqueReferencesToTables' => array(),
-                       'warnings' => array()
-               );
 
-                       // Create clause to filter by:
-               $filterClause = '';
-               if ($filter==='softref') {
-                       $filterClause = ' AND softref_key!='.$TYPO3_DB->fullQuoteStr('', 'sys_refindex');
-               }
-               if ($filter==='managed') {
-                       $filterClause = ' AND softref_key='.$TYPO3_DB->fullQuoteStr('', 'sys_refindex');
-               }
 
-                       // Select all files in the reference table not found by a soft reference parser (thus TCA configured)
-               $recs = $TYPO3_DB->exec_SELECTgetRows(
-                       '*',
-                       'sys_refindex',
-                       'ref_table!='.$TYPO3_DB->fullQuoteStr('_FILE', 'sys_refindex'). // Assuming that any other key will be a table name!
-                       ' AND ref_uid>0'.
-                       $filterClause.
-                       ' AND deleted=0'        // Check only for records which are not deleted (we don't care about missing relations in deleted-flagged records)
-               );
 
-                       // Traverse the files and put into a large table:
-               $tempExists = array();
-               if (is_array($recs)) {
-                       foreach($recs as $rec)  {
-                               $idx = $rec['ref_table'].':'.$rec['ref_uid'];
 
-                               if (!isset($tempExists[$idx]))  {
 
-                                               // Select all files in the reference table not found by a soft reference parser (thus TCA configured)
-                                       if (isset($GLOBALS['TCA'][$rec['ref_table']]))  {
-                                               $recs = $TYPO3_DB->exec_SELECTgetRows(
-                                                       'uid,pid'.($GLOBALS['TCA'][$rec['ref_table']]['ctrl']['delete'] ? ','.$GLOBALS['TCA'][$rec['ref_table']]['ctrl']['delete'] : ''),
-                                                       $rec['ref_table'],
-                                                       'uid='.intval($rec['ref_uid'])
-                                               );
 
-                                               $tempExists[$idx] = count($recs) ? TRUE : FALSE;
-                                       } else {
-                                               $tempExists[$idx] = FALSE;
-                                       }
-                                       $resultArray['uniqueReferencesToTables'][$rec['ref_table']]++;
-                               }
-
-                                       // Compile info string for location of reference:
-                               $infoString = $rec['tablename'].':'.$rec['recuid'].':'.$rec['field'].':'.$rec['flexpointer'].':'.$rec['softref_key'];
 
-                                       // Handle missing file:
-                               if ($tempExists[$idx])  {
-                                       if ($recs[0]['pid']==-1)        {
-                                               $resultArray['offlineVersionRecords'][$idx][$rec['hash']] = $infoString;
-                                       } elseif ($GLOBALS['TCA'][$rec['ref_table']]['ctrl']['delete'] && $recs[0][$GLOBALS['TCA'][$rec['ref_table']]['ctrl']['delete']])       {
-                                               $resultArray['deletedRecords'][$idx][$rec['hash']] = $infoString;
-                                       }
-                               } else {
-                                       $resultArray['nonExistingRecords'][$idx][$rec['hash']] = $infoString;
-                               }
-                       }
-               }
 
-               return $resultArray;
-       }
+       /**************************
+        *
+        * CLI functionality
+        *
+        *************************/
 
        /**
-        * Find managed files which are referred to more than one time
+        * CLI engine
         *
-        * @return      array
+        * @param       array           Command line arguments
+        * @return      string
         */
-       function double_files_analyze() {
-               global $TYPO3_DB;
-
-                       // Initialize result array:
-               $resultArray = array(
-                       'message' => '
-Objective: Looking for files from TYPO3 managed records which are referenced more than one time (only one time allowed)
-Assumptions:
-- a perfect integrity of the reference index table (always update the reference index table before using this tool!)
-- files found in deleted records are included (otherwise you would see a false list of lost files)
-' .
-               'Files attached to records in TYPO3 using a "group" type configuration in TCA or FlexForm DataStructure are managed exclusively by the system and there must always exist a 1-1 reference between the file and the reference in the record.' .
-               'This tool will expose when such files are referenced from multiple locations which is considered an integrity error. ' .
-               'If a multi-reference is found it was typically created because the record was copied or modified outside of TCEmain which will otherwise maintain the relations correctly. ' .
-               'Multi-references should be resolved to 1-1 references as soon as possible. The danger of keeping multi-references is that if the file is removed from one of the refering records it will actually be deleted in the file system, leaving missing files for the remaining referers!
-
-NOTICE: Uses the Reference Index Table (sys_refindex) for analysis. Update it before use!',
-                       'headers' => array(
-                               'multipleReferencesList_count' => array('Number of multi-reference files','(See below)',1),
-                               'singleReferencesList_count' => array('Number of files correctly referenced','The amount of correct 1-1 references',1),
-                               'multipleReferencesList' => array('Entries with files having multiple references','These are serious problems that should be resolved ASAP to prevent data loss! '.$this->label_infoString,3),
-                               'dirname_registry' => array('Registry of directories in which files are found.','Registry includes which table/field pairs store files in them plus how many files their store.',1),
-                               'missingFiles' => array('Tracking missing files','(Extra feature, not related to tracking of double references. Further, the list may include more files than found in the missing_files()-test because this list includes missing files from deleted records.)',0),
-                               'warnings' => array('Warnings picked up','',2)
-                       ),
-                       'multipleReferencesList_count' => 0,
-                       'singleReferencesList_count' => 0,
-                       'multipleReferencesList' => array(),
-                       'dirname_registry' => array(),
-                       'missingFiles' => array(),
-                       'warnings' => array()
-               );
-
-                       // Select all files in the reference table not found by a soft reference parser (thus TCA configured)
-               $recs = $TYPO3_DB->exec_SELECTgetRows(
-                       '*',
-                       'sys_refindex',
-                       'ref_table='.$TYPO3_DB->fullQuoteStr('_FILE', 'sys_refindex').
-                               ' AND softref_key='.$TYPO3_DB->fullQuoteStr('', 'sys_refindex')
-               );
-
-                       // Traverse the files and put into a large table:
-               $tempCount = array();
-               if (is_array($recs)) {
-                       foreach($recs as $rec)  {
+       function cli_main($argv) {
 
-                                       // Compile info string for location of reference:
-                               $infoString = $rec['tablename'].':'.$rec['recuid'].':'.$rec['field'].':'.$rec['flexpointer'].':';
+                       // Force user to admin state and set workspace to "Live":
+               $GLOBALS['BE_USER']->user['admin'] = 1;
+               $GLOBALS['BE_USER']->setWorkspace(0);
 
-                                       // Registering occurencies in directories:
-                               $resultArray['dirname_registry'][dirname($rec['ref_string'])][$rec['tablename'].':'.$rec['field']]++;
+                       // Print Howto:
+               if ($this->cli_isArg('--showhowto'))    {
+                       $howto = t3lib_div::getUrl(t3lib_extMgm::extPath('lowlevel').'HOWTO_clean_up_TYPO3_installations.txt');
+                       echo wordwrap($howto,120).chr(10);
+                       exit;
+               }
 
-                                       // Handle missing file:
-                               if (!@is_file(PATH_site.$rec['ref_string']))    {
-                                       $resultArray['missingFiles'][$rec['ref_string']][$rec['hash']] = $infoString;
-                               }
+                       // Print help
+               $analysisType = (string)$this->cli_args['_DEFAULT'][1];
+               if (!$analysisType)     {
+                       $this->cli_validateArgs();
+                       $this->cli_help();
+                       exit;
+               }
 
-                                       // Add entry if file has multiple references pointing to it:
-                               if (isset($tempCount[$rec['ref_string']]))      {
-                                       if (!is_array($resultArray['multipleReferencesList'][$rec['ref_string']]))      {
-                                               $resultArray['multipleReferencesList'][$rec['ref_string']] = array();
-                                               $resultArray['multipleReferencesList'][$rec['ref_string']][$tempCount[$rec['ref_string']][1]] = $tempCount[$rec['ref_string']][0];
+                       // Analysis type:
+               switch((string)$analysisType)    {
+                       default:
+                               if (is_array($this->cleanerModules[$analysisType]))     {
+                                       $cleanerMode = &t3lib_div::getUserObj($this->cleanerModules[$analysisType][0]);
+                                       $cleanerMode->cli_validateArgs();
+
+                                       if ($this->cli_isArg('-r'))     {       // Run it...
+                                               if (!$cleanerMode->checkRefIndex || $this->cli_referenceIndexCheck())   {
+                                                       $res = $cleanerMode->main();
+                                                       $this->cli_printInfo($analysisType, $res);
+
+                                                               // Autofix...
+                                                       if ($this->cli_isArg('--AUTOFIX'))      {
+                                                               if ($this->cli_isArg('--YES') || $this->cli_keyboardInput_yes("\n\nNOW Running --AUTOFIX on result. OK?".($this->cli_isArg('--dryrun')?' (--dryrun simulation)':'')))   {
+                                                                       $cleanerMode->main_autofix($res);
+                                                               } else {
+                                                                       $this->cli_echo("ABORTING AutoFix...\n",1);
+                                                               }
+                                                       }
+                                               }
+                                       } else {        // Help only...
+                                               $cleanerMode->cli_help();
+                                               exit;
                                        }
-                                       $resultArray['multipleReferencesList'][$rec['ref_string']][$rec['hash']] = $infoString;
                                } else {
-                                       $tempCount[$rec['ref_string']] = array($infoString,$rec['hash']);
+                                       $this->cli_echo("ERROR: Analysis Type '".$analysisType."' is unknown.\n",1);
+                                       exit;
                                }
-                       }
-               }
-
-                       // Add count for multi-references:
-               $resultArray['multipleReferencesList_count'] = count($resultArray['multipleReferencesList']);
-               $resultArray['singleReferencesList_count'] = count($tempCount) - $resultArray['multipleReferencesList_count'];
-
-                       // Sort dirname registry and add warnings for directories outside uploads/
-               ksort($resultArray['dirname_registry']);
-               foreach($resultArray['dirname_registry'] as $dir => $temp)      {
-                       if (!t3lib_div::isFirstPartOfStr($dir,'uploads/'))      {
-                               $resultArray['warnings'][] = 'Directory "'.$dir.'" was outside uploads/ which is unusual practice in TYPO3 although not forbidden. Directory used by the following table:field pairs: '.implode(',',array_keys($temp));
-                       }
+                       break;
                }
-
-               return $resultArray;
        }
 
        /**
-        * Analyse situation with RTE magic images.
+        * Checks reference index
         *
-        * @return      array
+        * @return      boolean         TRUE if reference index was OK (either OK, updated or ignored)
         */
-       function RTEmagic_files_analyze()       {
-               global $TYPO3_DB;
-
-                       // Initialize result array:
-               $resultArray = array(
-                       'message' => '
-Objective: Looking up all occurencies of RTEmagic images in the database and check existence of parent and copy files on the file system plus report possibly lost files of this type.
-Assumptions:
-- a perfect integrity of the reference index table (always update the reference index table before using this tool!)
-- that all RTEmagic image files in the database are registered with the soft reference parser "images"
-- images found in deleted records are included (means that you might find lost RTEmagic images after flushing deleted records)
-The assumptions are not requirements by the TYPO3 API but reflects the de facto implementation of most TYPO3 installations. ' .
-               'However, many custom fields using an RTE will probably not have the "images" soft reference parser registered and so the index will be incomplete and not listing all RTEmagic image files. ' .
-               'The consequence of this limitation is that you should be careful if you wish to delete lost RTEmagic images - they could be referenced from a field not parsed by the "images" soft reference parser!' .
-               'Another limitation: In theory a RTEmagic image should be used from only one record, however TCEmain does not support this (unfortunately!) so when a record is copied or versionized no new version will be produced. This leads to a usage count of more than one for many RTEmagic images which is also shown in the overview. At this point in time its not considered a bug and there is no fix for it.
-
-NOTICE: Uses the Reference Index Table (sys_refindex) for analysis. Update it before use!',
-                       'headers' => array(
-                               'completeFileList' => array('Complete list of used RTEmagic files','Both parent and copy are listed here including usage count (which should in theory all be "1")',1),
-                               'RTEmagicFilePairs' => array('Statistical info about RTEmagic files','(copy used as index)',0),
-                               'missingFiles' => array('Missing RTEmagic image files','Have either their parent or copy missing (look that up in RTEmagicFilePairs)',3),
-                               'lostFiles' => array('Lost RTEmagic files from uploads/','These files you might be able to deleted but only if _all_ RTEmagic images are found by the soft reference parser. If you are using the RTE in third-party extensions it is likely that the soft reference parser is not applied correctly to their RTE and thus these "lost" files actually represent valid RTEmagic images, just not registered.',2),
-                               'warnings' => array('Warnings picked up','',2)
-                       ),
-                       'RTEmagicFilePairs' => array(),
-                       'completeFileList' => array(),
-                       'missingFiles' => array(),
-                       'lostFiles' => array(),
-                       'warnings' => array()
-               );
+       function cli_referenceIndexCheck()      {
 
-                       // Select all RTEmagic files in the reference table (only from soft references of course)
-               $recs = $TYPO3_DB->exec_SELECTgetRows(
-                       '*',
-                       'sys_refindex',
-                       'ref_table='.$TYPO3_DB->fullQuoteStr('_FILE', 'sys_refindex').
-                               ' AND ref_string LIKE '.$TYPO3_DB->fullQuoteStr('%/RTEmagic%', 'sys_refindex').
-                               ' AND softref_key='.$TYPO3_DB->fullQuoteStr('images', 'sys_refindex')
-               );
-
-                       // Traverse the files and put into a large table:
-               if (is_array($recs)) {
-                       foreach($recs as $rec)  {
-                               $filename = basename($rec['ref_string']);
-                               if (t3lib_div::isFirstPartOfStr($filename,'RTEmagicC_'))        {
-                                       $original = 'RTEmagicP_'.ereg_replace('\.[[:alnum:]]+$','',substr($filename,10));
-                                       $infoString = $rec['tablename'].':'.$rec['recuid'].':'.$rec['field'].':'.$rec['flexpointer'].':'.$rec['softref_key'];
-
-                                               // Build index:
-                                       $resultArray['RTEmagicFilePairs'][$rec['ref_string']]['exists'] = @is_file(PATH_site.$rec['ref_string']);
-                                       $resultArray['RTEmagicFilePairs'][$rec['ref_string']]['original'] = substr($rec['ref_string'],0,-strlen($filename)).$original;
-                                       $resultArray['RTEmagicFilePairs'][$rec['ref_string']]['original_exists'] = @is_file(PATH_site.$resultArray['RTEmagicFilePairs'][$rec['ref_string']]['original']);
-                                       $resultArray['RTEmagicFilePairs'][$rec['ref_string']]['count']++;
-                                       $resultArray['RTEmagicFilePairs'][$rec['ref_string']]['usedIn'][$rec['hash']] = $infoString;
-
-                                       $resultArray['completeFileList'][$resultArray['RTEmagicFilePairs'][$rec['ref_string']]['original']]++;
-                                       $resultArray['completeFileList'][$rec['ref_string']]++;
-
-                                               // Missing files:
-                                       if (!$resultArray['RTEmagicFilePairs'][$rec['ref_string']]['exists'] || !$resultArray['RTEmagicFilePairs'][$rec['ref_string']]['original_exists'])      {
-                                               $resultArray['missingFiles'][$rec['ref_string']] = $rec['ref_string'];
-                                       }
-                               }
-                       }
+                       // Reference index option:
+               $refIndexMode = isset($this->cli_args['--refindex']) ? $this->cli_args['--refindex'][0] : 'check';
+               if (!t3lib_div::inList('update,ignore,check', $refIndexMode))   {
+                       $this->cli_echo("ERROR: Wrong value for --refindex argument.\n",1);
+                       exit;
                }
 
-                       // Now, ask for RTEmagic files inside uploads/ folder:
-               $resLostFiles = $this->clean_lost_files_analyze();
+               switch($refIndexMode)   {
+                       case 'check':
+                       case 'update':
+                               $refIndexObj = t3lib_div::makeInstance('t3lib_refindex');
+                               list($headerContent,$bodyContent,$errorCount) = $refIndexObj->updateIndex($refIndexMode=='check',$this->cli_echo());
 
-               if (is_array($resLostFiles['RTEmagicFiles']))   {
-                       foreach($resLostFiles['RTEmagicFiles'] as $fileName) {
-                               if (!isset($resultArray['completeFileList'][$fileName]))        {
-                                       $resultArray['lostFiles'][] = $fileName;
+                               if ($errorCount && $refIndexMode=='check')      {
+                                       $ok = FALSE;
+                                       $this->cli_echo("ERROR: Reference Index Check failed! (run with '--refindex update' to fix)\n",1);
+                               } else {
+                                       $ok = TRUE;
                                }
-                       }
+                       break;
+                       case 'ignore':
+                               $this->cli_echo("Reference Index Check: Bypassing reference index check...\n");
+                               $ok = TRUE;
+                       break;
                }
 
-               return $resultArray;
+               return $ok;
        }
 
        /**
-        * Find lost files in uploads/ folder
-        *
-        * TODO: Add parameter to exclude filepath
-        * TODO: Add parameter to list more file names/patterns to ignore
-        * TODO: Add parameter to include RTEmagic images
-        *
-        * @return      void
+        * @param       [type]          $matchString: ...
+        * @return      string          If string, it's the reason for not executing. Returning FALSE means it should execute.
         */
-       function clean_lost_files_analyze()     {
-               global $TYPO3_DB;
-
-                       // Initialize result array:
-               $resultArray = array(
-                       'message' => '
-Objective: Looking for files in the uploads/ folder which does not have a reference in TYPO3 managed records.
-Assumptions:
-- a perfect integrity of the reference index table (always update the reference index table before using this tool!)
-- that all contents in the uploads folder are files attached to TCA records and exclusively managed by TCEmain through "group" type fields
-- exceptions are: index.html and .htaccess files (ignored)
-- exceptions are: RTEmagic* image files (ignored)
-- files found in deleted records are included (otherwise you would see a false list of lost files)
-The assumptions are not requirements by the TYPO3 API but reflects the de facto implementation of most TYPO3 installations and therefore a practical approach to cleaning up the uploads/ folder. ' .
-               'Therefore, if all "group" type fields in TCA and flexforms are positioned inside the uploads/ folder and if no files inside are managed manually it should be safe to clean out files with no relations found in the system. ' .
-               'Under such circumstances there should theoretically be no lost files in the uploads/ folder since TCEmain should have managed relations automatically including adding and deleting files. ' .
-               'However, there is at least one reason known to why files might be found lost and that is when FlexForms are used. In such a case a change in the data structure used for the flexform could leave lost files behind. ' .
-               'Another scenario could of course be de-installation of extensions which managed files in the uploads/ folders.
-
-NOTICE: Uses the Reference Index Table (sys_refindex) for analysis. Update it before use!',
-                       'headers' => array(
-                               'managedFiles' => array('Files related to TYPO3 records and managed by TCEmain','These files you definitely want to keep.',0),
-                               'ignoredFiles' => array('Ignored files (index.html, .htaccess etc.)','These files are allowed in uploads/ folder',0),
-                               'RTEmagicFiles' => array('RTE magic images - those found (and ignored)','These files are also allowed in some uploads/ folders as RTEmagic images.',0),
-                               'lostFiles' => array('Lost files - those you can delete','You can delete these files!',3),
-                               'warnings' => array('Warnings picked up','',2)
-                       ),
-                       'managedFiles' => array(),
-                       'ignoredFiles' => array(),
-                       'RTEmagicFiles' => array(),
-                       'lostFiles' => array(),
-                       'warnings' => array()
-               );
-
-                       // Get all files:
-               $fileArr = array();
-               $fileArr = t3lib_div::getAllFilesAndFoldersInPath($fileArr,PATH_site.'uploads/');
-               $fileArr = t3lib_div::removePrefixPathFromList($fileArr,PATH_site);
-
-                       // Traverse files and for each, look up if its found in the reference index.
-               foreach($fileArr as $key => $value) {
-
-                               // First, allow "index.html", ".htaccess" files since they are often used for good reasons
-                       if (substr($value,-11) == '/index.html' || substr($value,-10) == '/.htaccess')  {
-                               unset($fileArr[$key])   ;
-                               $resultArray['ignoredFiles'][] = $value;
-                       } else {
-                                       // Looking for a reference from a field which is NOT a soft reference (thus, only fields with a proper TCA/Flexform configuration)
-                               $recs = $TYPO3_DB->exec_SELECTgetRows(
-                                       '*',
-                                       'sys_refindex',
-                                       'ref_table='.$TYPO3_DB->fullQuoteStr('_FILE', 'sys_refindex').
-                                               ' AND ref_string='.$TYPO3_DB->fullQuoteStr($value, 'sys_refindex').
-                                               ' AND softref_key='.$TYPO3_DB->fullQuoteStr('', 'sys_refindex')
-                               );
+       function cli_noExecutionCheck($matchString)     {
 
-                                       // If found, unset entry:
-                               if (count($recs))               {
-                                       unset($fileArr[$key])   ;
-                                       $resultArray['managedFiles'][] = $value;
-                                       if (count($recs)>1)     {
-                                               $resultArray['warnings'][]='Warning: File "'.$value.'" had '.count($recs).' references from group-fields, should have only one!';
-                                       }
-                               } else {
-                                               // When here it means the file was not found. So we test if it has a RTEmagic-image name and if so, we allow it:
-                                       if (ereg('^RTEmagic[P|C]_',basename($value)))   {
-                                               unset($fileArr[$key])   ;
-                                               $resultArray['RTEmagicFiles'][] = $value;
-                                       } else {
-                                                       // We conclude that the file is lost...:
-                                               unset($fileArr[$key])   ;
-                                               $resultArray['lostFiles'][] = $value;
-                                       }
-                               }
+                       // Check for filter:
+               if ($this->cli_isArg('--filterRegex') && $regex = $this->cli_argValue('--filterRegex',0))       {
+                       if (!preg_match($regex,$matchString))   return 'BYPASS: Filter Regex "'.$regex.'" did not match string "'.$matchString.'"';
+               }
+                       // Check for interactive mode
+               if ($this->cli_isArg('-i'))     {
+                       if (!$this->cli_keyboardInput_yes(' EXECUTE?')) {
+                               return 'BYPASS...';
                        }
                }
-
-               // $fileArr variable should now be empty with all contents transferred to the result array keys.
-
-               return $resultArray;
-       }
-
-       /**
-        * Find orphan records
-        * VERY CPU and memory intensive since it will look up the whole page tree!
-        *
-        * @return      void
-        */
-       function orphan_records_analyze()       {
-               global $TYPO3_DB;
-
-               $adminObj = t3lib_div::makeInstance('t3lib_admin');
-
-               $adminObj->genTree_includeDeleted = TRUE;               // if set, genTree() includes deleted pages. This is default.
-               $adminObj->genTree_includeVersions = TRUE;              // if set, genTree() includes verisonized pages/records. This is default.
-               $adminObj->genTree_includeRecords = TRUE;               // if set, genTree() includes records from pages.
-               $adminObj->perms_clause = '';                                   // extra where-clauses for the tree-selection
-               $adminObj->genTree_makeHTML = 0;                                // if set, genTree() generates HTML, that visualizes the tree.
-
-$pt = t3lib_div::milliseconds();
-               $adminObj->genTree(1,'');
-
-print_r($adminObj->recStats);
-               echo strlen(serialize($adminObj->recStats)).chr(10);
-               echo (t3lib_div::milliseconds()-$pt).' milliseconds';
-exit;
-
-               return $resultArray;
+                       // Check for
+               if ($this->cli_isArg('--dryrun'))       return 'BYPASS: --dryrun set';
        }
 
-
-
-
-
-
-
-
-
-
-
-       /**************************
-        *
-        * Helper functions
-        *
-        *************************/
-
        /**
-        * Formats a result array from a test so it fits HTML output
+        * Formats a result array from a test so it fits output in the shell
         *
         * @param       string          name of the test (eg. function name)
         * @param       array           Result array from an analyze function
-        * @param       boolean         Silent flag, if set, will only output when the result array contains data in arrays.
-        * @param       integer         Detail level: 0=all, 1=info and greater, 2=warnings and greater, 3=errors
-        * @return      string          HTML
+        * @return      void            Outputs with echo - capture content with output buffer if needed.
         */
-       function html_printInfo($header,$res,$silent=FALSE,$detailLevel=0)      {
+       function cli_printInfo($header,$res)    {
 
-               if (!$silent) {
-                               // Name:
-                       $output.= '<h3>'.htmlspecialchars($header).'</h3>';
+               $detailLevel = t3lib_div::intInRange($this->cli_isArg('-v') ? $this->cli_argValue('-v') : 1,0,3);
+               $silent = !$this->cli_echo();
 
-                               // Message:
-                       $output.= nl2br(htmlspecialchars(trim($res['message']))).'<hr/>';
+               $severity = array(
+                       0 => 'MESSAGE',
+                       1 => 'INFO',
+                       2 => 'WARNING',
+                       3 => 'ERROR',
+               );
+
+                       // Header output:
+               if ($detailLevel <= 1)  {
+                       $this->cli_echo(
+                               "*********************************************\n".
+                               $header."\n".
+                               "*********************************************\n");
+                       $this->cli_echo(wordwrap(trim($res['message'])).chr(10).chr(10));
                }
 
                        // Traverse headers for output:
@@ -584,34 +274,31 @@ exit;
                        foreach($res['headers'] as $key => $value)      {
 
                                if ($detailLevel <= intval($value[2]))  {
-                                       if (!$silent || (is_array($res[$key]) && count($res[$key]))) {
+                                       if (is_array($res[$key]) && (count($res[$key]) || !$silent)) {
+
                                                        // Header and explanaion:
-                                               $output.= '<b>'.
-                                                               ($silent ? '<i>'.htmlspecialchars($header).'</i><br/>' : '').
-                                                               (is_array($res[$key]) && count($res[$key]) ? $GLOBALS['SOBE']->doc->icons($value[2]) : '').
-                                                               htmlspecialchars($value[0]).
-                                                               '</b><br/>';
+                                               $this->cli_echo('---------------------------------------------'.chr(10),1);
+                                               $this->cli_echo('['.$header.']'.chr(10),1);
+                                               $this->cli_echo($value[0].' ['.$severity[$value[2]].']'.chr(10),1);
+                                               $this->cli_echo('---------------------------------------------'.chr(10),1);
                                                if (trim($value[1]))    {
-                                                       $output.= '<em>'.htmlspecialchars(trim($value[1])).'</em><br/>';
+                                                       $this->cli_echo('Explanation: '.wordwrap(trim($value[1])).chr(10).chr(10),1);
                                                }
-                                               $output.='<br/>';
                                        }
 
                                                // Content:
                                        if (is_array($res[$key]))       {
                                                if (count($res[$key]))  {
-                                                       $output.= t3lib_div::view_array($res[$key]).'<br/><br/>';
+                                                       if ($this->cli_echo('',1)) { print_r($res[$key]); }
                                                } else {
-                                                       if (!$silent) $output.= '(None)'.'<br/><br/>';
+                                                       $this->cli_echo('(None)'.chr(10).chr(10));
                                                }
                                        } else {
-                                               if (!$silent) $output.= htmlspecialchars($res[$key]).'<br/><br/>';
+                                               $this->cli_echo($res[$key].chr(10).chr(10));
                                        }
                                }
                        }
                }
-
-               return $output;
        }
 
 
@@ -625,111 +312,255 @@ exit;
 
 
 
-
-
        /**************************
         *
-        * CLI functionality
+        * Page tree traversal
         *
         *************************/
 
        /**
-        * CLI engine
+        * Traverses the FULL/part of page tree, mainly to register ALL validly connected records (to find orphans) but also to register deleted records, versions etc.
+        * Output (in $this->recStats) can be useful for multiple purposes.
         *
-        * @param       array           Command line arguments
-        * @return      string
+        * @param       integer         Root page id from where to start traversal. Use "0" (zero) to have full page tree (necessary when spotting orphans, otherwise you can run it on parts only)
+        * @param       integer         Depth to traverse. zero is do not traverse at all. 1 = 1 sublevel, 1000= 1000 sublevels (all...)
+        * @param       boolean         If >0, will echo information about the traversal process.
+        * @param       string          Call back function (from this class or subclass)
+        * @return      void
         */
-       function cli_main($argv) {
+       function genTree($rootID,$depth=1000,$echoLevel=0,$callBack='') {
+
+                       // Initialize:
+               $this->workspaceIndex = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid,title','sys_workspace','1=1'.t3lib_BEfunc::deleteClause('sys_workspace'),'','','','uid');
+               $this->workspaceIndex[-1] = TRUE;
+               $this->workspaceIndex[0] = TRUE;
+
+               $this->recStats = array(
+                       'all' => array(),                                                                       // All records connected in tree including versions (the reverse are orphans). All Info and Warning categories below are included here (and therefore safe if you delete the reverse of the list)
+                       'deleted' => array(),                                                           // Subset of "alL" that are deleted-flagged [Info]
+                       'versions' => array(),                                                          // Subset of "all" which are offline versions (pid=-1). [Info]
+                       'versions_published' => array(),                                        // Subset of "versions" that is a count of 1 or more (has been published) [Info]
+                       'versions_liveWS' => array(),                                           // Subset of "versions" that exists in live workspace [Info]
+                       'versions_lost_workspace' => array(),                           // Subset of "versions" that doesn't belong to an existing workspace [Warning: Fix by move to live workspace]
+                       'versions_inside_versioned_page' => array(),            // Subset of "versions" This is versions of elements found inside an already versioned branch / page. In real life this can work out, but is confusing and the backend should prevent this from happening to people. [Warning: Fix by deleting those versions (or publishing them)]
+                       'illegal_record_under_versioned_page' => array(),       // If a page is "element" or "page" version and records are found attached to it, they might be illegally attached, so this will tell you. [Error: Fix by deleting orphans since they are not registered in "all" category]
+                       'misplaced_at_rootlevel' => array(),                            // Subset of "all": Those that should not be at root level but are. [Warning: Fix by moving record into page tree]
+                       'misplaced_inside_tree' => array(),                                     // Subset of "all": Those that are inside page tree but should be at root level [Warning: Fix by setting PID to zero]
+               );
 
-               if (in_array('-h',$argv))       {
-                       echo "
-               Options:
-               -h = This help screen.
-               ";
-                       exit;
-               }
+                       // Start traversal:
+               $this->genTree_traverse($rootID,$depth,$echoLevel,$callBack);
 
+               if ($echoLevel>0)       echo chr(10).chr(10);
+       }
 
-#              $silentFlag = TRUE;
-               $filter = 1;
+       /**
+        * Recursive traversal of page tree:
+        *
+        * @param       integer         Page root id (must be online, valid page record - or zero for page tree root)
+        * @param       integer         Depth
+        * @param       integer         Echo Level
+        * @param       string          Call back function (from this class or subclass)
+        * @param       string          DON'T set from outside, internal. (indicates we are inside a version of a page)
+        * @param       integer         DON'T set from outside, internal. (1: Indicates that rootID is a version of a page, 2: ...that it is even a version of a version (which triggers a warning!)
+        * @param       string          Internal string that accumulates the path
+        * @return      void
+        * @access private
+        */
+       function genTree_traverse($rootID,$depth,$echoLevel=0,$callBack='',$versionSwapmode='',$rootIsVersion=0,$accumulatedPath='')    {
 
-                       // Missing files:
-#              $res = $this->missing_files_analyze();
-#              $this->cli_printInfo('missing_files_analyze()', $res, $silentFlag, $filter);
+                       // Register page:
+               $this->recStats['all']['pages'][$rootID] = $rootID;
+               $pageRecord = t3lib_BEfunc::getRecordRaw('pages','uid='.intval($rootID),'deleted,title,t3ver_count,t3ver_wsid');
+               $accumulatedPath.='/'.$pageRecord['title'];
 
-                       // Missing relations:
-#              $res = $this->missing_relations_analyze();
-#              $this->cli_printInfo('missing_relations_analyze()', $res, $silentFlag, $filter);
+                       // Register if page is deleted:
+               if ($pageRecord['deleted'])     {
+                       $this->recStats['deleted']['pages'][$rootID] = $rootID;
+               }
+                       // If rootIsVersion is set it means that the input rootID is that of a version of a page. See below where the recursive call is made.
+               if ($rootIsVersion)     {
+                       $this->recStats['versions']['pages'][$rootID] = $rootID;
+                       if ($pageRecord['t3ver_count']>=1 && $pageRecord['t3ver_wsid']==0)      {       // If it has been published and is in archive now...
+                               $this->recStats['versions_published']['pages'][$rootID] = $rootID;
+                       }
+                       if ($pageRecord['t3ver_wsid']==0)       {       // If it has been published and is in archive now...
+                               $this->recStats['versions_liveWS']['pages'][$rootID] = $rootID;
+                       }
+                       if (!isset($this->workspaceIndex[$pageRecord['t3ver_wsid']]))   {       // If it doesn't belong to a workspace...
+                               $this->recStats['versions_lost_workspace']['pages'][$rootID] = $rootID;
+                       }
+                       if ($rootIsVersion==2)  {       // In case the rootID is a version inside a versioned page
+                               $this->recStats['versions_inside_versioned_page']['pages'][$rootID] = $rootID;
+                       }
+               }
 
-                       // Double references
-#              $res = $this->double_files_analyze();
-#              $this->cli_printInfo('double_files_analyze()', $res, $silentFlag, $filter);
+               if ($echoLevel>0)
+                       echo chr(10).$accumulatedPath.' ['.$rootID.']'.
+                               ($pageRecord['deleted'] ? ' (DELETED)':'').
+                               ($this->recStats['versions_published']['pages'][$rootID] ? ' (PUBLISHED)':'')
+                               ;
+               if ($echoLevel>1 && $this->recStats['versions_lost_workspace']['pages'][$rootID])
+                       echo chr(10).'  ERROR! This version belongs to non-existing workspace ('.$pageRecord['t3ver_wsid'].')!';
+               if ($echoLevel>1 && $this->recStats['versions_inside_versioned_page']['pages'][$rootID])
+                       echo chr(10).'  WARNING! This version is inside an already versioned page or branch!';
+
+                       // Call back:
+               if ($callBack)  {
+                       $this->$callBack('pages',$rootID,$echoLevel,$versionSwapmode,$rootIsVersion);
+               }
 
-                       // RTE images
-#              $res = $this->RTEmagic_files_analyze();
-#              $this->cli_printInfo('RTEmagic_files_analyze()', $res, $silentFlag, $filter);
+                       // Traverse tables of records that belongs to page:
+               foreach($GLOBALS['TCA'] as $tableName => $cfg)  {
+                       if ($tableName!='pages') {
 
-                       // Lost files:
-#              $res = $this->clean_lost_files_analyze();
-#              $this->cli_printInfo('clean_lost_files_analyze()', $res, $silentFlag, $filter);
+                                       // Select all records belonging to page:
+                               $resSub = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
+                                       'uid'.($GLOBALS['TCA'][$tableName]['ctrl']['delete']?','.$GLOBALS['TCA'][$tableName]['ctrl']['delete']:''),
+                                       $tableName,
+                                       'pid='.intval($rootID).
+                                               ($this->genTree_traverseDeleted ? '' : t3lib_BEfunc::deleteClause($tableName))
+                               );
 
-               $res = $this->orphan_records_analyze();
-               $this->cli_printInfo('orphan_records_analyze()', $res, $silentFlag, $filter);
+                               $count = $GLOBALS['TYPO3_DB']->sql_num_rows($resSub);
+                               if ($count)     {
+                                       if ($echoLevel==2)      echo chr(10).'  \-'.$tableName.' ('.$count.')';
+                               }
 
-#                      ob_start();
-#                      $output.= ob_get_contents().chr(10);
-#                      ob_end_clean();
-       }
+                               while ($rowSub = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($resSub))        {
+                                       if ($echoLevel==3)      echo chr(10).'  \-'.$tableName.':'.$rowSub['uid'];
 
-       /**
-        * Formats a result array from a test so it fits output in the shell
-        *
-        * @param       string          name of the test (eg. function name)
-        * @param       array           Result array from an analyze function
-        * @param       boolean         Silent flag, if set, will only output when the result array contains data in arrays.
-        * @param       integer         Detail level: 0=all, 1=info and greater, 2=warnings and greater, 3=errors
-        * @return      void            Outputs with echo - capture content with output buffer if needed.
-        */
-       function cli_printInfo($header,$res,$silent=FALSE,$detailLevel=0)       {
+                                               // If the rootID represents an "element" or "page" version type, we must check if the record from this table is allowed to belong to this:
+                                       if ($versionSwapmode=='SWAPMODE:-1' || ($versionSwapmode=='SWAPMODE:0' && !$GLOBALS['TCA'][$tableName]['ctrl']['versioning_followPages']))      {
+                                                       // This is illegal records under a versioned page - therefore not registered in $this->recStats['all'] so they should be orphaned:
+                                               $this->recStats['illegal_record_under_versioned_page'][$tableName][$rowSub['uid']] = $rowSub['uid'];
+                                               if ($echoLevel>1)       echo chr(10).'          ERROR! Illegal record ('.$tableName.':'.$rowSub['uid'].') under versioned page!';
+                                       } else {
+                                               $this->recStats['all'][$tableName][$rowSub['uid']] = $rowSub['uid'];
 
-               if (!$silent) {
-                               // Name:
-                       echo chr(10).'*********************************************'.chr(10).$header.chr(10).'*********************************************'.chr(10);
+                                                       // Register deleted:
+                                               if ($GLOBALS['TCA'][$tableName]['ctrl']['delete'] && $rowSub[$GLOBALS['TCA'][$tableName]['ctrl']['delete']])    {
+                                                       $this->recStats['deleted'][$tableName][$rowSub['uid']] = $rowSub['uid'];
+                                                       if ($echoLevel==3)      echo ' (DELETED)';
+                                               }
 
-                               // Message:
-                       echo trim($res['message']).chr(10).chr(10);
-               }
+                                                       // Check location of records regarding tree root:
+                                               if (!$GLOBALS['TCA'][$tableName]['ctrl']['rootLevel'] && $rootID==0) {
+                                                       $this->recStats['misplaced_at_rootlevel'][$tableName][$rowSub['uid']] = $rowSub['uid'];
+                                                       if ($echoLevel>1)       echo chr(10).'          ERROR! Misplaced record ('.$tableName.':'.$rowSub['uid'].') on rootlevel!';
+                                               }
+                                               if ($GLOBALS['TCA'][$tableName]['ctrl']['rootLevel']==1 && $rootID>0) {
+                                                       $this->recStats['misplaced_inside_tree'][$tableName][$rowSub['uid']] = $rowSub['uid'];
+                                                       if ($echoLevel>1)       echo chr(10).'          ERROR! Misplaced record ('.$tableName.':'.$rowSub['uid'].') inside page tree!';
+                                               }
 
-                       // Traverse headers for output:
-               if (is_array($res['headers'])) {
-                       foreach($res['headers'] as $key => $value)      {
+                                                       // Traverse plugins:
+                                               if ($callBack)  {
+                                                       $this->$callBack($tableName,$rowSub['uid'],$echoLevel,$versionSwapmode,$rootIsVersion);
+                                               }
 
-                               if ($detailLevel <= intval($value[2]))  {
-                                       if (!$silent || (is_array($res[$key]) && count($res[$key]))) {
-                                                       // Header and explanaion:
-                                               echo '---------------------------------------------'.chr(10).
-                                                               ($silent ? '['.$header.']'.chr(10) : '').
-                                                               $value[0].' ['.$value[2].']'.chr(10).
-                                                               '---------------------------------------------'.chr(10);
-                                               if (trim($value[1]))    {
-                                                       echo '[Explanation: '.trim($value[1]).']'.chr(10);
+                                                       // Add any versions of those records:
+                                               if ($this->genTree_traverseVersions)    {
+                                                       $versions = t3lib_BEfunc::selectVersionsOfRecord($tableName, $rowSub['uid'], 'uid,t3ver_wsid,t3ver_count'.($GLOBALS['TCA'][$tableName]['ctrl']['delete']?','.$GLOBALS['TCA'][$tableName]['ctrl']['delete']:''), 0, TRUE);
+                                                       if (is_array($versions))        {
+                                                               foreach($versions as $verRec)   {
+                                                                       if (!$verRec['_CURRENT_VERSION'])       {
+                                                                               if ($echoLevel==3)      echo chr(10).'          \-[#OFFLINE VERSION: WS#'.$verRec['t3ver_wsid'].'/Cnt:'.$verRec['t3ver_count'].'] '.$tableName.':'.$verRec['uid'].')';
+                                                                               $this->recStats['all'][$tableName][$verRec['uid']] = $verRec['uid'];
+
+                                                                                       // Register deleted:
+                                                                               if ($GLOBALS['TCA'][$tableName]['ctrl']['delete'] && $verRec[$GLOBALS['TCA'][$tableName]['ctrl']['delete']])    {
+                                                                                       $this->recStats['deleted'][$tableName][$verRec['uid']] = $verRec['uid'];
+                                                                                       if ($echoLevel==3)      echo ' (DELETED)';
+                                                                               }
+
+                                                                                       // Register version:
+                                                                               $this->recStats['versions'][$tableName][$verRec['uid']] = $verRec['uid'];
+                                                                               if ($verRec['t3ver_count']>=1 && $verRec['t3ver_wsid']==0)      {       // Only register published versions in LIVE workspace (published versions in draft workspaces are allowed)
+                                                                                       $this->recStats['versions_published'][$tableName][$verRec['uid']] = $verRec['uid'];
+                                                                                       if ($echoLevel==3)      echo ' (PUBLISHED)';
+                                                                               }
+                                                                               if ($verRec['t3ver_wsid']==0)   {
+                                                                                       $this->recStats['versions_liveWS'][$tableName][$verRec['uid']] = $verRec['uid'];
+                                                                               }
+                                                                               if (!isset($this->workspaceIndex[$verRec['t3ver_wsid']]))       {
+                                                                                       $this->recStats['versions_lost_workspace'][$tableName][$verRec['uid']] = $verRec['uid'];
+                                                                                       if ($echoLevel>1)       echo chr(10).'          ERROR! Version ('.$tableName.':'.$verRec['uid'].') belongs to non-existing workspace ('.$verRec['t3ver_wsid'].')!';
+                                                                               }
+                                                                               if ($versionSwapmode)   {       // In case we are inside a versioned branch, there should not exists versions inside that "branch".
+                                                                                       $this->recStats['versions_inside_versioned_page'][$tableName][$verRec['uid']] = $verRec['uid'];
+                                                                                       if ($echoLevel>1)       echo chr(10).'          ERROR! This version ('.$tableName.':'.$verRec['uid'].') is inside an already versioned page or branch!';
+                                                                               }
+
+                                                                                       // Traverse plugins:
+                                                                               if ($callBack)  {
+                                                                                       $this->$callBack($tableName,$verRec['uid'],$echoLevel,$versionSwapmode,$rootIsVersion);
+                                                                               }
+                                                                       }
+                                                               }
+                                                       }
+                                                       unset($versions);
                                                }
                                        }
+                               }
+                       }
+               }
+               unset($resSub);
+               unset($rowSub);
+
+                       // Find subpages to root ID and traverse (only when rootID is not a version or is a branch-version):
+               if (!$versionSwapmode || $versionSwapmode=='SWAPMODE:1')        {
+                       if ($depth>0)   {
+                               $depth--;
+                               $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
+                                       'uid',
+                                       'pages',
+                                       'pid='.intval($rootID).
+                                               ($this->genTree_traverseDeleted ? '' : t3lib_BEfunc::deleteClause('pages')),
+                                       '',
+                                       'sorting'
+                               );
+                               while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))      {
+                                       $this->genTree_traverse($row['uid'],$depth,$echoLevel,$callBack,$versionSwapmode,0,$accumulatedPath);
+                               }
+                       }
 
-                                               // Content:
-                                       if (is_array($res[$key]))       {
-                                               if (count($res[$key]))  {
-                                                       print_r($res[$key]);
-                                               } else {
-                                                       if (!$silent) echo '(None)'.chr(10).chr(10);
+                               // Add any versions of pages
+                       if ($rootID>0 && $this->genTree_traverseVersions)       {
+                               $versions = t3lib_BEfunc::selectVersionsOfRecord('pages', $rootID, 'uid,t3ver_oid,t3ver_wsid,t3ver_count,t3ver_swapmode', 0, TRUE);
+                               if (is_array($versions))        {
+                                       foreach($versions as $verRec)   {
+                                               if (!$verRec['_CURRENT_VERSION'])       {
+                                                       $this->genTree_traverse($verRec['uid'],$depth,$echoLevel,$callBack,'SWAPMODE:'.t3lib_div::intInRange($verRec['t3ver_swapmode'],-1,1),$versionSwapmode?2:1,$accumulatedPath.' [#OFFLINE VERSION: WS#'.$verRec['t3ver_wsid'].'/Cnt:'.$verRec['t3ver_count'].']');
                                                }
-                                       } else {
-                                               if (!$silent) echo $res[$key].chr(10).chr(10);
                                        }
                                }
                        }
                }
        }
+
+
+
+
+
+
+
+
+       /**************************
+        *
+        * Helper functions
+        *
+        *************************/
+
+       /**
+        * Compile info-string
+        *
+        * @param       array           Input record from sys_refindex
+        * @return      string          String identifying the main record of the reference
+        */
+       function infoStr($rec)  {
+               return $rec['tablename'].':'.$rec['recuid'].':'.$rec['field'].':'.$rec['flexpointer'].':'.$rec['softref_key'].($rec['deleted'] ? ' (DELETED)':'');
+       }
 }
 
 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/lowlevel/class.tx_lowlevel_cleaner.php'])     {
index b15a4a6..d354666 100755 (executable)
@@ -4,6 +4,7 @@ if (!defined ('TYPO3_MODE'))    die ('Access denied.');
 if (TYPO3_MODE=='BE')  {
        t3lib_extMgm::addModule('tools','dbint','',t3lib_extMgm::extPath($_EXTKEY).'dbint/');
        t3lib_extMgm::addModule('tools','config','',t3lib_extMgm::extPath($_EXTKEY).'config/');
+
 /*
        t3lib_extMgm::insertModuleFunction(
                'web_func',
@@ -13,6 +14,6 @@ if (TYPO3_MODE=='BE') {
                'function',
                'online'
        );
-       */
+*/
 }
 ?>
\ No newline at end of file
index a28dc31..2e637c2 100755 (executable)
@@ -83,6 +83,15 @@ class tx_version_cm1 {
                                1
                        );
 
+                               // "Send to review" element added:
+                       $url = t3lib_extMgm::extRelPath('version').'cm1/index.php?id='.($table=='pages'?$uid:$backRef->rec['pid']).'&table='.rawurlencode($table).'&uid='.$uid.'&sendToReview=1';
+                       $localItems[] = $backRef->linkItem(
+                               $GLOBALS['LANG']->getLLL('title_review',$LL),
+                               $backRef->excludeIcon('<img src="'.$backRef->backPath.t3lib_extMgm::extRelPath('version').'cm1/cm_icon.gif" width="15" height="12" border="0" align="top" alt="" />'),
+                               $backRef->urlRefForCM($url),
+                               1
+                       );
+
                                // Find position of "delete" element:
                        reset($menuItems);
                        $c=0;
index a115fd5..b771a71 100755 (executable)
@@ -118,6 +118,7 @@ class tx_version_cm1 extends t3lib_SCbase {
        var $publishAccess = FALSE;
        var $be_user_Array = array();
        var $stageIndex = array();
+       var $recIndex = array();
 
 
 
@@ -177,6 +178,8 @@ class tx_version_cm1 extends t3lib_SCbase {
 
                        // Setting module configuration:
                $this->MCONF = $GLOBALS['MCONF'];
+               
+               $this->REQUEST_URI = str_replace('&sendToReview=1','',t3lib_div::getIndpEnv('REQUEST_URI'));
 
                        // Draw the header.
                $this->doc = t3lib_div::makeInstance('mediumDoc');
@@ -385,7 +388,7 @@ class tx_version_cm1 extends t3lib_SCbase {
                        // Display versions:
                $content.='
                        '.$recordIcon.$recTitle.'
-                       <form action="'.t3lib_div::getIndpEnv('REQUEST_URI').'" method="post">
+                       <form name="theform" action="'.str_replace('&sendToReview=1','',$this->REQUEST_URI).'" method="post">
                        <table border="0" cellspacing="1" cellpadding="1">';
                        $content.='
                                <tr class="bgColor5 tableheader">
@@ -467,7 +470,7 @@ class tx_version_cm1 extends t3lib_SCbase {
                        </select>' : '').'
                        <br/><input type="hidden" name="cmd['.$this->table.']['.$this->uid.'][version][action]" value="new" />
                        <input type="hidden" name="prErr" value="1" />
-                       <input type="hidden" name="redirect" value="'.t3lib_div::getIndpEnv('REQUEST_URI').'" />
+                       <input type="hidden" name="redirect" value="'.htmlspecialchars($this->REQUEST_URI).'" />
                        <input type="submit" name="_" value="Create new version" />
 
                        </form>
@@ -672,7 +675,7 @@ class tx_version_cm1 extends t3lib_SCbase {
 
                if (t3lib_div::_POST('_previewLink'))   {
                        $params = 'id='.$this->id.'&ADMCMD_view=1&ADMCMD_editIcons=1&ADMCMD_previewWS='.$GLOBALS['BE_USER']->workspace;
-                       $previewUrl = t3lib_div::getIndpEnv('TYPO3_SITE_URL').'?ADMCMD_prev='.t3lib_BEfunc::compilePreviewKeyword($params, $GLOBALS['BE_USER']->user['uid']);
+                       $previewUrl = t3lib_div::getIndpEnv('TYPO3_SITE_URL').'index.php?ADMCMD_prev='.t3lib_BEfunc::compilePreviewKeyword($params, $GLOBALS['BE_USER']->user['uid']);
 
                        $this->content.= $this->doc->section('Preview Url:','You can preview this page from the workspace using this link for the next 48 hours (does not require backend login):<br/><br/><a target="_blank" href="'.htmlspecialchars($previewUrl).'">'.$previewUrl.'</a>',0,1);
                }
@@ -857,7 +860,7 @@ class tx_version_cm1 extends t3lib_SCbase {
                                                $multipleWarning = (!$mainCell && $GLOBALS['BE_USER']->workspace!==0? '<br/>'.$this->doc->icons(3).'<b>Multiple versions in same workspace!</b>' : '');
                                                $verWarning = $warnAboutVersions || ($warnAboutVersions_nonPages && $GLOBALS['TCA'][$table]['ctrl']['versioning_followPages'])? '<br/>'.$this->doc->icons(3).'<b>Version inside version!</b>' : '';
                                                $verElement = $icon.
-                                                       (!$this->details ? '<a href="'.htmlspecialchars($this->doc->backPath.t3lib_extMgm::extRelPath('version').'cm1/index.php?id='.($table==='pages'?$rec_on['uid']:$rec_on['pid']).'&details='.rawurlencode($table.':'.$rec_off['uid']).'&returnUrl='.rawurlencode(t3lib_div::getIndpEnv('REQUEST_URI'))).'">' : '').
+                                                       (!$this->details ? '<a href="'.htmlspecialchars($this->doc->backPath.t3lib_extMgm::extRelPath('version').'cm1/index.php?id='.($table==='pages'?$rec_on['uid']:$rec_on['pid']).'&details='.rawurlencode($table.':'.$rec_off['uid']).'&returnUrl='.rawurlencode($this->REQUEST_URI)).'">' : '').
                                                        t3lib_BEfunc::getRecordTitle($table,$rec_off,TRUE).
                                                        (!$this->details ? '</a>' : '').
                                                        $versionsInOtherWSWarning.
@@ -939,32 +942,73 @@ class tx_version_cm1 extends t3lib_SCbase {
         */
        function displayWorkspaceOverview_allStageCmd() {
 
-               if (count($this->stageIndex[1]))        {       // Review:
-                       $sId = 1;
-                       $color = '#666666';
-                       $label = 'Comment for Reviewer:';
-                       $titleAttrib = 'Send all to Review';
-               } elseif(count($this->stageIndex[10]))  {       // Publish:
-                       $sId = 10;
-                       $color = '#6666cc';
-                       $label = 'Comment for Publisher:';
-                       $titleAttrib = 'Approve all for Publishing';
+               $table = t3lib_div::_GP('table');
+               if ($table && $table!='pages')  {
+                       $uid = t3lib_div::_GP('uid');
+                       if ($rec_off = t3lib_BEfunc::getRecordWSOL($table,$uid)) {
+                               $uid = $rec_off['_ORIG_uid'];
+                       }
+               } else $table = '';
+
+               if ($table)     {
+                       if ($uid && $this->recIndex[$table][$uid])      {
+                               $sId = $this->recIndex[$table][$uid];
+                               switch($sId)    {
+                                       case 1:
+                                               $label = 'Comment for Reviewer:';
+                                       break;
+                                       case 10:
+                                               $label = 'Comment for Publisher:';
+                                       break;
+                               }
+                       } else $sId = 0;
                } else {
-                       $sId = 0;
+                       if (count($this->stageIndex[1]))        {       // Review:
+                               $sId = 1;
+                               $color = '#666666';
+                               $label = 'Sending %s item(s) to review. Comment for Reviewer:';
+                               $titleAttrib = 'Send all to Review';
+                       } elseif(count($this->stageIndex[10]))  {       // Publish:
+                               $sId = 10;
+                               $color = '#6666cc';
+                               $label = 'Approving %s item(s) to publishing. Comment for Publisher:';
+                               $titleAttrib = 'Approve all for Publishing';
+                       } else {
+                               $sId = 0;
+                       }
                }
 
                if ($sId>0)     {
                        $issueCmd = '';
-                       foreach($this->stageIndex[$sId] as $table => $uidArray) {
-                               $issueCmd.='&cmd['.$table.']['.implode(',',$uidArray).'][version][action]=setStage';
-                               $issueCmd.='&cmd['.$table.']['.implode(',',$uidArray).'][version][stageId]='.$sId;
+                       $itemCount = 0;
+
+                       if ($table && $uid && $this->recIndex[$table][$uid])    {
+                               $issueCmd.='&cmd['.$table.']['.$uid.'][version][action]=setStage';
+                               $issueCmd.='&cmd['.$table.']['.$uid.'][version][stageId]='.$this->recIndex[$table][$uid];
+                       } else {
+                               foreach($this->stageIndex[$sId] as $table => $uidArray) {
+                                       $issueCmd.='&cmd['.$table.']['.implode(',',$uidArray).'][version][action]=setStage';
+                                       $issueCmd.='&cmd['.$table.']['.implode(',',$uidArray).'][version][stageId]='.$sId;
+                                       $itemCount+=count($uidArray);
+                               }
                        }
 
-                       $onClick = 'var commentTxt=window.prompt("'.$label.'","");
-                                                       if (commentTxt!=null) {window.location.href="'.$this->doc->issueCommand($issueCmd).'&generalComment="+escape(commentTxt);}'.
-                                                       ' return false;';
+                       $onClick = 'var commentTxt=window.prompt("'.sprintf($label,$itemCount).'","");
+                                                       if (commentTxt!=null) {window.location.href="'.$this->doc->issueCommand($issueCmd,$this->REQUEST_URI).'&generalComment="+escape(commentTxt);}';
+
+                       if (t3lib_div::_GP('sendToReview'))     {
+                               $onClick.= ' else {window.location.href = "'.$this->REQUEST_URI.'"}';
+                               $actionLinks.=
+                                       $this->doc->wrapScriptTags($onClick);
+                       } else {
+                               $onClick.= ' return false;';
+                               $actionLinks.=
+                                       '<input type="submit" name="_" value="'.htmlspecialchars($titleAttrib).'" onclick="'.htmlspecialchars($onClick).'" />';
+                       }       
+               } elseif (t3lib_div::_GP('sendToReview'))       {
+                       $onClick = 'window.location.href = "'.$this->REQUEST_URI.'";';
                        $actionLinks.=
-                               '<input type="submit" name="_" value="'.htmlspecialchars($titleAttrib).'" onclick="'.htmlspecialchars($onClick).'" />';
+                               $this->doc->wrapScriptTags($onClick);
                } else $actionLinks = '';
 
                return $actionLinks;
@@ -1119,7 +1163,7 @@ class tx_version_cm1 extends t3lib_SCbase {
                }
 
                return count($entry) ? '<span onmouseover="document.getElementById(\'log_'.$table.$id.'\').style.visibility = \'visible\';" onmouseout="document.getElementById(\'log_'.$table.$id.'\').style.visibility = \'hidden\';">'.$stageCommands.' ('.count($entry).')</span>'.
-                               '<div class="logLayer" style="visibility: hidden; position: absolute;" id="log_'.$table.$id.'">'.implode('<hr/>',$entry).'</div>' : $stageCommands;
+                               '<div class="logLayer" style="visibility: hidden; position: absolute;" id="log_'.$table.$id.'">'.implode('<hr/>',array_reverse($entry)).'</div>' : $stageCommands;
        }
 
        /**
@@ -1137,7 +1181,7 @@ class tx_version_cm1 extends t3lib_SCbase {
                        return '<br/>
                                        <img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/ol/joinbottom.gif','width="18" height="16"').' align="top" alt="" title="" />'.
                                        ($origId ?
-                                               '<a href="'.htmlspecialchars($this->doc->backPath.t3lib_extMgm::extRelPath('version').'cm1/index.php?id='.$uid.'&details='.rawurlencode('pages:'.$uid).'&returnUrl='.rawurlencode(t3lib_div::getIndpEnv('REQUEST_URI'))).'">'.
+                                               '<a href="'.htmlspecialchars($this->doc->backPath.t3lib_extMgm::extRelPath('version').'cm1/index.php?id='.$uid.'&details='.rawurlencode('pages:'.$uid).'&returnUrl='.rawurlencode($this->REQUEST_URI)).'">'.
                                                '<span class="typo3-dimmed"><em>[Sub elements, click for details]</em><span></a>' :
                                                '<span class="typo3-dimmed"><em>[Sub elements]</em><span>');
                } else {        // For an offline workspace, show sub elements:
@@ -1544,6 +1588,7 @@ class tx_version_cm1 extends t3lib_SCbase {
                                        '</a>';
 
                                $this->stageIndex[$sId][$table][] = $rec_off['uid'];
+                               $this->recIndex[$table][$rec_off['uid']] = $sId;
                        }
                }
                return $actionLinks;
@@ -1604,7 +1649,7 @@ class tx_version_cm1 extends t3lib_SCbase {
 
                        // History/Log
                $actionLinks.=
-                       '<a href="'.htmlspecialchars($this->doc->backPath.'show_rechis.php?element='.rawurlencode($table.':'.$rec_off['uid']).'&returnUrl='.rawurlencode(t3lib_div::getIndpEnv('REQUEST_URI'))).'">'.
+                       '<a href="'.htmlspecialchars($this->doc->backPath.'show_rechis.php?element='.rawurlencode($table.':'.$rec_off['uid']).'&returnUrl='.rawurlencode($this->REQUEST_URI)).'">'.
                        '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/history2.gif','width="13" height="12"').' title="Show Log" alt="" />'.
                        '</a>';
 
@@ -1649,7 +1694,7 @@ class tx_version_cm1 extends t3lib_SCbase {
 
                                // History/Log
                        $actionLinks.=
-                               '<a href="'.htmlspecialchars($this->doc->backPath.'show_rechis.php?element='.rawurlencode($table.':'.$uid).'&returnUrl='.rawurlencode(t3lib_div::getIndpEnv('REQUEST_URI'))).'">'.
+                               '<a href="'.htmlspecialchars($this->doc->backPath.'show_rechis.php?element='.rawurlencode($table.':'.$uid).'&returnUrl='.rawurlencode($this->REQUEST_URI)).'">'.
                                '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/history2.gif','width="13" height="12"').' title="Show Log" alt="" />'.
                                '</a>';
                }
index 7a0f117..7d416b8 100755 (executable)
@@ -7,6 +7,7 @@
        <data type="array">
                <languageKey index="default" type="array">
                        <label index="title">Versioning</label>
+                       <label index="title_review">Send to review/pub</label>
                        <label index="mlang_labels_tablabel">Page and content versioning and workspace management for pages.</label>
                        <label index="mlang_labels_tabdescr">The Web&gt;Versioning module provides access to versioning features for the page tree.</label>
                        <label index="mlang_tabs_tab">Versioning</label>
index c5591af..bcd26a9 100755 (executable)
@@ -882,7 +882,7 @@ $str.=$this->docBodyTagBegin().
         * @return      string          HTML body tag
         */
        function docBodyTagBegin()      {
-               $bodyContent = 'body '.trim($this->bodyTagAdditions.($this->bodyTagId ? ' id="'.$this->bodyTagId.'"' : ''));
+               $bodyContent = 'body onclick="if (top.menuReset) top.menuReset();" '.trim($this->bodyTagAdditions.($this->bodyTagId ? ' id="'.$this->bodyTagId.'"' : ''));
                return '<'.trim($bodyContent).'>';
        }