‪TYPO3CMS  10.4
WorkspaceVersionRecordsCommand.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use Symfony\Component\Console\Command\Command;
21 use Symfony\Component\Console\Input\InputInterface;
22 use Symfony\Component\Console\Input\InputOption;
23 use Symfony\Component\Console\Output\OutputInterface;
24 use Symfony\Component\Console\Style\SymfonyStyle;
33 
37 class ‪WorkspaceVersionRecordsCommand extends Command
38 {
39 
44  protected ‪$allWorkspaces = [0 => 'Live Workspace'];
45 
50  protected ‪$foundRecords = [
51  // All versions of records found
52  // Subset of "all" which are offline versions (pid=-1) [Informational]
53  'all_versioned_records' => [],
54  // All records that has been published and can therefore be removed permanently
55  // Subset of "versions" that is a count of 1 or more (has been published) [Informational]
56  'published_versions' => [],
57  // All versions that are offline versions in the Live workspace. You may wish to flush these if you only use
58  // workspaces for versioning since then you might find lots of versions piling up in the live workspace which
59  // have simply been disconnected from the workspace before they were published.
60  'versions_in_live' => [],
61  // Versions that has lost their connection to a workspace in TYPO3.
62  // Subset of "versions" that doesn't belong to an existing workspace [Warning: Fix by move to live workspace]
63  'invalid_workspace' => []
64  ];
65 
69  public function ‪configure()
70  {
71  $this
72  ->setDescription('Find all versioned records and possibly cleans up invalid records in the database.')
73  ->setHelp('Traverse page tree and find versioned records. Also list all versioned records, additionally with some inconsistencies in the database, which can cleaned up with the "action" option. If you want to get more detailed information, use the --verbose option.')
74  ->addOption(
75  'pid',
76  'p',
77  InputOption::VALUE_REQUIRED,
78  'Setting start page in page tree. Default is the page tree root, 0 (zero)'
79  )
80  ->addOption(
81  'depth',
82  'd',
83  InputOption::VALUE_REQUIRED,
84  'Setting traversal depth. 0 (zero) will only analyze start page (see --pid), 1 will traverse one level of subpages etc.'
85  )
86  ->addOption(
87  'dry-run',
88  null,
89  InputOption::VALUE_NONE,
90  'If this option is set, the records will not actually be deleted/modified, but just the output which records would be touched are shown'
91  )
92  ->addOption(
93  'action',
94  null,
95  InputOption::VALUE_OPTIONAL,
96  'Specify which action should be taken. Set it to "versions_in_live", "published_versions", "invalid_workspace" or "unused_placeholders"'
97  );
98  }
99 
107  protected function ‪execute(InputInterface $input, OutputInterface ‪$output)
108  {
109  // Make sure the _cli_ user is loaded
111 
112  $io = new SymfonyStyle($input, ‪$output);
113  $io->title($this->getDescription());
114 
115  $startingPoint = 0;
116  if ($input->hasOption('pid') && ‪MathUtility::canBeInterpretedAsInteger($input->getOption('pid'))) {
117  $startingPoint = ‪MathUtility::forceIntegerInRange((int)$input->getOption('pid'), 0);
118  }
119 
120  $depth = 1000;
121  if ($input->hasOption('depth') && ‪MathUtility::canBeInterpretedAsInteger($input->getOption('depth'))) {
122  $depth = ‪MathUtility::forceIntegerInRange((int)$input->getOption('depth'), 0);
123  }
124 
125  $action = '';
126  if ($input->hasOption('action') && !empty($input->getOption('action'))) {
127  $action = $input->getOption('action');
128  }
129 
130  // type unsafe comparison and explicit boolean setting on purpose
131  $dryRun = $input->hasOption('dry-run') && $input->getOption('dry-run') != false ? true : false;
132 
133  if ($io->isVerbose()) {
134  $io->section('Searching the database now for versioned records.');
135  }
136 
138 
139  // Find all records that are versioned
140  $this->‪traversePageTreeForVersionedRecords($startingPoint, $depth);
141  // Sort recStats (for diff'able displays)
142  foreach ($this->foundRecords as $kk => $vv) {
143  foreach ($this->foundRecords[$kk] as $tables => $recArrays) {
144  ksort($this->foundRecords[$kk][$tables]);
145  }
146  ksort($this->foundRecords[$kk]);
147  }
148 
149  $unusedPlaceholders = $this->‪findUnusedPlaceholderRecords();
150 
151  // Finding all move placeholders with inconsistencies
152  // Move-to placeholder records which have bad integrity
153  $invalidMovePlaceholders = $this->‪findInvalidMovePlaceholderRecords();
154 
155  // Finding move_id_check inconsistencies
156  // Checking if t3ver_move_id is correct. t3ver_move_id must only be set with online records having t3ver_state=3.
157  $recordsWithInvalidMoveIds = $this->‪findInvalidMoveIdRecords();
158 
159  if (!$io->isQuiet()) {
160  $numberOfVersionedRecords = 0;
161  foreach ($this->foundRecords['all_versioned_records'] as $records) {
162  $numberOfVersionedRecords += count($records);
163  }
164 
165  $io->section('Found ' . $numberOfVersionedRecords . ' versioned records in the database.');
166  if ($io->isVeryVerbose()) {
167  foreach ($this->foundRecords['all_versioned_records'] as $table => $records) {
168  $io->writeln('Table "' . $table . '"');
169  $io->listing($records);
170  }
171  }
172 
173  $numberOfPublishedVersions = 0;
174  foreach ($this->foundRecords['published_versions'] as $records) {
175  $numberOfPublishedVersions += count($records);
176  }
177  $io->section('Found ' . $numberOfPublishedVersions . ' versioned records that have been published.');
178  if ($io->isVeryVerbose()) {
179  foreach ($this->foundRecords['published_versions'] as $table => $records) {
180  $io->writeln('Table "' . $table . '"');
181  $io->listing($records);
182  }
183  }
184 
185  $numberOfVersionsInLiveWorkspace = 0;
186  foreach ($this->foundRecords['versions_in_live'] as $records) {
187  $numberOfVersionsInLiveWorkspace += count($records);
188  }
189  $io->section('Found ' . $numberOfVersionsInLiveWorkspace . ' versioned records that are in the live workspace.');
190  if ($io->isVeryVerbose()) {
191  foreach ($this->foundRecords['versions_in_live'] as $table => $records) {
192  $io->writeln('Table "' . $table . '"');
193  $io->listing($records);
194  }
195  }
196 
197  $numberOfVersionsWithInvalidWorkspace = 0;
198  foreach ($this->foundRecords['invalid_workspace'] as $records) {
199  $numberOfVersionsWithInvalidWorkspace += count($records);
200  }
201  $io->section('Found ' . $numberOfVersionsWithInvalidWorkspace . ' versioned records with an invalid workspace.');
202  if ($io->isVeryVerbose()) {
203  foreach ($this->foundRecords['invalid_workspace'] as $table => $records) {
204  $io->writeln('Table "' . $table . '"');
205  $io->listing($records);
206  }
207  }
208 
209  $io->section('Found ' . count($unusedPlaceholders) . ' unused placeholder records.');
210  if ($io->isVeryVerbose()) {
211  $io->listing(array_keys($unusedPlaceholders));
212  }
213 
214  $io->section('Found ' . count($invalidMovePlaceholders) . ' invalid move placeholders.');
215  if ($io->isVeryVerbose()) {
216  $io->listing($invalidMovePlaceholders);
217  }
218 
219  $io->section('Found ' . count($recordsWithInvalidMoveIds) . ' versions with an invalid move ID.');
220  if ($io->isVeryVerbose()) {
221  $io->listing($recordsWithInvalidMoveIds);
222  }
223  }
224 
225  // Actually permanently delete / update records
226  switch ($action) {
227  // All versions that are offline versions in the Live workspace. You may wish to flush these if you only use
228  // workspaces for versioning since then you might find lots of versions piling up in the live workspace which
229  // have simply been disconnected from the workspace before they were published.
230  case 'versions_in_live':
231  $io->section('Deleting versioned records in live workspace now. ' . ($dryRun ? ' (Not deleting now, just a dry run)' : ''));
232  $this->‪deleteRecords($this->foundRecords['versions_in_live'], $dryRun, $io);
233  break;
234 
235  // All records that has been published and can therefore be removed permanently
236  // Subset of "versions" that is a count of 1 or more (has been published)
237  case 'published_versions':
238  $io->section('Deleting published records in live workspace now. ' . ($dryRun ? ' (Not deleting now, just a dry run)' : ''));
239  $this->‪deleteRecords($this->foundRecords['published_versions'], $dryRun, $io);
240  break;
241 
242  // Versions that has lost their connection to a workspace in TYPO3.
243  // Subset of "versions" that doesn't belong to an existing workspace [Warning: Fix by move to live workspace]
244  case 'invalid_workspace':
245  $io->section('Moving versions in invalid workspaces to live workspace now. ' . ($dryRun ? ' (Not deleting now, just a dry run)' : ''));
246  $this->‪resetRecordsWithoutValidWorkspace($this->foundRecords['invalid_workspace'], $dryRun, $io);
247  break;
248 
249  // Finding all placeholders with no records attached
250  // Placeholder records which are not used anymore by offline versions.
251  case 'unused_placeholders':
252  $io->section('Deleting unused placeholder records now. ' . ($dryRun ? ' (Not deleting now, just a dry run)' : ''));
253  $this->‪deleteUnusedPlaceholders($unusedPlaceholders, $dryRun, $io);
254  break;
255 
256  default:
257  $io->note('No action specified, just displaying statistics. See --action option for details.');
258  break;
259  }
260  $io->success('All done!');
261  return 0;
262  }
263 
272  protected function ‪traversePageTreeForVersionedRecords(int $rootID, int $depth, bool $isInsideVersionedPage = false, bool $rootIsVersion = false)
273  {
274  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
275  $queryBuilder->getRestrictions()->removeAll();
276 
277  $pageRecord = $queryBuilder
278  ->select(
279  'deleted',
280  'title',
281  't3ver_count',
282  't3ver_wsid'
283  )
284  ->from('pages')
285  ->where($queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($rootID, \PDO::PARAM_INT)))
286  ->execute()
287  ->fetch();
288 
289  // If rootIsVersion is set it means that the input rootID is that of a version of a page. See below where the recursive call is made.
290  if ($rootIsVersion) {
291  $workspaceId = (int)$pageRecord['t3ver_wsid'];
292  $this->foundRecords['all_versioned_records']['pages'][$rootID] = $rootID;
293  // If it has been published and is in archive now...
294  if ($pageRecord['t3ver_count'] >= 1 && $workspaceId === 0) {
295  $this->foundRecords['published_versions']['pages'][$rootID] = $rootID;
296  }
297  // If it has been published and is in archive now...
298  if ($workspaceId === 0) {
299  $this->foundRecords['versions_in_live']['pages'][$rootID] = $rootID;
300  }
301  // If it doesn't belong to a workspace...
302  if (!isset($this->allWorkspaces[$workspaceId])) {
303  $this->foundRecords['invalid_workspace']['pages'][$rootID] = $rootID;
304  }
305  }
306  // Only check for records if not inside a version
307  if (!$isInsideVersionedPage) {
308  // Traverse tables of records that belongs to page
309  $tableNames = $this->‪getAllVersionableTables();
310  foreach ($tableNames as $tableName) {
311  if ($tableName !== 'pages') {
312  // Select all records belonging to page:
313  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
314  ->getQueryBuilderForTable($tableName);
315 
316  $queryBuilder->getRestrictions()->removeAll();
317 
318  $result = $queryBuilder
319  ->select('uid')
320  ->from($tableName)
321  ->where(
322  $queryBuilder->expr()->eq(
323  'pid',
324  $queryBuilder->createNamedParameter($rootID, \PDO::PARAM_INT)
325  )
326  )
327  ->execute();
328  while ($rowSub = $result->fetch()) {
329  // Add any versions of those records
330  $versions = ‪BackendUtility::selectVersionsOfRecord($tableName, $rowSub['uid'], 'uid,t3ver_wsid,t3ver_count' . (‪$GLOBALS['TCA'][$tableName]['ctrl']['delete'] ? ',' . ‪$GLOBALS['TCA'][$tableName]['ctrl']['delete'] : ''), null, true);
331  if (is_array($versions)) {
332  foreach ($versions as $verRec) {
333  if (!$verRec['_CURRENT_VERSION']) {
334  // Register version
335  $this->foundRecords['all_versioned_records'][$tableName][$verRec['uid']] = $verRec['uid'];
336  $workspaceId = (int)$verRec['t3ver_wsid'];
337  if ($verRec['t3ver_count'] >= 1 && $workspaceId === 0) {
338  // Only register published versions in LIVE workspace
339  // (published versions in draft workspaces are allowed)
340  $this->foundRecords['published_versions'][$tableName][$verRec['uid']] = $verRec['uid'];
341  }
342  if ($workspaceId === 0) {
343  $this->foundRecords['versions_in_live'][$tableName][$verRec['uid']] = $verRec['uid'];
344  }
345  if (!isset($this->allWorkspaces[$workspaceId])) {
346  $this->foundRecords['invalid_workspace'][$tableName][$verRec['uid']] = $verRec['uid'];
347  }
348  }
349  }
350  }
351  }
352  }
353  }
354  }
355  // Find subpages to root ID and traverse (only when rootID is not a version or is a branch-version):
356  if ($depth > 0) {
357  $depth--;
358  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
359  ->getQueryBuilderForTable('pages');
360 
361  $queryBuilder->getRestrictions()->removeAll();
362  $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
363 
364  $queryBuilder
365  ->select('uid')
366  ->from('pages')
367  ->where(
368  $queryBuilder->expr()->eq(
369  'pid',
370  $queryBuilder->createNamedParameter($rootID, \PDO::PARAM_INT)
371  )
372  )
373  ->orderBy('sorting');
374 
375  $result = $queryBuilder->execute();
376  while ($row = $result->fetch()) {
377  $this->‪traversePageTreeForVersionedRecords((int)$row['uid'], $depth, $isInsideVersionedPage, false);
378  }
379  }
380  // Add any versions of pages
381  if ($rootID > 0) {
382  $versions = ‪BackendUtility::selectVersionsOfRecord('pages', $rootID, 'uid,t3ver_oid,t3ver_wsid,t3ver_count', null, true);
383  if (is_array($versions)) {
384  foreach ($versions as $verRec) {
385  if (!$verRec['_CURRENT_VERSION']) {
386  $this->‪traversePageTreeForVersionedRecords((int)$verRec['uid'], $depth, true, true);
387  }
388  }
389  }
390  }
391  }
392 
398  protected function ‪findUnusedPlaceholderRecords(): array
399  {
400  $unusedPlaceholders = [];
401  $tableNames = $this->‪getAllVersionableTables();
402  foreach ($tableNames as $table) {
403  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
404  ->getQueryBuilderForTable($table);
405 
406  $queryBuilder->getRestrictions()
407  ->removeAll()
408  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
409 
410  $result = $queryBuilder
411  ->select('uid', 'pid')
412  ->from($table)
413  ->where(
414  $queryBuilder->expr()->gte('pid', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)),
415  $queryBuilder->expr()->eq(
416  't3ver_state',
417  $queryBuilder->createNamedParameter(
419  \PDO::PARAM_INT
420  )
421  )
422  )
423  ->execute();
424 
425  while ($placeholderRecord = $result->fetch()) {
426  $versions = (array)‪BackendUtility::selectVersionsOfRecord($table, $placeholderRecord['uid'], 'uid', null);
427  if (count($versions) <= 1) {
428  $unusedPlaceholders[$table . ':' . $placeholderRecord['uid']] = [
429  'table' => $table,
430  'uid' => $placeholderRecord['uid']
431  ];
432  }
433  }
434  }
435  ksort($unusedPlaceholders);
436  return $unusedPlaceholders;
437  }
438 
445  protected function ‪findInvalidMovePlaceholderRecords(): array
446  {
447  $invalidMovePlaceholders = [];
448  $tableNames = $this->‪getAllVersionableTables();
449  foreach ($tableNames as $table) {
450  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
451  ->getQueryBuilderForTable($table);
452 
453  $queryBuilder->getRestrictions()
454  ->removeAll()
455  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
456 
457  $result = $queryBuilder
458  ->select('uid', 'pid', 't3ver_move_id', 't3ver_wsid', 't3ver_state')
459  ->from($table)
460  ->where(
461  $queryBuilder->expr()->gte('pid', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)),
462  $queryBuilder->expr()->eq(
463  't3ver_state',
464  $queryBuilder->createNamedParameter(
466  \PDO::PARAM_INT
467  )
468  )
469  )
470  ->execute();
471  while ($placeholderRecord = $result->fetch()) {
472  $shortID = GeneralUtility::shortMD5($table . ':' . $placeholderRecord['uid']);
473  if ((int)$placeholderRecord['t3ver_wsid'] !== 0) {
474  $phrecCopy = $placeholderRecord;
475  if (‪BackendUtility::movePlhOL($table, $placeholderRecord)) {
476  if ($wsAlt = ‪BackendUtility::getWorkspaceVersionOfRecord($phrecCopy['t3ver_wsid'], $table, $placeholderRecord['uid'], 'uid,pid,t3ver_state')) {
477  if (!‪VersionState::cast($wsAlt['t3ver_state'])->equals(‪VersionState::MOVE_POINTER)) {
478  $invalidMovePlaceholders[$shortID] = $table . ':' . $placeholderRecord['uid'] . ' - State for version was not "4" as it should be!';
479  }
480  } else {
481  $invalidMovePlaceholders[$shortID] = $table . ':' . $placeholderRecord['uid'] . ' - No version was found for online record to be moved. A version must exist.';
482  }
483  } else {
484  $invalidMovePlaceholders[$shortID] = $table . ':' . $placeholderRecord['uid'] . ' - Did not find online record for "t3ver_move_id" value ' . $placeholderRecord['t3ver_move_id'];
485  }
486  } else {
487  $invalidMovePlaceholders[$shortID] = $table . ':' . $placeholderRecord['uid'] . ' - Placeholder was not assigned a workspace value in t3ver_wsid.';
488  }
489  }
490  }
491  ksort($invalidMovePlaceholders);
492  return $invalidMovePlaceholders;
493  }
494 
501  protected function ‪findInvalidMoveIdRecords(): array
502  {
503  $records = [];
504  $tableNames = $this->‪getAllVersionableTables();
505  foreach ($tableNames as $table) {
506  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
507  ->getQueryBuilderForTable($table);
508 
509  $queryBuilder->getRestrictions()
510  ->removeAll()
511  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
512 
513  $result = $queryBuilder
514  ->select('uid', 'pid', 't3ver_move_id', 't3ver_wsid', 't3ver_oid', 't3ver_state')
515  ->from($table)
516  ->where(
517  $queryBuilder->expr()->neq(
518  't3ver_move_id',
519  $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
520  )
521  )
522  ->execute();
523 
524  while ($placeholderRecord = $result->fetch()) {
525  if (‪VersionState::cast($placeholderRecord['t3ver_state'])->equals(‪VersionState::MOVE_PLACEHOLDER)) {
526  if ((int)$placeholderRecord['t3ver_oid'] > 0) {
527  $records[] = $table . ':' . $placeholderRecord['uid'] . ' - Record was offline, must not be!';
528  }
529  } else {
530  $records[] = $table . ':' . $placeholderRecord['uid'] . ' - Record had t3ver_move_id set to "' . $placeholderRecord['t3ver_move_id'] . '" while having t3ver_state=' . $placeholderRecord['t3ver_state'];
531  }
532  }
533  }
534  return $records;
535  }
536 
537  /**************************
538  * actions / delete methods
539  **************************/
540 
548  protected function ‪deleteRecords(array $records, bool $dryRun, SymfonyStyle $io)
549  {
550  // Putting "pages" table in the bottom
551  if (isset($records['pages'])) {
552  $_pages = $records['pages'];
553  unset($records['pages']);
554  // To delete sub pages first assuming they are accumulated from top of page tree.
555  $records['pages'] = array_reverse($_pages);
556  }
557 
558  // Set up the data handler instance
559  $dataHandler = GeneralUtility::makeInstance(DataHandler::class);
560  $dataHandler->start([], []);
561 
562  // Traversing records
563  foreach ($records as $table => $uidsInTable) {
564  if ($io->isVerbose()) {
565  $io->writeln('Flushing published records from table "' . $table . '"');
566  }
567  foreach ($uidsInTable as $uid) {
568  if ($io->isVeryVerbose()) {
569  $io->writeln('Flushing record "' . $table . ':' . $uid . '"');
570  }
571  if (!$dryRun) {
572  $dataHandler->deleteEl($table, $uid, true, true);
573  if (!empty($dataHandler->errorLog)) {
574  $errorMessage = array_merge(['DataHandler reported an error'], $dataHandler->errorLog);
575  $io->error($errorMessage);
576  } elseif (!$io->isQuiet()) {
577  $io->writeln('Flushed published record "' . $table . ':' . $uid . '".');
578  }
579  }
580  }
581  }
582  }
583 
592  protected function ‪resetRecordsWithoutValidWorkspace(array $records, bool $dryRun, SymfonyStyle $io)
593  {
594  foreach ($records as $table => $uidsInTable) {
595  if ($io->isVerbose()) {
596  $io->writeln('Resetting workspace to zero for records from table "' . $table . '"');
597  }
598  foreach ($uidsInTable as $uid) {
599  if ($io->isVeryVerbose()) {
600  $io->writeln('Flushing record "' . $table . ':' . $uid . '"');
601  }
602  if (!$dryRun) {
603  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
604  ->getQueryBuilderForTable($table);
605 
606  $queryBuilder
607  ->update($table)
608  ->where(
609  $queryBuilder->expr()->eq(
610  'uid',
611  $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)
612  )
613  )
614  ->set('t3ver_wsid', 0)
615  ->execute();
616  if (!$io->isQuiet()) {
617  $io->writeln('Flushed record "' . $table . ':' . $uid . '".');
618  }
619  }
620  }
621  }
622  }
623 
631  protected function ‪deleteUnusedPlaceholders(array $records, bool $dryRun, SymfonyStyle $io)
632  {
633  $dataHandler = GeneralUtility::makeInstance(DataHandler::class);
634  $dataHandler->start([], []);
635  foreach ($records as $record) {
636  $table = $record['table'];
637  $uid = $record['uid'];
638  if ($io->isVeryVerbose()) {
639  $io->writeln('Deleting unused placeholder (soft) "' . $table . ':' . $uid . '"');
640  }
641  if (!$dryRun) {
642  $dataHandler->deleteAction($table, $uid);
643  // Return errors if any
644  if (!empty($dataHandler->errorLog)) {
645  $errorMessage = array_merge(['DataHandler reported an error'], $dataHandler->errorLog);
646  $io->error($errorMessage);
647  } elseif (!$io->isQuiet()) {
648  $io->writeln('Permanently deleted unused placeholder "' . $table . ':' . $uid . '".');
649  }
650  }
651  }
652  }
653 
663  protected function ‪loadAllWorkspaceRecords(): array
664  {
665  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
666  ->getQueryBuilderForTable('sys_workspace');
667 
668  $queryBuilder->getRestrictions()
669  ->removeAll()
670  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
671 
672  $result = $queryBuilder
673  ->select('uid', 'title')
674  ->from('sys_workspace')
675  ->execute();
676 
677  while ($workspaceRecord = $result->fetch()) {
678  $this->allWorkspaces[(int)$workspaceRecord['uid']] = $workspaceRecord['title'];
679  }
681  }
682 
688  protected function ‪getAllVersionableTables(): array
689  {
690  $tables = [];
691  foreach (‪$GLOBALS['TCA'] as $tableName => $config) {
693  $tables[] = $tableName;
694  }
695  }
696  return $tables;
697  }
698 }
‪TYPO3\CMS\Core\DataHandling\DataHandler
Definition: DataHandler.php:84
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger($var)
Definition: MathUtility.php:74
‪TYPO3\CMS\Core\Versioning\VersionState\NEW_PLACEHOLDER
‪const NEW_PLACEHOLDER
Definition: VersionState.php:47
‪TYPO3\CMS\Workspaces\Command\WorkspaceVersionRecordsCommand\execute
‪int execute(InputInterface $input, OutputInterface $output)
Definition: WorkspaceVersionRecordsCommand.php:105
‪TYPO3\CMS\Workspaces\Command\WorkspaceVersionRecordsCommand
Definition: WorkspaceVersionRecordsCommand.php:38
‪TYPO3\CMS\Workspaces\Command\WorkspaceVersionRecordsCommand\deleteRecords
‪deleteRecords(array $records, bool $dryRun, SymfonyStyle $io)
Definition: WorkspaceVersionRecordsCommand.php:546
‪TYPO3\CMS\Core\Utility\MathUtility\forceIntegerInRange
‪static int forceIntegerInRange($theInt, $min, $max=2000000000, $defaultValue=0)
Definition: MathUtility.php:32
‪TYPO3\CMS\Core\Core\Bootstrap\initializeBackendAuthentication
‪static initializeBackendAuthentication($proceedIfNoUserIsLoggedIn=false)
Definition: Bootstrap.php:607
‪TYPO3\CMS\Workspaces\Command\WorkspaceVersionRecordsCommand\findInvalidMovePlaceholderRecords
‪array findInvalidMovePlaceholderRecords()
Definition: WorkspaceVersionRecordsCommand.php:443
‪TYPO3\CMS\Workspaces\Command\WorkspaceVersionRecordsCommand\getAllVersionableTables
‪array getAllVersionableTables()
Definition: WorkspaceVersionRecordsCommand.php:686
‪TYPO3\CMS\Workspaces\Command\WorkspaceVersionRecordsCommand\$allWorkspaces
‪array $allWorkspaces
Definition: WorkspaceVersionRecordsCommand.php:43
‪TYPO3\CMS\Core\Versioning\VersionState\MOVE_POINTER
‪const MOVE_POINTER
Definition: VersionState.php:73
‪TYPO3\CMS\Workspaces\Command\WorkspaceVersionRecordsCommand\loadAllWorkspaceRecords
‪array loadAllWorkspaceRecords()
Definition: WorkspaceVersionRecordsCommand.php:661
‪TYPO3\CMS\Workspaces\Command
Definition: AutoPublishCommand.php:18
‪TYPO3\CMS\Backend\Utility\BackendUtility\isTableWorkspaceEnabled
‪static bool isTableWorkspaceEnabled($table)
Definition: BackendUtility.php:4021
‪TYPO3\CMS\Core\Type\Enumeration\cast
‪static static cast($value)
Definition: Enumeration.php:186
‪TYPO3\CMS\Workspaces\Command\WorkspaceVersionRecordsCommand\$foundRecords
‪array $foundRecords
Definition: WorkspaceVersionRecordsCommand.php:48
‪TYPO3\CMS\Workspaces\Command\WorkspaceVersionRecordsCommand\traversePageTreeForVersionedRecords
‪traversePageTreeForVersionedRecords(int $rootID, int $depth, bool $isInsideVersionedPage=false, bool $rootIsVersion=false)
Definition: WorkspaceVersionRecordsCommand.php:270
‪TYPO3\CMS\Workspaces\Command\WorkspaceVersionRecordsCommand\configure
‪configure()
Definition: WorkspaceVersionRecordsCommand.php:67
‪TYPO3\CMS\Backend\Utility\BackendUtility
Definition: BackendUtility.php:75
‪TYPO3\CMS\Backend\Utility\BackendUtility\getWorkspaceVersionOfRecord
‪static array bool getWorkspaceVersionOfRecord($workspace, $table, $uid, $fields=' *')
Definition: BackendUtility.php:3705
‪TYPO3\CMS\Core\Versioning\VersionState
Definition: VersionState.php:24
‪$output
‪$output
Definition: annotationChecker.php:119
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction
Definition: DeletedRestriction.php:28
‪TYPO3\CMS\Core\Core\Bootstrap
Definition: Bootstrap.php:66
‪TYPO3\CMS\Backend\Utility\BackendUtility\selectVersionsOfRecord
‪static array null selectVersionsOfRecord( $table, $uid, $fields=' *', $workspace=0, $includeDeletedRecords=false, $row=null)
Definition: BackendUtility.php:3416
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:22
‪TYPO3\CMS\Workspaces\Command\WorkspaceVersionRecordsCommand\findInvalidMoveIdRecords
‪array findInvalidMoveIdRecords()
Definition: WorkspaceVersionRecordsCommand.php:499
‪TYPO3\CMS\Workspaces\Command\WorkspaceVersionRecordsCommand\findUnusedPlaceholderRecords
‪array findUnusedPlaceholderRecords()
Definition: WorkspaceVersionRecordsCommand.php:396
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:46
‪TYPO3\CMS\Backend\Utility\BackendUtility\movePlhOL
‪static bool movePlhOL($table, &$row)
Definition: BackendUtility.php:3669
‪TYPO3\CMS\Workspaces\Command\WorkspaceVersionRecordsCommand\resetRecordsWithoutValidWorkspace
‪resetRecordsWithoutValidWorkspace(array $records, bool $dryRun, SymfonyStyle $io)
Definition: WorkspaceVersionRecordsCommand.php:590
‪TYPO3\CMS\Workspaces\Command\WorkspaceVersionRecordsCommand\deleteUnusedPlaceholders
‪deleteUnusedPlaceholders(array $records, bool $dryRun, SymfonyStyle $io)
Definition: WorkspaceVersionRecordsCommand.php:629
‪TYPO3\CMS\Core\Versioning\VersionState\MOVE_PLACEHOLDER
‪const MOVE_PLACEHOLDER
Definition: VersionState.php:72