Beispiel #1
0
 /**
  * Swapping versions of a record
  * Version from archive (future/past, called "swap version") will get the uid of the "t3ver_oid", the official element with uid = "t3ver_oid" will get the new versions old uid. PIDs are swapped also
  *
  * @param string $table Table name
  * @param int $id UID of the online record to swap
  * @param int $swapWith UID of the archived version to swap with!
  * @param bool $swapIntoWS If set, swaps online into workspace instead of publishing out of workspace.
  * @param DataHandler $tcemainObj TCEmain object
  * @param string $comment Notification comment
  * @param bool $notificationEmailInfo Accumulate state changes in memory for compiled notification email?
  * @param array $notificationAlternativeRecipients comma separated list of recipients to notificate instead of normal be_users
  * @return void
  */
 protected function version_swap($table, $id, $swapWith, $swapIntoWS = 0, DataHandler $tcemainObj, $comment = '', $notificationEmailInfo = FALSE, $notificationAlternativeRecipients = array())
 {
     // Check prerequisites before start swapping
     // First, check if we may actually edit the online record
     if (!$tcemainObj->checkRecordUpdateAccess($table, $id)) {
         $tcemainObj->newlog('Error: You cannot swap versions for a record you do not have access to edit!', 1);
         return;
     }
     // Select the two versions:
     $curVersion = BackendUtility::getRecord($table, $id, '*');
     $swapVersion = BackendUtility::getRecord($table, $swapWith, '*');
     $movePlh = array();
     $movePlhID = 0;
     if (!(is_array($curVersion) && is_array($swapVersion))) {
         $tcemainObj->newlog('Error: Either online or swap version could not be selected!', 2);
         return;
     }
     if (!$tcemainObj->BE_USER->workspacePublishAccess($swapVersion['t3ver_wsid'])) {
         $tcemainObj->newlog('User could not publish records from workspace #' . $swapVersion['t3ver_wsid'], 1);
         return;
     }
     $wsAccess = $tcemainObj->BE_USER->checkWorkspace($swapVersion['t3ver_wsid']);
     if (!($swapVersion['t3ver_wsid'] <= 0 || !($wsAccess['publish_access'] & 1) || (int) $swapVersion['t3ver_stage'] === -10)) {
         $tcemainObj->newlog('Records in workspace #' . $swapVersion['t3ver_wsid'] . ' can only be published when in "Publish" stage.', 1);
         return;
     }
     if (!($tcemainObj->doesRecordExist($table, $swapWith, 'show') && $tcemainObj->checkRecordUpdateAccess($table, $swapWith))) {
         $tcemainObj->newlog('You cannot publish a record you do not have edit and show permissions for', 1);
         return;
     }
     if ($swapIntoWS && !$tcemainObj->BE_USER->workspaceSwapAccess()) {
         $tcemainObj->newlog('Workspace #' . $swapVersion['t3ver_wsid'] . ' does not support swapping.', 1);
         return;
     }
     // Check if the swapWith record really IS a version of the original!
     if (!((int) $swapVersion['pid'] == -1 && (int) $curVersion['pid'] >= 0 && (int) $swapVersion['t3ver_oid'] === (int) $id)) {
         $tcemainObj->newlog('In swap version, either pid was not -1 or the t3ver_oid didn\'t match the id of the online version as it must!', 2);
         return;
     }
     // Lock file name:
     $lockFileName = PATH_site . 'typo3temp/swap_locking/' . $table . ':' . $id . '.ser';
     if (@is_file($lockFileName)) {
         $tcemainObj->newlog('A swapping lock file was present. Either another swap process is already running or a previous swap process failed. Ask your administrator to handle the situation.', 2);
         return;
     }
     // Now start to swap records by first creating the lock file
     // Write lock-file:
     GeneralUtility::writeFileToTypo3tempDir($lockFileName, serialize(array('tstamp' => $GLOBALS['EXEC_TIME'], 'user' => $tcemainObj->BE_USER->user['username'], 'curVersion' => $curVersion, 'swapVersion' => $swapVersion)));
     // Find fields to keep
     $keepFields = $this->getUniqueFields($table);
     if ($GLOBALS['TCA'][$table]['ctrl']['sortby']) {
         $keepFields[] = $GLOBALS['TCA'][$table]['ctrl']['sortby'];
     }
     // l10n-fields must be kept otherwise the localization
     // will be lost during the publishing
     if (!isset($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerTable']) && $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']) {
         $keepFields[] = $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'];
     }
     // Swap "keepfields"
     foreach ($keepFields as $fN) {
         $tmp = $swapVersion[$fN];
         $swapVersion[$fN] = $curVersion[$fN];
         $curVersion[$fN] = $tmp;
     }
     // Preserve states:
     $t3ver_state = array();
     $t3ver_state['swapVersion'] = $swapVersion['t3ver_state'];
     $t3ver_state['curVersion'] = $curVersion['t3ver_state'];
     // Modify offline version to become online:
     $tmp_wsid = $swapVersion['t3ver_wsid'];
     // Set pid for ONLINE
     $swapVersion['pid'] = (int) $curVersion['pid'];
     // We clear this because t3ver_oid only make sense for offline versions
     // and we want to prevent unintentional misuse of this
     // value for online records.
     $swapVersion['t3ver_oid'] = 0;
     // In case of swapping and the offline record has a state
     // (like 2 or 4 for deleting or move-pointer) we set the
     // current workspace ID so the record is not deselected
     // in the interface by BackendUtility::versioningPlaceholderClause()
     $swapVersion['t3ver_wsid'] = 0;
     if ($swapIntoWS) {
         if ($t3ver_state['swapVersion'] > 0) {
             $swapVersion['t3ver_wsid'] = $tcemainObj->BE_USER->workspace;
         } else {
             $swapVersion['t3ver_wsid'] = (int) $curVersion['t3ver_wsid'];
         }
     }
     $swapVersion['t3ver_tstamp'] = $GLOBALS['EXEC_TIME'];
     $swapVersion['t3ver_stage'] = 0;
     if (!$swapIntoWS) {
         $swapVersion['t3ver_state'] = (string) new VersionState(VersionState::DEFAULT_STATE);
     }
     // Moving element.
     if ((int) $GLOBALS['TCA'][$table]['ctrl']['versioningWS'] >= 2) {
         //  && $t3ver_state['swapVersion']==4   // Maybe we don't need this?
         if ($plhRec = BackendUtility::getMovePlaceholder($table, $id, 't3ver_state,pid,uid' . ($GLOBALS['TCA'][$table]['ctrl']['sortby'] ? ',' . $GLOBALS['TCA'][$table]['ctrl']['sortby'] : ''))) {
             $movePlhID = $plhRec['uid'];
             $movePlh['pid'] = $swapVersion['pid'];
             $swapVersion['pid'] = (int) $plhRec['pid'];
             $curVersion['t3ver_state'] = (int) $swapVersion['t3ver_state'];
             $swapVersion['t3ver_state'] = (string) new VersionState(VersionState::DEFAULT_STATE);
             if ($GLOBALS['TCA'][$table]['ctrl']['sortby']) {
                 // sortby is a "keepFields" which is why this will work...
                 $movePlh[$GLOBALS['TCA'][$table]['ctrl']['sortby']] = $swapVersion[$GLOBALS['TCA'][$table]['ctrl']['sortby']];
                 $swapVersion[$GLOBALS['TCA'][$table]['ctrl']['sortby']] = $plhRec[$GLOBALS['TCA'][$table]['ctrl']['sortby']];
             }
         }
     }
     unset($swapVersion['uid']);
     // Modify online version to become offline:
     unset($curVersion['uid']);
     // Set pid for OFFLINE
     $curVersion['pid'] = -1;
     $curVersion['t3ver_oid'] = (int) $id;
     $curVersion['t3ver_wsid'] = $swapIntoWS ? (int) $tmp_wsid : 0;
     $curVersion['t3ver_tstamp'] = $GLOBALS['EXEC_TIME'];
     $curVersion['t3ver_count'] = $curVersion['t3ver_count'] + 1;
     // Increment lifecycle counter
     $curVersion['t3ver_stage'] = 0;
     if (!$swapIntoWS) {
         $curVersion['t3ver_state'] = (string) new VersionState(VersionState::DEFAULT_STATE);
     }
     // Registering and swapping MM relations in current and swap records:
     $tcemainObj->version_remapMMForVersionSwap($table, $id, $swapWith);
     // Generating proper history data to prepare logging
     $tcemainObj->compareFieldArrayWithCurrentAndUnset($table, $id, $swapVersion);
     $tcemainObj->compareFieldArrayWithCurrentAndUnset($table, $swapWith, $curVersion);
     // Execute swapping:
     $sqlErrors = array();
     $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid=' . (int) $id, $swapVersion);
     if ($GLOBALS['TYPO3_DB']->sql_error()) {
         $sqlErrors[] = $GLOBALS['TYPO3_DB']->sql_error();
     } else {
         $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid=' . (int) $swapWith, $curVersion);
         if ($GLOBALS['TYPO3_DB']->sql_error()) {
             $sqlErrors[] = $GLOBALS['TYPO3_DB']->sql_error();
         } else {
             unlink($lockFileName);
         }
     }
     if (!count($sqlErrors)) {
         // Register swapped ids for later remapping:
         $this->remappedIds[$table][$id] = $swapWith;
         $this->remappedIds[$table][$swapWith] = $id;
         // If a moving operation took place...:
         if ($movePlhID) {
             // Remove, if normal publishing:
             if (!$swapIntoWS) {
                 // For delete + completely delete!
                 $tcemainObj->deleteEl($table, $movePlhID, TRUE, TRUE);
             } else {
                 // Otherwise update the movePlaceholder:
                 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid=' . (int) $movePlhID, $movePlh);
                 $tcemainObj->addRemapStackRefIndex($table, $movePlhID);
             }
         }
         // Checking for delete:
         // Delete only if new/deleted placeholders are there.
         if (!$swapIntoWS && ((int) $t3ver_state['swapVersion'] === 1 || (int) $t3ver_state['swapVersion'] === 2)) {
             // Force delete
             $tcemainObj->deleteEl($table, $id, TRUE);
         }
         $tcemainObj->newlog2(($swapIntoWS ? 'Swapping' : 'Publishing') . ' successful for table "' . $table . '" uid ' . $id . '=>' . $swapWith, $table, $id, $swapVersion['pid']);
         // Update reference index of the live record:
         $tcemainObj->addRemapStackRefIndex($table, $id);
         // Set log entry for live record:
         $propArr = $tcemainObj->getRecordPropertiesFromRow($table, $swapVersion);
         if ($propArr['_ORIG_pid'] == -1) {
             $label = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_tcemain.xlf:version_swap.offline_record_updated');
         } else {
             $label = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_tcemain.xlf:version_swap.online_record_updated');
         }
         $theLogId = $tcemainObj->log($table, $id, 2, $propArr['pid'], 0, $label, 10, array($propArr['header'], $table . ':' . $id), $propArr['event_pid']);
         $tcemainObj->setHistory($table, $id, $theLogId);
         // Update reference index of the offline record:
         $tcemainObj->addRemapStackRefIndex($table, $swapWith);
         // Set log entry for offline record:
         $propArr = $tcemainObj->getRecordPropertiesFromRow($table, $curVersion);
         if ($propArr['_ORIG_pid'] == -1) {
             $label = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_tcemain.xlf:version_swap.offline_record_updated');
         } else {
             $label = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_tcemain.xlf:version_swap.online_record_updated');
         }
         $theLogId = $tcemainObj->log($table, $swapWith, 2, $propArr['pid'], 0, $label, 10, array($propArr['header'], $table . ':' . $swapWith), $propArr['event_pid']);
         $tcemainObj->setHistory($table, $swapWith, $theLogId);
         $stageId = -20;
         // \TYPO3\CMS\Workspaces\Service\StagesService::STAGE_PUBLISH_EXECUTE_ID;
         if ($notificationEmailInfo) {
             $notificationEmailInfoKey = $wsAccess['uid'] . ':' . $stageId . ':' . $comment;
             $this->notificationEmailInfo[$notificationEmailInfoKey]['shared'] = array($wsAccess, $stageId, $comment);
             $this->notificationEmailInfo[$notificationEmailInfoKey]['elements'][] = $table . ':' . $id;
             $this->notificationEmailInfo[$notificationEmailInfoKey]['alternativeRecipients'] = $notificationAlternativeRecipients;
         } else {
             $this->notifyStageChange($wsAccess, $stageId, $table, $id, $comment, $tcemainObj, $notificationAlternativeRecipients);
         }
         // Write to log with stageId -20
         $tcemainObj->newlog2('Stage for record was changed to ' . $stageId . '. Comment was: "' . substr($comment, 0, 100) . '"', $table, $id);
         $tcemainObj->log($table, $id, 6, 0, 0, 'Published', 30, array('comment' => $comment, 'stage' => $stageId));
         // Clear cache:
         $tcemainObj->registerRecordIdForPageCacheClearing($table, $id);
         // Checking for "new-placeholder" and if found, delete it (BUT FIRST after swapping!):
         if (!$swapIntoWS && $t3ver_state['curVersion'] > 0) {
             // For delete + completely delete!
             $tcemainObj->deleteEl($table, $swapWith, TRUE, TRUE);
         }
         //Update reference index for live workspace too:
         /** @var $refIndexObj \TYPO3\CMS\Core\Database\ReferenceIndex */
         $refIndexObj = GeneralUtility::makeInstance(ReferenceIndex::class);
         $refIndexObj->setWorkspaceId(0);
         $refIndexObj->updateRefIndexTable($table, $id);
         $refIndexObj->updateRefIndexTable($table, $swapWith);
     } else {
         $tcemainObj->newlog('During Swapping: SQL errors happened: ' . implode('; ', $sqlErrors), 2);
     }
 }