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