33312c1c94391e82018384991c03b93aef0ae623
[Packages/TYPO3.CMS.git] / typo3 / sysext / lowlevel / clmods / class.rte_images.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 1999-2011 Kasper Skårhøj (kasperYYYY@typo3.com)
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
18 *
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27 /**
28 * Cleaner module: RTE magicc images
29 * User function called from tx_lowlevel_cleaner_core configured in ext_localconf.php
30 *
31 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
32 */
33 /**
34 * Looking for RTE images integrity
35 *
36 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
37 * @package TYPO3
38 * @subpackage tx_lowlevel
39 */
40 class tx_lowlevel_rte_images extends tx_lowlevel_cleaner_core {
41
42 var $checkRefIndex = TRUE;
43
44 /**
45 * Constructor
46 *
47 * @return void
48 */
49 function __construct() {
50 parent::__construct();
51
52 // Setting up help:
53 $this->cli_help['name'] = 'rte_images -- 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.';
54 $this->cli_help['description'] = trim('
55 Assumptions:
56 - a perfect integrity of the reference index table (always update the reference index table before using this tool!)
57 - that all RTEmagic image files in the database are registered with the soft reference parser "images"
58 - images found in deleted records are included (means that you might find lost RTEmagic images after flushing deleted records)
59
60 The assumptions are not requirements by the TYPO3 API but reflects the de facto implementation of most TYPO3 installations.
61 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.
62 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!
63
64 Automatic Repair of Errors:
65 - Will search for double-usages of RTEmagic images and make copies as required.
66 - 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.
67
68 Manual repair suggestions:
69 - Missing files: Re-insert missing files or edit record where the reference is found.
70 ');
71
72 $this->cli_help['examples'] = '/.../cli_dispatch.phpsh lowlevel_cleaner rte_images -s -r
73 Reports problems with RTE images';
74 }
75
76 /**
77 * Analyse situation with RTE magic images. (still to define what the most useful output is).
78 * Fix methods: API in t3lib_refindex that allows to change the value of a reference (we could copy the files) or remove reference
79 *
80 * @return array
81 */
82 function main() {
83 global $TYPO3_DB;
84
85 // Initialize result array:
86 $resultArray = array(
87 'message' => $this->cli_help['name'].LF.LF.$this->cli_help['description'],
88 'headers' => array(
89 '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),
90 'RTEmagicFilePairs' => array('Statistical info about RTEmagic files', '(copy used as index)', 0),
91 '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),
92 'missingFiles' => array('Missing RTEmagic image files', 'These files are not found in the file system! Should be corrected!', 3),
93 '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),
94 ),
95 'RTEmagicFilePairs' => array(),
96 'doubleFiles' => array(),
97 'completeFileList' => array(),
98 'missingFiles' => array(),
99 'lostFiles' => array(),
100 );
101
102 // Select all RTEmagic files in the reference table (only from soft references of course)
103 $recs = $TYPO3_DB->exec_SELECTgetRows(
104 '*',
105 'sys_refindex',
106 'ref_table='.$TYPO3_DB->fullQuoteStr('_FILE', 'sys_refindex').
107 ' AND ref_string LIKE '.$TYPO3_DB->fullQuoteStr('%/RTEmagic%', 'sys_refindex').
108 ' AND softref_key='.$TYPO3_DB->fullQuoteStr('images', 'sys_refindex'),
109 '',
110 'sorting DESC'
111 );
112
113 // Traverse the files and put into a large table:
114 if (is_array($recs)) {
115 foreach($recs as $rec) {
116 $filename = basename($rec['ref_string']);
117 if (t3lib_div::isFirstPartOfStr($filename, 'RTEmagicC_')) {
118 $original = 'RTEmagicP_'.preg_replace('/\.[[:alnum:]]+$/', '', substr($filename, 10));
119 $infoString = $this->infoStr($rec);
120
121 // Build index:
122 $resultArray['RTEmagicFilePairs'][$rec['ref_string']]['exists'] = @is_file(PATH_site.$rec['ref_string']);
123 $resultArray['RTEmagicFilePairs'][$rec['ref_string']]['original'] = substr($rec['ref_string'], 0, -strlen($filename)).$original;
124 $resultArray['RTEmagicFilePairs'][$rec['ref_string']]['original_exists'] = @is_file(PATH_site.$resultArray['RTEmagicFilePairs'][$rec['ref_string']]['original']);
125 $resultArray['RTEmagicFilePairs'][$rec['ref_string']]['count']++;
126 $resultArray['RTEmagicFilePairs'][$rec['ref_string']]['usedIn'][$rec['hash']] = $infoString;
127
128 $resultArray['completeFileList'][$resultArray['RTEmagicFilePairs'][$rec['ref_string']]['original']]++;
129 $resultArray['completeFileList'][$rec['ref_string']]++;
130
131 // Missing files:
132 if (!$resultArray['RTEmagicFilePairs'][$rec['ref_string']]['exists']) {
133 $resultArray['missingFiles'][$rec['ref_string']] = $resultArray['RTEmagicFilePairs'][$rec['ref_string']]['usedIn'];
134 }
135 if (!$resultArray['RTEmagicFilePairs'][$rec['ref_string']]['original_exists']) {
136 $resultArray['missingFiles'][$resultArray['RTEmagicFilePairs'][$rec['ref_string']]['original']] = $resultArray['RTEmagicFilePairs'][$rec['ref_string']]['usedIn'];
137 }
138 }
139 }
140
141 // Searching for duplicates:
142 foreach($resultArray['RTEmagicFilePairs'] as $fileName => $fileInfo) {
143 if ($fileInfo['count']>1 && $fileInfo['exists'] && $fileInfo['original_exists']) {
144 $resultArray['doubleFiles'][$fileName] = $fileInfo['usedIn'];
145 }
146 }
147 }
148
149 // Now, ask for RTEmagic files inside uploads/ folder:
150 $cleanerModules = $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['lowlevel']['cleanerModules'];
151 $cleanerMode = t3lib_div::getUserObj($cleanerModules['lost_files'][0]);
152 $resLostFiles = $cleanerMode->main(array(), FALSE, TRUE);
153 if (is_array($resLostFiles['RTEmagicFiles'])) {
154 foreach($resLostFiles['RTEmagicFiles'] as $fileName) {
155 if (!isset($resultArray['completeFileList'][$fileName])) {
156 $resultArray['lostFiles'][$fileName] = $fileName;
157 }
158 }
159 }
160
161
162 ksort($resultArray['RTEmagicFilePairs']);
163 ksort($resultArray['completeFileList']);
164 ksort($resultArray['missingFiles']);
165 ksort($resultArray['doubleFiles']);
166 ksort($resultArray['lostFiles']);
167
168 return $resultArray;
169 }
170
171 /**
172 * Mandatory autofix function
173 * Will run auto-fix on the result array. Echos status during processing.
174 *
175 * @param array Result array from main() function
176 * @return void
177 */
178 function main_autoFix($resultArray) {
179
180 $limitTo = $this->cli_args['--AUTOFIX'][0];
181
182 if (is_array($resultArray['doubleFiles'])) {
183 if (!$limitTo || $limitTo==='doubleFiles') {
184
185 echo 'FIXING double-usages of RTE files in uploads/: '.LF;
186 foreach($resultArray['RTEmagicFilePairs'] as $fileName => $fileInfo) {
187
188 // Only fix something if there is a usage count of more than 1 plus if both original and copy exists:
189 if ($fileInfo['count']>1 && $fileInfo['exists'] && $fileInfo['original_exists']) {
190
191 // Traverse all records using the file:
192 $c=0;
193 foreach($fileInfo['usedIn'] as $hash => $recordID) {
194 if ($c==0) {
195 echo ' Keeping file '.$fileName.' for record '.$recordID.LF;
196 } else {
197 // CODE below is adapted from "class.tx_impexp.php" where there is support for duplication of RTE images:
198 echo ' Copying file '.basename($fileName).' for record '.$recordID.' ';
199
200 // Initialize; Get directory prefix for file and set the original name:
201 $dirPrefix = dirname($fileName).'/';
202 $rteOrigName = basename($fileInfo['original']);
203
204 // If filename looks like an RTE file, and the directory is in "uploads/", then process as a RTE file!
205 if ($rteOrigName && t3lib_div::isFirstPartOfStr($dirPrefix, 'uploads/') && @is_dir(PATH_site.$dirPrefix)) { // RTE:
206
207 // From the "original" RTE filename, produce a new "original" destination filename which is unused.
208 $fileProcObj = $this->getFileProcObj();
209 $origDestName = $fileProcObj->getUniqueName($rteOrigName, PATH_site.$dirPrefix);
210
211 // Create copy file name:
212 $pI = pathinfo($fileName);
213 $copyDestName = dirname($origDestName).'/RTEmagicC_'.substr(basename($origDestName), 10).'.'.$pI['extension'];
214 if (!@is_file($copyDestName) && !@is_file($origDestName)
215 && $origDestName===t3lib_div::getFileAbsFileName($origDestName) && $copyDestName===t3lib_div::getFileAbsFileName($copyDestName)) {
216
217 echo ' to '.basename($copyDestName);
218
219 if ($bypass = $this->cli_noExecutionCheck($fileName)) {
220 echo $bypass;
221 } else {
222 // Making copies:
223 t3lib_div::upload_copy_move(PATH_site.$fileInfo['original'], $origDestName);
224 t3lib_div::upload_copy_move(PATH_site.$fileName, $copyDestName);
225 clearstatcache();
226
227 if (@is_file($copyDestName)) {
228 $sysRefObj = t3lib_div::makeInstance('t3lib_refindex');
229 $error = $sysRefObj->setReferenceValue($hash, substr($copyDestName, strlen(PATH_site)));
230 if ($error) {
231 echo ' - ERROR: t3lib_refindex::setReferenceValue(): '.$error.LF;
232 exit;
233 } else echo " - DONE";
234 } else {
235 echo ' - ERROR: File "'.$copyDestName.'" was not created!';
236 }
237 }
238 } else echo ' - ERROR: Could not construct new unique names for file!';
239 } else echo ' - ERROR: Maybe directory of file was not within "uploads/"?';
240 echo LF;
241 }
242 $c++;
243 }
244 }
245 }
246 } else echo 'Bypassing fixing of double-usages since --AUTOFIX was not "doubleFiles"'.LF;
247 }
248
249
250 if (is_array($resultArray['lostFiles'])) {
251 if ($limitTo==='lostFiles') {
252 echo 'Removing lost RTEmagic files from folders inside uploads/: '.LF;
253
254 foreach($resultArray['lostFiles'] as $key => $value) {
255 $absFileName = t3lib_div::getFileAbsFileName($value);
256 echo 'Deleting file: "'.$absFileName.'": ';
257 if ($bypass = $this->cli_noExecutionCheck($absFileName)) {
258 echo $bypass;
259 } else {
260 if ($absFileName && @is_file($absFileName)) {
261 unlink($absFileName);
262 echo 'DONE';
263 } else {
264 echo ' ERROR: File "'.$absFileName.'" was not found!';
265 }
266 }
267 echo LF;
268 }
269 }
270 } else echo 'Bypassing fixing of double-usages since --AUTOFIX was not "lostFiles"'.LF;
271 }
272
273 /**
274 * Returns file processing object, initialized only once.
275 *
276 * @return object File processor object
277 */
278 function getFileProcObj() {
279 if (!is_object($this->fileProcObj)) {
280 $this->fileProcObj = t3lib_div::makeInstance('t3lib_extFileFunctions');
281 $this->fileProcObj->init($GLOBALS['FILEMOUNTS'], $GLOBALS['TYPO3_CONF_VARS']['BE']['fileExtensions']);
282 $this->fileProcObj->init_actionPerms($GLOBALS['BE_USER']->getFileoperationPermissions());
283 }
284 return $this->fileProcObj;
285 }
286 }
287
288 ?>