Ejemplo n.º 1
0
 /**
  * Flushes elements of a particular workspace to avoid orphan records.
  *
  * @param int $workspaceId The workspace to be flushed
  * @return void
  */
 protected function flushWorkspaceElements($workspaceId)
 {
     $command = array();
     foreach ($this->getTcaTables() as $tcaTable) {
         if (BackendUtility::isTableWorkspaceEnabled($tcaTable)) {
             $where = '1=1';
             $where .= BackendUtility::getWorkspaceWhereClause($tcaTable, $workspaceId);
             $where .= BackendUtility::deleteClause($tcaTable);
             $records = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid', $tcaTable, $where, '', '', '', 'uid');
             if (is_array($records)) {
                 foreach ($records as $recordId => $_) {
                     $command[$tcaTable][$recordId]['version']['action'] = 'flush';
                 }
             }
         }
     }
     if (!empty($command)) {
         $tceMain = $this->getTceMain();
         $tceMain->start(array(), $command);
         $tceMain->process_cmdmap();
     }
 }
Ejemplo n.º 2
0
 /**
  * Release version from this workspace (and into "Live" workspace but as an offline version).
  *
  * @param string $table Table name
  * @param int $id Record UID
  * @param bool $flush If set, will completely delete element
  * @param DataHandler $tcemainObj TCEmain object
  * @return void
  */
 protected function version_clearWSID($table, $id, $flush = false, DataHandler $tcemainObj)
 {
     if ($errorCode = $tcemainObj->BE_USER->workspaceCannotEditOfflineVersion($table, $id)) {
         $tcemainObj->newlog('Attempt to reset workspace for record failed: ' . $errorCode, 1);
         return;
     }
     if (!$tcemainObj->checkRecordUpdateAccess($table, $id)) {
         $tcemainObj->newlog('Attempt to reset workspace for record failed because you do not have edit access', 1);
         return;
     }
     $liveRec = BackendUtility::getLiveVersionOfRecord($table, $id, 'uid,t3ver_state');
     if (!$liveRec) {
         return;
     }
     // Clear workspace ID:
     $updateData = array('t3ver_wsid' => 0, 't3ver_tstamp' => $GLOBALS['EXEC_TIME']);
     $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid=' . (int) $id, $updateData);
     // Clear workspace ID for live version AND DELETE IT as well because it is a new record!
     if (VersionState::cast($liveRec['t3ver_state'])->equals(VersionState::NEW_PLACEHOLDER) || VersionState::cast($liveRec['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)) {
         $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid=' . (int) $liveRec['uid'], $updateData);
         // THIS assumes that the record was placeholder ONLY for ONE record (namely $id)
         $tcemainObj->deleteEl($table, $liveRec['uid'], true);
     }
     // If "deleted" flag is set for the version that got released
     // it doesn't make sense to keep that "placeholder" anymore and we delete it completly.
     $wsRec = BackendUtility::getRecord($table, $id);
     if ($flush || (VersionState::cast($wsRec['t3ver_state'])->equals(VersionState::NEW_PLACEHOLDER) || VersionState::cast($wsRec['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER))) {
         $tcemainObj->deleteEl($table, $id, true, true);
     }
     // Remove the move-placeholder if found for live record.
     if (BackendUtility::isTableWorkspaceEnabled($table)) {
         if ($plhRec = BackendUtility::getMovePlaceholder($table, $liveRec['uid'], 'uid')) {
             $tcemainObj->deleteEl($table, $plhRec['uid'], true, true);
         }
     }
 }
Ejemplo n.º 3
0
 /**
  * Find orphan records
  * VERY CPU and memory intensive since it will look up the whole page tree!
  *
  * @return array
  */
 public function main()
 {
     // Initialize result array:
     $resultArray = array('message' => $this->cli_help['name'] . LF . LF . $this->cli_help['description'], 'headers' => array('versions' => array('All versions', 'Showing all versions of records found', 0), 'versions_published' => array('All published versions', 'This is all records that has been published and can therefore be removed permanently', 1), 'versions_liveWS' => array('All versions in Live workspace', 'This is all records that are offline versions in the Live workspace. You may wish to flush these if you only use workspaces for versioning since then you might find lots of versions piling up in the live workspace which have simply been disconnected from the workspace before they were published.', 1), 'versions_lost_workspace' => array('Versions outside a workspace', 'Versions that has lost their connection to a workspace in TYPO3.', 3), 'versions_inside_versioned_page' => array('Versions in versions', 'Versions inside an already versioned page. Something that is confusing to users and therefore should not happen but is technically possible.', 2), 'versions_unused_placeholders' => array('Unused placeholder records', 'Placeholder records which are not used anymore by offline versions.', 2), 'versions_move_placeholders_ok' => array('Move placeholders', 'Move-to placeholder records which has good integrity', 0), 'versions_move_placeholders_bad' => array('Move placeholders with bad integrity', 'Move-to placeholder records which has bad integrity', 2), 'versions_move_id_check' => array('Checking if t3ver_move_id is correct', 't3ver_move_id must only be set with online records having t3ver_state=3.', 2)), 'versions' => array());
     $startingPoint = $this->cli_isArg('--pid') ? \TYPO3\CMS\Core\Utility\MathUtility::forceIntegerInRange($this->cli_argValue('--pid'), 0) : 0;
     $depth = $this->cli_isArg('--depth') ? \TYPO3\CMS\Core\Utility\MathUtility::forceIntegerInRange($this->cli_argValue('--depth'), 0) : 1000;
     $this->genTree($startingPoint, $depth, (int) $this->cli_argValue('--echotree'));
     $resultArray['versions'] = $this->recStats['versions'];
     $resultArray['versions_published'] = $this->recStats['versions_published'];
     $resultArray['versions_liveWS'] = $this->recStats['versions_liveWS'];
     $resultArray['versions_lost_workspace'] = $this->recStats['versions_lost_workspace'];
     $resultArray['versions_inside_versioned_page'] = $this->recStats['versions_inside_versioned_page'];
     // Finding all placeholders with no records attached!
     $resultArray['versions_unused_placeholders'] = array();
     foreach ($GLOBALS['TCA'] as $table => $cfg) {
         if ($cfg['ctrl']['versioningWS']) {
             $placeHolders = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid,pid', $table, 't3ver_state=' . new VersionState(VersionState::NEW_PLACEHOLDER) . ' AND pid>=0' . BackendUtility::deleteClause($table));
             foreach ($placeHolders as $phrec) {
                 if (count(BackendUtility::selectVersionsOfRecord($table, $phrec['uid'], 'uid', '*', null)) <= 1) {
                     $resultArray['versions_unused_placeholders'][GeneralUtility::shortMD5($table . ':' . $phrec['uid'])] = $table . ':' . $phrec['uid'];
                 }
             }
         }
     }
     asort($resultArray['versions_unused_placeholders']);
     // Finding all move placeholders with inconsistencies:
     $resultArray['versions_move_placeholders_ok'] = array();
     $resultArray['versions_move_placeholders_bad'] = array();
     foreach ($GLOBALS['TCA'] as $table => $cfg) {
         if (BackendUtility::isTableWorkspaceEnabled($table)) {
             $placeHolders = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid,pid,t3ver_move_id,t3ver_wsid,t3ver_state', $table, 't3ver_state=' . new VersionState(VersionState::MOVE_PLACEHOLDER) . ' AND pid>=0' . BackendUtility::deleteClause($table));
             foreach ($placeHolders as $phrec) {
                 $shortID = GeneralUtility::shortMD5($table . ':' . $phrec['uid']);
                 if ((int) $phrec['t3ver_wsid'] != 0) {
                     $phrecCopy = $phrec;
                     if (BackendUtility::movePlhOL($table, $phrec)) {
                         if ($wsAlt = BackendUtility::getWorkspaceVersionOfRecord($phrecCopy['t3ver_wsid'], $table, $phrec['uid'], 'uid,pid,t3ver_state')) {
                             if (!VersionState::cast($wsAlt['t3ver_state'])->equals(VersionState::MOVE_POINTER)) {
                                 $resultArray['versions_move_placeholders_bad'][$shortID] = array($table . ':' . $phrec['uid'], 'State for version was not "4" as it should be!', $phrecCopy);
                             } else {
                                 $resultArray['versions_move_placeholders_ok'][$shortID] = array($table . ':' . $phrec['uid'], 'PLH' => $phrecCopy, 'online' => $phrec, 'PNT' => $wsAlt);
                             }
                         } else {
                             $resultArray['versions_move_placeholders_bad'][$shortID] = array($table . ':' . $phrec['uid'], 'No version was found for online record to be moved. A version must exist.', $phrecCopy);
                         }
                     } else {
                         $resultArray['versions_move_placeholders_bad'][$shortID] = array($table . ':' . $phrec['uid'], 'Did not find online record for "t3ver_move_id" value ' . $phrec['t3ver_move_id'], $phrec);
                     }
                 } else {
                     $resultArray['versions_move_placeholders_bad'][$shortID] = array($table . ':' . $phrec['uid'], 'Placeholder was not assigned a workspace value in t3ver_wsid.', $phrec);
                 }
             }
         }
     }
     ksort($resultArray['versions_move_placeholders_ok']);
     ksort($resultArray['versions_move_placeholders_bad']);
     // Finding move_id_check inconsistencies:
     $resultArray['versions_move_id_check'] = array();
     foreach ($GLOBALS['TCA'] as $table => $cfg) {
         if (BackendUtility::isTableWorkspaceEnabled($table)) {
             $placeHolders = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid,pid,t3ver_move_id,t3ver_wsid,t3ver_state', $table, 't3ver_move_id<>0' . BackendUtility::deleteClause($table));
             foreach ($placeHolders as $phrec) {
                 if (VersionState::cast($phrec['t3ver_state'])->equals(VersionState::MOVE_PLACEHOLDER)) {
                     if ($phrec['pid'] != -1) {
                     } else {
                         $resultArray['versions_move_id_check'][] = array($table . ':' . $phrec['uid'], 'Record was offline, must not be!', $phrec);
                     }
                 } else {
                     $resultArray['versions_move_id_check'][] = array($table . ':' . $phrec['uid'], 'Record had t3ver_move_id set to "' . $phrec['t3ver_move_id'] . '" while having t3ver_state=' . $phrec['t3ver_state'], $phrec);
                 }
             }
         }
     }
     return $resultArray;
 }
 /**
  * @param string $table
  * @return boolean
  */
 protected function hasWorkspacesSupport($table)
 {
     return NULL !== $GLOBALS['BE_USER'] && BackendUtility::isTableWorkspaceEnabled($table);
 }
Ejemplo n.º 5
0
 /**
  * Write the sorting values to a foreign_table, that has a foreign_field (uid of the parent record)
  *
  * @param array $conf TCA configuration for current field
  * @param integer $parentUid The uid of the parent record
  * @param boolean $updateToUid Whether to update the foreign field with the $parentUid (on Copy)
  * @param boolean $skipSorting Do not update the sorting columns, this could happen for imported values
  * @return void
  * @todo Define visibility
  */
 public function writeForeignField($conf, $parentUid, $updateToUid = 0, $skipSorting = FALSE)
 {
     $c = 0;
     $foreign_table = $conf['foreign_table'];
     $foreign_field = $conf['foreign_field'];
     $symmetric_field = $conf['symmetric_field'];
     $foreign_table_field = $conf['foreign_table_field'];
     $foreign_match_fields = is_array($conf['foreign_match_fields']) ? $conf['foreign_match_fields'] : array();
     // If there are table items and we have a proper $parentUid
     if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($parentUid) && count($this->tableArray)) {
         // If updateToUid is not a positive integer, set it to '0', so it will be ignored
         if (!(\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($updateToUid) && $updateToUid > 0)) {
             $updateToUid = 0;
         }
         $considerWorkspaces = $GLOBALS['BE_USER']->workspace !== 0 && \TYPO3\CMS\Backend\Utility\BackendUtility::isTableWorkspaceEnabled($foreign_table);
         $fields = 'uid,' . $foreign_field;
         // Consider the symmetric field if defined:
         if ($symmetric_field) {
             $fields .= ',' . $symmetric_field;
         }
         // Consider workspaces if defined and currently used:
         if ($considerWorkspaces) {
             $fields .= ',' . 't3ver_state,t3ver_oid';
         }
         // Update all items
         foreach ($this->itemArray as $val) {
             $uid = $val['id'];
             $table = $val['table'];
             // Fetch the current (not overwritten) relation record if we should handle symmetric relations
             if ($symmetric_field || $considerWorkspaces) {
                 $row = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecord($table, $uid, $fields, '', FALSE);
             }
             if ($symmetric_field) {
                 $isOnSymmetricSide = self::isOnSymmetricSide($parentUid, $conf, $row);
             }
             $updateValues = $foreign_match_fields;
             $workspaceValues = array();
             // No update to the uid is requested, so this is the normal behaviour
             // just update the fields and care about sorting
             if (!$updateToUid) {
                 // Always add the pointer to the parent uid
                 if ($isOnSymmetricSide) {
                     $updateValues[$symmetric_field] = $parentUid;
                 } else {
                     $updateValues[$foreign_field] = $parentUid;
                 }
                 // If it is configured in TCA also to store the parent table in the child record, just do it
                 if ($foreign_table_field && $this->currentTable) {
                     $updateValues[$foreign_table_field] = $this->currentTable;
                 }
                 // Update sorting columns if not to be skipped
                 if (!$skipSorting) {
                     // Get the correct sorting field
                     // Specific manual sortby for data handled by this field
                     if ($conf['foreign_sortby']) {
                         $sortby = $conf['foreign_sortby'];
                     } elseif ($GLOBALS['TCA'][$foreign_table]['ctrl']['sortby']) {
                         // manual sortby for all table records
                         $sortby = $GLOBALS['TCA'][$foreign_table]['ctrl']['sortby'];
                     }
                     // Apply sorting on the symmetric side (it depends on who created the relation, so what uid is in the symmetric_field):
                     if ($isOnSymmetricSide && isset($conf['symmetric_sortby']) && $conf['symmetric_sortby']) {
                         $sortby = $conf['symmetric_sortby'];
                     } else {
                         $sortby = $GLOBALS['TYPO3_DB']->stripOrderBy($sortby);
                     }
                     if ($sortby) {
                         $updateValues[$sortby] = $workspaceValues[$sortby] = ++$c;
                     }
                 }
             } else {
                 if ($isOnSymmetricSide) {
                     $updateValues[$symmetric_field] = $updateToUid;
                 } else {
                     $updateValues[$foreign_field] = $updateToUid;
                 }
             }
             // Update accordant fields in the database:
             if (count($updateValues)) {
                 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid=' . intval($uid), $updateValues);
                 $this->updateRefIndex($table, $uid);
             }
             // Update accordant fields in the database for workspaces overlays/placeholders:
             if (count($workspaceValues) && $considerWorkspaces) {
                 if (isset($row['t3ver_oid']) && $row['t3ver_oid'] && $row['t3ver_state'] == -1) {
                     $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid=' . intval($row['t3ver_oid']), $workspaceValues);
                 }
             }
         }
     }
 }
Ejemplo n.º 6
0
 /**
  * Get a single record row for a TCA table from the database.
  * \TYPO3\CMS\Backend\Form\DataPreprocessor is used for "upgrading" the
  * values, especially the relations.
  *
  * @param integer $pid The pid of the page the record should be stored (only relevant for NEW records)
  * @param string $table The table to fetch data from (= foreign_table)
  * @param string $uid The uid of the record to fetch, or the pid if a new record should be created
  * @param string $cmd The command to perform, empty or 'new'
  * @return array A record row from the database post-processed by \TYPO3\CMS\Backend\Form\DataPreprocessor
  * @todo Define visibility
  */
 public function getRecord($pid, $table, $uid, $cmd = '')
 {
     // Fetch workspace version of a record (if any):
     if ($cmd !== 'new' && $GLOBALS['BE_USER']->workspace !== 0 && BackendUtility::isTableWorkspaceEnabled($table)) {
         $workspaceVersion = BackendUtility::getWorkspaceVersionOfRecord($GLOBALS['BE_USER']->workspace, $table, $uid, 'uid,t3ver_state');
         if ($workspaceVersion !== FALSE) {
             $versionState = VersionState::cast($workspaceVersion['t3ver_state']);
             if ($versionState->equals(VersionState::DELETE_PLACEHOLDER)) {
                 return FALSE;
             }
             $uid = $workspaceVersion['uid'];
         }
     }
     /** @var $trData \TYPO3\CMS\Backend\Form\DataPreprocessor */
     $trData = GeneralUtility::makeInstance('TYPO3\\CMS\\Backend\\Form\\DataPreprocessor');
     $trData->addRawData = TRUE;
     $trData->lockRecords = 1;
     $trData->disableRTE = $GLOBALS['SOBE']->MOD_SETTINGS['disableRTE'];
     // If a new record should be created
     $trData->fetchRecord($table, $uid, $cmd === 'new' ? 'new' : '');
     $rec = reset($trData->regTableItems_data);
     return $rec;
 }
Ejemplo n.º 7
0
 /**
  * Callback to add additional data to new elements created in the dependency resolver utility.
  *
  * @throws \RuntimeException
  * @param ElementEntity $caller
  * @param array $callerArguments
  * @param array $targetArgument
  * @param string $eventName
  * @return void
  */
 public function createNewDependentElementCallback(array $callerArguments, array $targetArgument, ElementEntity $caller, $eventName)
 {
     if (!BackendUtility::isTableWorkspaceEnabled($caller->getTable())) {
         $caller->setInvalid(true);
         return;
     }
     $versionRecord = $caller->getRecord();
     // If version record does not exist, it probably has been deleted (cleared from workspace), this means,
     // that the reference index still has an old reference pointer, which is "fine" for deleted parents
     if (empty($versionRecord)) {
         throw new \RuntimeException('Element "' . $caller::getIdentifier($caller->getTable(), $caller->getId()) . '" does not exist', 1393960943);
     }
     // If version is on live workspace, but the pid is negative, mark the record as invalid.
     // This happens if a change has been discarded (clearWSID) - it will be removed from the command map.
     if ((int) $versionRecord['t3ver_wsid'] === 0 && (int) $versionRecord['pid'] === -1) {
         $caller->setDataValue('liveId', $caller->getId());
         $caller->setInvalid(true);
         return;
     }
     if ($caller->hasDataValue('liveId') === false) {
         // Set the original uid from the version record
         if (!empty($versionRecord['t3ver_oid']) && (int) $versionRecord['pid'] === -1 && (int) $versionRecord['t3ver_wsid'] === $this->getWorkspace()) {
             $caller->setDataValue('liveId', $versionRecord['t3ver_oid']);
             // The current version record is actually a live record or an accordant placeholder for live
         } elseif ((int) $versionRecord['t3ver_wsid'] === 0 || (int) $versionRecord['pid'] !== -1) {
             $caller->setDataValue('liveId', $caller->getId());
             $versionRecord = BackendUtility::getWorkspaceVersionOfRecord($this->getWorkspace(), $caller->getTable(), $caller->getId(), 'uid,t3ver_state');
             // Set version uid to caller, most likely it's a delete placeholder
             // for a child record that is not recognized in the reference index
             if (!empty($versionRecord['uid'])) {
                 $caller->setId($versionRecord['uid']);
                 // If no version could be determined, mark record as invalid
                 // (thus, it will be removed from the command map)
             } else {
                 $caller->setInvalid(true);
             }
             // In case of an unexpected record state, mark the record as invalid
         } else {
             $caller->setInvalid(true);
         }
     }
 }
Ejemplo n.º 8
0
 /**
  * @return bool
  */
 protected function isWorkspaceEnabled()
 {
     return BackendUtility::isTableWorkspaceEnabled($this->tableName);
 }
Ejemplo n.º 9
0
 /**
  * Performs workspace and language overlay on the given row array. The language and workspace id is automatically
  * detected (depending on FE or BE context). You can also explicitly set the language/workspace id.
  *
  * @param Qom\SourceInterface $source The source (selector od join)
  * @param array $rows
  * @param \TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface $querySettings The TYPO3 CMS specific query settings
  * @param null|int $workspaceUid
  * @return array
  */
 protected function doLanguageAndWorkspaceOverlay(Qom\SourceInterface $source, array $rows, \TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface $querySettings, $workspaceUid = null)
 {
     if ($source instanceof Qom\SelectorInterface) {
         $tableName = $source->getSelectorName();
     } elseif ($source instanceof Qom\JoinInterface) {
         $tableName = $source->getRight()->getSelectorName();
     } else {
         // No proper source, so we do not have a table name here
         // we cannot do an overlay and return the original rows instead.
         return $rows;
     }
     $pageRepository = $this->getPageRepository();
     if (is_object($GLOBALS['TSFE'])) {
         if ($workspaceUid !== null) {
             $pageRepository->versioningWorkspaceId = $workspaceUid;
         }
     } else {
         if ($workspaceUid === null) {
             $workspaceUid = $GLOBALS['BE_USER']->workspace;
         }
         $pageRepository->versioningWorkspaceId = $workspaceUid;
     }
     // Fetches the move-placeholder in case it is supported
     // by the table and if there's only one row in the result set
     // (applying this to all rows does not work, since the sorting
     // order would be destroyed and possible limits not met anymore)
     if (!empty($pageRepository->versioningWorkspaceId) && BackendUtility::isTableWorkspaceEnabled($tableName) && count($rows) === 1) {
         $versionId = $pageRepository->versioningWorkspaceId;
         $queryBuilder = $this->connectionPool->getQueryBuilderForTable($tableName);
         $queryBuilder->getRestrictions()->removeAll();
         $movePlaceholder = $queryBuilder->select($tableName . '.*')->from($tableName)->where($queryBuilder->expr()->eq('t3ver_state', $queryBuilder->createNamedParameter(3, \PDO::PARAM_INT)), $queryBuilder->expr()->eq('t3ver_wsid', $queryBuilder->createNamedParameter($versionId, \PDO::PARAM_INT)), $queryBuilder->expr()->eq('t3ver_move_id', $queryBuilder->createNamedParameter($rows[0]['uid'], \PDO::PARAM_INT)))->setMaxResults(1)->execute()->fetch();
         if (!empty($movePlaceholder)) {
             $rows = [$movePlaceholder];
         }
     }
     $overlaidRows = [];
     foreach ($rows as $row) {
         // If current row is a translation select its parent
         if (isset($tableName) && isset($GLOBALS['TCA'][$tableName]) && isset($GLOBALS['TCA'][$tableName]['ctrl']['languageField']) && isset($GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']) && $tableName !== 'pages_language_overlay') {
             if (isset($row[$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']]) && $row[$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']] > 0) {
                 $queryBuilder = $this->connectionPool->getQueryBuilderForTable($tableName);
                 $queryBuilder->getRestrictions()->removeAll();
                 $row = $queryBuilder->select($tableName . '.*')->from($tableName)->where($queryBuilder->expr()->eq($tableName . '.uid', $queryBuilder->createNamedParameter($row[$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']], \PDO::PARAM_INT)), $queryBuilder->expr()->eq($tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'], $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)))->setMaxResults(1)->execute()->fetch();
             }
         }
         $pageRepository->versionOL($tableName, $row, true);
         if ($tableName == 'pages') {
             $row = $pageRepository->getPageOverlay($row, $querySettings->getLanguageUid());
         } elseif (isset($GLOBALS['TCA'][$tableName]['ctrl']['languageField']) && $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] !== '' && $tableName !== 'pages_language_overlay') {
             if (in_array($row[$GLOBALS['TCA'][$tableName]['ctrl']['languageField']], [-1, 0])) {
                 $overlayMode = $querySettings->getLanguageMode() === 'strict' ? 'hideNonTranslated' : '';
                 $row = $pageRepository->getRecordOverlay($tableName, $row, $querySettings->getLanguageUid(), $overlayMode);
             }
         }
         if ($row !== null && is_array($row)) {
             $overlaidRows[] = $row;
         }
     }
     return $overlaidRows;
 }
Ejemplo n.º 10
0
 /**
  * Performs workspace and language overlay on the given row array. The language and workspace id is automatically
  * detected (depending on FE or BE context). You can also explicitly set the language/workspace id.
  *
  * @param Qom\SourceInterface $source The source (selector od join)
  * @param array $rows
  * @param \TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface $querySettings The TYPO3 CMS specific query settings
  * @param null|int $workspaceUid
  * @return array
  */
 protected function doLanguageAndWorkspaceOverlay(Qom\SourceInterface $source, array $rows, \TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface $querySettings, $workspaceUid = null)
 {
     if ($source instanceof Qom\SelectorInterface) {
         $tableName = $source->getSelectorName();
     } elseif ($source instanceof Qom\JoinInterface) {
         $tableName = $source->getRight()->getSelectorName();
     } else {
         // No proper source, so we do not have a table name here
         // we cannot do an overlay and return the original rows instead.
         return $rows;
     }
     $pageRepository = $this->getPageRepository();
     if (is_object($GLOBALS['TSFE'])) {
         if ($workspaceUid !== null) {
             $pageRepository->versioningWorkspaceId = $workspaceUid;
         }
     } else {
         if ($workspaceUid === null) {
             $workspaceUid = $GLOBALS['BE_USER']->workspace;
         }
         $pageRepository->versioningWorkspaceId = $workspaceUid;
     }
     // Fetches the move-placeholder in case it is supported
     // by the table and if there's only one row in the result set
     // (applying this to all rows does not work, since the sorting
     // order would be destroyed and possible limits not met anymore)
     if (!empty($pageRepository->versioningWorkspaceId) && BackendUtility::isTableWorkspaceEnabled($tableName) && count($rows) === 1) {
         $movePlaceholder = $this->databaseHandle->exec_SELECTgetSingleRow($tableName . '.*', $tableName, 't3ver_state=3 AND t3ver_wsid=' . $pageRepository->versioningWorkspaceId . ' AND t3ver_move_id=' . $rows[0]['uid']);
         if (!empty($movePlaceholder)) {
             $rows = array($movePlaceholder);
         }
     }
     $overlaidRows = array();
     foreach ($rows as $row) {
         // If current row is a translation select its parent
         if (isset($tableName) && isset($GLOBALS['TCA'][$tableName]) && isset($GLOBALS['TCA'][$tableName]['ctrl']['languageField']) && isset($GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']) && !isset($GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerTable'])) {
             if (isset($row[$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']]) && $row[$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']] > 0) {
                 $row = $this->databaseHandle->exec_SELECTgetSingleRow($tableName . '.*', $tableName, $tableName . '.uid=' . (int) $row[$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']] . ' AND ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . '=0');
             }
         }
         $pageRepository->versionOL($tableName, $row, true);
         if ($tableName == 'pages') {
             $row = $pageRepository->getPageOverlay($row, $querySettings->getLanguageUid());
         } elseif (isset($GLOBALS['TCA'][$tableName]['ctrl']['languageField']) && $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] !== '' && !isset($GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerTable'])) {
             if (in_array($row[$GLOBALS['TCA'][$tableName]['ctrl']['languageField']], array(-1, 0))) {
                 $overlayMode = $querySettings->getLanguageMode() === 'strict' ? 'hideNonTranslated' : '';
                 $row = $pageRepository->getRecordOverlay($tableName, $row, $querySettings->getLanguageUid(), $overlayMode);
             }
         }
         if ($row !== null && is_array($row)) {
             $overlaidRows[] = $row;
         }
     }
     return $overlaidRows;
 }
Ejemplo n.º 11
0
 /**
  * Flushes elements of a particular workspace to avoid orphan records.
  *
  * @param int $workspaceId The workspace to be flushed
  * @return void
  */
 protected function flushWorkspaceElements($workspaceId)
 {
     $command = [];
     foreach ($this->getTcaTables() as $tcaTable) {
         if (BackendUtility::isTableWorkspaceEnabled($tcaTable)) {
             $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($tcaTable);
             $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class))->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class, $workspaceId, false));
             $result = $queryBuilder->select('uid')->from($tcaTable)->orderBy('uid')->execute();
             while (($recordId = $result->fetchColumn()) !== false) {
                 $command[$tcaTable][$recordId]['version']['action'] = 'flush';
             }
         }
     }
     if (!empty($command)) {
         $dataHandler = $this->getDataHandler();
         $dataHandler->start([], $command);
         $dataHandler->process_cmdmap();
     }
 }
 /**
  * Updating or checking Reference Index
  *
  * @param bool $dryRun
  * @param PersistenceContext $persistenceContext
  * @param ReferenceIndexIntegrityDelegateInterface|NULL $delegate
  * @return array Tuple ($errorCount, $recordCount, $processedTables)
  */
 protected function checkOrUpdateReferenceIndex($dryRun, PersistenceContext $persistenceContext, ReferenceIndexIntegrityDelegateInterface $delegate = NULL)
 {
     $processedTables = array();
     $errorCount = 0;
     $recordCount = 0;
     $existingTableNames = $persistenceContext->getDatabaseConnection()->admin_get_tables();
     $this->callDelegateForEvent($delegate, 'willStartOperation', array($this->countRowsOfAllRegisteredTables($persistenceContext, $existingTableNames)));
     // Traverse all tables:
     foreach (array_keys($persistenceContext->getPersistenceConfiguration()) as $tableName) {
         // Traverse all records in tables, including deleted records:
         $selectFields = BackendUtility::isTableWorkspaceEnabled($tableName) ? 'uid,t3ver_wsid' : 'uid';
         if (!empty($existingTableNames[$tableName])) {
             $records = $persistenceContext->getDatabaseConnection()->exec_SELECTgetRows($selectFields, $tableName, '1=1');
         } else {
             $this->delegateLog($delegate, 'warning', 'Table "%s" exists in $TCA but does not exist in the database. You should run the Database Analyzer in the Install Tool to fix this.', array($tableName));
             continue;
         }
         if (!is_array($records)) {
             $this->delegateLog($delegate, 'error', 'Table "%s" exists in $TCA but fetching records from database failed. Check the Database Analyzer in Install Tool for missing fields.', array($tableName));
             continue;
         }
         $processedTables[] = $tableName;
         $uidList = array(0);
         foreach ($records as $record) {
             $this->callDelegateForEvent($delegate, 'willUpdateRecord', array($tableName, $record));
             /** @var $refIndexObj ReferenceIndex */
             $refIndexObj = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Database\\ReferenceIndex');
             if (isset($record['t3ver_wsid'])) {
                 $refIndexObj->setWorkspaceId($record['t3ver_wsid']);
             }
             $result = $refIndexObj->updateRefIndexTable($tableName, $record['uid'], $dryRun);
             $uidList[] = $record['uid'];
             $recordCount++;
             if ($result['addedNodes'] || $result['deletedNodes']) {
                 $errorMessage = 'Record ' . $tableName . ':' . $record['uid'];
                 if ($dryRun) {
                     $errorMessage .= ' has ' . $result['addedNodes'] . ' missing indexes and ' . $result['deletedNodes'] . ' stale indexes.';
                 } else {
                     $errorMessage .= ' ' . $result['addedNodes'] . ' indexes were added and ' . $result['deletedNodes'] . ' stale indexes were removed.';
                 }
                 $errorCount++;
                 $this->delegateLog($delegate, 'notice', $errorMessage);
             }
         }
         // Searching lost indexes for this table:
         $where = 'tablename=' . $persistenceContext->getDatabaseConnection()->fullQuoteStr($tableName, 'sys_refindex') . ' AND recuid NOT IN (' . implode(',', $uidList) . ')';
         $lostIndexes = $persistenceContext->getDatabaseConnection()->exec_SELECTgetRows('hash', 'sys_refindex', $where);
         if (count($lostIndexes)) {
             $errorMessage = 'Table ' . $tableName . ' has ' . count($lostIndexes);
             if ($dryRun) {
                 $errorMessage .= ' which need to be deleted.';
             } else {
                 $errorMessage .= ' which have been deleted.';
             }
             $errorCount++;
             $this->delegateLog($delegate, 'notice', $errorMessage);
             if (!$dryRun) {
                 $persistenceContext->getDatabaseConnection()->exec_DELETEquery('sys_refindex', $where);
             }
         }
     }
     // Searching lost indexes for non-existing tables:
     $where = 'tablename NOT IN (' . implode(',', $persistenceContext->getDatabaseConnection()->fullQuoteArray($processedTables, 'sys_refindex')) . ')';
     $lostTablesCount = $persistenceContext->getDatabaseConnection()->exec_SELECTcountRows('hash', 'sys_refindex', $where);
     if ($lostTablesCount) {
         $errorCount++;
         $this->delegateLog($delegate, 'notice', 'Found ' . $lostTablesCount . ' indexes for not existing tables.');
         if (!$dryRun) {
             $persistenceContext->getDatabaseConnection()->exec_DELETEquery('sys_refindex', $where);
             $this->delegateLog($delegate, 'info', 'Removed indexes for not existing tables.');
         }
     }
     $this->callDelegateForEvent($delegate, 'operationHasEnded');
     return array($errorCount, $recordCount, $processedTables);
 }
Ejemplo n.º 13
0
 /**
  * Find orphan records
  * VERY CPU and memory intensive since it will look up the whole page tree!
  *
  * @return array
  */
 public function main()
 {
     // Initialize result array:
     $resultArray = ['message' => $this->cli_help['name'] . LF . LF . $this->cli_help['description'], 'headers' => ['versions' => ['All versions', 'Showing all versions of records found', 0], 'versions_published' => ['All published versions', 'This is all records that has been published and can therefore be removed permanently', 1], 'versions_liveWS' => ['All versions in Live workspace', 'This is all records that are offline versions in the Live workspace. You may wish to flush these if you only use workspaces for versioning since then you might find lots of versions piling up in the live workspace which have simply been disconnected from the workspace before they were published.', 1], 'versions_lost_workspace' => ['Versions outside a workspace', 'Versions that has lost their connection to a workspace in TYPO3.', 3], 'versions_inside_versioned_page' => ['Versions in versions', 'Versions inside an already versioned page. Something that is confusing to users and therefore should not happen but is technically possible.', 2], 'versions_unused_placeholders' => ['Unused placeholder records', 'Placeholder records which are not used anymore by offline versions.', 2], 'versions_move_placeholders_ok' => ['Move placeholders', 'Move-to placeholder records which has good integrity', 0], 'versions_move_placeholders_bad' => ['Move placeholders with bad integrity', 'Move-to placeholder records which has bad integrity', 2], 'versions_move_id_check' => ['Checking if t3ver_move_id is correct', 't3ver_move_id must only be set with online records having t3ver_state=3.', 2]], 'versions' => []];
     $startingPoint = $this->cli_isArg('--pid') ? MathUtility::forceIntegerInRange($this->cli_argValue('--pid'), 0) : 0;
     $depth = $this->cli_isArg('--depth') ? MathUtility::forceIntegerInRange($this->cli_argValue('--depth'), 0) : 1000;
     $this->genTree($startingPoint, $depth, (int) $this->cli_argValue('--echotree'));
     $resultArray['versions'] = $this->recStats['versions'];
     $resultArray['versions_published'] = $this->recStats['versions_published'];
     $resultArray['versions_liveWS'] = $this->recStats['versions_liveWS'];
     $resultArray['versions_lost_workspace'] = $this->recStats['versions_lost_workspace'];
     $resultArray['versions_inside_versioned_page'] = $this->recStats['versions_inside_versioned_page'];
     // Finding all placeholders with no records attached!
     $resultArray['versions_unused_placeholders'] = [];
     foreach ($GLOBALS['TCA'] as $table => $cfg) {
         if ($cfg['ctrl']['versioningWS']) {
             $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
             $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
             $result = $queryBuilder->select('uid', 'pid')->from($table)->where($queryBuilder->expr()->gte('pid', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)), $queryBuilder->expr()->eq('t3ver_state', $queryBuilder->createNamedParameter((string) new VersionState(VersionState::NEW_PLACEHOLDER), \PDO::PARAM_INT)))->execute();
             while ($placeholderRecord = $result->fetch()) {
                 if (count(BackendUtility::selectVersionsOfRecord($table, $placeholderRecord['uid'], 'uid', '*', null)) <= 1) {
                     $resultArray['versions_unused_placeholders'][GeneralUtility::shortMD5($table . ':' . $placeholderRecord['uid'])] = $table . ':' . $placeholderRecord['uid'];
                 }
             }
         }
     }
     asort($resultArray['versions_unused_placeholders']);
     // Finding all move placeholders with inconsistencies:
     $resultArray['versions_move_placeholders_ok'] = [];
     $resultArray['versions_move_placeholders_bad'] = [];
     foreach ($GLOBALS['TCA'] as $table => $cfg) {
         if (BackendUtility::isTableWorkspaceEnabled($table)) {
             $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
             $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
             $result = $queryBuilder->select('uid', 'pid', 't3ver_move_id', 't3ver_wsid', 't3ver_state')->from($table)->where($queryBuilder->expr()->gte('pid', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)), $queryBuilder->expr()->eq('t3ver_state', $queryBuilder->createNamedParameter((string) new VersionState(VersionState::MOVE_PLACEHOLDER), \PDO::PARAM_INT)))->execute();
             while ($placeholderRecord = $result->fetch()) {
                 $shortID = GeneralUtility::shortMD5($table . ':' . $placeholderRecord['uid']);
                 if ((int) $placeholderRecord['t3ver_wsid'] !== 0) {
                     $phrecCopy = $placeholderRecord;
                     if (BackendUtility::movePlhOL($table, $placeholderRecord)) {
                         if ($wsAlt = BackendUtility::getWorkspaceVersionOfRecord($phrecCopy['t3ver_wsid'], $table, $placeholderRecord['uid'], 'uid,pid,t3ver_state')) {
                             if (!VersionState::cast($wsAlt['t3ver_state'])->equals(VersionState::MOVE_POINTER)) {
                                 $resultArray['versions_move_placeholders_bad'][$shortID] = [$table . ':' . $placeholderRecord['uid'], 'State for version was not "4" as it should be!', $phrecCopy];
                             } else {
                                 $resultArray['versions_move_placeholders_ok'][$shortID] = [$table . ':' . $placeholderRecord['uid'], 'PLH' => $phrecCopy, 'online' => $placeholderRecord, 'PNT' => $wsAlt];
                             }
                         } else {
                             $resultArray['versions_move_placeholders_bad'][$shortID] = [$table . ':' . $placeholderRecord['uid'], 'No version was found for online record to be moved. A version must exist.', $phrecCopy];
                         }
                     } else {
                         $resultArray['versions_move_placeholders_bad'][$shortID] = [$table . ':' . $placeholderRecord['uid'], 'Did not find online record for "t3ver_move_id" value ' . $placeholderRecord['t3ver_move_id'], $placeholderRecord];
                     }
                 } else {
                     $resultArray['versions_move_placeholders_bad'][$shortID] = [$table . ':' . $placeholderRecord['uid'], 'Placeholder was not assigned a workspace value in t3ver_wsid.', $placeholderRecord];
                 }
             }
         }
     }
     ksort($resultArray['versions_move_placeholders_ok']);
     ksort($resultArray['versions_move_placeholders_bad']);
     // Finding move_id_check inconsistencies:
     $resultArray['versions_move_id_check'] = [];
     foreach ($GLOBALS['TCA'] as $table => $cfg) {
         if (BackendUtility::isTableWorkspaceEnabled($table)) {
             $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
             $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
             $result = $queryBuilder->select('uid', 'pid', 't3ver_move_id', 't3ver_wsid', 't3ver_state')->from($table)->where($queryBuilder->expr()->neq('t3ver_move_id', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)))->execute();
             while ($placeholderRecord = $result->fetch()) {
                 if (VersionState::cast($placeholderRecord['t3ver_state'])->equals(VersionState::MOVE_PLACEHOLDER)) {
                     if ($placeholderRecord['pid'] != -1) {
                     } else {
                         $resultArray['versions_move_id_check'][] = [$table . ':' . $placeholderRecord['uid'], 'Record was offline, must not be!', $placeholderRecord];
                     }
                 } else {
                     $resultArray['versions_move_id_check'][] = [$table . ':' . $placeholderRecord['uid'], 'Record had t3ver_move_id set to "' . $placeholderRecord['t3ver_move_id'] . '" while having t3ver_state=' . $placeholderRecord['t3ver_state'], $placeholderRecord];
                 }
             }
         }
     }
     return $resultArray;
 }
Ejemplo n.º 14
0
 /**
  * Updating Index (External API)
  *
  * @param bool $testOnly If set, only a test
  * @param bool $cli_echo If set, output CLI status
  * @return array Header and body status content
  */
 public function updateIndex($testOnly, $cli_echo = false)
 {
     $databaseConnection = $this->getDatabaseConnection();
     $errors = array();
     $tableNames = array();
     $recCount = 0;
     $tableCount = 0;
     $headerContent = $testOnly ? 'Reference Index being TESTED (nothing written, remove the "--check" argument)' : 'Reference Index being Updated';
     if ($cli_echo) {
         echo '*******************************************' . LF . $headerContent . LF . '*******************************************' . LF;
     }
     // Traverse all tables:
     foreach ($GLOBALS['TCA'] as $tableName => $cfg) {
         if (isset(static::$nonRelationTables[$tableName])) {
             continue;
         }
         // Traverse all records in tables, including deleted records:
         $fieldNames = BackendUtility::isTableWorkspaceEnabled($tableName) ? 'uid,t3ver_wsid' : 'uid';
         $res = $databaseConnection->exec_SELECTquery($fieldNames, $tableName, '1=1');
         if ($databaseConnection->sql_error()) {
             // Table exists in $TCA but does not exist in the database
             GeneralUtility::sysLog(sprintf('Table "%s" exists in $TCA but does not exist in the database. You should run the Database Analyzer in the Install Tool to fix this.', $tableName), 'core', GeneralUtility::SYSLOG_SEVERITY_ERROR);
             continue;
         }
         $tableNames[] = $tableName;
         $tableCount++;
         $uidList = array(0);
         while ($record = $databaseConnection->sql_fetch_assoc($res)) {
             /** @var $refIndexObj ReferenceIndex */
             $refIndexObj = GeneralUtility::makeInstance(ReferenceIndex::class);
             if (isset($record['t3ver_wsid'])) {
                 $refIndexObj->setWorkspaceId($record['t3ver_wsid']);
             }
             $result = $refIndexObj->updateRefIndexTable($tableName, $record['uid'], $testOnly);
             $uidList[] = $record['uid'];
             $recCount++;
             if ($result['addedNodes'] || $result['deletedNodes']) {
                 $error = 'Record ' . $tableName . ':' . $record['uid'] . ' had ' . $result['addedNodes'] . ' added indexes and ' . $result['deletedNodes'] . ' deleted indexes';
                 $errors[] = $error;
                 if ($cli_echo) {
                     echo $error . LF;
                 }
             }
         }
         $databaseConnection->sql_free_result($res);
         // Searching lost indexes for this table:
         $where = 'tablename=' . $databaseConnection->fullQuoteStr($tableName, 'sys_refindex') . ' AND recuid NOT IN (' . implode(',', $uidList) . ')';
         $lostIndexes = $databaseConnection->exec_SELECTgetRows('hash', 'sys_refindex', $where);
         $lostIndexesCount = count($lostIndexes);
         if ($lostIndexesCount) {
             $error = 'Table ' . $tableName . ' has ' . $lostIndexesCount . ' lost indexes which are now deleted';
             $errors[] = $error;
             if ($cli_echo) {
                 echo $error . LF;
             }
             if (!$testOnly) {
                 $databaseConnection->exec_DELETEquery('sys_refindex', $where);
             }
         }
     }
     // Searching lost indexes for non-existing tables:
     $where = 'tablename NOT IN (' . implode(',', $databaseConnection->fullQuoteArray($tableNames, 'sys_refindex')) . ')';
     $lostTables = $databaseConnection->exec_SELECTgetRows('hash', 'sys_refindex', $where);
     $lostTablesCount = count($lostTables);
     if ($lostTablesCount) {
         $error = 'Index table hosted ' . $lostTablesCount . ' indexes for non-existing tables, now removed';
         $errors[] = $error;
         if ($cli_echo) {
             echo $error . LF;
         }
         if (!$testOnly) {
             $databaseConnection->exec_DELETEquery('sys_refindex', $where);
         }
     }
     $errorCount = count($errors);
     $recordsCheckedString = $recCount . ' records from ' . $tableCount . ' tables were checked/updated.' . LF;
     $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, $errorCount ? implode('##LF##', $errors) : 'Index Integrity was perfect!', $recordsCheckedString, $errorCount ? FlashMessage::ERROR : FlashMessage::OK);
     /** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */
     $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
     /** @var $defaultFlashMessageQueue \TYPO3\CMS\Core\Messaging\FlashMessageQueue */
     $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
     $defaultFlashMessageQueue->enqueue($flashMessage);
     $bodyContent = $defaultFlashMessageQueue->renderFlashMessages();
     if ($cli_echo) {
         echo $recordsCheckedString . ($errorCount ? 'Updates: ' . $errorCount : 'Index Integrity was perfect!') . LF;
     }
     if (!$testOnly) {
         $registry = GeneralUtility::makeInstance(Registry::class);
         $registry->set('core', 'sys_refindex_lastUpdate', $GLOBALS['EXEC_TIME']);
     }
     return array($headerContent, $bodyContent, $errorCount);
 }
Ejemplo n.º 15
0
 /**
  * Find all page uids recursive starting from a specific page
  *
  * @param int $pageId
  * @param int $wsid
  * @param int $recursionLevel
  * @return string Comma sep. uid list
  */
 protected function getTreeUids($pageId, $wsid, $recursionLevel)
 {
     // Reusing existing functionality with the drawback that
     // mount points are not covered yet
     $perms_clause = $GLOBALS['BE_USER']->getPagePermsClause(1);
     /** @var $searchObj \TYPO3\CMS\Core\Database\QueryView */
     $searchObj = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\QueryView::class);
     if ($pageId > 0) {
         $pageList = $searchObj->getTreeList($pageId, $recursionLevel, 0, $perms_clause);
     } else {
         $mountPoints = $GLOBALS['BE_USER']->uc['pageTree_temporaryMountPoint'];
         if (!is_array($mountPoints) || empty($mountPoints)) {
             $mountPoints = array_map('intval', $GLOBALS['BE_USER']->returnWebmounts());
             $mountPoints = array_unique($mountPoints);
         }
         $newList = array();
         foreach ($mountPoints as $mountPoint) {
             $newList[] = $searchObj->getTreeList($mountPoint, $recursionLevel, 0, $perms_clause);
         }
         $pageList = implode(',', $newList);
     }
     unset($searchObj);
     if (BackendUtility::isTableWorkspaceEnabled('pages') && $pageList) {
         // Remove the "subbranch" if a page was moved away
         $movedAwayPages = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid, pid, t3ver_move_id', 'pages', 't3ver_move_id IN (' . $pageList . ') AND t3ver_wsid=' . (int) $wsid . BackendUtility::deleteClause('pages'), '', 'uid', '', 't3ver_move_id');
         $pageIds = GeneralUtility::intExplode(',', $pageList, true);
         // move all pages away
         $newList = array_diff($pageIds, array_keys($movedAwayPages));
         // keep current page in the list
         $newList[] = $pageId;
         // move back in if still connected to the "remaining" pages
         do {
             $changed = false;
             foreach ($movedAwayPages as $uid => $rec) {
                 if (in_array($rec['pid'], $newList) && !in_array($uid, $newList)) {
                     $newList[] = $uid;
                     $changed = true;
                 }
             }
         } while ($changed);
         $pageList = implode(',', $newList);
         // In case moving pages is enabled we need to replace all move-to pointer with their origin
         $pages = $this->getDatabaseConnection()->exec_SELECTgetRows('uid, t3ver_move_id', 'pages', 'uid IN (' . $pageList . ')' . BackendUtility::deleteClause('pages'), '', 'uid', '', 'uid');
         $newList = array();
         $pageIds = GeneralUtility::intExplode(',', $pageList, true);
         if (!in_array($pageId, $pageIds)) {
             $pageIds[] = $pageId;
         }
         foreach ($pageIds as $pageId) {
             if ((int) $pages[$pageId]['t3ver_move_id'] > 0) {
                 $newList[] = (int) $pages[$pageId]['t3ver_move_id'];
             } else {
                 $newList[] = $pageId;
             }
         }
         $pageList = implode(',', $newList);
     }
     return $pageList;
 }
 /**
  * Get a single record row for a TCA table from the database.
  * \TYPO3\CMS\Backend\Form\DataPreprocessor is used for "upgrading" the
  * values, especially the relations.
  * Used in inline context
  *
  * @param string $table The table to fetch data from (= foreign_table)
  * @param string $uid The uid of the record to fetch, or the pid if a new record should be created
  * @param string $cmd The command to perform, empty or 'new'
  * @return array A record row from the database post-processed by \TYPO3\CMS\Backend\Form\DataPreprocessor
  * @internal
  */
 public function getRecord($table, $uid, $cmd = '')
 {
     $backendUser = $this->getBackendUserAuthentication();
     // Fetch workspace version of a record (if any):
     if ($cmd !== 'new' && $backendUser->workspace !== 0 && BackendUtility::isTableWorkspaceEnabled($table)) {
         $workspaceVersion = BackendUtility::getWorkspaceVersionOfRecord($backendUser->workspace, $table, $uid, 'uid,t3ver_state');
         if ($workspaceVersion !== FALSE) {
             $versionState = VersionState::cast($workspaceVersion['t3ver_state']);
             if ($versionState->equals(VersionState::DELETE_PLACEHOLDER)) {
                 return FALSE;
             }
             $uid = $workspaceVersion['uid'];
         }
     }
     /** @var $trData DataPreprocessor */
     $trData = GeneralUtility::makeInstance(DataPreprocessor::class);
     $trData->addRawData = TRUE;
     $trData->lockRecords = 1;
     // If a new record should be created
     $trData->fetchRecord($table, $uid, $cmd === 'new' ? 'new' : '');
     $rec = reset($trData->regTableItems_data);
     return $rec;
 }
Ejemplo n.º 17
0
 /**
  * Update Reference Index (sys_refindex) for a record
  * Should be called any almost any update to a record which could affect references inside the record.
  *
  * @param string $table Table name
  * @param int $id Record UID
  * @return void
  */
 public function updateRefIndex($table, $id)
 {
     /** @var $refIndexObj ReferenceIndex */
     $refIndexObj = GeneralUtility::makeInstance(ReferenceIndex::class);
     if (BackendUtility::isTableWorkspaceEnabled($table)) {
         $refIndexObj->setWorkspaceId($this->BE_USER->workspace);
     }
     $refIndexObj->updateRefIndexTable($table, $id);
 }
Ejemplo n.º 18
0
 /**
  * Substitute given list of uids in child table with workspace uid if needed
  *
  * @param array $connectedUids List of connected uids
  * @param string $childTableName Name of child table
  * @return array List of uids in workspace
  */
 protected function getWorkspacedUids(array $connectedUids, $childTableName)
 {
     $backendUser = $this->getBackendUser();
     $newConnectedUids = [];
     foreach ($connectedUids as $uid) {
         // Fetch workspace version of a record (if any):
         // @todo: Needs handling
         if ($backendUser->workspace !== 0 && BackendUtility::isTableWorkspaceEnabled($childTableName)) {
             $workspaceVersion = BackendUtility::getWorkspaceVersionOfRecord($backendUser->workspace, $childTableName, $uid, 'uid,t3ver_state');
             if ($workspaceVersion !== false) {
                 $versionState = VersionState::cast($workspaceVersion['t3ver_state']);
                 if ($versionState->equals(VersionState::DELETE_PLACEHOLDER)) {
                     return [];
                 }
                 $uid = $workspaceVersion['uid'];
             }
         }
         $newConnectedUids[] = $uid;
     }
     return $newConnectedUids;
 }
Ejemplo n.º 19
0
 /**
  * Check if record ($table, $uid) is a workspace record
  *
  * @param string $table The table the record belongs to
  * @param integer $uid The record's uid
  * @return boolean TRUE if the record is in a draft workspace, FALSE if it's a LIVE record
  */
 public static function isDraftRecord($table, $uid)
 {
     $isWorkspaceRecord = FALSE;
     if (BackendUtility::isTableWorkspaceEnabled($table)) {
         $record = BackendUtility::getRecord($table, $uid);
         if ($record['pid'] == '-1' || $record['t3ver_state'] > 0) {
             $isWorkspaceRecord = TRUE;
         }
     }
     return $isWorkspaceRecord;
 }
Ejemplo n.º 20
0
 /**
  * Update Reference Index (sys_refindex) for a record
  * Should be called any almost any update to a record which could affect references inside the record.
  *
  * @param string $table Table name
  * @param integer $id Record UID
  * @return void
  * @todo Define visibility
  */
 public function updateRefIndex($table, $id)
 {
     /** @var $refIndexObj \TYPO3\CMS\Core\Database\ReferenceIndex */
     $refIndexObj = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Database\\ReferenceIndex');
     if (BackendUtility::isTableWorkspaceEnabled($table)) {
         $refIndexObj->setWorkspaceId($this->BE_USER->workspace);
     }
     $refIndexObj->updateRefIndexTable($table, $id);
 }
Ejemplo n.º 21
0
 /**
  * Updating Index (External API)
  *
  * @param boolean $testOnly If set, only a test
  * @param boolean $cli_echo If set, output CLI status
  * @return array Header and body status content
  */
 public function updateIndex($testOnly, $cli_echo = FALSE)
 {
     $errors = array();
     $tableNames = array();
     $recCount = 0;
     $tableCount = 0;
     $headerContent = $testOnly ? 'Reference Index being TESTED (nothing written, use "--refindex update" to update)' : 'Reference Index being Updated';
     if ($cli_echo) {
         echo '*******************************************' . LF . $headerContent . LF . '*******************************************' . LF;
     }
     // Traverse all tables:
     foreach ($GLOBALS['TCA'] as $tableName => $cfg) {
         if (isset(static::$nonRelationTables[$tableName])) {
             continue;
         }
         // Traverse all records in tables, including deleted records:
         $fieldNames = BackendUtility::isTableWorkspaceEnabled($tableName) ? 'uid,t3ver_wsid' : 'uid';
         $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery($fieldNames, $tableName, '1=1');
         if ($GLOBALS['TYPO3_DB']->sql_error()) {
             // Table exists in $TCA but does not exist in the database
             GeneralUtility::sysLog(sprintf('Table "%s" exists in $TCA but does not exist in the database. You should run the Database Analyzer in the Install Tool to fix this.', $tableName), 'core', GeneralUtility::SYSLOG_SEVERITY_ERROR);
             continue;
         }
         $tableNames[] = $tableName;
         $tableCount++;
         $uidList = array(0);
         while ($recdat = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
             /** @var $refIndexObj ReferenceIndex */
             $refIndexObj = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Database\\ReferenceIndex');
             if (isset($recdat['t3ver_wsid'])) {
                 $refIndexObj->setWorkspaceId($recdat['t3ver_wsid']);
             }
             $result = $refIndexObj->updateRefIndexTable($tableName, $recdat['uid'], $testOnly);
             $uidList[] = $recdat['uid'];
             $recCount++;
             if ($result['addedNodes'] || $result['deletedNodes']) {
                 $Err = 'Record ' . $tableName . ':' . $recdat['uid'] . ' had ' . $result['addedNodes'] . ' added indexes and ' . $result['deletedNodes'] . ' deleted indexes';
                 $errors[] = $Err;
                 if ($cli_echo) {
                     echo $Err . LF;
                 }
             }
         }
         $GLOBALS['TYPO3_DB']->sql_free_result($res);
         // Searching lost indexes for this table:
         $where = 'tablename=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($tableName, 'sys_refindex') . ' AND recuid NOT IN (' . implode(',', $uidList) . ')';
         $lostIndexes = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('hash', 'sys_refindex', $where);
         if (count($lostIndexes)) {
             $Err = 'Table ' . $tableName . ' has ' . count($lostIndexes) . ' lost indexes which are now deleted';
             $errors[] = $Err;
             if ($cli_echo) {
                 echo $Err . LF;
             }
             if (!$testOnly) {
                 $GLOBALS['TYPO3_DB']->exec_DELETEquery('sys_refindex', $where);
             }
         }
     }
     // Searching lost indexes for non-existing tables:
     $where = 'tablename NOT IN (' . implode(',', $GLOBALS['TYPO3_DB']->fullQuoteArray($tableNames, 'sys_refindex')) . ')';
     $lostTables = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('hash', 'sys_refindex', $where);
     if (count($lostTables)) {
         $Err = 'Index table hosted ' . count($lostTables) . ' indexes for non-existing tables, now removed';
         $errors[] = $Err;
         if ($cli_echo) {
             echo $Err . LF;
         }
         if (!$testOnly) {
             $GLOBALS['TYPO3_DB']->exec_DELETEquery('sys_refindex', $where);
         }
     }
     $testedHowMuch = $recCount . ' records from ' . $tableCount . ' tables were checked/updated.' . LF;
     $bodyContent = $testedHowMuch . (count($errors) ? implode(LF, $errors) : 'Index Integrity was perfect!');
     if ($cli_echo) {
         echo $testedHowMuch . (count($errors) ? 'Updates: ' . count($errors) : 'Index Integrity was perfect!') . LF;
     }
     if (!$testOnly) {
         $registry = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Registry');
         $registry->set('core', 'sys_refindex_lastUpdate', $GLOBALS['EXEC_TIME']);
     }
     return array($headerContent, $bodyContent, count($errors));
 }
Ejemplo n.º 22
0
 /**
  * Processing/Preparing content for copyRecord() function
  *
  * @param string $table Table name
  * @param integer $uid Record uid
  * @param string $field Field name being processed
  * @param string $value Input value to be processed.
  * @param array $row Record array
  * @param array $conf TCA field configuration
  * @param integer $realDestPid Real page id (pid) the record is copied to
  * @param integer $language Language ID (from sys_language table) used in the duplicated record
  * @return array $workspaceOptions Options to be forwarded if actions happen on a workspace currently
  * @access private
  * @see copyRecord()
  * @todo Define visibility
  */
 public function copyRecord_procBasedOnFieldType($table, $uid, $field, $value, $row, $conf, $realDestPid, $language = 0, array $workspaceOptions = array())
 {
     // Process references and files, currently that means only the files, prepending absolute paths (so the TCEmain engine will detect the file as new and one that should be made into a copy)
     $value = $this->copyRecord_procFilesRefs($conf, $uid, $value);
     $inlineSubType = $this->getInlineFieldType($conf);
     // Get the localization mode for the current (parent) record (keep|select):
     $localizationMode = \TYPO3\CMS\Backend\Utility\BackendUtility::getInlineLocalizationMode($table, $field);
     // Register if there are references to take care of or MM is used on an inline field (no change to value):
     if ($this->isReferenceField($conf) || $inlineSubType == 'mm') {
         $allowedTables = $conf['type'] == 'group' ? $conf['allowed'] : $conf['foreign_table'] . ',' . $conf['neg_foreign_table'];
         $prependName = $conf['type'] == 'group' ? $conf['prepend_tname'] : $conf['neg_foreign_table'];
         $mmTable = isset($conf['MM']) && $conf['MM'] ? $conf['MM'] : '';
         $localizeForeignTable = isset($conf['foreign_table']) && \TYPO3\CMS\Backend\Utility\BackendUtility::isTableLocalizable($conf['foreign_table']);
         $localizeReferences = $localizeForeignTable && isset($conf['localizeReferencesAtParentLocalization']) && $conf['localizeReferencesAtParentLocalization'];
         $localizeChildren = $localizeForeignTable && isset($conf['behaviour']['localizeChildrenAtParentLocalization']) && $conf['behaviour']['localizeChildrenAtParentLocalization'];
         /** @var $dbAnalysis \TYPO3\CMS\Core\Database\RelationHandler */
         $dbAnalysis = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Database\\RelationHandler');
         $dbAnalysis->start($value, $allowedTables, $mmTable, $uid, $table, $conf);
         // Localize referenced records of select fields:
         if ($language > 0 && ($localizeReferences && empty($mmTable) || $localizeChildren && $localizationMode === 'select' && $inlineSubType === 'mm')) {
             foreach ($dbAnalysis->itemArray as $index => $item) {
                 // Since select fields can reference many records, check whether there's already a localization:
                 $recordLocalization = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecordLocalization($item['table'], $item['id'], $language);
                 if ($recordLocalization) {
                     $dbAnalysis->itemArray[$index]['id'] = $recordLocalization[0]['uid'];
                 } elseif ($this->isNestedElementCallRegistered($item['table'], $item['id'], 'localize') === FALSE) {
                     $dbAnalysis->itemArray[$index]['id'] = $this->localize($item['table'], $item['id'], $language);
                 }
             }
             $value = implode(',', $dbAnalysis->getValueArray($prependName));
         } elseif ($language > 0 && $localizeChildren === FALSE && $localizationMode === 'select' && $inlineSubType === 'mm') {
             foreach ($dbAnalysis->itemArray as $index => $item) {
                 // Since select fields can reference many records, check whether there's already a localization:
                 $recordLocalization = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecordLocalization($item['table'], $item['id'], $language);
                 if ($recordLocalization) {
                     $dbAnalysis->itemArray[$index]['id'] = $recordLocalization[0]['uid'];
                 } elseif ($this->isNestedElementCallRegistered($item['table'], $item['id'], 'localize') === FALSE) {
                     unset($dbAnalysis->itemArray[$index]);
                 }
             }
             $value = implode(',', $dbAnalysis->getValueArray($prependName));
         } elseif ($mmTable) {
             $value = implode(',', $dbAnalysis->getValueArray($prependName));
         }
         // Setting the value in this array will notify the remapListedDBRecords() function that this field MAY need references to be corrected
         if ($value) {
             $this->registerDBList[$table][$uid][$field] = $value;
         }
     } elseif ($inlineSubType !== FALSE) {
         // Localization in mode 'keep', isn't a real localization, but keeps the children of the original parent record:
         if ($language > 0 && $localizationMode == 'keep') {
             $value = $inlineSubType == 'field' ? 0 : '';
         } else {
             // Fetch the related child records by using t3lib_loadDBGroup:
             /** @var $dbAnalysis \TYPO3\CMS\Core\Database\RelationHandler */
             $dbAnalysis = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Database\\RelationHandler');
             $dbAnalysis->start($value, $conf['foreign_table'], '', $uid, $table, $conf);
             // Walk through the items, copy them and remember the new id:
             foreach ($dbAnalysis->itemArray as $k => $v) {
                 // If language is set and differs from original record, this isn't a copy action but a localization of our parent/ancestor:
                 if ($language > 0 && \TYPO3\CMS\Backend\Utility\BackendUtility::isTableLocalizable($table) && $language != $row[$GLOBALS['TCA'][$table]['ctrl']['languageField']]) {
                     // If children should be localized when the parent gets localized the first time, just do it:
                     if ($localizationMode != FALSE && isset($conf['behaviour']['localizeChildrenAtParentLocalization']) && $conf['behaviour']['localizeChildrenAtParentLocalization']) {
                         $newId = $this->localize($v['table'], $v['id'], $language);
                     }
                 } else {
                     if (!\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($realDestPid)) {
                         $newId = $this->copyRecord($v['table'], $v['id'], -$v['id']);
                     } elseif ($realDestPid == -1 && \TYPO3\CMS\Backend\Utility\BackendUtility::isTableWorkspaceEnabled($v['table'])) {
                         $workspaceVersion = \TYPO3\CMS\Backend\Utility\BackendUtility::getWorkspaceVersionOfRecord($this->BE_USER->workspace, $v['table'], $v['id'], 'uid');
                         // If workspace version does not exist, create a new one:
                         if ($workspaceVersion === FALSE) {
                             $newId = $this->versionizeRecord($v['table'], $v['id'], isset($workspaceOptions['label']) ? $workspaceOptions['label'] : 'Auto-created for WS #' . $this->BE_USER->workspace, isset($workspaceOptions['delete']) ? $workspaceOptions['delete'] : FALSE);
                         } else {
                             $newId = $workspaceVersion['uid'];
                         }
                     } else {
                         $newId = $this->copyRecord_raw($v['table'], $v['id'], $realDestPid, array(), $workspaceOptions);
                     }
                 }
                 // If the current field is set on a page record, update the pid of related child records:
                 if ($table == 'pages') {
                     $this->registerDBPids[$v['table']][$v['id']] = $uid;
                 } elseif (isset($this->registerDBPids[$table][$uid])) {
                     $this->registerDBPids[$v['table']][$v['id']] = $this->registerDBPids[$table][$uid];
                 }
                 $dbAnalysis->itemArray[$k]['id'] = $newId;
             }
             // Store the new values, we will set up the uids for the subtype later on (exception keep localization from original record):
             $value = implode(',', $dbAnalysis->getValueArray());
             $this->registerDBList[$table][$uid][$field] = $value;
         }
     }
     // For "flex" fieldtypes we need to traverse the structure for two reasons: If there are file references they have to be prepended with absolute paths and if there are database reference they MIGHT need to be remapped (still done in remapListedDBRecords())
     if ($conf['type'] == 'flex') {
         // Get current value array:
         $dataStructArray = \TYPO3\CMS\Backend\Utility\BackendUtility::getFlexFormDS($conf, $row, $table);
         $currentValueArray = \TYPO3\CMS\Core\Utility\GeneralUtility::xml2array($value);
         // Traversing the XML structure, processing files:
         if (is_array($currentValueArray)) {
             $currentValueArray['data'] = $this->checkValue_flex_procInData($currentValueArray['data'], array(), array(), $dataStructArray, array($table, $uid, $field, $realDestPid), 'copyRecord_flexFormCallBack');
             // Setting value as an array! -> which means the input will be processed according to the 'flex' type when the new copy is created.
             $value = $currentValueArray;
         }
     }
     return $value;
 }
Ejemplo n.º 23
0
 /**
  * Handles a purge callback on $this->itemArray
  *
  * @param callable $purgeCallback
  * @return bool Whether items have been purged
  */
 protected function purgeItemArrayHandler($purgeCallback)
 {
     $itemArrayHasBeenPurged = false;
     foreach ($this->tableArray as $itemTableName => $itemIds) {
         if (empty($itemIds) || !BackendUtility::isTableWorkspaceEnabled($itemTableName)) {
             continue;
         }
         $purgedItemIds = call_user_func(array($this, $purgeCallback), $itemTableName, $itemIds);
         $removedItemIds = array_diff($itemIds, $purgedItemIds);
         foreach ($removedItemIds as $removedItemId) {
             $this->removeFromItemArray($itemTableName, $removedItemId);
         }
         $this->tableArray[$itemTableName] = $purgedItemIds;
         if (!empty($removedItemIds)) {
             $itemArrayHasBeenPurged = true;
         }
     }
     return $itemArrayHasBeenPurged;
 }
 /**
  * @param string $table
  * @return boolean
  */
 protected function hasWorkspacesSupport($table)
 {
     return BackendUtility::isTableWorkspaceEnabled($table);
 }