Revert "[TASK] Rebuild the calcAge functionality"
[Packages/TYPO3.CMS.git] / typo3 / sysext / lowlevel / Classes / OrphanRecordsCommand.php
1 <?php
2 namespace TYPO3\CMS\Lowlevel;
3
4 /**
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 /**
18 * Looking for Orphan Records
19 *
20 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
21 */
22 class OrphanRecordsCommand extends CleanerCommand {
23
24 /**
25 * Constructor
26 *
27 * @todo Define visibility
28 */
29 public function __construct() {
30 parent::__construct();
31 // Setting up help:
32 $this->cli_options[] = array('--echotree level', 'When "level" is set to 1 or higher you will see the page of the page tree outputted as it is traversed. A value of 2 for "level" will show even more information.');
33 $this->cli_help['name'] = 'orphan_records -- To find records that has lost their connection with the page tree';
34 $this->cli_help['description'] = trim('
35 Assumptions:
36 - That all actively used records on the website from TCA configured tables are located in the page tree exclusively.
37
38 All records managed by TYPO3 via the TCA array configuration has to belong to a page in the page tree, either directly or indirectly as a version of another record.
39 VERY TIME, CPU and MEMORY intensive operation since the full page tree is looked up!
40
41 Automatic Repair of Errors:
42 - Silently deleting the orphaned records. In theory they should not be used anywhere in the system, but there could be references. See below for more details on this matter.
43
44 Manual repair suggestions:
45 - Possibly re-connect orphaned records to page tree by setting their "pid" field to a valid page id. A lookup in the sys_refindex table can reveal if there are references to a orphaned record. If there are such references (from records that are not themselves orphans) you might consider to re-connect the record to the page tree, otherwise it should be safe to delete it.
46 ');
47 $this->cli_help['todo'] = trim('
48 - Implement a check for references to orphaned records and if a reference comes from a record that is not orphaned itself, we might rather like to re-connect the record to the page tree.
49 - Implement that orphans can be fixed by setting the PID to a certain page instead of deleting.');
50 $this->cli_help['examples'] = '/.../cli_dispatch.phpsh lowlevel_cleaner orphan_records -s -r
51 Will report orphan uids from TCA tables.';
52 }
53
54 /**
55 * Find orphan records
56 * VERY CPU and memory intensive since it will look up the whole page tree!
57 *
58 * @return array
59 * @todo Define visibility
60 */
61 public function main() {
62 global $TYPO3_DB;
63 // Initialize result array:
64 $resultArray = array(
65 'message' => $this->cli_help['name'] . LF . LF . $this->cli_help['description'],
66 'headers' => array(
67 'orphans' => array('Index of orphaned records', '', 3),
68 'misplaced_at_rootlevel' => array('Records that should not be at root level but are.', 'Fix manually by moving record into page tree', 2),
69 'misplaced_inside_tree' => array('Records that should be at root level but are not.', 'Fix manually by moving record to tree root', 2),
70 'illegal_record_under_versioned_page' => array('Records that cannot be attached to a versioned page', '(Listed under orphaned records so is fixed along with orphans.)', 2)
71 ),
72 'orphans' => array(),
73 'misplaced_at_rootlevel' => array(),
74 // Subset of "all": Those that should not be at root level but are. [Warning: Fix by moving record into page tree]
75 'misplaced_inside_tree' => array(),
76 // Subset of "all": Those that are inside page tree but should be at root level [Warning: Fix by setting PID to zero]
77 'illegal_record_under_versioned_page' => array()
78 );
79 // zero = tree root, must use tree root if you wish to reverse selection to find orphans!
80 $startingPoint = 0;
81 $pt = \TYPO3\CMS\Core\Utility\GeneralUtility::milliseconds();
82 $this->genTree($startingPoint, 1000, (int)$this->cli_argValue('--echotree'));
83 $resultArray['misplaced_at_rootlevel'] = $this->recStats['misplaced_at_rootlevel'];
84 $resultArray['misplaced_inside_tree'] = $this->recStats['misplaced_inside_tree'];
85 $resultArray['illegal_record_under_versioned_page'] = $this->recStats['illegal_record_under_versioned_page'];
86 // Find orphans:
87 foreach ($GLOBALS['TCA'] as $tableName => $cfg) {
88 $idList = is_array($this->recStats['all'][$tableName]) && count($this->recStats['all'][$tableName]) ? implode(',', $this->recStats['all'][$tableName]) : 0;
89 // Select all records belonging to page:
90 $orphanRecords = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid', $tableName, 'uid NOT IN (' . $idList . ')', '', 'uid', '', 'uid');
91 if (count($orphanRecords)) {
92 $resultArray['orphans'][$tableName] = array();
93 foreach ($orphanRecords as $oR) {
94 $resultArray['orphans'][$tableName][$oR['uid']] = $oR['uid'];
95 }
96 }
97 }
98 return $resultArray;
99 }
100
101 /**
102 * Mandatory autofix function
103 * Will run auto-fix on the result array. Echos status during processing.
104 *
105 * @param array $resultArray Result array from main() function
106 * @return void
107 * @todo Define visibility
108 */
109 public function main_autoFix($resultArray) {
110 // Putting "pages" table in the bottom:
111 if (isset($resultArray['orphans']['pages'])) {
112 $_pages = $resultArray['orphans']['pages'];
113 unset($resultArray['orphans']['pages']);
114 $resultArray['orphans']['pages'] = $_pages;
115 }
116 // Traversing records:
117 foreach ($resultArray['orphans'] as $table => $list) {
118 echo 'Removing orphans from table "' . $table . '":' . LF;
119 foreach ($list as $uid) {
120 echo ' Flushing orphan record "' . $table . ':' . $uid . '": ';
121 if ($bypass = $this->cli_noExecutionCheck($table . ':' . $uid)) {
122 echo $bypass;
123 } else {
124 // Execute CMD array:
125 $tce = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\DataHandling\\DataHandler');
126 $tce->stripslashes_values = FALSE;
127 $tce->start(array(), array());
128 // Notice, we are deleting pages with no regard to subpages/subrecords - we do this
129 // since they should also be included in the set of orphans of course!
130 $tce->deleteRecord($table, $uid, TRUE, TRUE);
131 // Return errors if any:
132 if (count($tce->errorLog)) {
133 echo ' ERROR from "TCEmain":' . LF . 'TCEmain:' . implode((LF . 'TCEmain:'), $tce->errorLog);
134 } else {
135 echo 'DONE';
136 }
137 }
138 echo LF;
139 }
140 }
141 }
142
143 }