[TASK] Merge ext:integrity with ext:lowlevel
[Packages/TYPO3.CMS.git] / typo3 / sysext / lowlevel / Classes / DoubleFilesCommand.php
1 <?php
2 namespace TYPO3\CMS\Lowlevel;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 1999-2011 Kasper Skårhøj (kasperYYYY@typo3.com)
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 * A copy is found in the textfile GPL.txt and important notices to the license
19 * from the author is found in LICENSE.txt distributed with these scripts.
20 *
21 *
22 * This script is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * This copyright notice MUST APPEAR in all copies of the script!
28 ***************************************************************/
29 /**
30 * Cleaner module: Double Files
31 * User function called from tx_lowlevel_cleaner_core configured in ext_localconf.php
32 *
33 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
34 */
35 /**
36 * Looking for double files
37 *
38 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
39 * @package TYPO3
40 * @subpackage tx_lowlevel
41 */
42 class DoubleFilesCommand extends CleanerCommand {
43
44 /**
45 * @todo Define visibility
46 */
47 public $checkRefIndex = TRUE;
48
49 /**
50 * Constructor
51 *
52 * @todo Define visibility
53 */
54 public function __construct() {
55 parent::__construct();
56 // Setting up help:
57 $this->cli_help['name'] = 'double_files -- Looking for files from TYPO3 managed records which are referenced more than one time (only one time allowed)';
58 $this->cli_help['description'] = trim('
59 Assumptions:
60 - a perfect integrity of the reference index table (always update the reference index table before using this tool!)
61 - files found in deleted records are included (otherwise you would see a false list of lost files)
62
63 Files attached to records in TYPO3 using a "group" type configuration in TCA or FlexForm DataStructure are managed exclusively by the system and there must always exist a 1-1 reference between the file and the reference in the record.
64 This tool will expose when such files are referenced from multiple locations which is considered an integrity error.
65 If a multi-reference is found it was typically created because the record was copied or modified outside of TCEmain which will otherwise maintain the relations correctly.
66 Multi-references should be resolved to 1-1 references as soon as possible. The danger of keeping multi-references is that if the file is removed from one of the refering records it will actually be deleted in the file system, leaving missing files for the remaining referers!
67
68 Automatic Repair of Errors:
69 - The multi-referenced file is copied under a new name and references updated.
70
71 Manual repair suggestions:
72 - None that can not be handled by the automatic repair.
73 ');
74 $this->cli_help['examples'] = '/.../cli_dispatch.phpsh lowlevel_cleaner double_files -s -r
75 This will check the system for double files relations.';
76 }
77
78 /**
79 * Find managed files which are referred to more than one time
80 * Fix methods: API in t3lib_refindex that allows to change the value of a reference (we could copy the file) or remove reference
81 *
82 * @return array
83 * @todo Define visibility
84 */
85 public function main() {
86 global $TYPO3_DB;
87 // Initialize result array:
88 $resultArray = array(
89 'message' => $this->cli_help['name'] . LF . LF . $this->cli_help['description'],
90 'headers' => array(
91 'multipleReferencesList_count' => array('Number of multi-reference files', '(See below)', 0),
92 'singleReferencesList_count' => array('Number of files correctly referenced', 'The amount of correct 1-1 references', 0),
93 'multipleReferencesList' => array('Entries with files having multiple references', 'These are serious problems that should be resolved ASAP to prevent data loss! ' . $this->label_infoString, 3),
94 'dirname_registry' => array('Registry of directories in which files are found.', 'Registry includes which table/field pairs store files in them plus how many files their store.', 0),
95 '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),
96 'warnings' => array('Warnings picked up', '', 2)
97 ),
98 'multipleReferencesList_count' => array('count' => 0),
99 'singleReferencesList_count' => array('count' => 0),
100 'multipleReferencesList' => array(),
101 'dirname_registry' => array(),
102 'missingFiles' => array(),
103 'warnings' => array()
104 );
105 // Select all files in the reference table not found by a soft reference parser (thus TCA configured)
106 $recs = $TYPO3_DB->exec_SELECTgetRows('*', 'sys_refindex', 'ref_table=' . $TYPO3_DB->fullQuoteStr('_FILE', 'sys_refindex') . ' AND softref_key=' . $TYPO3_DB->fullQuoteStr('', 'sys_refindex'), '', 'sorting DESC');
107 // Traverse the files and put into a large table:
108 $tempCount = array();
109 if (is_array($recs)) {
110 foreach ($recs as $rec) {
111 // Compile info string for location of reference:
112 $infoString = $this->infoStr($rec);
113 // Registering occurencies in directories:
114 $resultArray['dirname_registry'][dirname($rec['ref_string'])][($rec['tablename'] . ':' . $rec['field'])]++;
115 // Handle missing file:
116 if (!@is_file((PATH_site . $rec['ref_string']))) {
117 $resultArray['missingFiles'][$rec['ref_string']][$rec['hash']] = $infoString;
118 ksort($resultArray['missingFiles'][$rec['ref_string']]);
119 }
120 // Add entry if file has multiple references pointing to it:
121 if (isset($tempCount[$rec['ref_string']])) {
122 if (!is_array($resultArray['multipleReferencesList'][$rec['ref_string']])) {
123 $resultArray['multipleReferencesList'][$rec['ref_string']] = array();
124 $resultArray['multipleReferencesList'][$rec['ref_string']][$tempCount[$rec['ref_string']][1]] = $tempCount[$rec['ref_string']][0];
125 }
126 $resultArray['multipleReferencesList'][$rec['ref_string']][$rec['hash']] = $infoString;
127 ksort($resultArray['multipleReferencesList'][$rec['ref_string']]);
128 } else {
129 $tempCount[$rec['ref_string']] = array($infoString, $rec['hash']);
130 }
131 }
132 }
133 ksort($resultArray['missingFiles']);
134 ksort($resultArray['multipleReferencesList']);
135 // Add count for multi-references:
136 $resultArray['multipleReferencesList_count']['count'] = count($resultArray['multipleReferencesList']);
137 $resultArray['singleReferencesList_count']['count'] = count($tempCount) - $resultArray['multipleReferencesList_count']['count'];
138 // Sort dirname registry and add warnings for directories outside uploads/
139 ksort($resultArray['dirname_registry']);
140 foreach ($resultArray['dirname_registry'] as $dir => $temp) {
141 ksort($resultArray['dirname_registry'][$dir]);
142 if (!\TYPO3\CMS\Core\Utility\GeneralUtility::isFirstPartOfStr($dir, 'uploads/')) {
143 $resultArray['warnings'][\TYPO3\CMS\Core\Utility\GeneralUtility::shortmd5($dir)] = 'Directory "' . $dir . '" was outside uploads/ which is unusual practice in TYPO3 although not forbidden. Directory used by the following table:field pairs: ' . implode(',', array_keys($temp));
144 }
145 }
146 return $resultArray;
147 }
148
149 /**
150 * Mandatory autofix function
151 * Will run auto-fix on the result array. Echos status during processing.
152 *
153 * @param array $resultArray Result array from main() function
154 * @return void
155 * @todo Define visibility
156 */
157 public function main_autoFix($resultArray) {
158 foreach ($resultArray['multipleReferencesList'] as $key => $value) {
159 $absFileName = \TYPO3\CMS\Core\Utility\GeneralUtility::getFileAbsFileName($key);
160 if ($absFileName && @is_file($absFileName)) {
161 echo 'Processing file: ' . $key . LF;
162 $c = 0;
163 foreach ($value as $hash => $recReference) {
164 if ($c == 0) {
165 echo ' Keeping ' . $key . ' for record "' . $recReference . '"' . LF;
166 } else {
167 // Create unique name for file:
168 $fileFunc = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Utility\\File\\BasicFileUtility');
169 $newName = $fileFunc->getUniqueName(basename($key), dirname($absFileName));
170 echo ' Copying ' . $key . ' to ' . substr($newName, strlen(PATH_site)) . ' for record "' . $recReference . '": ';
171 if ($bypass = $this->cli_noExecutionCheck($recReference)) {
172 echo $bypass;
173 } else {
174 \TYPO3\CMS\Core\Utility\GeneralUtility::upload_copy_move($absFileName, $newName);
175 clearstatcache();
176 if (@is_file($newName)) {
177 $sysRefObj = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Database\\ReferenceIndex');
178 $error = $sysRefObj->setReferenceValue($hash, basename($newName));
179 if ($error) {
180 echo ' ERROR: TYPO3\\CMS\\Core\\Database\\ReferenceIndex::setReferenceValue(): ' . $error . LF;
181 die;
182 } else {
183 echo 'DONE';
184 }
185 } else {
186 echo ' ERROR: File "' . $newName . '" was not created!';
187 }
188 }
189 echo LF;
190 }
191 $c++;
192 }
193 } else {
194 echo ' ERROR: File "' . $absFileName . '" was not found!';
195 }
196 }
197 }
198
199 }
200
201
202 ?>