Beispiel #1
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;
 }
Beispiel #2
0
 /**
  * Creates a node with the given record information
  *
  * @param array $record
  * @param int $mountPoint
  * @return \TYPO3\CMS\Backend\Tree\Pagetree\PagetreeNode
  */
 public static function getNewNode($record, $mountPoint = 0)
 {
     if (self::$titleLength === NULL) {
         self::$useNavTitle = $GLOBALS['BE_USER']->getTSConfigVal('options.pageTree.showNavTitle');
         self::$addIdAsPrefix = $GLOBALS['BE_USER']->getTSConfigVal('options.pageTree.showPageIdWithTitle');
         self::$addDomainName = $GLOBALS['BE_USER']->getTSConfigVal('options.pageTree.showDomainNameWithTitle');
         self::$backgroundColors = $GLOBALS['BE_USER']->getTSConfigProp('options.pageTree.backgroundColor');
         self::$titleLength = (int) $GLOBALS['BE_USER']->uc['titleLen'];
     }
     /** @var $subNode \TYPO3\CMS\Backend\Tree\Pagetree\PagetreeNode */
     $subNode = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Tree\Pagetree\PagetreeNode::class);
     $subNode->setRecord($record);
     $subNode->setCls($record['_CSSCLASS']);
     $subNode->setType('pages');
     $subNode->setId($record['uid']);
     $subNode->setMountPoint($mountPoint);
     $subNode->setWorkspaceId($record['_ORIG_uid'] ?: $record['uid']);
     $subNode->setBackgroundColor(self::$backgroundColors[$record['uid']]);
     $field = 'title';
     $text = $record['title'];
     if (self::$useNavTitle && trim($record['nav_title']) !== '') {
         $field = 'nav_title';
         $text = $record['nav_title'];
     }
     if (trim($text) === '') {
         $visibleText = '[' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.no_title', TRUE) . ']';
     } else {
         $visibleText = $text;
     }
     $visibleText = GeneralUtility::fixed_lgd_cs($visibleText, self::$titleLength);
     $suffix = '';
     if (self::$addDomainName) {
         $domain = self::getDomainName($record['uid']);
         $suffix = $domain !== '' ? ' [' . $domain . ']' : '';
     }
     $qtip = str_replace(' - ', '<br />', htmlspecialchars(BackendUtility::titleAttribForPages($record, '', FALSE)));
     $prefix = '';
     $lockInfo = BackendUtility::isRecordLocked('pages', $record['uid']);
     if (is_array($lockInfo)) {
         $qtip .= '<br />' . htmlspecialchars($lockInfo['msg']);
         $prefix .= IconUtility::getSpriteIcon('status-warning-in-use', array('class' => 'typo3-pagetree-status'));
     }
     // Call stats information hook
     $stat = '';
     if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['GLOBAL']['recStatInfoHooks'])) {
         $_params = array('pages', $record['uid']);
         $fakeThis = NULL;
         foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['GLOBAL']['recStatInfoHooks'] as $_funcRef) {
             $stat .= GeneralUtility::callUserFunction($_funcRef, $_params, $fakeThis);
         }
     }
     $prefix .= htmlspecialchars(self::$addIdAsPrefix ? '[' . $record['uid'] . '] ' : '');
     $subNode->setEditableText($text);
     $subNode->setText(htmlspecialchars($visibleText), $field, $prefix, htmlspecialchars($suffix) . $stat);
     $subNode->setQTip($qtip);
     if ((int) $record['uid'] !== 0) {
         $spriteIconCode = IconUtility::getSpriteIconForRecord('pages', $record);
     } else {
         $spriteIconCode = IconUtility::getSpriteIcon('apps-pagetree-root');
     }
     $subNode->setSpriteIconCode($spriteIconCode);
     if (!$subNode->canCreateNewPages() || VersionState::cast($record['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)) {
         $subNode->setIsDropTarget(FALSE);
     }
     if (!$subNode->canBeEdited() || !$subNode->canBeRemoved() || VersionState::cast($record['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)) {
         $subNode->setDraggable(FALSE);
     }
     return $subNode;
 }
 /**
  * Checks if record is a move-placeholder (t3ver_state==VersionState::MOVE_PLACEHOLDER) and if so
  * it will set $row to be the pointed-to live record (and return TRUE)
  *
  * @param string $table Table name
  * @param array $row Row (passed by reference) - must be online record!
  * @return boolean TRUE if overlay is made.
  * @see PageRepository::movePlhOl()
  */
 public static function movePlhOL($table, &$row)
 {
     // Only for WS ver 2... (moving)
     if ($table == 'pages' || (int) $GLOBALS['TCA'][$table]['ctrl']['versioningWS'] >= 2) {
         // If t3ver_move_id or t3ver_state is not found, then find it... (but we like best if it is here...)
         if (!isset($row['t3ver_move_id']) || !isset($row['t3ver_state'])) {
             $moveIDRec = self::getRecord($table, $row['uid'], 't3ver_move_id, t3ver_state');
             $moveID = $moveIDRec['t3ver_move_id'];
             $versionState = VersionState::cast($moveIDRec['t3ver_state']);
         } else {
             $moveID = $row['t3ver_move_id'];
             $versionState = VersionState::cast($row['t3ver_state']);
         }
         // Find pointed-to record.
         if ($versionState->equals(VersionState::MOVE_PLACEHOLDER) && $moveID) {
             if ($origRow = self::getRecord($table, $moveID, implode(',', array_keys($row)))) {
                 $row = $origRow;
                 return TRUE;
             }
         }
     }
     return FALSE;
 }
Beispiel #4
0
 /**
  * Creates the icon for input table/row
  * Returns filename for the image icon, relative to PATH_typo3
  *
  * @param string $table The table name
  * @param array $row The table row ("enablefields" are at least needed for correct icon display and for pages records some more fields in addition!)
  * @param boolean $shaded If set, the icon will be grayed/shaded
  * @return string Icon filename
  * @see getIconImage()
  */
 public static function getIcon($table, $row = array(), $shaded = FALSE)
 {
     // Flags
     // If set, then the usergroup number will NOT be printed unto the icon. NOTICE.
     // The icon is generated only if a default icon for groups is not found... So effectively this is ineffective.
     $doNotRenderUserGroupNumber = TRUE;
     // Shadow
     if (!empty($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) && !empty($row['t3ver_state'])) {
         switch (VersionState::cast($row['t3ver_state'])) {
             case new VersionState(VersionState::NEW_PLACEHOLDER):
                 return 'gfx/i/shadow_hide.png';
                 break;
             case new VersionState(VersionState::DELETE_PLACEHOLDER):
                 return 'gfx/i/shadow_delete.png';
                 break;
             case new VersionState(VersionState::MOVE_PLACEHOLDER):
                 return 'gfx/i/shadow_moveto_plh.png';
                 break;
             case new VersionState(VersionState::MOVE_POINTER):
                 return 'gfx/i/shadow_moveto_pointer.png';
                 break;
         }
     }
     // First, find the icon file name. This can depend on configuration in TCA, field values and more:
     if ($table == 'pages') {
         $iconfile = $GLOBALS['PAGES_TYPES'][$row['doktype']]['icon'];
         if (!$iconfile) {
             $iconfile = $GLOBALS['PAGES_TYPES']['default']['icon'];
         }
     } else {
         if (!($iconfile = $GLOBALS['TCA'][$table]['ctrl']['typeicons'][$row[$GLOBALS['TCA'][$table]['ctrl']['typeicon_column']]])) {
             $iconfile = $GLOBALS['TCA'][$table]['ctrl']['iconfile'] ?: $table . '.gif';
         }
     }
     // Setting path of iconfile if not already set. Default is "gfx/i/"
     if (!strstr($iconfile, '/')) {
         $iconfile = 'gfx/i/' . $iconfile;
     }
     // Setting the absolute path where the icon should be found as a file:
     if (substr($iconfile, 0, 3) == '../') {
         $absfile = PATH_site . substr($iconfile, 3);
     } else {
         $absfile = PATH_typo3 . $iconfile;
     }
     // Initializing variables, all booleans except otherwise stated:
     $hidden = FALSE;
     $timing = FALSE;
     $futuretiming = FALSE;
     // In fact an integer value
     $user = FALSE;
     $deleted = FALSE;
     // Set, if a page-record (only pages!) has the extend-to-subpages flag set.
     $protectSection = FALSE;
     $noIconFound = $row['_NO_ICON_FOUND'] ? TRUE : FALSE;
     // + $shaded which is also boolean!
     // Icon state based on "enableFields":
     if (is_array($GLOBALS['TCA'][$table]['ctrl']['enablecolumns'])) {
         $enCols = $GLOBALS['TCA'][$table]['ctrl']['enablecolumns'];
         // If "hidden" is enabled:
         if ($enCols['disabled']) {
             if ($row[$enCols['disabled']]) {
                 $hidden = TRUE;
             }
         }
         // If a "starttime" is set and higher than current time:
         if ($enCols['starttime']) {
             if ($GLOBALS['EXEC_TIME'] < (int) $row[$enCols['starttime']]) {
                 $timing = TRUE;
                 // And if "endtime" is NOT set:
                 if ((int) $row[$enCols['endtime']] === 0) {
                     $futuretiming = TRUE;
                 }
             }
         }
         // If an "endtime" is set:
         if ($enCols['endtime']) {
             if ((int) $row[$enCols['endtime']] > 0) {
                 if ((int) $row[$enCols['endtime']] < $GLOBALS['EXEC_TIME']) {
                     // End-timing applies at this point.
                     $timing = TRUE;
                 } else {
                     // End-timing WILL apply in the future for this element.
                     $futuretiming = TRUE;
                 }
             }
         }
         // If a user-group field is set:
         if ($enCols['fe_group']) {
             $user = $row[$enCols['fe_group']];
             if ($user && $doNotRenderUserGroupNumber) {
                 $user = 100;
             }
         }
     }
     // If "deleted" flag is set (only when listing records which are also deleted!)
     if ($col = $row[$GLOBALS['TCA'][$table]['ctrl']['delete']]) {
         $deleted = TRUE;
     }
     // Detecting extendToSubpages (for pages only)
     if ($table == 'pages' && $row['extendToSubpages'] && ($hidden || $timing || $futuretiming || $user)) {
         $protectSection = TRUE;
     }
     // If ANY of the booleans are set it means we have to alter the icon:
     if ($hidden || $timing || $futuretiming || $user || $deleted || $shaded || $noIconFound) {
         $flags = '';
         $string = '';
         if ($deleted) {
             $string = 'deleted';
             $flags = 'd';
         } elseif ($noIconFound) {
             // This is ONLY for creating icons with "?" on easily...
             $string = 'no_icon_found';
             $flags = 'x';
         } else {
             if ($hidden) {
                 $string .= 'hidden';
             }
             if ($timing) {
                 $string .= 'timing';
             }
             if (!$string && $futuretiming) {
                 $string = 'futuretiming';
             }
             $flags .= ($hidden ? 'h' : '') . ($timing ? 't' : '') . ($futuretiming ? 'f' : '') . ($user ? 'u' : '') . ($protectSection ? 'p' : '') . ($shaded ? 's' : '');
         }
         // Create tagged icon file name:
         $iconFileName_stateTagged = preg_replace('/.([[:alnum:]]+)$/', '__' . $flags . '.\\1', basename($iconfile));
         // Check if tagged icon file name exists (a tagged icon means the icon base name with the flags added between body and extension of the filename, prefixed with underscore)
         if (@is_file(dirname($absfile) . '/' . $iconFileName_stateTagged) || @is_file($GLOBALS['TBE_STYLES']['skinImgAutoCfg']['absDir'] . '/' . dirname($iconfile) . '/' . $iconFileName_stateTagged)) {
             // Look for [iconname]_xxxx.[ext]
             return dirname($iconfile) . '/' . $iconFileName_stateTagged;
         } else {
             // Otherwise, create the icon:
             $theRes = self::makeIcon($GLOBALS['BACK_PATH'] . $iconfile, $string, $user, $protectSection, $absfile, $iconFileName_stateTagged);
             return $theRes;
         }
     } else {
         return $iconfile;
     }
 }
 /**
  * Helper function for getQuery(), creating the WHERE clause of the SELECT query
  *
  * @param string $table The table name
  * @param array $conf The TypoScript configuration properties
  * @param bool $returnQueryArray If set, the function will return the query not as a string but array with the various parts. RECOMMENDED!
  * @return mixed A WHERE clause based on the relevant parts of the TypoScript properties for a "select" function in TypoScript, see link. If $returnQueryArray is FALSE the where clause is returned as a string with WHERE, GROUP BY and ORDER BY parts, otherwise as an array with these parts.
  * @access private
  * @see getQuery()
  */
 public function getWhere($table, $conf, $returnQueryArray = false)
 {
     // Init:
     $query = '';
     $pid_uid_flag = 0;
     $enableFieldsIgnore = array();
     $queryParts = array('SELECT' => '', 'FROM' => '', 'WHERE' => '', 'GROUPBY' => '', 'ORDERBY' => '', 'LIMIT' => '');
     $tsfe = $this->getTypoScriptFrontendController();
     $considerMovePlaceholders = $tsfe->sys_page->versioningPreview && $table !== 'pages' && !empty($GLOBALS['TCA'][$table]['ctrl']['versioningWS']);
     if (trim($conf['uidInList'])) {
         $listArr = GeneralUtility::intExplode(',', str_replace('this', $tsfe->contentPid, $conf['uidInList']));
         if (count($listArr) === 1) {
             $comparison = '=' . (int) $listArr[0];
         } else {
             $comparison = ' IN (' . implode(',', $this->getDatabaseConnection()->cleanIntArray($listArr)) . ')';
         }
         // If move placeholder shall be considered, select via t3ver_move_id
         if ($considerMovePlaceholders) {
             $movePlaceholderComparison = $table . '.t3ver_state=' . VersionState::cast(VersionState::MOVE_PLACEHOLDER) . ' AND ' . $table . '.t3ver_move_id' . $comparison;
             $query .= ' AND (' . $table . '.uid' . $comparison . ' OR ' . $movePlaceholderComparison . ')';
         } else {
             $query .= ' AND ' . $table . '.uid' . $comparison;
         }
         $pid_uid_flag++;
     }
     // Static_* tables are allowed to be fetched from root page
     if (substr($table, 0, 7) === 'static_') {
         $pid_uid_flag++;
     }
     if (trim($conf['pidInList'])) {
         $listArr = GeneralUtility::intExplode(',', str_replace('this', $tsfe->contentPid, $conf['pidInList']));
         // Removes all pages which are not visible for the user!
         $listArr = $this->checkPidArray($listArr);
         if (GeneralUtility::inList($conf['pidInList'], 'root')) {
             $listArr[] = 0;
         }
         if (GeneralUtility::inList($conf['pidInList'], '-1')) {
             $listArr[] = -1;
             $enableFieldsIgnore['pid'] = true;
         }
         if (!empty($listArr)) {
             $query .= ' AND ' . $table . '.pid IN (' . implode(',', array_map('intval', $listArr)) . ')';
             $pid_uid_flag++;
         } else {
             // If not uid and not pid then uid is set to 0 - which results in nothing!!
             $pid_uid_flag = 0;
         }
     }
     // If not uid and not pid then uid is set to 0 - which results in nothing!!
     if (!$pid_uid_flag) {
         $query .= ' AND ' . $table . '.uid=0';
     }
     $where = isset($conf['where.']) ? trim($this->stdWrap($conf['where'], $conf['where.'])) : trim($conf['where']);
     if ($where) {
         $query .= ' AND ' . $where;
     }
     // Check if the table is translatable, and set the language field by default from the TCA information
     $languageField = '';
     if (!empty($conf['languageField']) || !isset($conf['languageField'])) {
         if (isset($conf['languageField']) && !empty($GLOBALS['TCA'][$table]['columns'][$conf['languageField']])) {
             $languageField = $conf['languageField'];
         } elseif (!empty($GLOBALS['TCA'][$table]['ctrl']['languageField']) && !empty($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'])) {
             $languageField = $table . '.' . $GLOBALS['TCA'][$table]['ctrl']['languageField'];
         }
     }
     if (!empty($languageField)) {
         // The sys_language record UID of the content of the page
         $sys_language_content = (int) $tsfe->sys_language_content;
         if ($tsfe->sys_language_contentOL && !empty($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'])) {
             // Sys language content is set to zero/-1 - and it is expected that whatever routine processes the output will
             // OVERLAY the records with localized versions!
             $languageQuery = $languageField . ' IN (0,-1)';
             // Use this option to include records that don't have a default translation
             // (originalpointerfield is 0 and the language field contains the requested language)
             $includeRecordsWithoutDefaultTranslation = isset($conf['includeRecordsWithoutDefaultTranslation.']) ? $this->stdWrap($conf['includeRecordsWithoutDefaultTranslation'], $conf['includeRecordsWithoutDefaultTranslation.']) : $conf['includeRecordsWithoutDefaultTranslation'];
             if (!empty(trim($includeRecordsWithoutDefaultTranslation))) {
                 $languageQuery .= ' OR (' . $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'] . ' = 0 AND ' . $languageField . ' = ' . $sys_language_content . ')';
             }
         } else {
             $languageQuery = $languageField . ' = ' . $sys_language_content;
         }
         $query .= ' AND (' . $languageQuery . ')';
     }
     // Enablefields
     if ($table === 'pages') {
         $query .= ' ' . $tsfe->sys_page->where_hid_del . $tsfe->sys_page->where_groupAccess;
     } else {
         $query .= $this->enableFields($table, false, $enableFieldsIgnore);
     }
     // MAKE WHERE:
     if ($query) {
         // Stripping of " AND"...
         $queryParts['WHERE'] = trim(substr($query, 4));
         $query = 'WHERE ' . $queryParts['WHERE'];
     }
     // GROUP BY
     if (trim($conf['groupBy'])) {
         $queryParts['GROUPBY'] = isset($conf['groupBy.']) ? trim($this->stdWrap($conf['groupBy'], $conf['groupBy.'])) : trim($conf['groupBy']);
         $query .= ' GROUP BY ' . $queryParts['GROUPBY'];
     }
     // ORDER BY
     if (trim($conf['orderBy'])) {
         $queryParts['ORDERBY'] = isset($conf['orderBy.']) ? trim($this->stdWrap($conf['orderBy'], $conf['orderBy.'])) : trim($conf['orderBy']);
         $query .= ' ORDER BY ' . $queryParts['ORDERBY'];
     }
     // Return result:
     return $returnQueryArray ? $queryParts : $query;
 }
 /**
  * This creates the buttons for die modules
  *
  * @param string $function Identifier for function of module
  * @return void
  */
 protected function makeButtons($function = '')
 {
     $lang = $this->getLanguageService();
     // View page
     if (!VersionState::cast($this->pageinfo['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)) {
         $viewButton = $this->buttonBar->makeLinkButton()->setOnClick(htmlspecialchars(BackendUtility::viewOnClick($this->pageinfo['uid'], '', BackendUtility::BEgetRootLine($this->pageinfo['uid']))))->setTitle($lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.showPage', true))->setIcon($this->iconFactory->getIcon('actions-document-view', Icon::SIZE_SMALL))->setHref('#');
         $this->buttonBar->addButton($viewButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
     }
     // Shortcut
     $shortcutButton = $this->buttonBar->makeShortcutButton()->setModuleName($this->moduleName)->setGetVariables(['id', 'M', 'edit_record', 'pointer', 'new_unique_uid', 'search_field', 'search_levels', 'showLimit'])->setSetVariables(array_keys($this->MOD_MENU));
     $this->buttonBar->addButton($shortcutButton);
     // Cache
     if (!$this->modTSconfig['properties']['disableAdvanced']) {
         $clearCacheButton = $this->buttonBar->makeLinkButton()->setHref(BackendUtility::getModuleUrl($this->moduleName, ['id' => $this->pageinfo['uid'], 'clear_cache' => '1']))->setTitle($lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.clear_cache', true))->setIcon($this->iconFactory->getIcon('actions-system-cache-clear', Icon::SIZE_SMALL));
         $this->buttonBar->addButton($clearCacheButton, ButtonBar::BUTTON_POSITION_RIGHT, 1);
     }
     if (!$this->modTSconfig['properties']['disableIconToolbar']) {
         // Move record
         if (MathUtility::canBeInterpretedAsInteger($this->eRParts[1])) {
             $urlParameters = ['table' => $this->eRParts[0], 'uid' => $this->eRParts[1], 'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')];
             $moveButton = $this->buttonBar->makeLinkButton()->setHref(BackendUtility::getModuleUrl('move_element', $urlParameters))->setTitle($lang->getLL('move_' . ($this->eRParts[0] == 'tt_content' ? 'record' : 'page'), true))->setIcon($this->iconFactory->getIcon('actions-' . ($this->eRParts[0] == 'tt_content' ? 'document' : 'page') . '-move', Icon::SIZE_SMALL));
             $this->buttonBar->addButton($moveButton, ButtonBar::BUTTON_POSITION_LEFT, 2);
         }
         // Edit page properties and page language overlay icons
         if ($this->pageIsNotLockedForEditors() && $this->getBackendUser()->checkLanguageAccess(0)) {
             // Edit localized page_language_overlay only when one specific language is selected
             if ($this->MOD_SETTINGS['function'] == 1 && $this->current_sys_language > 0) {
                 $overlayRecord = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('uid', 'pages_language_overlay', 'pid = ' . (int) $this->id . ' ' . 'AND sys_language_uid = ' . (int) $this->current_sys_language . BackendUtility::deleteClause('pages_language_overlay') . BackendUtility::versioningPlaceholderClause('pages_language_overlay'), '', '', '');
                 $editLanguageButton = $this->buttonBar->makeLinkButton()->setHref('#')->setTitle($lang->getLL('editPageLanguageOverlayProperties', true))->setOnClick(htmlspecialchars(BackendUtility::editOnClick('&edit[pages_language_overlay][' . $overlayRecord['uid'] . ']=edit')))->setIcon($this->iconFactory->getIcon('mimetypes-x-content-page-language-overlay', Icon::SIZE_SMALL));
                 $this->buttonBar->addButton($editLanguageButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
             }
             $editPageButton = $this->buttonBar->makeLinkButton()->setHref('#')->setTitle($lang->getLL('editPageProperties', true))->setOnClick(htmlspecialchars(BackendUtility::editOnClick('&edit[pages][' . $this->id . ']=edit')))->setIcon($this->iconFactory->getIcon('actions-page-open', Icon::SIZE_SMALL));
             $this->buttonBar->addButton($editPageButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
         }
         // Add CSH (Context Sensitive Help) icon to tool bar
         $contextSensitiveHelpButton = $this->buttonBar->makeHelpButton()->setModuleName($this->descrTable)->setFieldName($function === 'quickEdit' ? 'quickEdit' : 'columns_' . $this->MOD_SETTINGS['function']);
         $this->buttonBar->addButton($contextSensitiveHelpButton);
         // QuickEdit
         if ($function == 'quickEdit') {
             // Close Record
             $closeButton = $this->buttonBar->makeLinkButton()->setHref('#')->setOnClick(htmlspecialchars('jumpToUrl(' . GeneralUtility::quoteJSvalue($this->closeUrl) . '); return false;'))->setTitle($lang->sL('LLL:EXT:lang/locallang_core.xlf:rm.closeDoc', true))->setIcon($this->iconFactory->getIcon('actions-document-close', Icon::SIZE_SMALL));
             $this->buttonBar->addButton($closeButton, ButtonBar::BUTTON_POSITION_LEFT, 0);
             // Save Record
             $saveButtonDropdown = $this->buttonBar->makeSplitButton();
             $saveButton = $this->buttonBar->makeInputButton()->setName('_savedok')->setValue('1')->setForm('PageLayoutController')->setTitle($lang->sL('LLL:EXT:lang/locallang_core.xlf:rm.saveDoc', true))->setIcon($this->iconFactory->getIcon('actions-document-save', Icon::SIZE_SMALL));
             $saveButtonDropdown->addItem($saveButton);
             $saveAndCloseButton = $this->buttonBar->makeInputButton()->setName('_saveandclosedok')->setValue('1')->setForm('PageLayoutController')->setOnClick('document.editform.redirect.value=\'' . $this->closeUrl . '\';')->setTitle($lang->sL('LLL:EXT:lang/locallang_core.xlf:rm.saveCloseDoc', true))->setIcon($this->iconFactory->getIcon('actions-document-save-close', Icon::SIZE_SMALL));
             $saveButtonDropdown->addItem($saveAndCloseButton);
             $saveAndShowPageButton = $this->buttonBar->makeInputButton()->setName('_savedokview')->setValue('1')->setForm('PageLayoutController')->setOnClick('document.editform.redirect.value+=\'&popView=1\';')->setTitle($lang->sL('LLL:EXT:lang/locallang_core.xlf:rm.saveDocShow', true))->setIcon($this->iconFactory->getIcon('actions-document-save-view', Icon::SIZE_SMALL));
             $saveButtonDropdown->addItem($saveAndShowPageButton);
             $this->buttonBar->addButton($saveButtonDropdown, ButtonBar::BUTTON_POSITION_LEFT, 1);
             // Delete record
             if ($this->deleteButton) {
                 $deleteButton = $this->buttonBar->makeLinkButton()->setHref('#')->setOnClick(htmlspecialchars('return deleteRecord(' . GeneralUtility::quoteJSvalue($this->eRParts[0]) . ',' . GeneralUtility::quoteJSvalue($this->eRParts[1]) . ',' . GeneralUtility::quoteJSvalue(GeneralUtility::getIndpEnv('SCRIPT_NAME') . '?id=' . $this->id) . ');'))->setTitle($lang->getLL('deleteItem', true))->setIcon($this->iconFactory->getIcon('actions-edit-delete', Icon::SIZE_SMALL));
                 $this->buttonBar->addButton($deleteButton, ButtonBar::BUTTON_POSITION_LEFT, 4);
             }
             // History
             if ($this->undoButton) {
                 $undoButton = $this->buttonBar->makeLinkButton()->setHref('#')->setOnClick(htmlspecialchars('window.location.href=' . GeneralUtility::quoteJSvalue(BackendUtility::getModuleUrl('record_history', array('element' => $this->eRParts[0] . ':' . $this->eRParts[1], 'revert' => 'ALL_FIELDS', 'sumUp' => -1, 'returnUrl' => $this->R_URI))) . '; return false;'))->setTitle(htmlspecialchars(sprintf($lang->getLL('undoLastChange'), BackendUtility::calcAge($GLOBALS['EXEC_TIME'] - $this->undoButtonR['tstamp'], $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.minutesHoursDaysYears')))))->setIcon($this->iconFactory->getIcon('actions-edit-undo', Icon::SIZE_SMALL));
                 $this->buttonBar->addButton($undoButton, ButtonBar::BUTTON_POSITION_LEFT, 5);
                 $historyButton = $this->buttonBar->makeLinkButton()->setHref('#')->setOnClick(htmlspecialchars('jumpToUrl(' . GeneralUtility::quoteJSvalue(BackendUtility::getModuleUrl('record_history', array('element' => $this->eRParts[0] . ':' . $this->eRParts[1], 'returnUrl' => $this->R_URI)) . '#latest') . ');return false;'))->setTitle($lang->getLL('recordHistory', true))->setIcon($this->iconFactory->getIcon('actions-document-history-open', Icon::SIZE_SMALL));
                 $this->buttonBar->addButton($historyButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
             }
         }
     }
 }
Beispiel #7
0
 /**
  * Visualizes the deleted status for a versionized record.
  *
  * @param string $table Name of the table
  * @param array $row Record row containing the field values
  * @param array $status Status to be used for rendering the icon
  * @return void
  */
 public function overrideIconOverlay($table, array $row, array &$status)
 {
     if (isset($row['t3ver_state']) && VersionState::cast($row['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)) {
         $status['deleted'] = TRUE;
     }
 }
 /**
  * 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;
 }
Beispiel #9
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;
 }
Beispiel #10
0
 /**
  * This creates the buttons for die modules
  *
  * @param string $function Identifier for function of module
  * @return void
  */
 protected function makeButtons($function = '')
 {
     $lang = $this->getLanguageService();
     // View page
     if (!VersionState::cast($this->pageinfo['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)) {
         $viewButton = $this->buttonBar->makeLinkButton()->setOnClick(BackendUtility::viewOnClick($this->pageinfo['uid'], '', BackendUtility::BEgetRootLine($this->pageinfo['uid'])))->setTitle($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.showPage'))->setIcon($this->iconFactory->getIcon('actions-document-view', Icon::SIZE_SMALL))->setHref('#');
         $this->buttonBar->addButton($viewButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
     }
     // Shortcut
     $shortcutButton = $this->buttonBar->makeShortcutButton()->setModuleName($this->moduleName)->setGetVariables(['id', 'M', 'edit_record', 'pointer', 'new_unique_uid', 'search_field', 'search_levels', 'showLimit'])->setSetVariables(array_keys($this->MOD_MENU));
     $this->buttonBar->addButton($shortcutButton);
     // Cache
     if (!$this->modTSconfig['properties']['disableAdvanced']) {
         $clearCacheButton = $this->buttonBar->makeLinkButton()->setHref(BackendUtility::getModuleUrl($this->moduleName, ['id' => $this->pageinfo['uid'], 'clear_cache' => '1']))->setTitle($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.clear_cache'))->setIcon($this->iconFactory->getIcon('actions-system-cache-clear', Icon::SIZE_SMALL));
         $this->buttonBar->addButton($clearCacheButton, ButtonBar::BUTTON_POSITION_RIGHT, 1);
     }
     if (!$this->modTSconfig['properties']['disableIconToolbar']) {
         // Move record
         if (MathUtility::canBeInterpretedAsInteger($this->eRParts[1])) {
             $urlParameters = ['table' => $this->eRParts[0], 'uid' => $this->eRParts[1], 'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')];
             $moveButton = $this->buttonBar->makeLinkButton()->setHref(BackendUtility::getModuleUrl('move_element', $urlParameters))->setTitle($lang->getLL('move_' . ($this->eRParts[0] == 'tt_content' ? 'record' : 'page')))->setIcon($this->iconFactory->getIcon('actions-' . ($this->eRParts[0] == 'tt_content' ? 'document' : 'page') . '-move', Icon::SIZE_SMALL));
             $this->buttonBar->addButton($moveButton, ButtonBar::BUTTON_POSITION_LEFT, 2);
         }
         // Edit page properties and page language overlay icons
         if ($this->pageIsNotLockedForEditors() && $this->getBackendUser()->checkLanguageAccess(0)) {
             // Edit localized page_language_overlay only when one specific language is selected
             if ($this->MOD_SETTINGS['function'] == 1 && $this->current_sys_language > 0) {
                 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages_language_overlay');
                 $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class))->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
                 $overlayRecord = $queryBuilder->select('uid')->from('pages_language_overlay')->where($queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)), $queryBuilder->expr()->eq('sys_language_uid', $queryBuilder->createNamedParameter($this->current_sys_language, \PDO::PARAM_INT)))->setMaxResults(1)->execute()->fetch();
                 // Edit button
                 $urlParameters = ['edit' => ['pages_language_overlay' => [$overlayRecord['uid'] => 'edit']], 'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')];
                 $url = BackendUtility::getModuleUrl('record_edit', $urlParameters);
                 $editLanguageButton = $this->buttonBar->makeLinkButton()->setHref($url)->setTitle($lang->getLL('editPageLanguageOverlayProperties'))->setIcon($this->iconFactory->getIcon('mimetypes-x-content-page-language-overlay', Icon::SIZE_SMALL));
                 $this->buttonBar->addButton($editLanguageButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
             }
             $urlParameters = ['edit' => ['pages' => [$this->id => 'edit']], 'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')];
             $url = BackendUtility::getModuleUrl('record_edit', $urlParameters);
             $editPageButton = $this->buttonBar->makeLinkButton()->setHref($url)->setTitle($lang->getLL('editPageProperties'))->setIcon($this->iconFactory->getIcon('actions-page-open', Icon::SIZE_SMALL));
             $this->buttonBar->addButton($editPageButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
         }
         // Add CSH (Context Sensitive Help) icon to tool bar
         $contextSensitiveHelpButton = $this->buttonBar->makeHelpButton()->setModuleName($this->descrTable)->setFieldName($function === 'quickEdit' ? 'quickEdit' : 'columns_' . $this->MOD_SETTINGS['function']);
         $this->buttonBar->addButton($contextSensitiveHelpButton);
         // QuickEdit
         if ($function == 'quickEdit') {
             // Close Record
             $closeButton = $this->buttonBar->makeLinkButton()->setHref('#')->setOnClick('jumpToUrl(' . GeneralUtility::quoteJSvalue($this->closeUrl) . '); return false;')->setTitle($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:rm.closeDoc'))->setIcon($this->iconFactory->getIcon('actions-document-close', Icon::SIZE_SMALL));
             $this->buttonBar->addButton($closeButton, ButtonBar::BUTTON_POSITION_LEFT, 0);
             // Save Record
             $saveButtonDropdown = $this->buttonBar->makeSplitButton();
             $saveButton = $this->buttonBar->makeInputButton()->setName('_savedok')->setValue('1')->setForm('PageLayoutController')->setTitle($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:rm.saveDoc'))->setIcon($this->iconFactory->getIcon('actions-document-save', Icon::SIZE_SMALL));
             $saveButtonDropdown->addItem($saveButton);
             $saveAndCloseButton = $this->buttonBar->makeInputButton()->setName('_saveandclosedok')->setValue('1')->setForm('PageLayoutController')->setOnClick('document.editform.redirect.value=\'' . $this->closeUrl . '\';')->setTitle($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:rm.saveCloseDoc'))->setIcon($this->iconFactory->getIcon('actions-document-save-close', Icon::SIZE_SMALL));
             $saveButtonDropdown->addItem($saveAndCloseButton);
             $saveAndShowPageButton = $this->buttonBar->makeInputButton()->setName('_savedokview')->setValue('1')->setForm('PageLayoutController')->setOnClick('document.editform.redirect.value+=\'&popView=1\';')->setTitle($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:rm.saveDocShow'))->setIcon($this->iconFactory->getIcon('actions-document-save-view', Icon::SIZE_SMALL));
             $saveButtonDropdown->addItem($saveAndShowPageButton);
             $this->buttonBar->addButton($saveButtonDropdown, ButtonBar::BUTTON_POSITION_LEFT, 1);
             // Delete record
             if ($this->deleteButton) {
                 $dataAttributes = [];
                 $dataAttributes['table'] = $this->eRParts[0];
                 $dataAttributes['uid'] = $this->eRParts[1];
                 $dataAttributes['return-url'] = BackendUtility::getModuleUrl($this->moduleName) . '&id=' . $this->id;
                 $deleteButton = $this->buttonBar->makeLinkButton()->setHref('#')->setClasses('t3js-editform-delete-record')->setDataAttributes($dataAttributes)->setTitle($lang->getLL('deleteItem'))->setIcon($this->iconFactory->getIcon('actions-edit-delete', Icon::SIZE_SMALL));
                 $this->buttonBar->addButton($deleteButton, ButtonBar::BUTTON_POSITION_LEFT, 4);
             }
             // History
             if ($this->undoButton) {
                 $undoButton = $this->buttonBar->makeLinkButton()->setHref('#')->setOnClick('window.location.href=' . GeneralUtility::quoteJSvalue(BackendUtility::getModuleUrl('record_history', ['element' => $this->eRParts[0] . ':' . $this->eRParts[1], 'revert' => 'ALL_FIELDS', 'returnUrl' => $this->R_URI])) . '; return false;')->setTitle(sprintf($lang->getLL('undoLastChange'), BackendUtility::calcAge($GLOBALS['EXEC_TIME'] - $this->undoButtonR['tstamp'], $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.minutesHoursDaysYears'))))->setIcon($this->iconFactory->getIcon('actions-edit-undo', Icon::SIZE_SMALL));
                 $this->buttonBar->addButton($undoButton, ButtonBar::BUTTON_POSITION_LEFT, 5);
                 $historyButton = $this->buttonBar->makeLinkButton()->setHref('#')->setOnClick('jumpToUrl(' . GeneralUtility::quoteJSvalue(BackendUtility::getModuleUrl('record_history', ['element' => $this->eRParts[0] . ':' . $this->eRParts[1], 'returnUrl' => $this->R_URI]) . '#latest') . ');return false;')->setTitle($lang->getLL('recordHistory'))->setIcon($this->iconFactory->getIcon('actions-document-history-open', Icon::SIZE_SMALL));
                 $this->buttonBar->addButton($historyButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
             }
         }
     }
 }
 /**
  * Checks if record is a move-placeholder (t3ver_state==VersionState::MOVE_PLACEHOLDER) and if so
  * it will set $row to be the pointed-to live record (and return TRUE)
  *
  * @param string $table Table name
  * @param array $row Row (passed by reference) - must be online record!
  * @return bool TRUE if overlay is made.
  * @see PageRepository::movePlhOl()
  */
 public static function movePlhOL($table, &$row)
 {
     if (static::isTableWorkspaceEnabled($table)) {
         // If t3ver_move_id or t3ver_state is not found, then find it... (but we like best if it is here...)
         if (!isset($row['t3ver_move_id']) || !isset($row['t3ver_state'])) {
             $moveIDRec = self::getRecord($table, $row['uid'], 't3ver_move_id, t3ver_state');
             $moveID = $moveIDRec['t3ver_move_id'];
             $versionState = VersionState::cast($moveIDRec['t3ver_state']);
         } else {
             $moveID = $row['t3ver_move_id'];
             $versionState = VersionState::cast($row['t3ver_state']);
         }
         // Find pointed-to record.
         if ($versionState->equals(VersionState::MOVE_PLACEHOLDER) && $moveID) {
             if ($origRow = self::getRecord($table, $moveID, implode(',', array_keys($row)))) {
                 $row = $origRow;
                 return true;
             }
         }
     }
     return false;
 }
Beispiel #12
0
 /**
  * Processes version overlays on the final result set.
  *
  * @param int[] $ids
  * @return int[]
  * @internal
  */
 public function processVersionOverlays(array $ids)
 {
     if (empty($this->workspaceId) || !$this->isWorkspaceEnabled() || empty($ids)) {
         return $ids;
     }
     $ids = $this->reindex($this->processVersionMovePlaceholders($ids));
     $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->tableName);
     $queryBuilder->getRestrictions()->removeAll();
     $result = $queryBuilder->select('uid', 't3ver_oid', 't3ver_state')->from($this->tableName)->where($queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter(-1, \PDO::PARAM_INT)), $queryBuilder->expr()->in('t3ver_oid', $queryBuilder->createNamedParameter($ids, Connection::PARAM_INT_ARRAY)), $queryBuilder->expr()->eq('t3ver_wsid', $queryBuilder->createNamedParameter($this->workspaceId, \PDO::PARAM_INT)))->execute();
     while ($version = $result->fetch()) {
         $liveReferenceId = $version['t3ver_oid'];
         $versionId = $version['uid'];
         if (isset($ids[$liveReferenceId])) {
             if (!$this->keepDeletePlaceholder && VersionState::cast($version['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)) {
                 unset($ids[$liveReferenceId]);
             } else {
                 $ids[$liveReferenceId] = $versionId;
             }
         }
     }
     return $ids;
 }
Beispiel #13
0
 /**
  * Helper function for getQuery(), creating the WHERE clause of the SELECT query
  *
  * @param string $table The table name
  * @param array $conf The TypoScript configuration properties
  * @return array Associative array containing the prepared data for WHERE, ORDER BY and GROUP BY fragments
  * @throws \InvalidArgumentException
  * @see getQuery()
  */
 protected function getQueryConstraints(string $table, array $conf) : array
 {
     // Init:
     $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
     $expressionBuilder = $queryBuilder->expr();
     $tsfe = $this->getTypoScriptFrontendController();
     $constraints = [];
     $pid_uid_flag = 0;
     $enableFieldsIgnore = [];
     $queryParts = ['where' => null, 'groupBy' => null, 'orderBy' => null];
     $considerMovePlaceholders = $tsfe->sys_page->versioningPreview && $table !== 'pages' && !empty($GLOBALS['TCA'][$table]['ctrl']['versioningWS']);
     if (trim($conf['uidInList'])) {
         $listArr = GeneralUtility::intExplode(',', str_replace('this', $tsfe->contentPid, $conf['uidInList']));
         // If move placeholder shall be considered, select via t3ver_move_id
         if ($considerMovePlaceholders) {
             $constraints[] = (string) $expressionBuilder->orX($expressionBuilder->in($table . '.uid', $listArr), $expressionBuilder->andX($expressionBuilder->eq($table . '.t3ver_state', (int) (string) VersionState::cast(VersionState::MOVE_PLACEHOLDER)), $expressionBuilder->in($table . '.t3ver_move_id', $listArr)));
         } else {
             $constraints[] = (string) $expressionBuilder->in($table . '.uid', $listArr);
         }
         $pid_uid_flag++;
     }
     // Static_* tables are allowed to be fetched from root page
     if (strpos($table, 'static_') === 0) {
         $pid_uid_flag++;
     }
     if (trim($conf['pidInList'])) {
         $listArr = GeneralUtility::intExplode(',', str_replace('this', $tsfe->contentPid, $conf['pidInList']));
         // Removes all pages which are not visible for the user!
         $listArr = $this->checkPidArray($listArr);
         if (GeneralUtility::inList($conf['pidInList'], 'root')) {
             $listArr[] = 0;
         }
         if (GeneralUtility::inList($conf['pidInList'], '-1')) {
             $listArr[] = -1;
             $enableFieldsIgnore['pid'] = true;
         }
         if (!empty($listArr)) {
             $constraints[] = $expressionBuilder->in($table . '.pid', array_map('intval', $listArr));
             $pid_uid_flag++;
         } else {
             // If not uid and not pid then uid is set to 0 - which results in nothing!!
             $pid_uid_flag = 0;
         }
     }
     // If not uid and not pid then uid is set to 0 - which results in nothing!!
     if (!$pid_uid_flag) {
         $constraints[] = $expressionBuilder->eq($table . '.uid', 0);
     }
     $where = isset($conf['where.']) ? trim($this->stdWrap($conf['where'], $conf['where.'])) : trim($conf['where']);
     if ($where) {
         $constraints[] = QueryHelper::stripLogicalOperatorPrefix($where);
     }
     // Check if the table is translatable, and set the language field by default from the TCA information
     $languageField = '';
     if (!empty($conf['languageField']) || !isset($conf['languageField'])) {
         if (isset($conf['languageField']) && !empty($GLOBALS['TCA'][$table]['columns'][$conf['languageField']])) {
             $languageField = $conf['languageField'];
         } elseif (!empty($GLOBALS['TCA'][$table]['ctrl']['languageField']) && !empty($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'])) {
             $languageField = $table . '.' . $GLOBALS['TCA'][$table]['ctrl']['languageField'];
         }
     }
     if (!empty($languageField)) {
         // The sys_language record UID of the content of the page
         $sys_language_content = (int) $tsfe->sys_language_content;
         if ($tsfe->sys_language_contentOL && !empty($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'])) {
             // Sys language content is set to zero/-1 - and it is expected that whatever routine processes the output will
             // OVERLAY the records with localized versions!
             $languageQuery = $expressionBuilder->in($languageField, [0, -1]);
             // Use this option to include records that don't have a default translation
             // (originalpointerfield is 0 and the language field contains the requested language)
             $includeRecordsWithoutDefaultTranslation = isset($conf['includeRecordsWithoutDefaultTranslation.']) ? $this->stdWrap($conf['includeRecordsWithoutDefaultTranslation'], $conf['includeRecordsWithoutDefaultTranslation.']) : $conf['includeRecordsWithoutDefaultTranslation'];
             if (trim($includeRecordsWithoutDefaultTranslation) !== '') {
                 $languageQuery = $expressionBuilder->orX($languageQuery, $expressionBuilder->andX($expressionBuilder->eq($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'], 0), $expressionBuilder->eq($languageField, $sys_language_content)));
             }
         } else {
             $languageQuery = $expressionBuilder->eq($languageField, $sys_language_content);
         }
         $constraints[] = $languageQuery;
     }
     // Enablefields
     if ($table === 'pages') {
         $constraints[] = QueryHelper::stripLogicalOperatorPrefix($tsfe->sys_page->where_hid_del);
         $constraints[] = QueryHelper::stripLogicalOperatorPrefix($tsfe->sys_page->where_groupAccess);
     } else {
         $constraints[] = QueryHelper::stripLogicalOperatorPrefix($this->enableFields($table, false, $enableFieldsIgnore));
     }
     // MAKE WHERE:
     if (count($constraints) !== 0) {
         $queryParts['where'] = $expressionBuilder->andX(...$constraints);
     }
     // GROUP BY
     if (trim($conf['groupBy'])) {
         $groupBy = isset($conf['groupBy.']) ? trim($this->stdWrap($conf['groupBy'], $conf['groupBy.'])) : trim($conf['groupBy']);
         $queryParts['groupBy'] = QueryHelper::parseGroupBy($groupBy);
     }
     // ORDER BY
     if (trim($conf['orderBy'])) {
         $orderByString = isset($conf['orderBy.']) ? trim($this->stdWrap($conf['orderBy'], $conf['orderBy.'])) : trim($conf['orderBy']);
         $queryParts['orderBy'] = QueryHelper::parseOrderBy($orderByString);
     }
     // Return result:
     return $queryParts;
 }
Beispiel #14
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 $dataHandler DataHandler object
  * @return void
  */
 protected function version_clearWSID($table, $id, $flush = false, DataHandler $dataHandler)
 {
     if ($errorCode = $dataHandler->BE_USER->workspaceCannotEditOfflineVersion($table, $id)) {
         $dataHandler->newlog('Attempt to reset workspace for record failed: ' . $errorCode, 1);
         return;
     }
     if (!$dataHandler->checkRecordUpdateAccess($table, $id)) {
         $dataHandler->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 = ['t3ver_wsid' => 0, 't3ver_tstamp' => $GLOBALS['EXEC_TIME']];
     $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($table);
     $connection->update($table, $updateData, ['uid' => (int) $id]);
     // 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)) {
         $connection->update($table, $updateData, ['uid' => (int) $liveRec['uid']]);
         // THIS assumes that the record was placeholder ONLY for ONE record (namely $id)
         $dataHandler->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))) {
         $dataHandler->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')) {
             $dataHandler->deleteEl($table, $plhRec['uid'], true, true);
         }
     }
 }
 /**
  * Processes version overlays on the final result set.
  *
  * @param int[] $ids
  * @return int[]
  */
 protected function processVersionOverlays(array $ids)
 {
     if (empty($this->workspaceId) || !$this->isWorkspaceEnabled() || empty($ids)) {
         return $ids;
     }
     $ids = $this->processVersionMovePlaceholders($ids);
     $versions = $this->getDatabaseConnection()->exec_SELECTgetRows('uid,t3ver_oid,t3ver_state', $this->tableName, 'pid=-1 AND t3ver_oid IN (' . $this->intImplode(',', $ids) . ')' . ' AND t3ver_wsid=' . $this->workspaceId);
     if (!empty($versions)) {
         foreach ($versions as $version) {
             $liveReferenceId = $version['t3ver_oid'];
             $versionId = $version['uid'];
             if (isset($ids[$liveReferenceId])) {
                 if (!$this->keepDeletePlaceholder && VersionState::cast($version['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)) {
                     unset($ids[$liveReferenceId]);
                 } else {
                     $ids[$liveReferenceId] = $versionId;
                 }
             }
         }
         $ids = $this->reindex($ids);
     }
     return $ids;
 }
Beispiel #16
0
 /**
  * This helper functions checks if the DB record ($row) has any special status based on the TCA settings like hidden,
  * starttime etc, and then returns a specific icon overlay identifier for the overlay of this DB record
  * This method solely takes care of the overlay of this record, not any type
  *
  * @param string $table The TCA table
  * @param array $row The selected record
  * @return string The status with the highest priority
  */
 protected function mapRecordTypeToOverlayIdentifier($table, array $row)
 {
     $tcaCtrl = $GLOBALS['TCA'][$table]['ctrl'];
     // Calculate for a given record the actual visibility at the moment
     $status = array('hidden' => false, 'starttime' => false, 'endtime' => false, 'futureendtime' => false, 'fe_group' => false, 'deleted' => false, 'protectedSection' => false, 'nav_hide' => (bool) $row['nav_hide']);
     // Icon state based on "enableFields":
     if (is_array($tcaCtrl['enablecolumns'])) {
         $enableColumns = $tcaCtrl['enablecolumns'];
         // If "hidden" is enabled:
         if (isset($enableColumns['disabled']) && !empty($row[$enableColumns['disabled']])) {
             $status['hidden'] = true;
         }
         // If a "starttime" is set and higher than current time:
         if (!empty($enableColumns['starttime']) && $GLOBALS['EXEC_TIME'] < (int) $row[$enableColumns['starttime']]) {
             $status['starttime'] = true;
         }
         // If an "endtime" is set
         if (!empty($enableColumns['endtime'])) {
             if ((int) $row[$enableColumns['endtime']] > 0) {
                 if ((int) $row[$enableColumns['endtime']] < $GLOBALS['EXEC_TIME']) {
                     // End-timing applies at this point.
                     $status['endtime'] = true;
                 } else {
                     // End-timing WILL apply in the future for this element.
                     $status['futureendtime'] = true;
                 }
             }
         }
         // If a user-group field is set
         if (!empty($enableColumns['fe_group']) && $row[$enableColumns['fe_group']]) {
             $status['fe_group'] = true;
         }
     }
     // If "deleted" flag is set (only when listing records which are also deleted!)
     if (isset($tcaCtrl['delete']) && !empty($row[$tcaCtrl['delete']])) {
         $status['deleted'] = true;
     }
     // Detecting extendToSubpages (for pages only)
     if ($table === 'pages' && (int) $row['extendToSubpages'] > 0) {
         $status['protectedSection'] = true;
     }
     if (isset($row['t3ver_state']) && VersionState::cast($row['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)) {
         $status['deleted'] = true;
     }
     // Now only show the status with the highest priority
     $iconName = '';
     foreach ($this->overlayPriorities as $priority) {
         if ($status[$priority]) {
             $iconName = $this->recordStatusMapping[$priority];
             break;
         }
     }
     return $iconName;
 }
Beispiel #17
0
 /**
  * Checks if record is a move-placeholder
  * (t3ver_state==VersionState::MOVE_PLACEHOLDER) and if so it will set $row to be
  * the pointed-to live record (and return TRUE) Used from versionOL
  *
  * @param string $table Table name
  * @param array $row Row (passed by reference) - only online records...
  * @return boolean TRUE if overlay is made.
  * @see \TYPO3\CMS\Backend\Utility\BackendUtility::movePlhOl()
  * @todo Define visibility
  */
 public function movePlhOL($table, &$row)
 {
     if (($table == 'pages' || (int) $GLOBALS['TCA'][$table]['ctrl']['versioningWS'] >= 2) && (int) VersionState::cast($row['t3ver_state'])->equals(VersionState::MOVE_PLACEHOLDER)) {
         // Only for WS ver 2... (moving)
         // If t3ver_move_id is not found, then find it (but we like best if it is here)
         if (!isset($row['t3ver_move_id'])) {
             $moveIDRec = $this->getRawRecord($table, $row['uid'], 't3ver_move_id', TRUE);
             $moveID = $moveIDRec['t3ver_move_id'];
         } else {
             $moveID = $row['t3ver_move_id'];
         }
         // Find pointed-to record.
         if ($moveID) {
             $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(implode(',', array_keys($row)), $table, 'uid=' . (int) $moveID . $this->enableFields($table));
             $origRow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
             $GLOBALS['TYPO3_DB']->sql_free_result($res);
             if ($origRow) {
                 $row = $origRow;
                 return TRUE;
             }
         }
     }
     return FALSE;
 }
 /**
  * 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;
 }
 /**
  * Checks if record is a move-placeholder
  * (t3ver_state==VersionState::MOVE_PLACEHOLDER) and if so it will set $row to be
  * the pointed-to live record (and return TRUE) Used from versionOL
  *
  * @param string $table Table name
  * @param array $row Row (passed by reference) - only online records...
  * @return bool TRUE if overlay is made.
  * @see BackendUtility::movePlhOl()
  */
 public function movePlhOL($table, &$row)
 {
     if (!empty($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) && (int) VersionState::cast($row['t3ver_state'])->equals(VersionState::MOVE_PLACEHOLDER)) {
         // Only for WS ver 2... (moving) - enabled by default with CMS7
         // If t3ver_move_id is not found, then find it (but we like best if it is here)
         if (!isset($row['t3ver_move_id'])) {
             $moveIDRec = $this->getRawRecord($table, $row['uid'], 't3ver_move_id', true);
             $moveID = $moveIDRec['t3ver_move_id'];
         } else {
             $moveID = $row['t3ver_move_id'];
         }
         // Find pointed-to record.
         if ($moveID) {
             $res = $this->getDatabaseConnection()->exec_SELECTquery(implode(',', array_keys($row)), $table, 'uid=' . (int) $moveID . $this->enableFields($table));
             $origRow = $this->getDatabaseConnection()->sql_fetch_assoc($res);
             $this->getDatabaseConnection()->sql_free_result($res);
             if ($origRow) {
                 $row = $origRow;
                 return true;
             }
         }
     }
     return false;
 }
Beispiel #20
0
 /**
  * Determine whether this page for the current
  *
  * @param int $pageUid
  * @param int $workspaceUid
  * @return bool
  */
 public function canCreatePreviewLink($pageUid, $workspaceUid)
 {
     $result = true;
     if ($pageUid > 0 && $workspaceUid > 0) {
         $pageRecord = BackendUtility::getRecord('pages', $pageUid);
         BackendUtility::workspaceOL('pages', $pageRecord, $workspaceUid);
         if (!GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['FE']['content_doktypes'], $pageRecord['doktype']) || VersionState::cast($pageRecord['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)) {
             $result = false;
         }
     } else {
         $result = false;
     }
     return $result;
 }
 /**
  * Callback to determine whether a new parent reference shall be considered in the dependency resolver utility.
  * Only elements that are a delete placeholder are considered.
  *
  * @param array $callerArguments
  * @param array $targetArgument
  * @param ElementEntity $caller
  * @param string $eventName
  * @return NULL|string Skip response (if required)
  */
 public function createClearDependentElementParentReferenceCallback(array $callerArguments, array $targetArgument, ElementEntity $caller, $eventName)
 {
     $response = $this->createNewDependentElementParentReferenceCallback($callerArguments, $targetArgument, $caller, $eventName);
     if (empty($response)) {
         $record = BackendUtility::getRecord($callerArguments['table'], $callerArguments['id']);
         if (!VersionState::cast($record['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)) {
             $response = ElementEntity::RESPONSE_Skip;
         }
     }
     return $response;
 }
Beispiel #22
0
    /**
     * Renders Content Elements from the tt_content table from page id
     *
     * @param integer $id Page id
     * @return string HTML for the listing
     * @todo Define visibility
     */
    public function getTable_tt_content($id)
    {
        $this->initializeLanguages();
        $this->initializeClipboard();
        // Initialize:
        $RTE = $this->getBackendUser()->isRTE();
        $lMarg = 1;
        $showHidden = $this->tt_contentConfig['showHidden'] ? '' : BackendUtility::BEenableFields('tt_content');
        $pageTitleParamForAltDoc = '&recTitle=' . rawurlencode(BackendUtility::getRecordTitle('pages', BackendUtility::getRecordWSOL('pages', $id), TRUE));
        /** @var $pageRenderer \TYPO3\CMS\Core\Page\PageRenderer */
        $pageRenderer = $this->getPageLayoutController()->doc->getPageRenderer();
        $pageRenderer->loadExtJs();
        $pageRenderer->addJsFile($GLOBALS['BACK_PATH'] . 'sysext/cms/layout/js/typo3pageModule.js');
        // Get labels for CTypes and tt_content element fields in general:
        $this->CType_labels = array();
        foreach ($GLOBALS['TCA']['tt_content']['columns']['CType']['config']['items'] as $val) {
            $this->CType_labels[$val[1]] = $this->getLanguageService()->sL($val[0]);
        }
        $this->itemLabels = array();
        foreach ($GLOBALS['TCA']['tt_content']['columns'] as $name => $val) {
            $this->itemLabels[$name] = $this->getLanguageService()->sL($val['label']);
        }
        $languageColumn = array();
        $out = '';
        // Select display mode:
        // MULTIPLE column display mode, side by side:
        if (!$this->tt_contentConfig['single']) {
            // Setting language list:
            $langList = $this->tt_contentConfig['sys_language_uid'];
            if ($this->tt_contentConfig['languageMode']) {
                if ($this->tt_contentConfig['languageColsPointer']) {
                    $langList = '0,' . $this->tt_contentConfig['languageColsPointer'];
                } else {
                    $langList = implode(',', array_keys($this->tt_contentConfig['languageCols']));
                }
                $languageColumn = array();
            }
            $langListArr = GeneralUtility::intExplode(',', $langList);
            $defLanguageCount = array();
            $defLangBinding = array();
            // For each languages... :
            // If not languageMode, then we'll only be through this once.
            foreach ($langListArr as $lP) {
                $lP = (int) $lP;
                if (count($langListArr) === 1 || $lP === 0) {
                    $showLanguage = ' AND sys_language_uid IN (' . $lP . ',-1)';
                } else {
                    $showLanguage = ' AND sys_language_uid=' . $lP;
                }
                $cList = explode(',', $this->tt_contentConfig['cols']);
                $content = array();
                $head = array();
                // Select content records per column
                $contentRecordsPerColumn = $this->getContentRecordsPerColumn('table', $id, array_values($cList), $showHidden . $showLanguage);
                // For each column, render the content into a variable:
                foreach ($cList as $key) {
                    if (!$lP) {
                        $defLanguageCount[$key] = array();
                    }
                    // Start wrapping div
                    $content[$key] .= '<div class="t3-page-ce-wrapper';
                    if (count($contentRecordsPerColumn[$key]) === 0) {
                        $content[$key] .= ' t3-page-ce-empty';
                    }
                    $content[$key] .= '">';
                    // Add new content at the top most position
                    $content[$key] .= '
					<div class="t3-page-ce" id="' . uniqid() . '">
						<div class="t3-page-ce-dropzone" id="colpos-' . $key . '-' . 'page-' . $id . '-' . uniqid() . '">
							<div class="t3-page-ce-wrapper-new-ce">
								<a href="#" onclick="' . htmlspecialchars($this->newContentElementOnClick($id, $key, $lP)) . '" title="' . $this->getLanguageService()->getLL('newRecordHere', TRUE) . '">' . IconUtility::getSpriteIcon('actions-document-new') . '</a>
							</div>
						</div>
					</div>
					';
                    $editUidList = '';
                    $rowArr = $contentRecordsPerColumn[$key];
                    $this->generateTtContentDataArray($rowArr);
                    foreach ((array) $rowArr as $rKey => $row) {
                        if ($this->tt_contentConfig['languageMode']) {
                            $languageColumn[$key][$lP] = $head[$key] . $content[$key];
                            if (!$this->defLangBinding) {
                                $languageColumn[$key][$lP] .= $this->newLanguageButton($this->getNonTranslatedTTcontentUids($defLanguageCount[$key], $id, $lP), $lP);
                            }
                        }
                        if (is_array($row) && !VersionState::cast($row['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)) {
                            $singleElementHTML = '';
                            if (!$lP && ($this->defLangBinding || $row['sys_language_uid'] != -1)) {
                                $defLanguageCount[$key][] = $row['uid'];
                            }
                            $editUidList .= $row['uid'] . ',';
                            $disableMoveAndNewButtons = $this->defLangBinding && $lP > 0;
                            if (!$this->tt_contentConfig['languageMode']) {
                                $singleElementHTML .= '<div class="t3-page-ce-dragitem" id="' . uniqid() . '">';
                            }
                            $singleElementHTML .= $this->tt_content_drawHeader($row, $this->tt_contentConfig['showInfo'] ? 15 : 5, $disableMoveAndNewButtons, TRUE, !$this->tt_contentConfig['languageMode']);
                            $isRTE = $RTE && $this->isRTEforField('tt_content', $row, 'bodytext');
                            $innerContent = '<div ' . ($row['_ORIG_uid'] ? ' class="ver-element"' : '') . '>' . $this->tt_content_drawItem($row, $isRTE) . '</div>';
                            $singleElementHTML .= '<div class="t3-page-ce-body-inner">' . $innerContent . '</div>' . $this->tt_content_drawFooter($row);
                            // NOTE: this is the end tag for <div class="t3-page-ce-body">
                            // because of bad (historic) conception, starting tag has to be placed inside tt_content_drawHeader()
                            $singleElementHTML .= '</div>';
                            $statusHidden = $this->isDisabled('tt_content', $row) ? ' t3-page-ce-hidden' : '';
                            $singleElementHTML = '<div class="t3-page-ce' . $statusHidden . '" id="element-tt_content-' . $row['uid'] . '">' . $singleElementHTML . '</div>';
                            if ($this->tt_contentConfig['languageMode']) {
                                $singleElementHTML .= '<div class="t3-page-ce">';
                            }
                            $singleElementHTML .= '<div class="t3-page-ce-dropzone" id="colpos-' . $key . '-' . 'page-' . $id . '-' . uniqid() . '">';
                            // Add icon "new content element below"
                            if (!$disableMoveAndNewButtons) {
                                // New content element:
                                if ($this->option_newWizard) {
                                    $onClick = 'window.location.href=\'db_new_content_el.php?id=' . $row['pid'] . '&sys_language_uid=' . $row['sys_language_uid'] . '&colPos=' . $row['colPos'] . '&uid_pid=' . -$row['uid'] . '&returnUrl=' . rawurlencode(GeneralUtility::getIndpEnv('REQUEST_URI')) . '\';';
                                } else {
                                    $params = '&edit[tt_content][' . -$row['uid'] . ']=new';
                                    $onClick = BackendUtility::editOnClick($params, $this->backPath);
                                }
                                $singleElementHTML .= '
									<div class="t3-page-ce-wrapper-new-ce">
										<a href="#" onclick="' . htmlspecialchars($onClick) . '" title="' . $this->getLanguageService()->getLL('newRecordHere', TRUE) . '">' . IconUtility::getSpriteIcon('actions-document-new') . '</a>
									</div>
								';
                            }
                            $singleElementHTML .= '</div></div>';
                            if ($this->defLangBinding && $this->tt_contentConfig['languageMode']) {
                                $defLangBinding[$key][$lP][$row[$lP ? 'l18n_parent' : 'uid']] = $singleElementHTML;
                            } else {
                                $content[$key] .= $singleElementHTML;
                            }
                        } else {
                            unset($rowArr[$rKey]);
                        }
                    }
                    $content[$key] .= '</div>';
                    // Add new-icon link, header:
                    $newP = $this->newContentElementOnClick($id, $key, $lP);
                    $colTitle = BackendUtility::getProcessedValue('tt_content', 'colPos', $key);
                    $tcaItems = GeneralUtility::callUserFunction('TYPO3\\CMS\\Backend\\View\\BackendLayoutView->getColPosListItemsParsed', $id, $this);
                    foreach ($tcaItems as $item) {
                        if ($item[1] == $key) {
                            $colTitle = $this->getLanguageService()->sL($item[0]);
                        }
                    }
                    $pasteP = array('colPos' => $key, 'sys_language_uid' => $lP);
                    $editParam = $this->doEdit && count($rowArr) ? '&edit[tt_content][' . $editUidList . ']=edit' . $pageTitleParamForAltDoc : '';
                    $head[$key] .= $this->tt_content_drawColHeader($colTitle, $editParam, $newP, $pasteP);
                }
                // For each column, fit the rendered content into a table cell:
                $out = '';
                if ($this->tt_contentConfig['languageMode']) {
                    // in language mode process the content elements, but only fill $languageColumn. output will be generated later
                    foreach ($cList as $key) {
                        $languageColumn[$key][$lP] = $head[$key] . $content[$key];
                        if (!$this->defLangBinding) {
                            $languageColumn[$key][$lP] .= $this->newLanguageButton($this->getNonTranslatedTTcontentUids($defLanguageCount[$key], $id, $lP), $lP);
                        }
                    }
                } else {
                    $backendLayout = $this->getBackendLayoutView()->getSelectedBackendLayout($this->id);
                    // GRID VIEW:
                    $grid = '<div class="t3-gridContainer"><table border="0" cellspacing="0" cellpadding="0" width="100%" height="100%" class="t3-page-columns t3-gridTable">';
                    // Add colgroups
                    $colCount = (int) $backendLayout['__config']['backend_layout.']['colCount'];
                    $rowCount = (int) $backendLayout['__config']['backend_layout.']['rowCount'];
                    $grid .= '<colgroup>';
                    for ($i = 0; $i < $colCount; $i++) {
                        $grid .= '<col style="width:' . 100 / $colCount . '%"></col>';
                    }
                    $grid .= '</colgroup>';
                    // Cycle through rows
                    for ($row = 1; $row <= $rowCount; $row++) {
                        $rowConfig = $backendLayout['__config']['backend_layout.']['rows.'][$row . '.'];
                        if (!isset($rowConfig)) {
                            continue;
                        }
                        $grid .= '<tr>';
                        for ($col = 1; $col <= $colCount; $col++) {
                            $columnConfig = $rowConfig['columns.'][$col . '.'];
                            if (!isset($columnConfig)) {
                                continue;
                            }
                            // Which tt_content colPos should be displayed inside this cell
                            $columnKey = (int) $columnConfig['colPos'];
                            // Render the grid cell
                            $colSpan = (int) $columnConfig['colspan'];
                            $rowSpan = (int) $columnConfig['rowspan'];
                            $grid .= '<td valign="top"' . ($colSpan > 0 ? ' colspan="' . $colSpan . '"' : '') . ($rowSpan > 0 ? ' rowspan="' . $rowSpan . '"' : '') . ' class="t3-gridCell t3-page-column t3-page-column-' . $columnKey . (!isset($columnConfig['colPos']) || $columnConfig['colPos'] === '' ? ' t3-gridCell-unassigned' : '') . (isset($columnConfig['colPos']) && $columnConfig['colPos'] !== '' && !$head[$columnKey] ? ' t3-gridCell-restricted' : '') . ($colSpan > 0 ? ' t3-gridCell-width' . $colSpan : '') . ($rowSpan > 0 ? ' t3-gridCell-height' . $rowSpan : '') . '">';
                            // Draw the pre-generated header with edit and new buttons if a colPos is assigned.
                            // If not, a new header without any buttons will be generated.
                            if (isset($columnConfig['colPos']) && $columnConfig['colPos'] !== '' && $head[$columnKey]) {
                                $grid .= $head[$columnKey] . $content[$columnKey];
                            } elseif (isset($columnConfig['colPos']) && $columnConfig['colPos'] !== '') {
                                $grid .= $this->tt_content_drawColHeader($this->getLanguageService()->getLL('noAccess'), '', '');
                            } elseif (isset($columnConfig['name']) && strlen($columnConfig['name']) > 0) {
                                $grid .= $this->tt_content_drawColHeader($this->getLanguageService()->sL($columnConfig['name']) . ' (' . $this->getLanguageService()->getLL('notAssigned') . ')', '', '');
                            } else {
                                $grid .= $this->tt_content_drawColHeader($this->getLanguageService()->getLL('notAssigned'), '', '');
                            }
                            $grid .= '</td>';
                        }
                        $grid .= '</tr>';
                    }
                    $out .= $grid . '</table></div>';
                }
                // CSH:
                $out .= BackendUtility::cshItem($this->descrTable, 'columns_multi', $GLOBALS['BACK_PATH']);
            }
            // If language mode, then make another presentation:
            // Notice that THIS presentation will override the value of $out!
            // But it needs the code above to execute since $languageColumn is filled with content we need!
            if ($this->tt_contentConfig['languageMode']) {
                // Get language selector:
                $languageSelector = $this->languageSelector($id);
                // Reset out - we will make new content here:
                $out = '';
                // Traverse languages found on the page and build up the table displaying them side by side:
                $cCont = array();
                $sCont = array();
                foreach ($langListArr as $lP) {
                    // Header:
                    $lP = (int) $lP;
                    $cCont[$lP] = '
						<td valign="top" class="t3-page-lang-column">
							<h3>' . htmlspecialchars($this->tt_contentConfig['languageCols'][$lP]) . '</h3>
						</td>';
                    // "View page" icon is added:
                    $onClick = BackendUtility::viewOnClick($this->id, $this->backPath, BackendUtility::BEgetRootLine($this->id), '', '', '&L=' . $lP);
                    $viewLink = '<a href="#" onclick="' . htmlspecialchars($onClick) . '">' . IconUtility::getSpriteIcon('actions-document-view') . '</a>';
                    // Language overlay page header:
                    if ($lP) {
                        list($lpRecord) = BackendUtility::getRecordsByField('pages_language_overlay', 'pid', $id, 'AND sys_language_uid=' . $lP);
                        BackendUtility::workspaceOL('pages_language_overlay', $lpRecord);
                        $params = '&edit[pages_language_overlay][' . $lpRecord['uid'] . ']=edit&overrideVals[pages_language_overlay][sys_language_uid]=' . $lP;
                        $lPLabel = $this->getPageLayoutController()->doc->wrapClickMenuOnIcon(IconUtility::getSpriteIconForRecord('pages_language_overlay', $lpRecord), 'pages_language_overlay', $lpRecord['uid']) . $viewLink . ($this->getBackendUser()->check('tables_modify', 'pages_language_overlay') ? '<a href="#" onclick="' . htmlspecialchars(BackendUtility::editOnClick($params, $this->backPath)) . '" title="' . $this->getLanguageService()->getLL('edit', TRUE) . '">' . IconUtility::getSpriteIcon('actions-document-open') . '</a>' : '') . htmlspecialchars(GeneralUtility::fixed_lgd_cs($lpRecord['title'], 20));
                    } else {
                        $lPLabel = $viewLink;
                    }
                    $sCont[$lP] = '
						<td nowrap="nowrap" class="t3-page-lang-column t3-page-lang-label">' . $lPLabel . '</td>';
                }
                // Add headers:
                $out .= '<tr>' . implode($cCont) . '</tr>';
                $out .= '<tr>' . implode($sCont) . '</tr>';
                // Traverse previously built content for the columns:
                foreach ($languageColumn as $cKey => $cCont) {
                    $out .= '
					<tr>
						<td valign="top" class="t3-gridCell t3-page-column t3-page-lang-column">' . implode('</td>' . '
						<td valign="top" class="t3-gridCell t3-page-column t3-page-lang-column">', $cCont) . '</td>
					</tr>';
                    if ($this->defLangBinding) {
                        // "defLangBinding" mode
                        foreach ($defLanguageCount[$cKey] as $defUid) {
                            $cCont = array();
                            foreach ($langListArr as $lP) {
                                $cCont[] = $defLangBinding[$cKey][$lP][$defUid] . $this->newLanguageButton($this->getNonTranslatedTTcontentUids(array($defUid), $id, $lP), $lP);
                            }
                            $out .= '
							<tr>
								<td valign="top" class="t3-page-lang-column">' . implode('</td>' . '
								<td valign="top" class="t3-page-lang-column">', $cCont) . '</td>
							</tr>';
                        }
                        // Create spacer:
                        $cCont = array_fill(0, count($langListArr), '&nbsp;');
                        $out .= '
						<tr>
							<td valign="top" class="t3-page-lang-column">' . implode('</td>' . '
							<td valign="top" class="t3-page-lang-column">', $cCont) . '</td>
						</tr>';
                    }
                }
                // Finally, wrap it all in a table and add the language selector on top of it:
                $out = $languageSelector . '
					<div class="t3-lang-gridContainer">
						<table cellpadding="0" cellspacing="0" class="t3-page-langMode">
							' . $out . '
						</table>
					</div>';
                // CSH:
                $out .= BackendUtility::cshItem($this->descrTable, 'language_list', $GLOBALS['BACK_PATH']);
            }
        } else {
            // SINGLE column mode (columns shown beneath each other):
            if ($this->tt_contentConfig['sys_language_uid'] == 0 || !$this->defLangBinding) {
                // Initialize:
                if ($this->defLangBinding && $this->tt_contentConfig['sys_language_uid'] == 0) {
                    $showLanguage = ' AND sys_language_uid IN (0,-1)';
                    $lP = 0;
                } else {
                    $showLanguage = ' AND sys_language_uid=' . $this->tt_contentConfig['sys_language_uid'];
                    $lP = $this->tt_contentConfig['sys_language_uid'];
                }
                $cList = explode(',', $this->tt_contentConfig['showSingleCol']);
                $out = '';
                // Expand the table to some preset dimensions:
                $out .= '
					<tr>
						<td><img src="clear.gif" width="' . $lMarg . '" height="1" alt="" /></td>
						<td valign="top"><img src="clear.gif" width="150" height="1" alt="" /></td>
						<td><img src="clear.gif" width="10" height="1" alt="" /></td>
						<td valign="top"><img src="clear.gif" width="300" height="1" alt="" /></td>
					</tr>';
                // Select content records per column
                $contentRecordsPerColumn = $this->getContentRecordsPerColumn('tt_content', $id, array_values($cList), $showHidden . $showLanguage);
                // Traverse columns to display top-on-top
                foreach ($cList as $counter => $key) {
                    $c = 0;
                    $rowArr = $contentRecordsPerColumn[$key];
                    $this->generateTtContentDataArray($rowArr);
                    $numberOfContentElementsInColumn = count($rowArr);
                    $rowOut = '';
                    // If it turns out that there are not content elements in the column, then display a big button which links directly to the wizard script:
                    if ($this->doEdit && $this->option_showBigButtons && !(int) $key && $numberOfContentElementsInColumn == 0) {
                        $onClick = 'window.location.href=\'db_new_content_el.php?id=' . $id . '&colPos=' . (int) $key . '&sys_language_uid=' . $lP . '&uid_pid=' . $id . '&returnUrl=' . rawurlencode(GeneralUtility::getIndpEnv('REQUEST_URI')) . '\';';
                        $theNewButton = $this->getPageLayoutController()->doc->t3Button($onClick, $this->getLanguageService()->getLL('newPageContent'));
                        $theNewButton = '<img src="clear.gif" width="1" height="5" alt="" /><br />' . $theNewButton;
                    } else {
                        $theNewButton = '';
                    }
                    $editUidList = '';
                    // Traverse any selected elements:
                    foreach ($rowArr as $rKey => $row) {
                        if (is_array($row) && !VersionState::cast($row['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)) {
                            $c++;
                            $editUidList .= $row['uid'] . ',';
                            $isRTE = $RTE && $this->isRTEforField('tt_content', $row, 'bodytext');
                            // Create row output:
                            $rowOut .= '
								<tr>
									<td></td>
									<td valign="top">' . $this->tt_content_drawHeader($row) . '</td>
									<td>&nbsp;</td>
									<td' . ($row['_ORIG_uid'] ? ' class="ver-element"' : '') . ' valign="top">' . $this->tt_content_drawItem($row, $isRTE) . '</td>
								</tr>';
                            // If the element was not the last element, add a divider line:
                            if ($c != $numberOfContentElementsInColumn) {
                                $rowOut .= '
								<tr>
									<td></td>
									<td colspan="3"><img' . IconUtility::skinImg($this->backPath, 'gfx/stiblet_medium2.gif', 'width="468" height="1"') . ' class="c-divider" alt="" /></td>
								</tr>';
                            }
                        } else {
                            unset($rowArr[$rKey]);
                        }
                    }
                    // Add spacer between sections in the vertical list
                    if ($counter) {
                        $out .= '
							<tr>
								<td></td>
								<td colspan="3"><br /><br /><br /><br /></td>
							</tr>';
                    }
                    // Add section header:
                    $newP = $this->newContentElementOnClick($id, $key, $this->tt_contentConfig['sys_language_uid']);
                    $pasteP = array('colPos' => $key, 'sys_language_uid' => $this->tt_contentConfig['sys_language_uid']);
                    $out .= '

						<!-- Column header: -->
						<tr>
							<td></td>
							<td valign="top" colspan="3">' . $this->tt_content_drawColHeader(BackendUtility::getProcessedValue('tt_content', 'colPos', $key), $this->doEdit && count($rowArr) ? '&edit[tt_content][' . $editUidList . ']=edit' . $pageTitleParamForAltDoc : '', $newP, $pasteP) . $theNewButton . '<br /></td>
						</tr>';
                    // Finally, add the content from the records in this column:
                    $out .= $rowOut;
                }
                // Finally, wrap all table rows in one, big table:
                $out = '
					<table border="0" cellpadding="0" cellspacing="0" width="400" class="typo3-page-columnsMode">
						' . $out . '
					</table>';
                // CSH:
                $out .= BackendUtility::cshItem($this->descrTable, 'columns_single', $GLOBALS['BACK_PATH']);
            } else {
                $out = '<br/><br/>' . $this->getPageLayoutController()->doc->icons(1) . 'Sorry, you cannot view a single language in this localization mode (Default Language Binding is enabled)<br/><br/>';
            }
        }
        // Add the big buttons to page:
        if ($this->option_showBigButtons) {
            $bArray = array();
            if (!$this->getPageLayoutController()->current_sys_language) {
                if ($this->ext_CALC_PERMS & 2) {
                    $bArray[0] = $this->getPageLayoutController()->doc->t3Button(BackendUtility::editOnClick('&edit[pages][' . $id . ']=edit', $this->backPath, ''), $this->getLanguageService()->getLL('editPageProperties'));
                }
            } else {
                if ($this->doEdit && $this->getBackendUser()->check('tables_modify', 'pages_language_overlay')) {
                    list($languageOverlayRecord) = BackendUtility::getRecordsByField('pages_language_overlay', 'pid', $id, 'AND sys_language_uid=' . (int) $this->getPageLayoutController()->current_sys_language);
                    $bArray[0] = $this->getPageLayoutController()->doc->t3Button(BackendUtility::editOnClick('&edit[pages_language_overlay][' . $languageOverlayRecord['uid'] . ']=edit', $this->backPath, ''), $this->getLanguageService()->getLL('editPageProperties_curLang'));
                }
            }
            if ($this->ext_CALC_PERMS & 4 || $this->ext_CALC_PERMS & 2) {
                $bArray[1] = $this->getPageLayoutController()->doc->t3Button('window.location.href=\'' . $this->backPath . 'move_el.php?table=pages&uid=' . $id . '&returnUrl=' . rawurlencode(GeneralUtility::getIndpEnv('REQUEST_URI')) . '\';', $this->getLanguageService()->getLL('move_page'));
            }
            if ($this->ext_CALC_PERMS & 8) {
                $bArray[2] = $this->getPageLayoutController()->doc->t3Button('window.location.href=\'' . $this->backPath . 'db_new.php?id=' . $id . '&pagesOnly=1&returnUrl=' . rawurlencode(GeneralUtility::getIndpEnv('REQUEST_URI')) . '\';', $this->getLanguageService()->getLL('newPage2'));
            }
            if ($this->doEdit && $this->ext_function == 1) {
                $bArray[3] = $this->getPageLayoutController()->doc->t3Button('window.location.href=\'db_new_content_el.php?id=' . $id . '&sys_language_uid=' . $this->getPageLayoutController()->current_sys_language . '&returnUrl=' . rawurlencode(GeneralUtility::getIndpEnv('REQUEST_URI')) . '\';', $this->getLanguageService()->getLL('newPageContent2'));
            }
            $out = '
				<table border="0" cellpadding="4" cellspacing="0" class="typo3-page-buttons">
					<tr>
						<td>' . implode('</td>
						<td>', $bArray) . '</td>
						<td>' . BackendUtility::cshItem($this->descrTable, 'button_panel', $GLOBALS['BACK_PATH']) . '</td>
					</tr>
				</table>
				<br />
				' . $out;
        }
        // Return content:
        return $out;
    }
Beispiel #23
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 ((int) $GLOBALS['TCA'][$table]['ctrl']['versioningWS'] >= 2) {
         if ($plhRec = BackendUtility::getMovePlaceholder($table, $liveRec['uid'], 'uid')) {
             $tcemainObj->deleteEl($table, $plhRec['uid'], TRUE, TRUE);
         }
     }
 }
Beispiel #24
0
 /**
  * Determines whether this node is deleted.
  *
  * @return bool
  */
 public function isDeleted()
 {
     return !empty($this->record['deleted']) || VersionState::cast($this->record['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER);
 }
Beispiel #25
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;
 }
 /**
  * Checks workspace localization integrity of a single elements.
  * If current record is a localization and its localization parent
  * is new in this workspace (has only a placeholder record in live),
  * then boths (localization and localization parent) should be published.
  *
  * @param \TYPO3\CMS\Workspaces\Domain\Model\CombinedRecord $element
  * @return void
  */
 protected function checkLocalization(\TYPO3\CMS\Workspaces\Domain\Model\CombinedRecord $element)
 {
     $table = $element->getTable();
     if (BackendUtility::isTableLocalizable($table)) {
         $languageField = $GLOBALS['TCA'][$table]['ctrl']['languageField'];
         $languageParentField = $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'];
         $versionRow = $element->getVersionRecord()->getRow();
         // If element is a localization:
         if ($versionRow[$languageField] > 0) {
             // Get localization parent from live workspace:
             $languageParentRecord = BackendUtility::getRecord($table, $versionRow[$languageParentField], 'uid,t3ver_state');
             // If localization parent is a "new placeholder" record:
             if (VersionState::cast($languageParentRecord['t3ver_state'])->equals(VersionState::NEW_PLACEHOLDER)) {
                 $title = BackendUtility::getRecordTitle($table, $versionRow);
                 // Add warning for current versionized record:
                 $this->addIssue($element->getLiveRecord()->getIdentifier(), self::STATUS_Warning, sprintf(\TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate('integrity.dependsOnDefaultLanguageRecord', 'workspaces'), $title));
                 // Add info for related localization parent record:
                 $this->addIssue($table . ':' . $languageParentRecord['uid'], self::STATUS_Info, sprintf(\TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate('integrity.isDefaultLanguageRecord', 'workspaces'), $title));
             }
         }
     }
 }
 /**
  * Purges ids that have a delete placeholder
  *
  * @param string $tableName
  * @param array $ids
  * @return array
  */
 protected function purgeDeletePlaceholder($tableName, array $ids)
 {
     $ids = $this->getDatabaseConnection()->cleanIntArray($ids);
     $ids = array_combine($ids, $ids);
     $versions = $this->getDatabaseConnection()->exec_SELECTgetRows('uid,t3ver_oid,t3ver_state', $tableName, 'pid=-1 AND t3ver_oid IN (' . implode(',', $ids) . ') AND t3ver_wsid=' . $this->getWorkspaceId() . ' AND t3ver_state=' . VersionState::cast(VersionState::DELETE_PLACEHOLDER));
     if (!empty($versions)) {
         foreach ($versions as $version) {
             $liveId = $version['t3ver_oid'];
             if (isset($ids[$liveId])) {
                 unset($ids[$liveId]);
             }
         }
     }
     return array_values($ids);
 }
Beispiel #28
0
 /**
  * Creates a new version of a record
  * (Requires support in the table)
  *
  * @param string $table Table name
  * @param int $id Record uid to versionize
  * @param string $label Version label
  * @param bool $delete If TRUE, the version is created to delete the record.
  * @return int|NULL Returns the id of the new version (if any)
  * @see copyRecord()
  */
 public function versionizeRecord($table, $id, $label, $delete = false)
 {
     $id = (int) $id;
     // Stop any actions if the record is marked to be deleted:
     // (this can occur if IRRE elements are versionized and child elements are removed)
     if ($this->isElementToBeDeleted($table, $id)) {
         return null;
     }
     if (!$GLOBALS['TCA'][$table] || !$GLOBALS['TCA'][$table]['ctrl']['versioningWS'] || $id <= 0) {
         if ($this->enableLogging) {
             $this->newlog('Versioning is not supported for this table "' . $table . '" / ' . $id, 1);
         }
         return null;
     }
     if (!$this->doesRecordExist($table, $id, 'show')) {
         if ($this->enableLogging) {
             $this->newlog('You didn\'t have correct permissions to make a new version (copy) of this record "' . $table . '" / ' . $id, 1);
         }
         return null;
     }
     // Select main record:
     $row = $this->recordInfo($table, $id, 'pid,t3ver_id,t3ver_state');
     if (!is_array($row)) {
         if ($this->enableLogging) {
             $this->newlog('Record "' . $table . ':' . $id . '" you wanted to versionize did not exist!', 1);
         }
         return null;
     }
     // Record must be online record
     if ($row['pid'] < 0) {
         if ($this->enableLogging) {
             $this->newlog('Record "' . $table . ':' . $id . '" you wanted to versionize was already a version in archive (pid=-1)!', 1);
         }
         return null;
     }
     // Record must not be placeholder for moving.
     if (VersionState::cast($row['t3ver_state'])->equals(VersionState::MOVE_PLACEHOLDER)) {
         if ($this->enableLogging) {
             $this->newlog('Record cannot be versioned because it is a placeholder for a moving operation', 1);
         }
         return null;
     }
     if ($delete && $this->cannotDeleteRecord($table, $id)) {
         if ($this->enableLogging) {
             $this->newlog('Record cannot be deleted: ' . $this->cannotDeleteRecord($table, $id), 1);
         }
         return null;
     }
     // Look for next version number:
     $res = $this->databaseConnection->exec_SELECTquery('t3ver_id', $table, '((pid=-1 && t3ver_oid=' . $id . ') OR uid=' . $id . ')' . $this->deleteClause($table), '', 't3ver_id DESC', '1');
     list($highestVerNumber) = $this->databaseConnection->sql_fetch_row($res);
     $this->databaseConnection->sql_free_result($res);
     // Look for version number of the current:
     $subVer = $row['t3ver_id'] . '.' . ($highestVerNumber + 1);
     // Set up the values to override when making a raw-copy:
     $overrideArray = array('t3ver_id' => $highestVerNumber + 1, 't3ver_oid' => $id, 't3ver_label' => $label ?: $subVer . ' / ' . date('d-m-Y H:m:s'), 't3ver_wsid' => $this->BE_USER->workspace, 't3ver_state' => (string) ($delete ? new VersionState(VersionState::DELETE_PLACEHOLDER) : new VersionState(VersionState::DEFAULT_STATE)), 't3ver_count' => 0, 't3ver_stage' => 0, 't3ver_tstamp' => 0);
     if ($GLOBALS['TCA'][$table]['ctrl']['editlock']) {
         $overrideArray[$GLOBALS['TCA'][$table]['ctrl']['editlock']] = 0;
     }
     // Checking if the record already has a version in the current workspace of the backend user
     if ($this->BE_USER->workspace !== 0) {
         // Look for version already in workspace:
         $versionRecord = BackendUtility::getWorkspaceVersionOfRecord($this->BE_USER->workspace, $table, $id, 'uid');
     }
     // Create new version of the record and return the new uid
     if (empty($versionRecord['uid'])) {
         // Create raw-copy and return result:
         // The information of the label to be used for the workspace record
         // as well as the information whether the record shall be removed
         // must be forwarded (creating remove placeholders on a workspace are
         // done by copying the record and override several fields).
         $workspaceOptions = array('delete' => $delete, 'label' => $label);
         return $this->copyRecord_raw($table, $id, -1, $overrideArray, $workspaceOptions);
         // Reuse the existing record and return its uid
         // (prior to TYPO3 CMS 6.2, an error was thrown here, which
         // did not make much sense since the information is available)
     } else {
         return $versionRecord['uid'];
     }
     return null;
 }
    /**
     * Renders Content Elements from the tt_content table from page id
     *
     * @param int $id Page id
     * @return string HTML for the listing
     */
    public function getTable_tt_content($id)
    {
        $backendUser = $this->getBackendUser();
        $this->pageinfo = BackendUtility::readPageAccess($this->id, $backendUser->getPagePermsClause($this->ext_CALC_PERMS));
        $this->initializeLanguages();
        $this->initializeClipboard();
        $pageTitleParamForAltDoc = '&recTitle=' . rawurlencode(BackendUtility::getRecordTitle('pages', BackendUtility::getRecordWSOL('pages', $id), true));
        /** @var $pageRenderer PageRenderer */
        $pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
        $pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/LayoutModule/DragDrop');
        $pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Modal');
        $userCanEditPage = $this->ext_CALC_PERMS & Permission::PAGE_EDIT && !empty($this->id) && ($backendUser->isAdmin() || (int) $this->pageinfo['editlock'] === 0);
        if ($this->tt_contentConfig['languageColsPointer'] > 0) {
            $userCanEditPage = $this->getBackendUser()->check('tables_modify', 'pages_language_overlay');
        }
        if ($userCanEditPage) {
            $pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/PageActions', 'function(PageActions) {
                PageActions.setPageId(' . (int) $this->id . ');
                PageActions.setLanguageOverlayId(' . $this->tt_contentConfig['languageColsPointer'] . ');
                PageActions.initializePageTitleRenaming();
            }');
        }
        // Get labels for CTypes and tt_content element fields in general:
        $this->CType_labels = array();
        foreach ($GLOBALS['TCA']['tt_content']['columns']['CType']['config']['items'] as $val) {
            $this->CType_labels[$val[1]] = $this->getLanguageService()->sL($val[0]);
        }
        $this->itemLabels = array();
        foreach ($GLOBALS['TCA']['tt_content']['columns'] as $name => $val) {
            $this->itemLabels[$name] = $this->getLanguageService()->sL($val['label']);
        }
        $languageColumn = array();
        $out = '';
        // Setting language list:
        $langList = $this->tt_contentConfig['sys_language_uid'];
        if ($this->tt_contentConfig['languageMode']) {
            if ($this->tt_contentConfig['languageColsPointer']) {
                $langList = '0,' . $this->tt_contentConfig['languageColsPointer'];
            } else {
                $langList = implode(',', array_keys($this->tt_contentConfig['languageCols']));
            }
            $languageColumn = array();
        }
        $langListArr = GeneralUtility::intExplode(',', $langList);
        $defLanguageCount = array();
        $defLangBinding = array();
        // For each languages... :
        // If not languageMode, then we'll only be through this once.
        foreach ($langListArr as $lP) {
            $lP = (int) $lP;
            if (!isset($this->contentElementCache[$lP])) {
                $this->contentElementCache[$lP] = array();
            }
            if (count($langListArr) === 1 || $lP === 0) {
                $showLanguage = ' AND sys_language_uid IN (' . $lP . ',-1)';
            } else {
                $showLanguage = ' AND sys_language_uid=' . $lP;
            }
            $cList = explode(',', $this->tt_contentConfig['cols']);
            $content = array();
            $head = array();
            // Select content records per column
            $contentRecordsPerColumn = $this->getContentRecordsPerColumn('table', $id, array_values($cList), $showLanguage);
            // For each column, render the content into a variable:
            foreach ($cList as $key) {
                if (!isset($this->contentElementCache[$lP][$key])) {
                    $this->contentElementCache[$lP][$key] = array();
                }
                if (!$lP) {
                    $defLanguageCount[$key] = array();
                }
                // Start wrapping div
                $content[$key] .= '<div data-colpos="' . $key . '" data-language-uid="' . $lP . '" class="t3js-sortable t3js-sortable-lang t3js-sortable-lang-' . $lP . ' t3-page-ce-wrapper';
                if (empty($contentRecordsPerColumn[$key])) {
                    $content[$key] .= ' t3-page-ce-empty';
                }
                $content[$key] .= '">';
                // Add new content at the top most position
                $link = '';
                if ($this->getPageLayoutController()->pageIsNotLockedForEditors() && $this->getBackendUser()->doesUserHaveAccess($this->pageinfo, Permission::CONTENT_EDIT)) {
                    $link = '<a href="#" onclick="' . htmlspecialchars($this->newContentElementOnClick($id, $key, $lP)) . '" title="' . $this->getLanguageService()->getLL('newContentElement', true) . '" class="btn btn-default btn-sm">' . $this->iconFactory->getIcon('actions-document-new', Icon::SIZE_SMALL)->render() . ' ' . $this->getLanguageService()->getLL('content', true) . '</a>';
                }
                $content[$key] .= '
				<div class="t3-page-ce t3js-page-ce" data-page="' . (int) $id . '" id="' . StringUtility::getUniqueId() . '">
					<div class="t3js-page-new-ce t3-page-ce-wrapper-new-ce" id="colpos-' . $key . '-' . 'page-' . $id . '-' . StringUtility::getUniqueId() . '">' . $link . '</div>
                    <div class="t3-page-ce-dropzone-available t3js-page-ce-dropzone-available"></div>
                </div>
				';
                $editUidList = '';
                $rowArr = $contentRecordsPerColumn[$key];
                $this->generateTtContentDataArray($rowArr);
                foreach ((array) $rowArr as $rKey => $row) {
                    $this->contentElementCache[$lP][$key][$row['uid']] = $row;
                    if ($this->tt_contentConfig['languageMode']) {
                        $languageColumn[$key][$lP] = $head[$key] . $content[$key];
                        if (!$this->defLangBinding) {
                            $languageColumn[$key][$lP] .= $this->newLanguageButton($this->getNonTranslatedTTcontentUids($defLanguageCount[$key], $id, $lP), $lP, $key);
                        }
                    }
                    if (is_array($row) && !VersionState::cast($row['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)) {
                        $singleElementHTML = '';
                        if (!$lP && ($this->defLangBinding || $row['sys_language_uid'] != -1)) {
                            $defLanguageCount[$key][] = isset($row['_ORIG_uid']) ? $row['_ORIG_uid'] : $row['uid'];
                        }
                        $editUidList .= $row['uid'] . ',';
                        $disableMoveAndNewButtons = $this->defLangBinding && $lP > 0;
                        if (!$this->tt_contentConfig['languageMode']) {
                            $singleElementHTML .= '<div class="t3-page-ce-dragitem" id="' . StringUtility::getUniqueId() . '">';
                        }
                        $singleElementHTML .= $this->tt_content_drawHeader($row, $this->tt_contentConfig['showInfo'] ? 15 : 5, $disableMoveAndNewButtons, !$this->tt_contentConfig['languageMode'], $this->getBackendUser()->doesUserHaveAccess($this->pageinfo, Permission::CONTENT_EDIT));
                        $innerContent = '<div ' . ($row['_ORIG_uid'] ? ' class="ver-element"' : '') . '>' . $this->tt_content_drawItem($row) . '</div>';
                        $singleElementHTML .= '<div class="t3-page-ce-body-inner">' . $innerContent . '</div>' . $this->tt_content_drawFooter($row);
                        $isDisabled = $this->isDisabled('tt_content', $row);
                        $statusHidden = $isDisabled ? ' t3-page-ce-hidden t3js-hidden-record' : '';
                        $displayNone = !$this->tt_contentConfig['showHidden'] && $isDisabled ? ' style="display: none;"' : '';
                        $singleElementHTML = '<div class="t3-page-ce t3js-page-ce t3js-page-ce-sortable ' . $statusHidden . '" id="element-tt_content-' . $row['uid'] . '" data-table="tt_content" data-uid="' . $row['uid'] . '"' . $displayNone . '>' . $singleElementHTML . '</div>';
                        if ($this->tt_contentConfig['languageMode']) {
                            $singleElementHTML .= '<div class="t3-page-ce t3js-page-ce">';
                        }
                        $singleElementHTML .= '<div class="t3js-page-new-ce t3-page-ce-wrapper-new-ce" id="colpos-' . $key . '-' . 'page-' . $id . '-' . StringUtility::getUniqueId() . '">';
                        // Add icon "new content element below"
                        if (!$disableMoveAndNewButtons && $this->getPageLayoutController()->pageIsNotLockedForEditors() && $this->getBackendUser()->doesUserHaveAccess($this->pageinfo, Permission::CONTENT_EDIT)) {
                            // New content element:
                            if ($this->option_newWizard) {
                                $onClick = 'window.location.href=' . GeneralUtility::quoteJSvalue(BackendUtility::getModuleUrl('new_content_element') . '&id=' . $row['pid'] . '&sys_language_uid=' . $row['sys_language_uid'] . '&colPos=' . $row['colPos'] . '&uid_pid=' . -$row['uid'] . '&returnUrl=' . rawurlencode(GeneralUtility::getIndpEnv('REQUEST_URI'))) . ';';
                            } else {
                                $params = '&edit[tt_content][' . -$row['uid'] . ']=new';
                                $onClick = BackendUtility::editOnClick($params);
                            }
                            $singleElementHTML .= '
								<a href="#" onclick="' . htmlspecialchars($onClick) . '" title="' . $this->getLanguageService()->getLL('newContentElement', true) . '" class="btn btn-default btn-sm">' . $this->iconFactory->getIcon('actions-document-new', Icon::SIZE_SMALL)->render() . ' ' . $this->getLanguageService()->getLL('content', true) . '</a>
							';
                        }
                        $singleElementHTML .= '</div></div><div class="t3-page-ce-dropzone-available t3js-page-ce-dropzone-available"></div></div>';
                        if ($this->defLangBinding && $this->tt_contentConfig['languageMode']) {
                            $defLangBinding[$key][$lP][$row[$lP ? 'l18n_parent' : 'uid']] = $singleElementHTML;
                        } else {
                            $content[$key] .= $singleElementHTML;
                        }
                    } else {
                        unset($rowArr[$rKey]);
                    }
                }
                $content[$key] .= '</div>';
                // Add new-icon link, header:
                $newP = $this->newContentElementOnClick($id, $key, $lP);
                $colTitle = BackendUtility::getProcessedValue('tt_content', 'colPos', $key);
                $tcaItems = GeneralUtility::callUserFunction(\TYPO3\CMS\Backend\View\BackendLayoutView::class . '->getColPosListItemsParsed', $id, $this);
                foreach ($tcaItems as $item) {
                    if ($item[1] == $key) {
                        $colTitle = $this->getLanguageService()->sL($item[0]);
                    }
                }
                $pasteP = array('colPos' => $key, 'sys_language_uid' => $lP);
                $editParam = $this->doEdit && !empty($rowArr) ? '&edit[tt_content][' . $editUidList . ']=edit' . $pageTitleParamForAltDoc : '';
                $head[$key] .= $this->tt_content_drawColHeader($colTitle, $editParam, $newP, $pasteP);
            }
            // For each column, fit the rendered content into a table cell:
            $out = '';
            if ($this->tt_contentConfig['languageMode']) {
                // in language mode process the content elements, but only fill $languageColumn. output will be generated later
                $sortedLanguageColumn = array();
                foreach ($cList as $key) {
                    $languageColumn[$key][$lP] = $head[$key] . $content[$key];
                    if (!$this->defLangBinding) {
                        $languageColumn[$key][$lP] .= $this->newLanguageButton($this->getNonTranslatedTTcontentUids($defLanguageCount[$key], $id, $lP), $lP, $key);
                    }
                    // We sort $languageColumn again according to $cList as it may contain data already from above.
                    $sortedLanguageColumn[$key] = $languageColumn[$key];
                }
                $languageColumn = $sortedLanguageColumn;
            } else {
                $backendLayout = $this->getBackendLayoutView()->getSelectedBackendLayout($this->id);
                // GRID VIEW:
                $grid = '<div class="t3-grid-container"><table border="0" cellspacing="0" cellpadding="0" width="100%" class="t3-page-columns t3-grid-table t3js-page-columns">';
                // Add colgroups
                $colCount = (int) $backendLayout['__config']['backend_layout.']['colCount'];
                $rowCount = (int) $backendLayout['__config']['backend_layout.']['rowCount'];
                $grid .= '<colgroup>';
                for ($i = 0; $i < $colCount; $i++) {
                    $grid .= '<col style="width:' . 100 / $colCount . '%"></col>';
                }
                $grid .= '</colgroup>';
                // Cycle through rows
                for ($row = 1; $row <= $rowCount; $row++) {
                    $rowConfig = $backendLayout['__config']['backend_layout.']['rows.'][$row . '.'];
                    if (!isset($rowConfig)) {
                        continue;
                    }
                    $grid .= '<tr>';
                    for ($col = 1; $col <= $colCount; $col++) {
                        $columnConfig = $rowConfig['columns.'][$col . '.'];
                        if (!isset($columnConfig)) {
                            continue;
                        }
                        // Which tt_content colPos should be displayed inside this cell
                        $columnKey = (int) $columnConfig['colPos'];
                        // Render the grid cell
                        $colSpan = (int) $columnConfig['colspan'];
                        $rowSpan = (int) $columnConfig['rowspan'];
                        $grid .= '<td valign="top"' . ($colSpan > 0 ? ' colspan="' . $colSpan . '"' : '') . ($rowSpan > 0 ? ' rowspan="' . $rowSpan . '"' : '') . ' data-colpos="' . (int) $columnConfig['colPos'] . '" data-language-uid="' . $lP . '" class="t3js-page-lang-column-' . $lP . ' t3js-page-column t3-grid-cell t3-page-column t3-page-column-' . $columnKey . (!isset($columnConfig['colPos']) || $columnConfig['colPos'] === '' ? ' t3-grid-cell-unassigned' : '') . (isset($columnConfig['colPos']) && $columnConfig['colPos'] !== '' && !$head[$columnKey] || !GeneralUtility::inList($this->tt_contentConfig['activeCols'], $columnConfig['colPos']) ? ' t3-grid-cell-restricted' : '') . ($colSpan > 0 ? ' t3-gridCell-width' . $colSpan : '') . ($rowSpan > 0 ? ' t3-gridCell-height' . $rowSpan : '') . '">';
                        // Draw the pre-generated header with edit and new buttons if a colPos is assigned.
                        // If not, a new header without any buttons will be generated.
                        if (isset($columnConfig['colPos']) && $columnConfig['colPos'] !== '' && $head[$columnKey] && GeneralUtility::inList($this->tt_contentConfig['activeCols'], $columnConfig['colPos'])) {
                            $grid .= $head[$columnKey] . $content[$columnKey];
                        } elseif (isset($columnConfig['colPos']) && $columnConfig['colPos'] !== '' && GeneralUtility::inList($this->tt_contentConfig['activeCols'], $columnConfig['colPos'])) {
                            $grid .= $this->tt_content_drawColHeader($this->getLanguageService()->getLL('noAccess'), '', '');
                        } elseif (isset($columnConfig['colPos']) && $columnConfig['colPos'] !== '' && !GeneralUtility::inList($this->tt_contentConfig['activeCols'], $columnConfig['colPos'])) {
                            $grid .= $this->tt_content_drawColHeader($this->getLanguageService()->sL($columnConfig['name']) . ' (' . $this->getLanguageService()->getLL('noAccess') . ')', '', '');
                        } elseif (isset($columnConfig['name']) && $columnConfig['name'] !== '') {
                            $grid .= $this->tt_content_drawColHeader($this->getLanguageService()->sL($columnConfig['name']) . ' (' . $this->getLanguageService()->getLL('notAssigned') . ')', '', '');
                        } else {
                            $grid .= $this->tt_content_drawColHeader($this->getLanguageService()->getLL('notAssigned'), '', '');
                        }
                        $grid .= '</td>';
                    }
                    $grid .= '</tr>';
                }
                $out .= $grid . '</table></div>';
            }
            // CSH:
            $out .= BackendUtility::cshItem($this->descrTable, 'columns_multi');
        }
        // If language mode, then make another presentation:
        // Notice that THIS presentation will override the value of $out!
        // But it needs the code above to execute since $languageColumn is filled with content we need!
        if ($this->tt_contentConfig['languageMode']) {
            // Get language selector:
            $languageSelector = $this->languageSelector($id);
            // Reset out - we will make new content here:
            $out = '';
            // Traverse languages found on the page and build up the table displaying them side by side:
            $cCont = array();
            $sCont = array();
            foreach ($langListArr as $lP) {
                // Header:
                $lP = (int) $lP;
                $cCont[$lP] = '
					<td valign="top" class="t3-page-column" data-language-uid="' . $lP . '">
						<h2>' . htmlspecialchars($this->tt_contentConfig['languageCols'][$lP]) . '</h2>
					</td>';
                // "View page" icon is added:
                $viewLink = '';
                if (!VersionState::cast($this->getPageLayoutController()->pageinfo['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)) {
                    $onClick = BackendUtility::viewOnClick($this->id, '', BackendUtility::BEgetRootLine($this->id), '', '', '&L=' . $lP);
                    $viewLink = '<a href="#" class="btn btn-default btn-sm" onclick="' . htmlspecialchars($onClick) . '" title="' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.showPage', true) . '">' . $this->iconFactory->getIcon('actions-view', Icon::SIZE_SMALL)->render() . '</a>';
                }
                // Language overlay page header:
                if ($lP) {
                    list($lpRecord) = BackendUtility::getRecordsByField('pages_language_overlay', 'pid', $id, 'AND sys_language_uid=' . $lP);
                    BackendUtility::workspaceOL('pages_language_overlay', $lpRecord);
                    $params = '&edit[pages_language_overlay][' . $lpRecord['uid'] . ']=edit&overrideVals[pages_language_overlay][sys_language_uid]=' . $lP;
                    $recordIcon = BackendUtility::wrapClickMenuOnIcon($this->iconFactory->getIconForRecord('pages_language_overlay', $lpRecord, Icon::SIZE_SMALL)->render(), 'pages_language_overlay', $lpRecord['uid']);
                    $editLink = $this->getBackendUser()->check('tables_modify', 'pages_language_overlay') ? '<a href="#" class="btn btn-default btn-sm" onclick="' . htmlspecialchars(BackendUtility::editOnClick($params)) . '" title="' . $this->getLanguageService()->getLL('edit', true) . '">' . $this->iconFactory->getIcon('actions-open', Icon::SIZE_SMALL)->render() . '</a>' : '';
                    $lPLabel = '<div class="btn-group">' . $viewLink . $editLink . '</div>' . ' ' . $recordIcon . ' ' . htmlspecialchars(GeneralUtility::fixed_lgd_cs($lpRecord['title'], 20));
                } else {
                    $params = '&edit[pages][' . $this->id . ']=edit';
                    $recordIcon = BackendUtility::wrapClickMenuOnIcon($this->iconFactory->getIconForRecord('pages', $this->pageRecord, Icon::SIZE_SMALL)->render(), 'pages', $this->id);
                    $editLink = $this->getBackendUser()->check('tables_modify', 'pages_language_overlay') ? '<a href="#" class="btn btn-default btn-sm" onclick="' . htmlspecialchars(BackendUtility::editOnClick($params)) . '" title="' . $this->getLanguageService()->getLL('edit', true) . '">' . $this->iconFactory->getIcon('actions-open', Icon::SIZE_SMALL)->render() . '</a>' : '';
                    $lPLabel = '<div class="btn-group">' . $viewLink . $editLink . '</div>' . ' ' . $recordIcon . ' ' . htmlspecialchars(GeneralUtility::fixed_lgd_cs($this->pageRecord['title'], 20));
                }
                $sCont[$lP] = '
					<td nowrap="nowrap" class="t3-page-column t3-page-lang-label">' . $lPLabel . '</td>';
            }
            // Add headers:
            $out .= '<tr>' . implode($cCont) . '</tr>';
            $out .= '<tr>' . implode($sCont) . '</tr>';
            unset($cCont, $sCont);
            // Traverse previously built content for the columns:
            foreach ($languageColumn as $cKey => $cCont) {
                $out .= '<tr>';
                foreach ($cCont as $languageId => $columnContent) {
                    $out .= '<td valign="top" class="t3-grid-cell t3-page-column t3js-page-column t3js-page-lang-column t3js-page-lang-column-' . $languageId . '">' . $columnContent . '</td>';
                }
                $out .= '</tr>';
                if ($this->defLangBinding) {
                    // "defLangBinding" mode
                    foreach ($defLanguageCount[$cKey] as $defUid) {
                        $cCont = array();
                        foreach ($langListArr as $lP) {
                            $cCont[] = $defLangBinding[$cKey][$lP][$defUid] . $this->newLanguageButton($this->getNonTranslatedTTcontentUids(array($defUid), $id, $lP), $lP, $cKey);
                        }
                        $out .= '
                        <tr>
							<td valign="top" class="t3-grid-cell">' . implode('</td>' . '
							<td valign="top" class="t3-grid-cell">', $cCont) . '</td>
						</tr>';
                    }
                }
            }
            // Finally, wrap it all in a table and add the language selector on top of it:
            $out = $languageSelector . '
                <div class="t3-grid-container">
                    <table cellpadding="0" cellspacing="0" class="t3-page-columns t3-grid-table t3js-page-columns">
						' . $out . '
                    </table>
				</div>';
            // CSH:
            $out .= BackendUtility::cshItem($this->descrTable, 'language_list');
        }
        return $out;
    }
    /**
     * Create the panel of buttons for submitting the form or otherwise perform operations.
     *
     * @param string $function Identifier for function of module
     * @return array all available buttons as an assoc. array
     */
    protected function getButtons($function = '')
    {
        $buttons = array('view' => '', 'history_page' => '', 'new_content' => '', 'move_page' => '', 'move_record' => '', 'new_page' => '', 'edit_page' => '', 'edit_language' => '', 'csh' => '', 'shortcut' => '', 'cache' => '', 'savedok' => '', 'save_close' => '', 'savedokshow' => '', 'closedok' => '', 'deletedok' => '', 'undo' => '', 'history_record' => '');
        // View page
        if (!VersionState::cast($this->pageinfo['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)) {
            $buttons['view'] = '<a href="#" onclick="' . htmlspecialchars(BackendUtility::viewOnClick($this->pageinfo['uid'], $GLOBALS['BACK_PATH'], BackendUtility::BEgetRootLine($this->pageinfo['uid']))) . '" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.showPage', TRUE) . '">' . IconUtility::getSpriteIcon('actions-document-view') . '</a>';
        }
        // Shortcut
        if ($GLOBALS['BE_USER']->mayMakeShortcut()) {
            $buttons['shortcut'] = $this->doc->makeShortcutIcon('id, edit_record, pointer, new_unique_uid, search_field, search_levels, showLimit', implode(',', array_keys($this->MOD_MENU)), $this->MCONF['name']);
        }
        // Cache
        if (!$this->modTSconfig['properties']['disableAdvanced']) {
            $buttons['cache'] = '<a href="' . htmlspecialchars('db_layout.php?id=' . $this->pageinfo['uid'] . '&clear_cache=1') . '" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.clear_cache', TRUE) . '">' . IconUtility::getSpriteIcon('actions-system-cache-clear') . '</a>';
        }
        if (!$this->modTSconfig['properties']['disableIconToolbar']) {
            // Move record
            if (MathUtility::canBeInterpretedAsInteger($this->eRParts[1])) {
                $buttons['move_record'] = '<a href="' . htmlspecialchars($GLOBALS['BACK_PATH'] . 'move_el.php?table=' . $this->eRParts[0] . '&uid=' . $this->eRParts[1] . '&returnUrl=' . rawurlencode(GeneralUtility::getIndpEnv('REQUEST_URI'))) . '">' . IconUtility::getSpriteIcon('actions-' . ($this->eRParts[0] == 'tt_content' ? 'document' : 'page') . '-move', array('class' => 'c-inputButton', 'title' => $GLOBALS['LANG']->getLL('move_' . ($this->eRParts[0] == 'tt_content' ? 'record' : 'page'), TRUE))) . '</a>';
            }
            // Edit page properties and page language overlay icons
            if ($this->CALC_PERMS & 2) {
                // Edit localized page_language_overlay only when one specific language is selected
                if ($this->MOD_SETTINGS['function'] == 1 && $this->current_sys_language > 0) {
                    $overlayRecord = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('uid', 'pages_language_overlay', 'pid = ' . (int) $this->id . ' ' . 'AND sys_language_uid = ' . (int) $this->current_sys_language . BackendUtility::deleteClause('pages_language_overlay') . BackendUtility::versioningPlaceholderClause('pages_language_overlay'), '', '', '', 'sys_language_uid');
                    $editLanguageOnClick = htmlspecialchars(BackendUtility::editOnClick('&edit[pages_language_overlay][' . $overlayRecord['uid'] . ']=edit', $GLOBALS['BACK_PATH']));
                    $buttons['edit_language'] = '<a href="#" ' . 'onclick="' . $editLanguageOnClick . '"' . 'title="' . $GLOBALS['LANG']->getLL('editPageLanguageOverlayProperties', TRUE) . '">' . \TYPO3\CMS\Backend\Utility\IconUtility::getSpriteIcon('mimetypes-x-content-page-language-overlay') . '</a>';
                }
                // Edit page properties
                $editPageOnClick = htmlspecialchars(BackendUtility::editOnClick('&edit[pages][' . $this->id . ']=edit', $GLOBALS['BACK_PATH']));
                $buttons['edit_page'] = '<a href="#" ' . 'onclick="' . $editPageOnClick . '"' . 'title="' . $GLOBALS['LANG']->getLL('editPageProperties', TRUE) . '">' . \TYPO3\CMS\Backend\Utility\IconUtility::getSpriteIcon('actions-page-open') . '</a>';
            }
            // Add CSH (Context Sensitive Help) icon to tool bar
            if ($function == 'quickEdit') {
                $buttons['csh'] = BackendUtility::cshItem($this->descrTable, 'quickEdit', $GLOBALS['BACK_PATH'], '', TRUE, 'margin-top: 0px; margin-bottom: 0px;');
            } else {
                $buttons['csh'] = BackendUtility::cshItem($this->descrTable, 'columns_' . $this->MOD_SETTINGS['function'], $GLOBALS['BACK_PATH'], '', TRUE, 'margin-top: 0px; margin-bottom: 0px;');
            }
            if ($function == 'quickEdit') {
                // Save record
                $buttons['savedok'] = IconUtility::getSpriteIcon('actions-document-save', array('html' => '<input type="image" name="_savedok" class="c-inputButton" src="clear.gif" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:rm.saveDoc', TRUE) . '" />'));
                // Save and close
                $buttons['save_close'] = IconUtility::getSpriteIcon('actions-document-save-close', array('html' => '<input type="image" class="c-inputButton" name="_saveandclosedok" src="clear.gif" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:rm.saveCloseDoc', TRUE) . '" />'));
                // Save record and show page
                $buttons['savedokshow'] = '<a href="#" onclick="' . htmlspecialchars('document.editform.redirect.value+=\'&popView=1\'; TBE_EDITOR.checkAndDoSubmit(1); return false;') . '" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:rm.saveDocShow', TRUE) . '">' . IconUtility::getSpriteIcon('actions-document-save-view') . '</a>';
                // Close record
                $buttons['closedok'] = '<a href="#" onclick="' . htmlspecialchars('jumpToUrl(unescape(\'' . rawurlencode($this->closeUrl) . '\')); return false;') . '" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:rm.closeDoc', TRUE) . '">' . IconUtility::getSpriteIcon('actions-document-close') . '</a>';
                // Delete record
                if ($this->deleteButton) {
                    $buttons['deletedok'] = '<a href="#" onclick="' . htmlspecialchars('return deleteRecord(\'' . $this->eRParts[0] . '\',\'' . $this->eRParts[1] . '\',\'' . GeneralUtility::getIndpEnv('SCRIPT_NAME') . '?id=' . $this->id . '\');') . '" title="' . $GLOBALS['LANG']->getLL('deleteItem', TRUE) . '">' . IconUtility::getSpriteIcon('actions-edit-delete') . '</a>';
                }
                if ($this->undoButton) {
                    // Undo button
                    $buttons['undo'] = '<a href="#"
						onclick="' . htmlspecialchars('window.location.href=' . GeneralUtility::quoteJSvalue($GLOBALS['BACK_PATH'] . BackendUtility::getModuleUrl('record_history', array('element' => $this->eRParts[0] . ':' . $this->eRParts[1], 'revert' => 'ALL_FIELDS', 'sumUp' => -1, 'returnUrl' => $this->R_URI))) . '; return false;') . '"
						title="' . htmlspecialchars(sprintf($GLOBALS['LANG']->getLL('undoLastChange'), BackendUtility::calcAge($GLOBALS['EXEC_TIME'] - $this->undoButtonR['tstamp'], $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.minutesHoursDaysYears')))) . '">' . IconUtility::getSpriteIcon('actions-edit-undo') . '</a>';
                    // History button
                    $buttons['history_record'] = '<a href="#"
						onclick="' . htmlspecialchars('jumpToUrl(' . GeneralUtility::quoteJSvalue($GLOBALS['BACK_PATH'] . BackendUtility::getModuleUrl('record_history', array('element' => $this->eRParts[0] . ':' . $this->eRParts[1], 'returnUrl' => $this->R_URI)) . '#latest') . ');return false;') . '"
						title="' . $GLOBALS['LANG']->getLL('recordHistory', TRUE) . '">' . IconUtility::getSpriteIcon('actions-document-history-open') . '</a>';
                }
            }
        }
        return $buttons;
    }