Multiple improvements as announced in June on the core list (see changelog)
authorKasper Skårhøj <kasper@typo3.org>
Tue, 28 Aug 2007 15:43:43 +0000 (15:43 +0000)
committerKasper Skårhøj <kasper@typo3.org>
Tue, 28 Aug 2007 15:43:43 +0000 (15:43 +0000)
git-svn-id: https://svn.typo3.org/TYPO3v4/Core/trunk@2460 709f56b5-9817-0410-a4d7-c38de5d9e867

26 files changed:
ChangeLog
t3lib/class.t3lib_befunc.php
t3lib/class.t3lib_cli.php
t3lib/class.t3lib_flexformtools.php
t3lib/class.t3lib_refindex.php
t3lib/class.t3lib_softrefproc.php
t3lib/class.t3lib_tcemain.php
t3lib/stddb/tbl_be.php
typo3/alt_clickmenu.php
typo3/alt_db_navframe.php
typo3/alt_main_new.php
typo3/class.webpagetree.php
typo3/file_upload.php
typo3/mod/user/ws/class.wslib_gui.php
typo3/mod/user/ws/index.php
typo3/stylesheet.css
typo3/sysext/cms/ext_tables.sql
typo3/sysext/cms/tslib/class.tslib_fe.php
typo3/sysext/lang/locallang_mod_user_ws.xml
typo3/sysext/lowlevel/HOWTO_clean_up_TYPO3_installations.txt
typo3/sysext/lowlevel/class.tx_lowlevel_cleaner_core.php
typo3/sysext/lowlevel/clmods/class.double_files.php
typo3/sysext/lowlevel/clmods/class.rte_images.php
typo3/sysext/t3skin/stylesheets/typo3-workspaces.css
typo3/sysext/version/cm1/index.php
typo3/tree.js

index cd60361..d88fb1d 100755 (executable)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,22 @@
+2007-08-28 Kasper Skaarhoj <kasper2007@typo3.com>
+
+       * Improvements as announced in June on the core list (except workspace moving)
+               - Dual-preview for workspaces are only used now if the checkbox "Frontend Preview" is not checked.
+               - Whole workspaces can be previewed now for users with no backend login by a simple link you can email. Same functionality as the preview link for individual pages available from the Web>Versioning module. The function is found in User>Workspace (Workspace manager)
+               - In the t3lib_cli class, optional parameters to CLI script options are supported (optional parameters are configured with [] brackets)
+               - The "lowlevel" cleaner scripts are updated to allow fixing of double-references to RTEmagic images (by duplicating them). Also the script allows to remove all "lost" RTEmagic images found inside uploads/ folder.
+               - Moving elements in FlexForms up and down is now fixed so it works even if indexes of elements in sections are not ordered numerically (bugfix)
+               - Workspace titles are required to be unique now.
+               - File upload in the Web>File module will always result in redirection to the Filelist view of the target folder. This is a usability enhancement for people who were puzzled that it could redirect to other locations or a blank page (namely the previously shown page).
+               - The navigation page tree now has a nifty searchbox that uses JavaScript and the prototype library to search and highly/dim elements in the tree. Very handy for quickly finding your way in huge page trees. (I think there is a stylesheet problem with Firefox on PC which doesn't dim elements). TSconfig options exists to disable this behaviour.
+               - The navigation page tree is organized so that all pages shown in the menu are grouped in their normal order while pages not in menu and sysFolders and ordered alphabetically below. TSconfig options exists to configure/disable this behaviour.
+               - User > Workspace module now has a browser-functionality so only 50 versions are shown at a time and you can browse the list.
+               - Changed label "Draft versions" to "Workspace versions" - some found it more intuitive.
+               - Change in t3lib_refindex so setReferenceValue() can be run without being admin (by special parameter)
+               - Fixed bug in t3lib_BEfunc::selectVersionsOfRecord(): now offline versions are not only selected by their t3ver_oid but also pid=-1 which is the correct thing to do. This actually resulted in dataloss in some known cases with the lowlevel cleaner CLI script.
+               - Added fields to indexes on pages, sys_template and pages_language_uid.
+               - RTEmagic images are now duplicated when records are copied/versioned in tcemain. This is a long awaited feature that solves the bug that RTE magic images shared between too records would result in both disappearing if the one was deleted. So its in fact a critical fix for workspaces.
+
 2007-08-23 Dmitry Dulepov <dmitry@typo3.org>
 
        * Fixed: hanging recordset in t3lib_BEfunc
index f9ce8d4..4bf4f98 100755 (executable)
@@ -2388,7 +2388,7 @@ class t3lib_BEfunc        {
                        $url = $altUrl;
                } else {
 
-                       if ($GLOBALS['BE_USER']->workspace!=0)  {
+                       if ($GLOBALS['BE_USER']->workspace!=0 && !$GLOBALS['BE_USER']->user['workspace_preview'])       {
                                $url = t3lib_div::getIndpEnv('TYPO3_SITE_URL').TYPO3_mainDir.'mod/user/ws/wsol_preview.php?id='.$id.$addGetVars.$anchor;
                        } else {
                                if ($rootLine)  {
@@ -2673,14 +2673,16 @@ class t3lib_BEfunc      {
         * @param       string          Get variables to preview, eg. 'id=1150&L=0&ADMCMD_view=1&ADMCMD_editIcons=1&ADMCMD_previewWS=8'
         * @param       string          32 byte MD5 hash keyword for the URL: "?ADMCMD_prev=[keyword]"
         * @param       integer         Time-To-Live for keyword
+        * @param       integer         Which workspace to preview. Workspace UID, -1 or >0. If set, the getVars is ignored in the frontend, so that string can be empty
         * @return      string          Returns keyword to use in URL for ADMCMD_prev=
         */
-       function compilePreviewKeyword($getVarsStr, $beUserUid, $ttl=172800)    {
+       function compilePreviewKeyword($getVarsStr, $beUserUid, $ttl=172800,$fullWorkspace=NULL)        {
                $field_array = array(
                        'keyword' => md5(uniqid(microtime())),
                        'tstamp' => time(),
                        'endtime' => time()+$ttl,
                        'config' => serialize(array(
+                               'fullWorkspace' => $fullWorkspace,
                                'getVars' => $getVarsStr,
                                'BEUSER_uid' => $beUserUid
                        ))
@@ -3288,7 +3290,7 @@ class t3lib_BEfunc        {
                        $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
                                $fields,
                                $table,
-                               '((t3ver_oid='.intval($uid).($workspace!=0?' AND t3ver_wsid='.intval($workspace):'').') OR uid='.intval($uid).')'.
+                               '((pid=-1 AND t3ver_oid='.intval($uid).($workspace!=0?' AND t3ver_wsid='.intval($workspace):'').') OR uid='.intval($uid).')'.
                                        ($includeDeletedRecords ? '' : t3lib_BEfunc::deleteClause($table)),
                                '',
                                't3ver_id DESC'
@@ -3360,9 +3362,10 @@ class t3lib_BEfunc       {
         * Workspace Preview Overlay
         * Generally ALWAYS used when records are selected based on uid or pid. If records are selected on other fields than uid or pid (eg. "email = ....") then usage might produce undesired results and that should be evaluated on individual basis.
         * Principle; Record online! => Find offline?
+        * Recently, this function has been modified so it MAY set $row to FALSE. This happens if a version overlay with the move-id pointer is found in which case we would like a backend preview. In other words, you should check if the input record is still an array afterwards when using this function.
         *
         * @param       string          Table name
-        * @param       array           Record array passed by reference. As minimum, the "uid", "pid" and "t3ver_swapmode" (pages) fields must exist! Fake fields cannot exist since the fields in the array is used as field names in the SQL look up.
+        * @param       array           Record array passed by reference. As minimum, the "uid", "pid" and "t3ver_swapmode" (pages) fields must exist! Fake fields cannot exist since the fields in the array is used as field names in the SQL look up. It would be nice to have fields like "t3ver_state" and "t3ver_mode_id" as well to avoid a new lookup inside movePlhOL(). 
         * @param       integer         Workspace ID, if not specified will use $GLOBALS['BE_USER']->workspace
         * @return      void            (Passed by ref).
         * @see fixVersioningPid()
index 75b098b..8fce610 100644 (file)
@@ -172,7 +172,7 @@ class t3lib_cli {
                                foreach($argSplit as $i => $v)  {
                                        $ii=$i;
                                        if ($i>0)       {
-                                               if (!isset($cli_args_copy[$argSplit[0]][$i-1])) {
+                                               if (!isset($cli_args_copy[$argSplit[0]][$i-1]) && $v{0}!='[')   {       // Using "[]" around a paramter makes it optional
                                                        echo 'ERROR: Option "'.$argSplit[0].'" requires a value ("'.$v.'") on position '.$i.chr(10);
                                                        exit;
                                                }
index d3279fa..0b6d952 100644 (file)
@@ -235,17 +235,19 @@ class t3lib_flexformtools {
                                                                }
 
                                                                foreach($editData[$key]['el'] as $k3 => $v3)    {
-                                                                       $cc=$k3;
-                                                                       $theType = key($v3);
-                                                                       $theDat = $v3[$theType];
-                                                                       $newSectionEl = $value['el'][$theType];
-                                                                       if (is_array($newSectionEl))    {
-                                                                               $this->traverseFlexFormXMLData_recurse(
-                                                                                       array($theType => $newSectionEl),
-                                                                                       array($theType => $theDat),
-                                                                                       $PA,
-                                                                                       $path.'/'.$key.'/el/'.$cc
-                                                                               );
+                                                                       if (is_array($v3))      {
+                                                                               $cc=$k3;
+                                                                               $theType = key($v3);
+                                                                               $theDat = $v3[$theType];
+                                                                               $newSectionEl = $value['el'][$theType];
+                                                                               if (is_array($newSectionEl))    {
+                                                                                       $this->traverseFlexFormXMLData_recurse(
+                                                                                               array($theType => $newSectionEl),
+                                                                                               array($theType => $theDat),
+                                                                                               $PA,
+                                                                                               $path.'/'.$key.'/el/'.$cc
+                                                                                       );
+                                                                               }
                                                                        }
                                                                }
                                                        }
index ae5bda9..bd28d4a 100755 (executable)
@@ -631,11 +631,12 @@ class t3lib_refindex {
         * @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.
+        * @param       boolean         If set, it will bypass check for workspace-zero and admin user
         * @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     {
+       function setReferenceValue($hash,$newValue,$returnDataArray=FALSE,$bypassWorkspaceAdminCheck=FALSE)     {
 
-               if ($GLOBALS['BE_USER']->workspace===0 && $GLOBALS['BE_USER']->isAdmin())       {
+               if (($GLOBALS['BE_USER']->workspace===0 && $GLOBALS['BE_USER']->isAdmin()) || $bypassWorkspaceAdminCheck)       {
 
                                // Get current index from Database:
                        list($refRec) = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
@@ -700,6 +701,7 @@ class t3lib_refindex {
                                                        if ($returnDataArray)   {
                                                                return $dataArray;
                                                        } else {
+               
                                                                        // Execute CMD array:
                                                                $tce = t3lib_div::makeInstance('t3lib_TCEmain');
                                                                $tce->stripslashes_values = FALSE;
index a5fa5fd..538e9d9 100755 (executable)
@@ -640,7 +640,7 @@ class t3lib_softrefproc {
                if (strlen($cssClass))  $finalTagParts['class'] = $cssClass;
 
                        // Parse URL:
-               $pU = parse_url($link_param);
+               $pU = @parse_url($link_param);
 
                        // Detecting the kind of reference:
                if(strstr($link_param,'@') && !$pU['scheme'])   {               // If it's a mail address:
index 135a242..7a883e3 100755 (executable)
@@ -285,8 +285,9 @@ class t3lib_TCEmain {
        var $newRelatedIDs = Array();                           // Holds the tables and there the ids of newly created child records from IRRE
        var $copyMappingArray_merged = Array();         // This array is the sum of all copying operations in this class. May be READ from outside, thus partly public.
        var $copiedFileMap = Array();                           // A map between input file name and final destination for files being attached to records.
-       var $errorLog = Array();                                        // Errors are collected in this variable.
-
+       var $RTEmagic_copyIndex = Array();                      // Contains [table][id][field] of fiels where RTEmagic images was copied. Holds old filename as key and new filename as value.
+       var     $errorLog = Array();                                    // Errors are collected in this variable.
+       
 
 
                // *********************
@@ -773,7 +774,7 @@ class t3lib_TCEmain {
                                                                                        $cmd = array();
                                                                                        $cmd[$table][$id]['version'] = array(
                                                                                                'action' => 'new',
-                                                                                               'treeLevels' => -1,     // Default is to create a version of the individual records...
+                                                                                               'treeLevels' => -1,     // Default is to create a version of the individual records... element versioning that is.
                                                                                                'label' => 'Auto-created for WS #'.$this->BE_USER->workspace
                                                                                        );
                                                                                        $tce->start(array(),$cmd);
@@ -786,6 +787,7 @@ class t3lib_TCEmain {
                                                                                                        foreach ($origIdArray as $origId => $newId) {
                                                                                                                $this->uploadedFileArray[$origTable][$newId] = $this->uploadedFileArray[$origTable][$origId];
                                                                                                                $this->autoVersionIdMap[$origTable][$origId] = $newId;
+                                                                                                               $this->RTEmagic_copyIndex = t3lib_div::array_merge_recursive_overrule($this->RTEmagic_copyIndex, $tce->RTEmagic_copyIndex);             // See where RTEmagic_copyIndex is used inside fillInFieldArray() for more information...
                                                                                                        }
                                                                                                }
 
@@ -959,13 +961,13 @@ class t3lib_TCEmain       {
         * $this->exclude_array is used to filter fields if needed.
         *
         * @param       string          Table name
-        * @param       [type]          $id: ...
+        * @param       integer         Record ID
         * @param       array           Default values, Preset $fieldArray with 'pid' maybe (pid and uid will be not be overridden anyway)
         * @param       array           $incomingFieldArray is which fields/values you want to set. There are processed and put into $fieldArray if OK
         * @param       integer         The real PID value of the record. For updates, this is just the pid of the record. For new records this is the PID of the page where it is inserted.
         * @param       string          $status = 'new' or 'update'
-        * @param       [type]          $tscPID: ...
-        * @return      [type]          ...
+        * @param       integer         $tscPID: TSconfig PID
+        * @return      array           Field Array
         */
        function fillInFieldArray($table,$id,$fieldArray,$incomingFieldArray,$realPid,$status,$tscPID)  {
                global $TCA;
@@ -1080,6 +1082,20 @@ class t3lib_TCEmain      {
                                                                                $diffStorageFlag = TRUE;
                                                                        }
                                                                }
+                                                               
+
+                                                                       // If autoversioning is happening we need to perform a nasty hack. The case is parallel to a similar hack inside checkValue_group_select_file().
+                                                                       // When a copy or version is made of a record, a search is made for any RTEmagic* images in fields having the "images" soft reference parser applied. That should be true for RTE fields. If any are found they are duplicated to new names and the file reference in the bodytext is updated accordingly.
+                                                                       // However, with auto-versioning the submitted content of the field will just overwrite the corrected values. This leaves a) lost RTEmagic files and b) creates a double reference to the old files.
+                                                                       // The only solution I can come up with is detecting when auto versioning happens, then see if any RTEmagic images was copied and if so make a stupid string-replace of the content ! 
+                                                               if ($this->autoVersioningUpdate===TRUE) {
+                                                                       if (is_array($this->RTEmagic_copyIndex[$table][$id][$field]))   {
+                                                                               foreach($this->RTEmagic_copyIndex[$table][$id][$field] as $oldRTEmagicName => $newRTEmagicName) {
+                                                                                       $fieldArray[$field] = str_replace(' src="'.$oldRTEmagicName.'"',' src="'.$newRTEmagicName.'"',$fieldArray[$field]);
+                                                                               }
+                                                                       }
+                                                               }
+                                                               
                                                        } elseif ($TCA[$table]['ctrl']['origUid']===$field) {   // Allow value for original UID to pass by...
                                                                $fieldArray[$field] = $fieldValue;
                                                        }
@@ -1539,7 +1555,7 @@ class t3lib_TCEmain       {
                                                // 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 versions uid is 456 and the file is copied to "logo_01.gif". But the form data that we sent 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) {
@@ -1786,25 +1802,40 @@ class t3lib_TCEmain     {
 
                                // Only execute the first move command:
                        list ($key, $value) = each ($moveCMDS);
-
+                       
                        if (is_array($moveCMDS[$key]))  {
                                $this->_MOVE_FLEX_FORMdata($valueArrayToMoveIn[$key],$moveCMDS[$key], $direction);
                        } else {
-                               switch ($direction) {
-                                       case 'up':
-                                               if ($key > 1) {
-                                                       $tmpArr = $valueArrayToMoveIn[$key];
-                                                       $valueArrayToMoveIn[$key] = $valueArrayToMoveIn[$key-1];
-                                                       $valueArrayToMoveIn[$key-1] = $tmpArr;
-                                               }
-                                       break;
-                                       case 'down':
-                                               if ($key < count($valueArrayToMoveIn)) {
-                                                       $tmpArr = $valueArrayToMoveIn[$key];
-                                                       $valueArrayToMoveIn[$key] = $valueArrayToMoveIn[$key+1];
-                                                       $valueArrayToMoveIn[$key+1] = $tmpArr;
-                                               }
-                                       break;
+
+                                       // Keys may not be arranged in numerical order and there could be gaps. Therefore we create a nice index first:
+                               $keyOrder = array();
+                               $keyIndex = 0;
+                               $c=0;
+                               foreach($valueArrayToMoveIn as $kk => $vv)      {
+                                       if (is_array($vv))      {       // Sections are expected to be arrays and if not, it's gotta be an error in the XML data.
+                                               $c++;
+                                               $keyOrder[$c] = $kk;
+                                               if ($kk===$key) $keyIndex=$c;
+                                       }
+                               }
+
+                               if ($keyIndex>0)        {
+                                       switch ($direction) {
+                                               case 'up':
+                                                       if (isset($keyOrder[$keyIndex-1])) {
+                                                               $tmpArr = $valueArrayToMoveIn[$keyOrder[$keyIndex]];
+                                                               $valueArrayToMoveIn[$keyOrder[$keyIndex]] = $valueArrayToMoveIn[$keyOrder[$keyIndex-1]];
+                                                               $valueArrayToMoveIn[$keyOrder[$keyIndex-1]] = $tmpArr;
+                                                       }
+                                               break;
+                                               case 'down':
+                                                       if (isset($keyOrder[$keyIndex+1])) {
+                                                               $tmpArr = $valueArrayToMoveIn[$keyOrder[$keyIndex]];
+                                                               $valueArrayToMoveIn[$keyOrder[$keyIndex]] = $valueArrayToMoveIn[$keyOrder[$keyIndex+1]];
+                                                               $valueArrayToMoveIn[$keyOrder[$keyIndex+1]] = $tmpArr;
+                                                       }
+                                               break;
+                                       }
                                }
                        }
                }
@@ -2518,7 +2549,6 @@ class t3lib_TCEmain       {
                                        $defaultData = $this->newFieldArray($table);
 
                                                // Getting "copy-after" fields if applicable:
-                                               // origDestPid is retrieve before it may possibly be converted to resolvePid if the table is not sorted anyway. In this way, copying records to after another records which are not sorted still lets you use this function in order to copy fields from the one before.
                                        $copyAfterFields = $destPid<0 ? $this->fixCopyAfterDuplFields($table,$uid,abs($destPid),0) : array();
 
                                                // Page TSconfig related:
@@ -2583,6 +2613,7 @@ class t3lib_TCEmain       {
                                                // Getting the new UID:
                                        $theNewSQLID = $copyTCE->substNEWwithIDs[$theNewID];
                                        if ($theNewSQLID)       {
+                                               $this->copyRecord_fixRTEmagicImages($table,t3lib_BEfunc::wsMapId($table,$theNewSQLID));
                                                $this->copyMappingArray[$table][$origUid] = $theNewSQLID;
                                        }
 
@@ -2741,6 +2772,7 @@ class t3lib_TCEmain       {
                                        if ($theNewSQLID)       {
                                                $this->dbAnalysisStoreExec();
                                                $this->dbAnalysisStore = array();
+                                               $this->copyRecord_fixRTEmagicImages($table,t3lib_BEfunc::wsMapId($table,$theNewSQLID));
                                                return $this->copyMappingArray[$table][$uid] = $theNewSQLID;
                                        }
                                } else $this->log($table,$uid,3,0,1,'Attempt to rawcopy/versionize record that did not exist!');
@@ -2998,6 +3030,93 @@ class t3lib_TCEmain      {
                return $value;
        }
 
+       /**
+        * Copies any "RTEmagic" image files found in record with table/id to new names.
+        * Usage: After copying a record this function should be called to search for "RTEmagic"-images inside the record. If such are found they should be duplicated to new names so all records have a 1-1 relation to them.
+        * Reason for copying RTEmagic files: a) if you remove an RTEmagic image from a record it will remove the file - any other record using it will have a lost reference! b) RTEmagic images keeps an original and a copy. The copy always is re-calculated to have the correct physical measures as the HTML tag inserting it defines. This is calculated from the original. Two records using the same image could have difference HTML-width/heights for the image and the copy could only comply with one of them. If you don't want a 1-1 relation you should NOT use RTEmagic files but just insert it as a normal file reference to a file inside fileadmin/ folder
+        * 
+        * @param       string          Table name
+        * @param       integer         Record UID
+        * @return      void
+        */
+       function copyRecord_fixRTEmagicImages($table,$theNewSQLID)      {
+               global $TYPO3_DB;
+
+                       // Creating fileFunc object.
+               if (!$this->fileFunc)   {
+                       $this->fileFunc = t3lib_div::makeInstance('t3lib_basicFileFunctions');
+                       $this->include_filefunctions=1;
+               }
+                               
+                       // Select all RTEmagic files in the reference table from the table/ID
+               $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').
+                               ' AND tablename='.$TYPO3_DB->fullQuoteStr($table, 'sys_refindex').
+                               ' AND recuid='.intval($theNewSQLID),
+                       '',
+                       'sorting DESC'
+               );      
+               
+
+                       // Traverse the files found and copy them:
+               if (is_array($recs)) {
+                       foreach($recs as $rec)  {
+                               $filename = basename($rec['ref_string']);
+                               $fileInfo = array();
+                               if (t3lib_div::isFirstPartOfStr($filename,'RTEmagicC_'))        {
+
+                                       $fileInfo['exists'] = @is_file(PATH_site.$rec['ref_string']);
+                                       $fileInfo['original'] = substr($rec['ref_string'],0,-strlen($filename)).'RTEmagicP_'.ereg_replace('\.[[:alnum:]]+$','',substr($filename,10));
+                                       $fileInfo['original_exists'] = @is_file(PATH_site.$fileInfo['original']);
+
+                                       // CODE from tx_impexp and class.rte_images.php adapted for use here:
+
+                                       if ($fileInfo['exists'] && $fileInfo['original_exists'])        {
+
+                                                       // Initialize; Get directory prefix for file and set the original name:
+                                               $dirPrefix = dirname($rec['ref_string']).'/';
+                                               $rteOrigName = basename($fileInfo['original']);
+
+                                                       // If filename looks like an RTE file, and the directory is in "uploads/", then process as a RTE file!
+                                               if ($rteOrigName && t3lib_div::isFirstPartOfStr($dirPrefix,'uploads/') && @is_dir(PATH_site.$dirPrefix))        {       // RTE:
+
+                                                               // From the "original" RTE filename, produce a new "original" destination filename which is unused. 
+                                                       $origDestName = $this->fileFunc->getUniqueName($rteOrigName, PATH_site.$dirPrefix);
+                                                       
+                                                               // Create copy file name:
+                                                       $pI = pathinfo($rec['ref_string']);
+                                                       $copyDestName = dirname($origDestName).'/RTEmagicC_'.substr(basename($origDestName),10).'.'.$pI['extension'];
+                                                       if (!@is_file($copyDestName) && !@is_file($origDestName)
+                                                               && $origDestName===t3lib_div::getFileAbsFileName($origDestName) && $copyDestName===t3lib_div::getFileAbsFileName($copyDestName))        {
+
+                                                                       // Making copies:
+                                                               t3lib_div::upload_copy_move(PATH_site.$fileInfo['original'],$origDestName);
+                                                               t3lib_div::upload_copy_move(PATH_site.$rec['ref_string'],$copyDestName);
+                                                               clearstatcache();
+
+                                                                       // Register this:
+                                                               $this->RTEmagic_copyIndex[$rec['tablename']][$rec['recuid']][$rec['field']][$rec['ref_string']] = substr($copyDestName,strlen(PATH_site));
+
+                                                                       // Check and update the record using the t3lib_refindex class:
+                                                               if (@is_file($copyDestName))    {
+                                                                       $sysRefObj = t3lib_div::makeInstance('t3lib_refindex');
+                                                                       $error = $sysRefObj->setReferenceValue($rec['hash'],substr($copyDestName,strlen(PATH_site)),FALSE,TRUE);
+                                                                       if ($error)     {
+                                                                               echo $this->newlog('t3lib_refindex::setReferenceValue(): '.$error,1);
+                                                                       } 
+                                                               } else $this->newlog('File "'.$copyDestName.'" was not created!',1);
+                                                       } else $this->newlog('Could not construct new unique names for file!',1);
+                                               } else $this->newlog('Maybe directory of file was not within "uploads/"?',1);
+                                       } else $this->newlog('Trying to copy RTEmagic files ('.$rec['ref_string'].' / '.$fileInfo['original'].') but one or both were missing',1);
+                               }
+                       }
+               }
+       }
+
 
 
 
index c81cfa5..84fb710 100755 (executable)
@@ -709,7 +709,7 @@ $TCA['sys_workspace'] = Array (
                                'type' => 'input',
                                'size' => '20',
                                'max' => '30',
-                               'eval' => 'required,trim'
+                               'eval' => 'required,trim,unique'
                        )
                ),
                'description' => Array (
index b66e04e..46f3e40 100755 (executable)
@@ -839,7 +839,7 @@ class clickMenu {
                                // rename
                        if (!in_array('rename',$this->disabledItems))   $menuItems['rename']=$this->FILE_launch($path,'file_rename.php','rename','rename.gif');
                                // upload
-                       if (!in_array('upload',$this->disabledItems) && is_dir($path)) $menuItems['upload']=$this->FILE_launch($path,'file_upload.php','upload','upload.gif');
+                       if (!in_array('upload',$this->disabledItems) && is_dir($path)) $menuItems['upload']=$this->FILE_launch($path,'file_upload.php','upload','upload.gif',TRUE);
                                // new
                        if (!in_array('new',$this->disabledItems) && is_dir($path)) $menuItems['new']=$this->FILE_launch($path,'file_newfolder.php','new','new_file.gif');
                                // info
@@ -900,13 +900,14 @@ class clickMenu {
         * @param       string          Script (eg. file_edit.php) to pass &target= to
         * @param       string          "type" is the code which fetches the correct label for the element from "cm."
         * @param       string          icon image-filename from "gfx/" (12x12 icon)
+        * @param       boolean         If set, the return URL parameter will not be set in the link
         * @return      array           Item array, element in $menuItems
         * @internal
         */
-       function FILE_launch($path,$script,$type,$image)        {
+       function FILE_launch($path,$script,$type,$image,$noReturnUrl=FALSE)     {
                $loc='top.content'.(!$this->alwaysContentFrame?'.list_frame':'');
 
-               $editOnClick='if('.$loc.'){'.$loc.".location.href=top.TS.PATH_typo3+'".$script.'?target='.rawurlencode($path)."&returnUrl='+top.rawurlencode(".$this->frameLocation($loc.'.document').");}";
+               $editOnClick='if('.$loc.'){'.$loc.".location.href=top.TS.PATH_typo3+'".$script.'?target='.rawurlencode($path).($noReturnUrl ? "'" : "&returnUrl='+top.rawurlencode(".$this->frameLocation($loc.'.document').")").";}";
 
                return $this->linkItem(
                        $this->label($type),
index 06d0c07..d8f09bb 100755 (executable)
@@ -107,6 +107,8 @@ class SC_alt_db_navframe {
                $this->pagetree = t3lib_div::makeInstance('webPageTree');
                $this->pagetree->ext_IconMode = $BE_USER->getTSConfigVal('options.pageTree.disableIconLinkToContextmenu');
                $this->pagetree->ext_showPageId = $BE_USER->getTSConfigVal('options.pageTree.showPageIdWithTitle');
+               $this->pagetree->ext_dontSeparateNotinmenuPages = $BE_USER->getTSConfigVal('options.pageTree.dontSeparateNotinmenuPages');
+               $this->pagetree->ext_dontAlphasortNotinmenuPages = $BE_USER->getTSConfigVal('options.pageTree.dontAlphasortNotinmenuPages');
                $this->pagetree->thisScript = 'alt_db_navframe.php';
                $this->pagetree->addField('alias');
                $this->pagetree->addField('shortcut');
@@ -168,6 +170,7 @@ class SC_alt_db_navframe {
                                // Click menu code is added:
                        $CMparts=$this->doc->getContextMenuCode();
                        $this->doc->bodyTagAdditions = $CMparts[1];
+                       $this->doc->bodyTagId = 'bodyTag';
                        $this->doc->JScode.= $CMparts[0];
                        $this->doc->postCode.= $CMparts[2];
                }
@@ -217,6 +220,14 @@ class SC_alt_db_navframe {
                        ';
                }
 
+               if (!$GLOBALS['BE_USER']->getTSConfigVal('options.pageTree.hideFilter'))        {
+                       $this->content.= '
+                                       <div class="bgColor4">
+                                               Filter: <input type="text" value="" name="_livesearch" id="_livesearch" onkeyup="filter(this.value);"/>
+                                       </div><br>
+                       ';
+               }
+
                        // Outputting Temporary DB mount notice:
                if ($this->active_tempMountPoint)       {
                        $this->content.= '
index 1d49326..fc3d7c7 100644 (file)
@@ -621,7 +621,7 @@ class SC_alt_main {
                                }
                                Element.setStyle(menyLayerObj,{top: \''.$this->topMenu.'px\'}); 
                                
-                               Effect.Appear(menyLayerObj,{duration: 0.4});
+                               Effect.Appear(menyLayerObj,{duration: 0.2});
                        }       
                        
                        function menuOpenSub(el)        {
@@ -632,7 +632,7 @@ class SC_alt_main {
                                        if (node.nodeType==1)   {       // This type seems to represent tags, not CDATA (which for some reason stops JS execution!)
                                                //debugObj(node);
                                                if (Element.hasClassName(node,"menulayer") && !Element.visible(node))   {
-                                                       Effect.Appear(node,{duration: 0.4});
+                                                       Effect.Appear(node,{duration: 0.2});
                                                        Element.setStyle(node,{left: ($(el).offsetLeft + Element.getDimensions(el).width - 5) +\'px\'}); 
                                                        
                                                        if (el.id && menuItemObjects[""+el.id]) {
index 0d75444..1ddb4b4 100755 (executable)
@@ -73,6 +73,8 @@ class webPageTree extends t3lib_browseTree {
 
        var $ext_showPageId;
        var $ext_IconMode;
+       var $ext_dontSeparateNotinmenuPages;
+       var $ext_dontAlphasortNotinmenuPages;
        var $ajaxStatus = false; // Indicates, whether the ajax call was successful, i.e. the requested page has been found
 
        /**
@@ -394,10 +396,43 @@ class webPageTree extends t3lib_browseTree {
                $c = $this->getDataCount($res);
                $crazyRecursionLimiter = 999;
 
-                       // Traverse the records:
+               $inMenuPages = array();
+               $outOfMenuPages = array();
+               $outOfMenuPagesTextIndex = array();
                while ($crazyRecursionLimiter > 0 && $row = $this->getDataNext($res,$subCSSclass))      {
-                       $a++;
                        $crazyRecursionLimiter--;
+       
+                               // Not in menu:
+                       if (!$this->ext_dontSeparateNotinmenuPages && (t3lib_div::inList('5,6',$row['doktype']) || $row['doktype']>=200 || $row['nav_hide']))   {
+                               $outOfMenuPages[] = $row;
+                               $outOfMenuPagesTextIndex[] = ($row['doktype']>=200 ? 'zzz'.$row['doktype'].'_' : '').$row['title'];
+                       } else {
+                               $inMenuPages[] = $row;
+                       }
+               }
+
+               $label_shownAlphabetically = "";
+               if (count($outOfMenuPages))     {
+                               // Sort out-of-menu pages:
+                       $outOfMenuPages_alphabetic = array();
+                       if (!$this->ext_dontAlphasortNotinmenuPages)    {
+                               asort($outOfMenuPagesTextIndex);
+                               $label_shownAlphabetically = " (alphabetic)";
+                       } 
+                       foreach($outOfMenuPagesTextIndex as $idx => $txt)       {
+                               $outOfMenuPages_alphabetic[] = $outOfMenuPages[$idx];
+                       }
+       
+                               // Merge:
+                       $outOfMenuPages_alphabetic[0]['_FIRST_NOT_IN_MENU']=TRUE;
+                       $allRows = array_merge($inMenuPages,$outOfMenuPages_alphabetic);
+               } else {
+                       $allRows = $inMenuPages;
+               }
+
+                       // Traverse the records:
+               foreach ($allRows as $row)      {
+                       $a++;
                        
                        $newID = $row['uid'];
                        $this->tree[]=array();    // Reserve space.
@@ -429,7 +464,13 @@ class webPageTree extends t3lib_browseTree {
 
                                // Set HTML-icons, if any:
                        if ($this->makeHTML)    {
-                               $HTML = $this->PMicon($row,$a,$c,$nextCount,$exp);
+                               if ($row['_FIRST_NOT_IN_MENU']) {
+                                       $HTML = '<img'.t3lib_iconWorks::skinImg($this->backPath,'gfx/ol/line.gif').' alt="" /><br/><img'.t3lib_iconWorks::skinImg($this->backPath,'gfx/ol/line.gif').' alt="" /><i>Not shown in menu'.$label_shownAlphabetically.':</i><br>';
+                               } else {
+                                       $HTML = '';
+                               }
+                               
+                               $HTML.= $this->PMicon($row,$a,$c,$nextCount,$exp);
                                $HTML.= $this->wrapStop($this->getIcon($row),$row);
                        }
 
index 64ef149..c5c89c2 100755 (executable)
@@ -118,6 +118,7 @@ class SC_file_upload {
                $this->number = t3lib_div::_GP('number');
                $this->target = t3lib_div::_GP('target');
                $this->returnUrl = t3lib_div::_GP('returnUrl');
+               $this->returnUrl = $this->returnUrl ? $this->returnUrl : t3lib_div::getIndpEnv('TYPO3_SITE_URL').TYPO3_mainDir.'file_list.php?id='.rawurlencode($this->target);
 
                if (empty($this->number))       {
                        $defaultFileUploads = $GLOBALS['BE_USER']->getTSConfigVal('options.defaultFileUploads');
index c4d92ff..d97889c 100644 (file)
@@ -170,6 +170,20 @@ class wslib_gui {
                        }
                }
 
+                       // Page-browser:
+               $resultsPerPage = 50;
+               $pointer = t3lib_div::_GP('browsePointer');
+               $browseStat = $this->cropWorkspaceOverview_list($pArray,$pointer,$resultsPerPage);
+               $browse = '';
+               $browse.='Showing '.$browseStat['begin'].' to '.($browseStat['end'] ? $browseStat['end'].' out of '.$browseStat['allItems'] : $browseStat['allItems']).' versions:<br/>';
+               if (!($browseStat['begin']==1 && !$browseStat['end']))  {
+                       for($a=0;$a<ceil($browseStat['allItems']/$resultsPerPage);$a++) {
+                               $browse.=($a==(int)$pointer?'<b>':'').'<a href="'.htmlspecialchars('index.php?browsePointer='.rawurlencode($a)).'">['.($a+1).']</a>'.($a==(int)$pointer?'</b>':'').' ';
+                       }
+                       $browse.='<br/>';
+               }
+               $browse.='<br/>';
+               
                $workspaceOverviewList = $this->displayWorkspaceOverview_list($pArray);
                if ($workspaceOverviewList || $this->alwaysDisplayHeader) {
                        // Make header of overview:
@@ -211,7 +225,7 @@ class wslib_gui {
                                ') . $table;
                        }
 
-                       return $table . $this->markupNewOriginals();
+                       return $browse . $table . $this->markupNewOriginals();
                }
                return '';
        }
@@ -503,6 +517,71 @@ class wslib_gui {
        }
 
        /**
+        * Filtering out items in pArray according to pointer and result-per-page setting
+        * 
+        * @param       array           Hierarchical storage of the elements to display (see displayWorkspaceOverview() / displayWorkspaceOverview_setInPageArray())
+        * @return      array           Returns statistics about the pointer state.
+        */
+       function cropWorkspaceOverview_list(&$pArray,$pointer=0,$resPerPage=50,$stat=array())   {
+
+                       // Traverse $pArray
+               if (is_array($pArray))  {
+                       foreach($pArray as $k => $v)    {
+                               if (t3lib_div::testInt($k))     {
+
+                                       if (is_array($pArray[$k.'_']))  {
+                                               foreach($pArray[$k.'_'] as $table => $oidArray) {
+                                                       foreach($oidArray as $oid => $recs)     {
+                                                                       
+                                                                       // Check, if the item count has reached the point where we want to set the in-point.    
+                                                               $beginWasSet = FALSE;
+                                                               if (!isset($stat['begin']) && (int)$stat['allItems'] >= $pointer*$resPerPage)   {
+                                                                       $stat['begin']=(int)$stat['allItems']+1;
+                                                                       $beginWasSet = TRUE;
+                                                               }
+                                                               
+                                                                       // If in-point is not set, unset the previous items.
+                                                               if (!isset($stat['begin']))     {
+                                                                       unset($pArray[$k.'_'][$table][$oid]);
+                                                               }
+
+                                                                       // Increase counter:
+                                                               $stat['allItems']+=count($recs);
+
+                                                                       // Check if end-point is reached:
+                                                               if (!$beginWasSet && !isset($stat['end']) && $stat['allItems'] > ($pointer+1)*$resPerPage)      {
+                                                                       $stat['end']=$stat['allItems']-1;
+                                                               }
+
+                                                                       // If end-point is reached, unset following items.
+                                                               if (isset($stat['end']))        {
+                                                                       unset($pArray[$k.'_'][$table][$oid]);
+                                                               }
+                                                       }
+                                                       
+                                                               // Clean-up if no more items:
+                                                       if (!count($pArray[$k.'_'][$table]))    {
+                                                               unset($pArray[$k.'_'][$table]);
+                                                       }
+                                               }
+
+                                                       // Clean-up if no more items:
+                                               if (!count($pArray[$k.'_']))    {
+                                                       unset($pArray[$k.'_']);
+                                               }
+                                       }
+                                       
+                                               // Call recursively for sub-rows:
+                                       if (is_array($pArray[$k.'.']))  {
+                                               $stat = $this->cropWorkspaceOverview_list($pArray[$k.'.'],$pointer,$resPerPage,$stat);
+                                       }
+                               }
+                       }
+               }
+               return $stat;
+       }
+
+       /**
         * Create indentation, icon and title for the page tree identification for the list.
         *
         * @param       integer         Page UID (record will be looked up again)
@@ -774,7 +853,7 @@ class wslib_gui {
                                )).'">'.
                                '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/insert1.gif','width="14" height="14"').' alt="" align="top" title="' . $LANG->getLL('img_title_publish') . '" />'.
                                '</a>';
-                       if ($GLOBALS['BE_USER']->workspaceSwapAccess() && (int)$rec_on['t3ver_state']!==1 && (int)$rec_off['t3ver_state']!==2)  {
+                       if ($GLOBALS['BE_USER']->workspaceSwapAccess()) {               //  && (int)$rec_on['t3ver_state']!==1 && (int)$rec_off['t3ver_state']!==2
                                $actionLinks.=
                                        '<a href="'.htmlspecialchars($this->doc->issueCommand(
                                        '&cmd['.$table.']['.$rec_on['uid'].'][version][action]=swap'.
index 3bd58fa..f914def 100755 (executable)
@@ -367,6 +367,14 @@ class SC_mod_user_ws_index extends t3lib_SCbase {
                        } else {
                                $actionLinks.= $this->doc->icons(1) . $LANG->getLL('no_publish_permission');
                        }
+                       
+                               // Preview of workspace link
+                       if (t3lib_div::_POST('_previewLink'))   {
+                               $previewUrl = t3lib_div::getIndpEnv('TYPO3_SITE_URL').'index.php?ADMCMD_prev='.t3lib_BEfunc::compilePreviewKeyword('', $GLOBALS['BE_USER']->user['uid'],60*60*24*2,$GLOBALS['BE_USER']->workspace);
+                               $actionLinks.=  '<br/>Any user can browse the workspace frontend using this link for the next 48 hours (does not require backend login):<br/><br/><a target="_blank" href="'.htmlspecialchars($previewUrl).'">'.$previewUrl.'</a>';
+                       } else {
+                               $actionLinks.= '<input type="submit" name="_previewLink" value="Generate Workspace Preview Link" />';
+                       }
                }
 
                $wsAccess = $GLOBALS['BE_USER']->checkWorkspace($GLOBALS['BE_USER']->workspaceRec);
index a1bbe3f..f72c826 100755 (executable)
@@ -495,11 +495,20 @@ BODY#typo3-mod-tools-em-index-php TR TD.extstate { color: #fff; font-weight: bol
 
 /* Workspace */
 DIV.workspace-info { padding: 0px 2px 0px 2px; margin: 0px 0px 4px 0px; border: 1px solid #999; }
-.ver-element { background-color: #ccddcc; }
-.ver-page { background-color: #ccccff; }
-.ver-branch { background-color: #ffcccc; }
-.ver-branchpoint { background-color: #ff9999; }
-.ver-versions { background-color: #ffffaa; }
+.ver-element, UL.tree UL LI.ver-element { background-color: #ccddcc; }
+.ver-page, UL.tree UL LI.ver-page { background-color: #ccccff; }
+.ver-branch, UL.tree UL LI.ver-branch { background-color: #ffcccc; }
+.ver-branchpoint, UL.tree UL LI.ver-branchpoint { background-color: #ff9999; }
+.ver-versions, UL.tree UL LI.ver-versions { background-color: #ffffaa; }
+
+UL.tree UL LI.ver-element UL { background-color: #f7f3ef; }
+UL.tree UL LI.ver-page UL { background-color: #f7f3ef; }
+UL.tree UL LI.ver-branch UL { background-color: #f7f3ef; }
+UL.tree UL LI.ver-branchpoint UL { background-color: #f7f3ef; }
+UL.tree UL LI.ver-versions UL { background-color: #f7f3ef; }
+
+
+
 TABLE.workspace-overview TD { vertical-align: top; }
 TABLE.ver-subtree A { text-decoration: none; }
 TABLE.ver-subtree TR TD.iconTitle { white-space: nowrap; }
@@ -531,6 +540,7 @@ TABLE.ver-subtree TR.typo3-ver-noComp TD { font-style: italic; }
 /* Dimmed span tag color (disabled) */
 .typo3-dimmed { color: #666; }
 .c-na { color: #666; font-style: italic; }
+.not-found A { color: #cccccc; }
 
 /* Red span tag color (warning, notice) */
 .typo3-red { color: red; }
index 7f6465d..80ba545 100755 (executable)
@@ -41,7 +41,7 @@ CREATE TABLE cache_typo3temp_log (
   md5hash varchar(32) DEFAULT '' NOT NULL,
   tstamp int(11) DEFAULT '0' NOT NULL,
   filename varchar(255) DEFAULT '' NOT NULL,
-  orig_filename varchar(255) NOT NULL,
+  orig_filename varchar(255) DEFAULT '' NOT NULL,
   PRIMARY KEY (md5hash)
 ) ENGINE=InnoDB;
 
@@ -196,7 +196,7 @@ CREATE TABLE pages_language_overlay (
 
   PRIMARY KEY (uid),
   KEY t3ver_oid (t3ver_oid,t3ver_wsid),
-  KEY parent (pid)
+  KEY parent (pid,sys_language_uid)
 );
 
 
@@ -279,7 +279,7 @@ CREATE TABLE sys_template (
   tx_impexp_origuid int(11) DEFAULT '0' NOT NULL,
   PRIMARY KEY (uid),
   KEY t3ver_oid (t3ver_oid,t3ver_wsid),
-  KEY parent (pid,sorting)
+  KEY parent (pid,sorting,deleted,hidden)
 );
 
 
@@ -408,5 +408,5 @@ CREATE TABLE pages (
   l18n_cfg tinyint(4) DEFAULT '0' NOT NULL,
   fe_login_mode tinyint(4) DEFAULT '0' NOT NULL,
   KEY alias (alias),
-  KEY parent (pid,sorting)
+  KEY parent (pid,sorting,deleted,hidden)
 );
index 7a472f5..b826015 100755 (executable)
         */
        function ADMCMD_preview(){
                $inputCode = t3lib_div::_GP('ADMCMD_prev');
+               $cookieTTL = 60*60;
+
+                       // If cookie is set, see what to do:
+               if ($_COOKIE['ADMCMD_prev'])    {
+                       
+                               // If no input code is given by GET method, lets look it up in a cookie (for workspace previews not only tied to the page) and update the cookie time:
+                       if (!$inputCode)        {
+                               $inputCode = $_COOKIE['ADMCMD_prev'];
+                               SetCookie('ADMCMD_prev', $inputCode, time()+$cookieTTL);
+                               echo "1";
+                       } else {        // Otherwise "log out":
+                               SetCookie('ADMCMD_prev', '', 0);
+                               die("You logged out from Workspace preview mode. Reload the browser to log in again.");
+                       }
+               }
 
+                       // If inputcode now, look up the settings:
                if ($inputCode) {
 
                                // Look for keyword configuration 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').'index.php?ADMCMD_prev='.$inputCode === t3lib_div::getIndpEnv('TYPO3_REQUEST_URL')) {
-
-                                                       // Unserialize configuration:
-                                               $previewConfig = unserialize($previewData['config']);
+                                               // Unserialize configuration:
+                                       $previewConfig = unserialize($previewData['config']);
+
+                                       if ($previewConfig['fullWorkspace']) {  // For full workspace preview we only ADD a get variable to set the preview of the workspace - so all other Get vars are accepted. Hope this is not a security problem. Still posting is not allowed and even if a backend user get initialized it shouldn't lead to situations where users can use those credentials.
+                                       
+                                                       // Set the workspace preview value:
+                                               t3lib_div::_GETset($previewConfig['fullWorkspace'],'ADMCMD_previewWS');
+                                               
+                                                       // If ADMCMD_prev is set the $inputCode value cannot come from a cookie and we set that cookie here. Next time it will be found from the cookie if ADMCMD_prev is not set again...
+                                               if (t3lib_div::_GP('ADMCMD_prev'))      {
+                                                       SetCookie('ADMCMD_prev', t3lib_div::_GP('ADMCMD_prev'), time()+$cookieTTL);     // Lifetime is 1 hour, does it matter much? Requires the user to click the link from their email again if it expires.
+                                               }
+                                               return $previewConfig;
+                                       } elseif (t3lib_div::getIndpEnv('TYPO3_SITE_URL').'index.php?ADMCMD_prev='.$inputCode === t3lib_div::getIndpEnv('TYPO3_REQUEST_URL'))   {
 
                                                        // Set GET variables:
                                                $GET_VARS = '';
index e9771cf..3370617 100755 (executable)
@@ -37,7 +37,7 @@
                        <label index="label_status">Status:</label>
                        <label index="label_pagetree">Pagetree:</label>
                        <label index="label_live_version">Live Version:</label>
-                       <label index="label_draft_versions">Draft Versions:</label>
+                       <label index="label_draft_versions">Workspace Versions:</label>
                        <label index="label_stage">Stage:</label>
                        <label index="label_publish">Publish:</label>
                        <label index="label_lifecycle">Lifecycle:</label>
index c19ffdc..96bd5d6 100644 (file)
@@ -86,19 +86,20 @@ NIGHTLY REPORTS OF PROBLEMS IN THE SYSTEM:
 If you wish to scan your TYPO3 installations for problems with a cronjob or so, a shell script that outputs a report could look like this:
 
        #!/bin/sh
-       /[WEBROOT_ABS_PATH]/typo3/cli_dispatch.phpsh lowlevel_cleaner missing_files -r -v 2 -s --refindex check
-       /[WEBROOT_ABS_PATH]/typo3/cli_dispatch.phpsh lowlevel_cleaner double_files -r -v 2 -s --refindex ignore
-       /[WEBROOT_ABS_PATH]/typo3/cli_dispatch.phpsh lowlevel_cleaner lost_files -r -v 2 -s --refindex ignore
-       /[WEBROOT_ABS_PATH]/typo3/cli_dispatch.phpsh lowlevel_cleaner orphan_records -r -v 2 -s
-       /[WEBROOT_ABS_PATH]/typo3/cli_dispatch.phpsh lowlevel_cleaner versions -r -v 2 -s
-       /[WEBROOT_ABS_PATH]/typo3/cli_dispatch.phpsh lowlevel_cleaner deleted -r -v 1 -s
-       /[WEBROOT_ABS_PATH]/typo3/cli_dispatch.phpsh lowlevel_cleaner missing_relations -r -v 2 -s --refindex ignore
-       /[WEBROOT_ABS_PATH]/typo3/cli_dispatch.phpsh lowlevel_cleaner cleanflexform -r -v 2 -s
-       /[WEBROOT_ABS_PATH]/typo3/cli_dispatch.phpsh lowlevel_cleaner rte_images -r -v 2 -s --refindex ignore
+       /[WEBROOT_ABS_PATH]/typo3/dummy_4.0/typo3/cli_dispatch.phpsh lowlevel_cleaner orphan_records -r -v 2 -s
+       /[WEBROOT_ABS_PATH]/typo3/dummy_4.0/typo3/cli_dispatch.phpsh lowlevel_cleaner versions -r -v 2 -s
+       /[WEBROOT_ABS_PATH]/typo3/dummy_4.0/typo3/cli_dispatch.phpsh lowlevel_cleaner tx_templavoila_unusedce -r --refindex update -v 2 -s
+       /[WEBROOT_ABS_PATH]/typo3/dummy_4.0/typo3/cli_dispatch.phpsh lowlevel_cleaner double_files -r --refindex update -v 2 -s
+       /[WEBROOT_ABS_PATH]/typo3/dummy_4.0/typo3/cli_dispatch.phpsh lowlevel_cleaner deleted -r -v 1 -s
+       /[WEBROOT_ABS_PATH]/typo3/dummy_4.0/typo3/cli_dispatch.phpsh lowlevel_cleaner missing_relations -r --refindex update -v 2 -s
+       /[WEBROOT_ABS_PATH]/typo3/dummy_4.0/typo3/cli_dispatch.phpsh lowlevel_cleaner cleanflexform -r -v 2 -s
+       /[WEBROOT_ABS_PATH]/typo3/dummy_4.0/typo3/cli_dispatch.phpsh lowlevel_cleaner rte_images -r --refindex update -v 2 -s
+       /[WEBROOT_ABS_PATH]/typo3/dummy_4.0/typo3/cli_dispatch.phpsh lowlevel_cleaner missing_files -r --refindex update -v 2 -s 
+       /[WEBROOT_ABS_PATH]/typo3/dummy_4.0/typo3/cli_dispatch.phpsh lowlevel_cleaner lost_files -r --refindex update -v 2 -s   
 
 You may wish to set the verbosity level (-v) to "3" instead of "2" as in the case above, depending on how important you consider the warnings.
 You might also wish to disable tests like "deleted" which would report deleted records - something that might not warrant a warning, frankly speaking...
-
+If you append "--AUTOFIX --YES" to each test it will actually perform clean up operations after checking, however it is NOT RECOMMENDED to do that as a nightly cron-job! In addition you should study what repair operations each test does to your system before using it!
 
 
 ADDING YOUR OWN TOOLS TO THE TEST:
index 7e14474..017d9e9 100644 (file)
@@ -96,7 +96,7 @@ class tx_lowlevel_cleaner_core extends t3lib_cli {
                $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('--AUTOFIX [testName]', '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! The optional parameter "[testName]" works for some tool keys to limit the fixing to a particular test.');
                $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.');
index 40a0ee6..7315c32 100644 (file)
@@ -110,8 +110,8 @@ This will check the system for double files relations.';
                                '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_count' => array('count' => 0),
+                       'singleReferencesList_count' => array('count' => 0),
                        'multipleReferencesList' => array(),
                        'dirname_registry' => array(),
                        'missingFiles' => array(),
@@ -158,8 +158,8 @@ This will check the system for double files relations.';
                }
 
                        // Add count for multi-references:
-               $resultArray['multipleReferencesList_count'] = count($resultArray['multipleReferencesList']);
-               $resultArray['singleReferencesList_count'] = count($tempCount) - $resultArray['multipleReferencesList_count'];
+               $resultArray['multipleReferencesList_count']['count'] = count($resultArray['multipleReferencesList']);
+               $resultArray['singleReferencesList_count']['count'] = count($tempCount) - $resultArray['multipleReferencesList_count']['count'];
 
                        // Sort dirname registry and add warnings for directories outside uploads/
                ksort($resultArray['dirname_registry']);
index 5fcf15c..ffe2a66 100644 (file)
  */
 
 
+require_once (PATH_t3lib.'class.t3lib_basicfilefunc.php');
+require_once (PATH_t3lib.'class.t3lib_extfilefunc.php');
+
+
 /**
  * Looking for RTE images integrity
  *
@@ -76,14 +80,13 @@ Assumptions:
 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 versioned 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.
 
 Automatic Repair of Errors:
-- There is currently no automatic repair available
+- Will search for double-usages of RTEmagic images and make copies as required.
+- Lost files can be deleted automatically by setting the value "lostFiles" as an optional parameter to --AUTOFIX, but otherwise delete them manually if you do not recognize them as used somewhere the system does not know about.
 
 Manual repair suggestions:
 - Missing files: Re-insert missing files or edit record where the reference is found.
-- Lost files: Delete them if you do not recognize them as used somewhere the system does not know about.
 ');
 
                $this->cli_help['examples'] = '/.../cli_dispatch.phpsh lowlevel_cleaner rte_images -s -r
@@ -105,11 +108,13 @@ Reports problems with RTE images';
                        '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"). This list does not exclude files that might be missing.',1),
                                'RTEmagicFilePairs' => array('Statistical info about RTEmagic files','(copy used as index)',0),
+                               'doubleFiles' => array('Duplicate RTEmagic image files','These files are RTEmagic images found used in multiple records! RTEmagic images should be used by only one record at a time. A large amount of such images probably stems from previous versions of TYPO3 (before 4.2) which did not support making copies automatically of RTEmagic images in case of new copies / versions.',3),
                                'missingFiles' => array('Missing RTEmagic image files','These files are not found in the file system! Should be corrected!',3),
-                               'lostFiles' => array('Lost RTEmagic files from uploads/','These files you might be able to delete 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),
+                               'lostFiles' => array('Lost RTEmagic files from uploads/','These files you might be able to delete 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. Lost files can be auto-fixed but only if you specifically set "lostFiles" as parameter to the --AUTOFIX option.',2),
                                'warnings' => array('Warnings picked up','',2)
                        ),
                        'RTEmagicFilePairs' => array(),
+                       'doubleFiles' => array(),
                        'completeFileList' => array(),
                        'missingFiles' => array(),
                        'lostFiles' => array(),
@@ -154,6 +159,13 @@ Reports problems with RTE images';
                                        }
                                }
                        }
+
+                               // Searching for duplicates:
+                       foreach($resultArray['RTEmagicFilePairs'] as $fileName => $fileInfo) {
+                               if ($fileInfo['count']>1 && $fileInfo['exists'] && $fileInfo['original_exists'])        {
+                                       $resultArray['doubleFiles'][$fileName] = $fileInfo['usedIn'];
+                               }
+                       }
                }
 
                        // Now, ask for RTEmagic files inside uploads/ folder:
@@ -168,6 +180,8 @@ Reports problems with RTE images';
                        }
                }
 
+       #       print_r($resultArray);
+
                return $resultArray;
        }
 
@@ -179,8 +193,116 @@ Reports problems with RTE images';
         * @return      void
         */
        function main_autoFix($resultArray)     {
-               echo "There is currently no automatic repair available\n";
+               
+               $limitTo = $this->cli_args['--AUTOFIX'][0];
+
+               if (is_array($resultArray['doubleFiles']))      {
+                       if (!$limitTo || $limitTo==='doubleFiles')      {
+                       
+                               echo 'FIXING double-usages of RTE files in uploads/: '.chr(10);
+                               foreach($resultArray['RTEmagicFilePairs'] as $fileName => $fileInfo) {
+                               
+                                               // Only fix something if there is a usage count of more than 1 plus if both original and copy exists:
+                                       if ($fileInfo['count']>1 && $fileInfo['exists'] && $fileInfo['original_exists'])        {
+                               
+                                                       // Traverse all records using the file:
+                                               $c=0;
+                                               foreach($fileInfo['usedIn'] as $hash => $recordID)      {
+                                                       if ($c==0)      {
+                                                               echo '  Keeping file '.$fileName.' for record '.$recordID.chr(10);
+                                                       } else {
+                                                                       // CODE below is adapted from "class.tx_impexp.php" where there is support for duplication of RTE images:
+                                                               echo '  Copying file '.basename($fileName).' for record '.$recordID.' ';
+                                               
+                                                                       // Initialize; Get directory prefix for file and set the original name:
+                                                               $dirPrefix = dirname($fileName).'/';
+                                                               $rteOrigName = basename($fileInfo['original']);
+
+                                                                       // If filename looks like an RTE file, and the directory is in "uploads/", then process as a RTE file!
+                                                               if ($rteOrigName && t3lib_div::isFirstPartOfStr($dirPrefix,'uploads/') && @is_dir(PATH_site.$dirPrefix))        {       // RTE:
+
+                                                                               // From the "original" RTE filename, produce a new "original" destination filename which is unused. 
+                                                                       $fileProcObj = &$this->getFileProcObj();
+                                                                       $origDestName = $fileProcObj->getUniqueName($rteOrigName, PATH_site.$dirPrefix);
+
+                                                                               // Create copy file name:
+                                                                       $pI = pathinfo($fileName);
+                                                                       $copyDestName = dirname($origDestName).'/RTEmagicC_'.substr(basename($origDestName),10).'.'.$pI['extension'];
+                                                                       if (!@is_file($copyDestName) && !@is_file($origDestName)
+                                                                               && $origDestName===t3lib_div::getFileAbsFileName($origDestName) && $copyDestName===t3lib_div::getFileAbsFileName($copyDestName))        {
+
+                                                                               echo ' to '.basename($copyDestName);
+
+                                                                               if ($bypass = $this->cli_noExecutionCheck($fileName))   {
+                                                                                       echo $bypass;
+                                                                               } else {
+                                                                                               // Making copies:
+                                                                                       t3lib_div::upload_copy_move(PATH_site.$fileInfo['original'],$origDestName);
+                                                                                       t3lib_div::upload_copy_move(PATH_site.$fileName,$copyDestName);
+                                                                                       clearstatcache();
+
+                                                                                       if (@is_file($copyDestName))    {
+                                                                                               $sysRefObj = t3lib_div::makeInstance('t3lib_refindex');
+                                                                                               $error = $sysRefObj->setReferenceValue($hash,substr($copyDestName,strlen(PATH_site)));
+                                                                                               if ($error)     {
+                                                                                                       echo '  - ERROR:        t3lib_refindex::setReferenceValue(): '.$error.chr(10);
+                                                                                                       exit;
+                                                                                               } else echo " - DONE";
+                                                                                       } else {
+                                                                                               echo '  - ERROR: File "'.$copyDestName.'" was not created!';
+                                                                                       }
+                                                                               }
+                                                                       } else echo '   - ERROR: Could not construct new unique names for file!';
+                                                               } else echo '   - ERROR: Maybe directory of file was not within "uploads/"?';
+                                                               echo chr(10);
+                                                       }
+                                                       $c++;
+                                               }
+                                       }
+                               }
+                       } else echo 'Bypassing fixing of double-usages since --AUTOFIX was not "doubleFiles"'.chr(10);
+               }
+               
+               
+               if (is_array($resultArray['lostFiles']))        {
+                       if ($limitTo==='lostFiles')     {
+                               echo 'Removing lost RTEmagic files from folders inside uploads/: '.chr(10);
+
+                               foreach($resultArray['lostFiles'] as $key => $value)    {
+                                       $absFileName = t3lib_div::getFileAbsFileName($value);
+                                       echo 'Deleting file: "'.$absFileName.'": ';
+                                       if ($bypass = $this->cli_noExecutionCheck($absFileName))        {
+                                               echo $bypass;
+                                       } else {
+                                               if ($absFileName && @is_file($absFileName))     {
+                                                       unlink($absFileName);
+                                                       echo 'DONE';
+                                               } else {
+                                                       echo '  ERROR: File "'.$absFileName.'" was not found!';
+                                               }
+                                       }
+                                       echo chr(10);
+                               }
+                       } 
+               } else echo 'Bypassing fixing of double-usages since --AUTOFIX was not "lostFiles"'.chr(10);
        }
+       
+       /**
+        * Returns file processing object, initialized only once.
+        *
+        * @return      object          File processor object
+        */
+       function &getFileProcObj()      {
+               global $FILEMOUNTS, $TYPO3_CONF_VARS, $BE_USER;
+
+               if (!is_object($this->fileProcObj))     {
+                       $this->fileProcObj = t3lib_div::makeInstance('t3lib_extFileFunctions');
+                       $this->fileProcObj->init($FILEMOUNTS, $TYPO3_CONF_VARS['BE']['fileExtensions']);
+                       $this->fileProcObj->init_actionPerms($BE_USER->user['fileoper_perms']);
+               }
+
+               return $this->fileProcObj;
+       }       
 }
 
 ?>
\ No newline at end of file
index df2ae4c..3589c86 100644 (file)
@@ -18,8 +18,15 @@ table#typo3-versionSelector td {
        color: #FFFFFF;
 }
 
-.ver-element { background-color: #D7DBE2; }
-.ver-page { background-color: #ccccff; }
-.ver-branch { background-color: #ffcccc; }
-.ver-branchpoint { background-color: #ff9999; }
-.ver-versions { background-color: #ffffaa; }
+.ver-element, UL.tree UL LI.ver-element { background-color: #D7DBE2; }
+.ver-page, UL.tree UL LI.ver-page { background-color: #ccccff; }
+.ver-branch, UL.tree UL LI.ver-branch { background-color: #ffcccc; }
+.ver-branchpoint, UL.tree UL LI.ver-branchpoint { background-color: #ff9999; }
+.ver-versions, UL.tree UL LI.ver-versions { background-color: #ffffaa; }
+
+UL.tree UL LI.ver-element UL { background-color: #efeff4; }
+UL.tree UL LI.ver-page UL { background-color: #efeff4; }
+UL.tree UL LI.ver-branch UL { background-color: #efeff4; }
+UL.tree UL LI.ver-branchpoint UL { background-color: #efeff4; }
+UL.tree UL LI.ver-versions UL { background-color: #efeff4; }
+
index 19f8736..3c7be7c 100755 (executable)
@@ -680,7 +680,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;
+                       $params = 'id='.$this->id.'&ADMCMD_previewWS='.$GLOBALS['BE_USER']->workspace;
                        $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);
@@ -743,7 +743,7 @@ class tx_version_cm1 extends t3lib_SCbase {
                        $tableRows[] = '
                                <tr class="bgColor5 tableheader">
                                        '.($this->diffOnly?'':'<td nowrap="nowrap" colspan="2">Live Version:</td>').'
-                                       <td nowrap="nowrap" colspan="2">Draft Versions:</td>
+                                       <td nowrap="nowrap" colspan="2">Workspace Versions:</td>
                                        <td nowrap="nowrap"'.($this->diffOnly?' colspan="2"':' colspan="4"').'>Controls:</td>
                                </tr>';
 
@@ -832,6 +832,10 @@ class tx_version_cm1 extends t3lib_SCbase {
                                                                $diffCode.= $diffHTML;
                                                        } elseif ($rec_off['t3ver_state']==2)   {
                                                                $diffCode.= $this->doc->icons(2).'Deleted element<br/>';
+                                                       } elseif ($rec_on['t3ver_state']==3)    {
+                                                               $diffCode.= $this->doc->icons(1).'Move-to placeholder (destination)<br/>';
+                                                       } elseif ($rec_off['t3ver_state']==4)   {
+                                                               $diffCode.= $this->doc->icons(1).'Move-to pointer (source)<br/>';
                                                        } else {
                                                                $diffCode.= ($diffPct<0 ? 'N/A' : ($diffPct ? $diffPct.'% change:' : ''));
                                                                $diffCode.= $diffHTML;
@@ -1621,7 +1625,7 @@ class tx_version_cm1 extends t3lib_SCbase {
                                                )).'">'.
                                '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/insert1.gif','width="14" height="14"').' alt="" align="top" title="Publish" />'.
                                '</a>';
-                       if ($GLOBALS['BE_USER']->workspaceSwapAccess() && (int)$rec_on['t3ver_state']!==1 && (int)$rec_off['t3ver_state']!==2)  {
+                       if ($GLOBALS['BE_USER']->workspaceSwapAccess()) {               //  && (int)$rec_on['t3ver_state']!==1 && (int)$rec_off['t3ver_state']!==2
                                $actionLinks.=
                                        '<a href="'.htmlspecialchars($this->doc->issueCommand(
                                                        '&cmd['.$table.']['.$rec_on['uid'].'][version][action]=swap'.
index e64e960..f51bf31 100755 (executable)
@@ -34,6 +34,43 @@ function refresh_nav() { window.setTimeout('Tree.refresh();',0); }
        // compatibility. Please use the function in the "Tree" object for future implementations.
 function hilight_row(frameSetModule, highLightID) { Tree.highlightActiveItem(frameSetModule, highlightID); }
 
+       // Filters the tree by setting a class on items not matching search input string
+function filter(strToDim)      {
+       filterTraverse($("bodyTag").getElementsByClassName("tree")[0],strToDim);
+}
+
+
+       // returns the inner content of an item, this is need because gecko does not know the innerText property
+function getInnerText(el)      {
+       if (el.innerText)       {
+               return el.innerText;
+       } else {
+               return el.textContent;
+       }
+}
+
+function filterTraverse (eUL,strToDim) {
+       var searchRegex = new RegExp(strToDim, "i");
+       eUL.immediateDescendants().each(function(item) {
+               item.immediateDescendants().each(function(eLI) {
+                       if (eLI.nodeName=="UL") {
+                               filterTraverse(eLI,strToDim);
+                       };
+                       if (eLI.nodeName=="SPAN")       {
+                               if (strToDim)   {
+                                       if (getInnerText(eLI).search(searchRegex) != -1) {
+                                               eLI.removeClassName("not-found");
+                                       } else {
+                                               eLI.addClassName("not-found");
+                                       }
+                               } else {
+                                       eLI.removeClassName("not-found");
+                               }
+                       }
+               });
+       });
+}
+
 
 var Tree = {
        thisScript: null,
@@ -75,6 +112,7 @@ var Tree = {
                                $(obj.parentNode).replace(xhr.responseText);
                                this.registerDragDropHandlers();
                                this.reSelectActiveItem();
+                               filter($('_livesearch').value);
                        }.bind(this)
                });
        },
@@ -125,7 +163,6 @@ var Tree = {
                top.fsMod.navFrameHighlightedID[frameSetModule] = highlightID;
                if ($(highlightID)) Element.addClassName(highlightID, this.highlightClass);
        }
-
 }