/** * @return bool */ protected function isLocalizationEnabled() { return BackendUtility::isTableLocalizable($this->tableName); }
/** * 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)); } } } }
/** * Find l10n-overlay records and perform the requested delete action for these records. * * @param string $table Record Table * @param string $uid Record UID * @return void */ public function deleteL10nOverlayRecords($table, $uid) { // Check whether table can be localized or has a different table defined to store localizations: if (!BackendUtility::isTableLocalizable($table) || !empty($GLOBALS['TCA'][$table]['ctrl']['transForeignTable']) || !empty($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerTable'])) { return; } $where = ''; if (isset($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) && $GLOBALS['TCA'][$table]['ctrl']['versioningWS']) { $where = ' AND t3ver_oid=0'; } $l10nRecords = BackendUtility::getRecordsByField($table, $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'], $uid, $where); if (is_array($l10nRecords)) { foreach ($l10nRecords as $record) { // Ignore workspace delete placeholders. Those records have been marked for // deletion before - deleting them again in a workspace would revert that state. if ($this->BE_USER->workspace > 0 && BackendUtility::isTableWorkspaceEnabled($table)) { BackendUtility::workspaceOL($table, $record); if (VersionState::cast($record['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)) { continue; } } $this->deleteAction($table, (int) $record['t3ver_oid'] > 0 ? (int) $record['t3ver_oid'] : (int) $record['uid']); } } }
/** * Builds the WHERE clauses of the Index Queue initialization query based * on TCA information for the type to be initialized. * * @return string Conditions to only add indexable items to the Index Queue */ protected function buildTcaWhereClause() { $tcaWhereClause = ''; $conditions = array(); if (isset($GLOBALS['TCA'][$this->type]['ctrl']['delete'])) { $conditions['delete'] = $GLOBALS['TCA'][$this->type]['ctrl']['delete'] . ' = 0'; } if (isset($GLOBALS['TCA'][$this->type]['ctrl']['enablecolumns']['disabled'])) { $conditions['disabled'] = $GLOBALS['TCA'][$this->type]['ctrl']['enablecolumns']['disabled'] . ' = 0'; } if (isset($GLOBALS['TCA'][$this->type]['ctrl']['enablecolumns']['endtime'])) { // only include records with a future endtime or default value (0) $endTimeFieldName = $GLOBALS['TCA'][$this->type]['ctrl']['enablecolumns']['endtime']; $conditions['endtime'] = '(' . $endTimeFieldName . ' > ' . time() . ' OR ' . $endTimeFieldName . ' = 0)'; } if (BackendUtility::isTableLocalizable($this->type)) { $conditions['languageField'] = array($GLOBALS['TCA'][$this->type]['ctrl']['languageField'] . ' = 0', $GLOBALS['TCA'][$this->type]['ctrl']['languageField'] . ' = -1'); if (isset($GLOBALS['TCA'][$this->type]['ctrl']['transOrigPointerField'])) { $conditions['languageField'][] = $GLOBALS['TCA'][$this->type]['ctrl']['transOrigPointerField'] . ' = 0'; // translations without original language source } $conditions['languageField'] = '(' . implode(' OR ', $conditions['languageField']) . ')'; } if (!empty($GLOBALS['TCA'][$this->type]['ctrl']['versioningWS'])) { // versioning is enabled for this table: exclude draft workspace records $conditions['versioningWS'] = 'pid != -1'; } if (count($conditions)) { $tcaWhereClause = ' AND ' . implode(' AND ', $conditions); } return $tcaWhereClause; }
/** * Gets all localizations of the current record. * * @param string $table The table * @param array $parentRec The current record * @param string $bgColClass Class for the background color of a column * @param string $pad Pad reference * @return string HTML table rows */ public function getLocalizations($table, $parentRec, $bgColClass, $pad) { $lines = array(); $tcaCtrl = $GLOBALS['TCA'][$table]['ctrl']; if ($table != 'pages' && BackendUtility::isTableLocalizable($table) && !$tcaCtrl['transOrigPointerTable']) { $where = array(); $where[] = $tcaCtrl['transOrigPointerField'] . '=' . (int) $parentRec['uid']; $where[] = $tcaCtrl['languageField'] . '<>0'; if (isset($tcaCtrl['delete']) && $tcaCtrl['delete']) { $where[] = $tcaCtrl['delete'] . '=0'; } if (isset($tcaCtrl['versioningWS']) && $tcaCtrl['versioningWS']) { $where[] = 't3ver_wsid=' . $parentRec['t3ver_wsid']; } $rows = $this->getDatabaseConnection()->exec_SELECTgetRows('*', $table, implode(' AND ', $where)); if (is_array($rows)) { $modeData = ''; if ($pad == 'normal') { $mode = $this->clipData['normal']['mode'] == 'copy' ? 'copy' : 'cut'; $modeData = ' <strong>(' . $this->clLabel($mode, 'cm') . ')</strong>'; } foreach ($rows as $rec) { $lines[] = ' <tr> <td nowrap="nowrap" class="col-icon">' . $this->iconFactory->getIconForRecord($table, $rec, Icon::SIZE_SMALL)->render() . '</td> <td nowrap="nowrap" width="95%">' . htmlspecialchars(GeneralUtility::fixed_lgd_cs(BackendUtility::getRecordTitle($table, $rec), $this->getBackendUser()->uc['titleLen'])) . $modeData . '</td> <td nowrap="nowrap" class="col-control"></td> </tr>'; } } } return implode('', $lines); }
/** * Make 1st level clickmenu: * * @param string $table Table name * @param integer $uid UID for the current record. * @return string HTML content * @todo Define visibility */ public function printDBClickMenu($table, $uid) { // Get record: $this->rec = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecordWSOL($table, $uid); $menuItems = array(); $root = 0; $DBmount = FALSE; // Rootlevel if ($table == 'pages' && !strcmp($uid, '0')) { $root = 1; } // DB mount if ($table == 'pages' && in_array($uid, $GLOBALS['BE_USER']->returnWebmounts())) { $DBmount = TRUE; } // Used to hide cut,copy icons for l10n-records $l10nOverlay = FALSE; // Should only be performed for overlay-records within the same table if (\TYPO3\CMS\Backend\Utility\BackendUtility::isTableLocalizable($table) && !isset($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerTable'])) { $l10nOverlay = intval($this->rec[$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']]) != 0; } // If record found (or root), go ahead and fill the $menuItems array which will contain data for the elements to render. if (is_array($this->rec) || $root) { // Get permissions $lCP = $GLOBALS['BE_USER']->calcPerms(\TYPO3\CMS\Backend\Utility\BackendUtility::getRecord('pages', $table == 'pages' ? $this->rec['uid'] : $this->rec['pid'])); // View if (!in_array('view', $this->disabledItems)) { if ($table == 'pages') { $menuItems['view'] = $this->DB_view($uid); } if ($table == $GLOBALS['TYPO3_CONF_VARS']['SYS']['contentTable']) { $ws_rec = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecordWSOL($table, $this->rec['uid']); $menuItems['view'] = $this->DB_view($ws_rec['pid']); } } // Edit: if (!$root && ($GLOBALS['BE_USER']->isPSet($lCP, $table, 'edit') || $GLOBALS['BE_USER']->isPSet($lCP, $table, 'editcontent'))) { if (!in_array('edit', $this->disabledItems)) { $menuItems['edit'] = $this->DB_edit($table, $uid); } $this->editOK = 1; } // New: if (!in_array('new', $this->disabledItems) && $GLOBALS['BE_USER']->isPSet($lCP, $table, 'new')) { $menuItems['new'] = $this->DB_new($table, $uid); } // Info: if (!in_array('info', $this->disabledItems) && !$root) { $menuItems['info'] = $this->DB_info($table, $uid); } $menuItems['spacer1'] = 'spacer'; // Copy: if (!in_array('copy', $this->disabledItems) && !$root && !$DBmount && !$l10nOverlay) { $menuItems['copy'] = $this->DB_copycut($table, $uid, 'copy'); } // Cut: if (!in_array('cut', $this->disabledItems) && !$root && !$DBmount && !$l10nOverlay) { $menuItems['cut'] = $this->DB_copycut($table, $uid, 'cut'); } // Paste: $elFromAllTables = count($this->clipObj->elFromTable('')); if (!in_array('paste', $this->disabledItems) && $elFromAllTables) { $selItem = $this->clipObj->getSelectedRecord(); $elInfo = array(GeneralUtility::fixed_lgd_cs($selItem['_RECORD_TITLE'], $GLOBALS['BE_USER']->uc['titleLen']), $root ? $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] : GeneralUtility::fixed_lgd_cs(\TYPO3\CMS\Backend\Utility\BackendUtility::getRecordTitle($table, $this->rec), $GLOBALS['BE_USER']->uc['titleLen']), $this->clipObj->currentMode()); if ($table == 'pages' && $lCP & 8) { if ($elFromAllTables) { $menuItems['pasteinto'] = $this->DB_paste('', $uid, 'into', $elInfo); } } $elFromTable = count($this->clipObj->elFromTable($table)); if (!$root && !$DBmount && $elFromTable && $GLOBALS['TCA'][$table]['ctrl']['sortby']) { $menuItems['pasteafter'] = $this->DB_paste($table, -$uid, 'after', $elInfo); } } // Delete: $elInfo = array(GeneralUtility::fixed_lgd_cs(\TYPO3\CMS\Backend\Utility\BackendUtility::getRecordTitle($table, $this->rec), $GLOBALS['BE_USER']->uc['titleLen'])); if (!in_array('delete', $this->disabledItems) && !$root && !$DBmount && $GLOBALS['BE_USER']->isPSet($lCP, $table, 'delete')) { $menuItems['spacer2'] = 'spacer'; $menuItems['delete'] = $this->DB_delete($table, $uid, $elInfo); } if (!in_array('history', $this->disabledItems)) { $menuItems['history'] = $this->DB_history($table, $uid, $elInfo); } } // Adding external elements to the menuItems array $menuItems = $this->processingByExtClassArray($menuItems, $table, $uid); // Processing by external functions? $menuItems = $this->externalProcessingOfDBMenuItems($menuItems); if (!is_array($this->rec)) { $this->rec = array(); } // Return the printed elements: return $this->printItems($menuItems, $root ? \TYPO3\CMS\Backend\Utility\IconUtility::getSpriteIcon('apps-pagetree-root') . htmlspecialchars($GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename']) : \TYPO3\CMS\Backend\Utility\IconUtility::getSpriteIconForRecord($table, $this->rec, array('title' => htmlspecialchars(\TYPO3\CMS\Backend\Utility\BackendUtility::getRecordIconAltText($this->rec, $table)))) . \TYPO3\CMS\Backend\Utility\BackendUtility::getRecordTitle($table, $this->rec, TRUE)); }
/** * Gets the used language value (sys_language.uid) of * a given database record. * * @param string $table Name of the table * @param array $record Database record * @return integer */ protected function getLanguageValue($table, array $record) { $languageValue = 0; if (BackendUtility::isTableLocalizable($table)) { $languageField = $GLOBALS['TCA'][$table]['ctrl']['languageField']; if (!empty($record[$languageField])) { $languageValue = $record[$languageField]; } } return $languageValue; }
/** * Find l10n-overlay records and perform the requested delete action for these records. * * @param string $table Record Table * @param string $uid Record UID * @return void * @todo Define visibility */ public function deleteL10nOverlayRecords($table, $uid) { // Check whether table can be localized or has a different table defined to store localizations: if (!\TYPO3\CMS\Backend\Utility\BackendUtility::isTableLocalizable($table) || !empty($GLOBALS['TCA'][$table]['ctrl']['transForeignTable']) || !empty($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerTable'])) { return; } $where = ''; if (isset($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) && $GLOBALS['TCA'][$table]['ctrl']['versioningWS']) { $where = ' AND t3ver_oid=0'; } $l10nRecords = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecordsByField($table, $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'], $uid, $where); if (is_array($l10nRecords)) { foreach ($l10nRecords as $record) { $this->deleteAction($table, intval($record['t3ver_oid']) > 0 ? intval($record['t3ver_oid']) : intval($record['uid'])); } } }
/** * Gets all localizations of the current record. * * @param string $table The table * @param array $parentRec The current record * @param string $bgColClass Class for the background color of a column * @param string $pad Pad reference * @return string HTML table rows */ public function getLocalizations($table, $parentRec, $bgColClass, $pad) { $lines = []; $tcaCtrl = $GLOBALS['TCA'][$table]['ctrl']; if ($table !== 'pages' && BackendUtility::isTableLocalizable($table) && $table !== 'pages_language_overlay') { $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table); $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class)); $queryBuilder->select('*')->from($table)->where($queryBuilder->expr()->eq($tcaCtrl['transOrigPointerField'], $queryBuilder->createNamedParameter($parentRec['uid'], \PDO::PARAM_INT)), $queryBuilder->expr()->neq($tcaCtrl['languageField'], $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT))); if (isset($tcaCtrl['versioningWS']) && $tcaCtrl['versioningWS']) { $queryBuilder->andWhere($queryBuilder->expr()->eq('t3ver_wsid', $queryBuilder->createNamedParameter($parentRec['t3ver_wsid'], \PDO::PARAM_INT))); } $rows = $queryBuilder->execute()->fetchAll(); if (is_array($rows)) { foreach ($rows as $rec) { $lines[] = ['icon' => $this->iconFactory->getIconForRecord($table, $rec, Icon::SIZE_SMALL)->render(), 'title' => htmlspecialchars(GeneralUtility::fixed_lgd_cs(BackendUtility::getRecordTitle($table, $rec), $this->getBackendUser()->uc['titleLen']))]; } } } return $lines; }
/** * Gets all localizations of the current record. * * @param string $table The table * @param array $parentRec The current record * @param string $bgColClass Class for the background color of a column * @param string $pad Pad reference * @return string HTML table rows */ public function getLocalizations($table, $parentRec, $bgColClass, $pad) { $lines = array(); $tcaCtrl = $GLOBALS['TCA'][$table]['ctrl']; if ($table != 'pages' && BackendUtility::isTableLocalizable($table) && !$tcaCtrl['transOrigPointerTable']) { $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table); $queryBuilder->getQueryContext()->setContext(QueryContextType::UNRESTRICTED); $queryBuilder->select('*')->from($table)->where($queryBuilder->expr()->eq($tcaCtrl['transOrigPointerField'], (int) $parentRec['uid']))->andWhere($queryBuilder->expr()->neq($tcaCtrl['languageField'], 0)); if (isset($tcaCtrl['delete']) && $tcaCtrl['delete']) { $queryBuilder->andWhere($queryBuilder->expr()->eq($tcaCtrl['delete'], 0)); } if (isset($tcaCtrl['versioningWS']) && $tcaCtrl['versioningWS']) { $queryBuilder->andWhere($queryBuilder->expr()->eq('t3ver_wsid', $parentRec['t3ver_wsid'])); } $rows = $queryBuilder->execute()->fetchAll(); if (is_array($rows)) { $modeData = ''; if ($pad == 'normal') { $mode = $this->clipData['normal']['mode'] == 'copy' ? 'copy' : 'cut'; $modeData = ' <strong>(' . $this->clLabel($mode, 'cm') . ')</strong>'; } foreach ($rows as $rec) { $lines[] = ' <tr> <td nowrap="nowrap" class="col-icon">' . $this->iconFactory->getIconForRecord($table, $rec, Icon::SIZE_SMALL)->render() . '</td> <td nowrap="nowrap" width="95%">' . htmlspecialchars(GeneralUtility::fixed_lgd_cs(BackendUtility::getRecordTitle($table, $rec), $this->getBackendUser()->uc['titleLen'])) . $modeData . '</td> <td nowrap="nowrap" class="col-control"></td> </tr>'; } } } return implode('', $lines); }
/** * Check current be users language access on given record. * * @param string $table Name of the table * @param array $record Record row to be checked * @return boolean */ protected function isLanguageAccessibleForCurrentUser($table, array $record) { $languageUid = 0; if (BackendUtility::isTableLocalizable($table)) { $languageUid = $record[$GLOBALS['TCA'][$table]['ctrl']['languageField']]; } else { return TRUE; } return $GLOBALS['BE_USER']->checkLanguageAccess($languageUid); }
/** * Fetches the localization for a given record. * * @FIXME: This method is a clone of BackendUtility::getRecordLocalization, using origUid instead of transOrigPointerField * * @param string $table Table name present in $GLOBALS['TCA'] * @param int $uid The uid of the record * @param int $language The uid of the language record in sys_language * @param string $andWhereClause Optional additional WHERE clause (default: '') * @return mixed Multidimensional array with selected records; if none exist, FALSE is returned */ public function getRecordLocalization($table, $uid, $language, $andWhereClause = '') { $recordLocalization = false; // Pages still stores translations in the pages_language_overlay table, all other tables store in themself if ($table === 'pages') { $table = 'pages_language_overlay'; } if (BackendUtility::isTableLocalizable($table)) { $tcaCtrl = $GLOBALS['TCA'][$table]['ctrl']; if (isset($tcaCtrl['origUid'])) { $recordLocalization = BackendUtility::getRecordsByField($table, $tcaCtrl['origUid'], $uid, 'AND ' . $tcaCtrl['languageField'] . '=' . (int) $language . ($andWhereClause ? ' ' . $andWhereClause : ''), '', '', '1'); } } return $recordLocalization; }
/** * Make 1st level clickmenu: * * @param string $table Table name * @param integer $uid UID for the current record. * @return string HTML content * @todo Define visibility */ public function printDBClickMenu($table, $uid) { $uid = (int) $uid; // Get record: $this->rec = BackendUtility::getRecordWSOL($table, $uid); $menuItems = array(); $root = 0; $DBmount = FALSE; // Rootlevel if ($table === 'pages' && $uid === 0) { $root = 1; } // DB mount if ($table === 'pages' && in_array($uid, $GLOBALS['BE_USER']->returnWebmounts())) { $DBmount = TRUE; } // Used to hide cut,copy icons for l10n-records $l10nOverlay = FALSE; // Should only be performed for overlay-records within the same table if (BackendUtility::isTableLocalizable($table) && !isset($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerTable'])) { $l10nOverlay = (int) $this->rec[$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']] != 0; } // If record found (or root), go ahead and fill the $menuItems array which will contain data for the elements to render. if (is_array($this->rec) || $root) { // Get permissions $lCP = $GLOBALS['BE_USER']->calcPerms(BackendUtility::getRecord('pages', $table == 'pages' ? $this->rec['uid'] : $this->rec['pid'])); // View if (!in_array('view', $this->disabledItems)) { if ($table === 'pages') { $menuItems['view'] = $this->DB_view($uid); } if ($table === 'tt_content') { $ws_rec = BackendUtility::getRecordWSOL($table, $this->rec['uid']); $menuItems['view'] = $this->DB_view($ws_rec['pid']); } } // Edit: if (!$root && ($GLOBALS['BE_USER']->isPSet($lCP, $table, 'edit') || $GLOBALS['BE_USER']->isPSet($lCP, $table, 'editcontent'))) { if (!in_array('edit', $this->disabledItems)) { $menuItems['edit'] = $this->DB_edit($table, $uid); } $this->editOK = 1; } // New: if (!in_array('new', $this->disabledItems) && $GLOBALS['BE_USER']->isPSet($lCP, $table, 'new')) { $menuItems['new'] = $this->DB_new($table, $uid); } // Info: if (!in_array('info', $this->disabledItems) && !$root) { $menuItems['info'] = $this->DB_info($table, $uid); } $menuItems['spacer1'] = 'spacer'; // Copy: if (!in_array('copy', $this->disabledItems) && !$root && !$DBmount && !$l10nOverlay) { $menuItems['copy'] = $this->DB_copycut($table, $uid, 'copy'); } // Cut: if (!in_array('cut', $this->disabledItems) && !$root && !$DBmount && !$l10nOverlay) { $menuItems['cut'] = $this->DB_copycut($table, $uid, 'cut'); } // Paste: $elFromAllTables = count($this->clipObj->elFromTable('')); if (!in_array('paste', $this->disabledItems) && $elFromAllTables) { $selItem = $this->clipObj->getSelectedRecord(); $elInfo = array(GeneralUtility::fixed_lgd_cs($selItem['_RECORD_TITLE'], $GLOBALS['BE_USER']->uc['titleLen']), $root ? $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] : GeneralUtility::fixed_lgd_cs(BackendUtility::getRecordTitle($table, $this->rec), $GLOBALS['BE_USER']->uc['titleLen']), $this->clipObj->currentMode()); if ($table === 'pages' && $lCP & 8) { if ($elFromAllTables) { $menuItems['pasteinto'] = $this->DB_paste('', $uid, 'into', $elInfo); } } $elFromTable = count($this->clipObj->elFromTable($table)); if (!$root && !$DBmount && $elFromTable && $GLOBALS['TCA'][$table]['ctrl']['sortby']) { $menuItems['pasteafter'] = $this->DB_paste($table, -$uid, 'after', $elInfo); } } $subname = \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('subname'); $localItems = array(); if (!$this->cmLevel && !in_array('moreoptions', $this->disabledItems, TRUE)) { // Creating menu items here: if ($this->editOK) { $localItems[] = 'spacer'; $localItems['moreoptions'] = $this->linkItem($this->label('more'), $this->excludeIcon(''), 'top.loadTopMenu(\'' . \TYPO3\CMS\Core\Utility\GeneralUtility::linkThisScript() . '&cmLevel=1&subname=moreoptions\');return false;', FALSE, TRUE); $menuItemHideUnhideAllowed = FALSE; $hiddenField = ''; // Check if column for disabled is defined if (isset($GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled'])) { $hiddenField = $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled']; if ($hiddenField !== '' && !empty($GLOBALS['TCA'][$table]['columns'][$hiddenField]['exclude']) && $GLOBALS['BE_USER']->check('non_exclude_fields', $table . ':' . $hiddenField)) { $menuItemHideUnhideAllowed = TRUE; } } if ($menuItemHideUnhideAllowed && !in_array('hide', $this->disabledItems, TRUE)) { $localItems['hide'] = $this->DB_hideUnhide($table, $this->rec, $hiddenField); } $anyEnableColumnsFieldAllowed = FALSE; // Check if columns are defined if (isset($GLOBALS['TCA'][$table]['ctrl']['enablecolumns'])) { $columnsToCheck = $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']; if ($table === 'pages' && !empty($columnsToCheck)) { $columnsToCheck[] = 'extendToSubpages'; } foreach ($columnsToCheck as $currentColumn) { if (!empty($GLOBALS['TCA'][$table]['columns'][$currentColumn]['exclude']) && $GLOBALS['BE_USER']->check('non_exclude_fields', $table . ':' . $currentColumn)) { $anyEnableColumnsFieldAllowed = TRUE; } } } if ($anyEnableColumnsFieldAllowed && !in_array('edit_access', $this->disabledItems, TRUE)) { $localItems['edit_access'] = $this->DB_editAccess($table, $uid); } if ($table === 'pages' && $this->editPageIconSet && !in_array('edit_pageproperties', $this->disabledItems, TRUE)) { $localItems['edit_pageproperties'] = $this->DB_editPageProperties($uid); } } // Find delete element among the input menu items and insert the local items just before that: $c = 0; $deleteFound = FALSE; foreach ($menuItems as $key => $value) { $c++; if ($key === 'delete') { $deleteFound = TRUE; break; } } if ($deleteFound) { // .. subtract two... (delete item + its spacer element...) $c -= 2; // and insert the items just before the delete element. array_splice($menuItems, $c, 0, $localItems); } else { $menuItems = array_merge($menuItems, $localItems); } } elseif ($subname === 'moreoptions') { // If the page can be edited, then show this: if ($this->editOK) { if (($table === 'pages' || $table === 'tt_content') && !in_array('move_wizard', $this->disabledItems, TRUE)) { $localItems['move_wizard'] = $this->DB_moveWizard($table, $uid, $this->rec); } if (($table === 'pages' || $table === 'tt_content') && !in_array('new_wizard', $this->disabledItems, TRUE)) { $localItems['new_wizard'] = $this->DB_newWizard($table, $uid, $this->rec); } if ($table === 'pages' && !in_array('perms', $this->disabledItems, TRUE) && $GLOBALS['BE_USER']->check('modules', 'web_perm')) { $localItems['perms'] = $this->DB_perms($table, $uid, $this->rec); } if (!in_array('db_list', $this->disabledItems, TRUE) && $GLOBALS['BE_USER']->check('modules', 'web_list')) { $localItems['db_list'] = $this->DB_db_list($table, $uid, $this->rec); } } // Temporary mount point item: if ($table === 'pages') { $localItems['temp_mount_point'] = $this->DB_tempMountPoint($uid); } // Merge the locally created items into the current menu items passed to this function. $menuItems = array_merge($menuItems, $localItems); } // Delete: $elInfo = array(GeneralUtility::fixed_lgd_cs(BackendUtility::getRecordTitle($table, $this->rec), $GLOBALS['BE_USER']->uc['titleLen'])); if (!in_array('delete', $this->disabledItems) && !$root && !$DBmount && $GLOBALS['BE_USER']->isPSet($lCP, $table, 'delete')) { $menuItems['spacer2'] = 'spacer'; $menuItems['delete'] = $this->DB_delete($table, $uid, $elInfo); } if (!in_array('history', $this->disabledItems)) { $menuItems['history'] = $this->DB_history($table, $uid, $elInfo); } } // Adding external elements to the menuItems array $menuItems = $this->processingByExtClassArray($menuItems, $table, $uid); // Processing by external functions? $menuItems = $this->externalProcessingOfDBMenuItems($menuItems); if (!is_array($this->rec)) { $this->rec = array(); } // Return the printed elements: return $this->printItems($menuItems, $root ? IconUtility::getSpriteIcon('apps-pagetree-root') . htmlspecialchars($GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename']) : IconUtility::getSpriteIconForRecord($table, $this->rec, array('title' => htmlspecialchars(BackendUtility::getRecordIconAltText($this->rec, $table)))) . BackendUtility::getRecordTitle($table, $this->rec, TRUE)); }
/** * Changes the clickmenu Items for the Commerce Records * * @param ClickMenu $clickMenu Clickenu object * @param array $menuItems Current menu Items * @param string $table Table * @param int $uid Uid * * @return array Menu Items Array */ public function main(ClickMenu &$clickMenu, array $menuItems, $table, $uid) { // Only modify the menu Items if we have the correct table if (!in_array($table, $this->commerceTables)) { return $menuItems; } $backendUser = $this->getBackendUser(); // Check for List allow if (!$backendUser->check('tables_select', $table)) { if (TYPO3_DLOG) { GeneralUtility::devLog('Clickmenu not allowed for user.', COMMERCE_EXTKEY, 1); } return ''; } // Configure the parent clickmenu $this->clickMenu = $clickMenu; $this->ajax = $this->clickMenu->ajax; $this->listFrame = $this->clickMenu->listFrame; $this->alwaysContentFrame = $this->clickMenu->alwaysContentFrame; $this->clipObj = $this->clickMenu->clipObj; $this->disabledItems = $this->clickMenu->disabledItems; $this->clickMenu->backPath = $this->backPath; $this->additionalParameter = GeneralUtility::explodeUrl2Array(urldecode(GeneralUtility::_GET('addParams'))); $this->newWizardAddParams = '&parentCategory=' . $this->additionalParameter['parentCategory']; $this->rec = BackendUtility::getRecordWSOL($table, $this->additionalParameter['control[' . $table . '][uid]']); // Initialize the rights-variables $rights = array('delete' => FALSE, 'edit' => FALSE, 'new' => FALSE, 'editLock' => FALSE, 'DBmount' => FALSE, 'copy' => FALSE, 'paste' => FALSE, 'overwrite' => FALSE, 'version' => FALSE, 'review' => FALSE, 'l10nOverlay' => FALSE, 'root' => 0, 'copyType' => 'after'); // used to hide cut,copy icons for l10n-records // should only be performed for overlay-records within the same table if (BackendUtility::isTableLocalizable($table) && !isset($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerTable'])) { $rights['l10nOverlay'] = intval($this->rec[$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']]) != 0; } // get rights based on the table switch ($table) { case 'tx_commerce_categories': $rights = $this->calculateCategoryRights($this->rec['uid'], $rights); break; case 'tx_commerce_products': $rights = $this->calculateProductRights($this->rec['uid'], $rights); break; case 'tx_commerce_articles': $rights = $this->calculateArticleRights($this->rec['uid'], $rights); break; default: } $menuItems = array(); // If record found, go ahead and fill the $menuItems array which will contain // data for the elements to render. if (is_array($this->rec)) { // Edit: if (!$rights['root'] && !$rights['editLock'] && $rights['edit']) { if (!in_array('hide', $this->disabledItems) && is_array($GLOBALS['TCA'][$table]['ctrl']['enablecolumns']) && $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled']) { $menuItems['hide'] = $this->DB_hideUnhide($table, $this->rec, $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled']); } if (!in_array('edit', $this->disabledItems)) { $menuItems['edit'] = $this->DB_edit($table, $uid); } $this->clickMenu->editOK = 1; } // fix: always give the UID of the products page to create any commerce object if (!in_array('new', $this->disabledItems) && $rights['new']) { $menuItems['new'] = $this->DB_new($table, $uid); } // Info: if (!in_array('info', $this->disabledItems) && !$rights['root']) { $menuItems['info'] = $this->DB_info($table, $uid); } $menuItems['spacer1'] = 'spacer'; // Cut not included // Copy: if (!in_array('copy', $this->disabledItems) && !$rights['root'] && !$rights['DBmount'] && !$rights['l10nOverlay'] && $rights['copy']) { $clipboardUid = $uid; if ($this->additionalParameter['category']) { $clipboardUid .= '|' . $this->additionalParameter['category']; } $menuItems['copy'] = $this->DB_copycut($table, $clipboardUid, 'copy'); } // Cut: if (!in_array('cut', $this->disabledItems) && !$rights['root'] && !$rights['DBmount'] && !$rights['l10nOverlay'] && $rights['copy']) { $menuItems['cut'] = $this->DB_copycut($table, $uid, 'cut'); } // Paste $elFromAllTables = count($this->clickMenu->clipObj->elFromTable('')); if (!in_array('paste', $this->disabledItems) && $elFromAllTables && $rights['paste']) { $selItem = $this->clipObj->getSelectedRecord(); $elInfo = array(GeneralUtility::fixed_lgd_cs($selItem['_RECORD_TITLE'], $backendUser->uc['titleLen']), $rights['root'] ? $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] : GeneralUtility::fixed_lgd_cs(BackendUtility::getRecordTitle($table, $this->rec), $backendUser->uc['titleLen']), $this->clipObj->currentMode()); $pasteUid = $uid; if ($this->additionalParameter['category']) { $pasteUid .= '|' . $this->additionalParameter['category']; } $elFromTable = count($this->clipObj->elFromTable($table)); if ($table == 'tx_commerce_products' && $rights['overwrite'] && $elFromTable) { // overwrite product with product $menuItems['overwrite'] = $this->DB_overwrite($table, $pasteUid, $elInfo); } if ($table == 'tx_commerce_categories') { $pasteIntoUid = $this->rec['pid']; if ($this->additionalParameter['category']) { $pasteIntoUid .= '|' . $this->additionalParameter['category']; } if ($elFromAllTables) { $menuItems['pasteinto'] = $this->DB_paste('', $pasteIntoUid, 'into', $elInfo); } } if (!$rights['root'] && !$rights['DBmount'] && $elFromTable && $GLOBALS['TCA'][$table]['ctrl']['sortby']) { $menuItems['pasteafter'] = $this->DB_paste($table, '-' . $pasteUid, 'after', $elInfo); } } // Delete: $elInfo = array(GeneralUtility::fixed_lgd_cs(BackendUtility::getRecordTitle($table, $this->rec), $backendUser->uc['titleLen'])); if (!$rights['editLock'] && !in_array('delete', $this->disabledItems) && !$rights['root'] && !$rights['DBmount'] && $rights['delete']) { $menuItems['spacer2'] = 'spacer'; $menuItems['delete'] = $this->DB_delete($table, $uid, $elInfo); } if (!in_array('history', $this->disabledItems)) { $menuItems['history'] = $this->DB_history($table, $uid); } } else { // if no item was found we clicked the top most node if (!in_array('new', $this->disabledItems) && $rights['new']) { $menuItems = array(); $menuItems['new'] = $this->DB_new($table, $uid); } } return $menuItems; }
/** * Make 1st level clickmenu: * * @param string $table Table name * @param int $uid UID for the current record. * @return string HTML content */ public function printDBClickMenu($table, $uid) { $uid = (int) $uid; // Get record: $this->rec = BackendUtility::getRecordWSOL($table, $uid); $menuItems = array(); $root = 0; $DBmount = false; // Rootlevel if ($table === 'pages' && $uid === 0) { $root = 1; } // DB mount if ($table === 'pages' && in_array($uid, $this->backendUser->returnWebmounts())) { $DBmount = true; } // Used to hide cut,copy icons for l10n-records $l10nOverlay = false; // Should only be performed for overlay-records within the same table if (BackendUtility::isTableLocalizable($table) && !isset($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerTable'])) { $l10nOverlay = (int) $this->rec[$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']] != 0; } // If record found (or root), go ahead and fill the $menuItems array which will contain data for the elements to render. if (is_array($this->rec) || $root) { // Get permissions $lCP = $this->backendUser->calcPerms(BackendUtility::getRecord('pages', $table === 'pages' ? (int) $this->rec['uid'] : (int) $this->rec['pid'])); // View if (!in_array('view', $this->disabledItems, true)) { if ($table === 'pages') { $menuItems['view'] = $this->DB_view($uid); } if ($table === 'tt_content') { $ws_rec = BackendUtility::getRecordWSOL($table, (int) $this->rec['uid']); $menuItems['view'] = $this->DB_view((int) $ws_rec['pid']); } } // Edit: if (!$root && ($this->backendUser->isPSet($lCP, $table, 'edit') || $this->backendUser->isPSet($lCP, $table, 'editcontent'))) { if (!in_array('edit', $this->disabledItems, true)) { $menuItems['edit'] = $this->DB_edit($table, $uid); } $this->editOK = true; } // New: if (!in_array('new', $this->disabledItems, true) && $this->backendUser->isPSet($lCP, $table, 'new')) { $menuItems['new'] = $this->DB_new($table, $uid); } // Info: if (!in_array('info', $this->disabledItems, true) && !$root) { $menuItems['info'] = $this->DB_info($table, $uid); } $menuItems['spacer1'] = 'spacer'; // Copy: if (!in_array('copy', $this->disabledItems, true) && !$root && !$DBmount && !$l10nOverlay) { $menuItems['copy'] = $this->DB_copycut($table, $uid, 'copy'); } // Cut: if (!in_array('cut', $this->disabledItems, true) && !$root && !$DBmount && !$l10nOverlay) { $menuItems['cut'] = $this->DB_copycut($table, $uid, 'cut'); } // Paste: $elFromAllTables = count($this->clipObj->elFromTable('')); if (!in_array('paste', $this->disabledItems, true) && $elFromAllTables) { $selItem = $this->clipObj->getSelectedRecord(); $elInfo = array(GeneralUtility::fixed_lgd_cs($selItem['_RECORD_TITLE'], $this->backendUser->uc['titleLen']), $root ? $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] : GeneralUtility::fixed_lgd_cs(BackendUtility::getRecordTitle($table, $this->rec), $this->backendUser->uc['titleLen']), $this->clipObj->currentMode()); if ($table === 'pages' && $lCP & Permission::PAGE_NEW) { if ($elFromAllTables) { $menuItems['pasteinto'] = $this->DB_paste('', $uid, 'into', $elInfo); } } $elFromTable = count($this->clipObj->elFromTable($table)); if (!$root && !$DBmount && $elFromTable && $GLOBALS['TCA'][$table]['ctrl']['sortby']) { $menuItems['pasteafter'] = $this->DB_paste($table, -$uid, 'after', $elInfo); } } // Delete: $elInfo = array(GeneralUtility::fixed_lgd_cs(BackendUtility::getRecordTitle($table, $this->rec), $this->backendUser->uc['titleLen'])); if (!in_array('delete', $this->disabledItems, true) && !$root && !$DBmount && $this->backendUser->isPSet($lCP, $table, 'delete')) { $menuItems['spacer2'] = 'spacer'; $menuItems['delete'] = $this->DB_delete($table, $uid, $elInfo); } if (!in_array('history', $this->disabledItems, true)) { $menuItems['history'] = $this->DB_history($table, $uid); } $localItems = array(); if (!$this->cmLevel && !in_array('moreoptions', $this->disabledItems, true)) { // Creating menu items here: if ($this->editOK) { $localItems['spacer3'] = 'spacer'; $localItems['moreoptions'] = $this->linkItem($this->label('more'), '', 'TYPO3.ClickMenu.fetch(' . GeneralUtility::quoteJSvalue(GeneralUtility::linkThisScript() . '&cmLevel=1&subname=moreoptions') . ');return false;', false, true); $menuItemHideUnhideAllowed = false; $hiddenField = ''; // Check if column for disabled is defined if (isset($GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled'])) { $hiddenField = $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled']; if ($hiddenField !== '' && !empty($GLOBALS['TCA'][$table]['columns'][$hiddenField]['exclude']) && $this->backendUser->check('non_exclude_fields', $table . ':' . $hiddenField)) { $menuItemHideUnhideAllowed = true; } } if ($menuItemHideUnhideAllowed && !in_array('hide', $this->disabledItems, true)) { $localItems['hide'] = $this->DB_hideUnhide($table, $this->rec, $hiddenField); } $anyEnableColumnsFieldAllowed = false; // Check if columns are defined if (isset($GLOBALS['TCA'][$table]['ctrl']['enablecolumns'])) { $columnsToCheck = $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']; if ($table === 'pages' && !empty($columnsToCheck)) { $columnsToCheck[] = 'extendToSubpages'; } foreach ($columnsToCheck as $currentColumn) { if (!empty($GLOBALS['TCA'][$table]['columns'][$currentColumn]['exclude']) && $this->backendUser->check('non_exclude_fields', $table . ':' . $currentColumn)) { $anyEnableColumnsFieldAllowed = true; } } } if ($anyEnableColumnsFieldAllowed && !in_array('edit_access', $this->disabledItems, true)) { $localItems['edit_access'] = $this->DB_editAccess($table, $uid); } if ($table === 'pages' && $this->editPageIconSet && !in_array('edit_pageproperties', $this->disabledItems, true)) { $localItems['edit_pageproperties'] = $this->DB_editPageProperties($uid); } } // Find delete element among the input menu items and insert the local items just before that: $c = 0; $deleteFound = false; foreach ($menuItems as $key => $value) { $c++; if ($key === 'delete') { $deleteFound = true; break; } } if ($deleteFound) { // .. subtract two... (delete item + its spacer element...) $c -= 2; // and insert the items just before the delete element. array_splice($menuItems, $c, 0, $localItems); } else { $menuItems = array_merge($menuItems, $localItems); } } } // Adding external elements to the menuItems array $menuItems = $this->processingByExtClassArray($menuItems, $table, $uid); // Processing by external functions? $menuItems = $this->externalProcessingOfDBMenuItems($menuItems); if (!is_array($this->rec)) { $this->rec = array(); } // Return the printed elements: return $this->printItems($menuItems); }
/** * Check if user has access to all existing localizations for a certain record * * @param string $table The table * @param array $record The current record * @return boolean * @todo Define visibility */ public function checkFullLanguagesAccess($table, $record) { $recordLocalizationAccess = $this->checkLanguageAccess(0); if ($recordLocalizationAccess && (BackendUtility::isTableLocalizable($table) || isset($GLOBALS['TCA'][$table]['ctrl']['transForeignTable']))) { if (isset($GLOBALS['TCA'][$table]['ctrl']['transForeignTable'])) { $l10nTable = $GLOBALS['TCA'][$table]['ctrl']['transForeignTable']; $pointerField = $GLOBALS['TCA'][$l10nTable]['ctrl']['transOrigPointerField']; $pointerValue = $record['uid']; } else { $l10nTable = $table; $pointerField = $GLOBALS['TCA'][$l10nTable]['ctrl']['transOrigPointerField']; $pointerValue = $record[$pointerField] > 0 ? $record[$pointerField] : $record['uid']; } $recordLocalizations = BackendUtility::getRecordsByField($l10nTable, $pointerField, $pointerValue, '', '', '', '1'); if (is_array($recordLocalizations)) { foreach ($recordLocalizations as $localization) { $recordLocalizationAccess = $recordLocalizationAccess && $this->checkLanguageAccess($localization[$GLOBALS['TCA'][$l10nTable]['ctrl']['languageField']]); if (!$recordLocalizationAccess) { break; } } } } return $recordLocalizationAccess; }
/** * Gets all localizations of the current record. * * @param string $table The table * @param array $parentRec The current record * @param string $bgColClass Class for the background color of a column * @param string $pad Pad reference * @return string HTML table rows * @todo Define visibility */ public function getLocalizations($table, $parentRec, $bgColClass, $pad) { $lines = array(); $tcaCtrl = $GLOBALS['TCA'][$table]['ctrl']; if ($table != 'pages' && \TYPO3\CMS\Backend\Utility\BackendUtility::isTableLocalizable($table) && !$tcaCtrl['transOrigPointerTable']) { $where = array(); $where[] = $tcaCtrl['transOrigPointerField'] . '=' . intval($parentRec['uid']); $where[] = $tcaCtrl['languageField'] . '<>0'; if (isset($tcaCtrl['delete']) && $tcaCtrl['delete']) { $where[] = $tcaCtrl['delete'] . '=0'; } if (isset($tcaCtrl['versioningWS']) && $tcaCtrl['versioningWS']) { $where[] = 't3ver_wsid=' . $parentRec['t3ver_wsid']; } $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('*', $table, implode(' AND ', $where)); if (is_array($rows)) { $modeData = ''; if ($pad == 'normal') { $mode = $this->clipData['normal']['mode'] == 'copy' ? 'copy' : 'cut'; $modeData = ' <strong>(' . $this->clLabel($mode, 'cm') . ')</strong>'; } foreach ($rows as $rec) { $lines[] = ' <tr> <td class="' . $bgColClass . '">' . \TYPO3\CMS\Backend\Utility\IconUtility::getSpriteIconForRecord($table, $rec, array('style' => 'margin-left: 38px;')) . '</td> <td class="' . $bgColClass . '" nowrap="nowrap" width="95%"> ' . htmlspecialchars(\TYPO3\CMS\Core\Utility\GeneralUtility::fixed_lgd_cs(\TYPO3\CMS\Backend\Utility\BackendUtility::getRecordTitle($table, $rec), $GLOBALS['BE_USER']->uc['titleLen'])) . $modeData . ' </td> <td class="' . $bgColClass . '" align="center" nowrap="nowrap"> </td> </tr>'; } } } return implode('', $lines); }
/** * Generates a view link for a page. * * @static * @param string $table Table to be used * @param int $uid Uid of the version(!) record * @param array $liveRecord Optional live record data * @param array $versionRecord Optional version record data * @return string */ public static function viewSingleRecord($table, $uid, array $liveRecord = null, array $versionRecord = null) { if ($table === 'pages') { return BackendUtility::viewOnClick(BackendUtility::getLiveVersionIdOfRecord('pages', $uid)); } if ($liveRecord === null) { $liveRecord = BackendUtility::getLiveVersionOfRecord($table, $uid); } if ($versionRecord === null) { $versionRecord = BackendUtility::getRecord($table, $uid); } if (VersionState::cast($versionRecord['t3ver_state'])->equals(VersionState::MOVE_POINTER)) { $movePlaceholder = BackendUtility::getMovePlaceholder($table, $liveRecord['uid'], 'pid'); } // Directly use pid value and consider move placeholders $previewPageId = empty($movePlaceholder['pid']) ? $liveRecord['pid'] : $movePlaceholder['pid']; $additionalParameters = '&tx_workspaces_web_workspacesworkspaces[previewWS]=' . $versionRecord['t3ver_wsid']; // Add language parameter if record is a localization if (BackendUtility::isTableLocalizable($table)) { $languageField = $GLOBALS['TCA'][$table]['ctrl']['languageField']; if ($versionRecord[$languageField] > 0) { $additionalParameters .= '&L=' . $versionRecord[$languageField]; } } $pageTsConfig = BackendUtility::getPagesTSconfig($previewPageId); $viewUrl = ''; // Directly use determined direct page id if ($table === 'pages_language_overlay' || $table === 'tt_content') { $viewUrl = BackendUtility::viewOnClick($previewPageId, '', '', '', '', $additionalParameters); // Analyze Page TSconfig options.workspaces.previewPageId } elseif (!empty($pageTsConfig['options.']['workspaces.']['previewPageId.'][$table]) || !empty($pageTsConfig['options.']['workspaces.']['previewPageId'])) { if (!empty($pageTsConfig['options.']['workspaces.']['previewPageId.'][$table])) { $previewConfiguration = $pageTsConfig['options.']['workspaces.']['previewPageId.'][$table]; } else { $previewConfiguration = $pageTsConfig['options.']['workspaces.']['previewPageId']; } // Extract possible settings (e.g. "field:pid") list($previewKey, $previewValue) = explode(':', $previewConfiguration, 2); if ($previewKey === 'field') { $previewPageId = (int) $liveRecord[$previewValue]; } else { $previewPageId = (int) $previewConfiguration; } $viewUrl = BackendUtility::viewOnClick($previewPageId, '', '', '', '', $additionalParameters); // Call user function to render the single record view } elseif (!empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['workspaces']['viewSingleRecord'])) { $_params = array('table' => $table, 'uid' => $uid, 'record' => $liveRecord, 'liveRecord' => $liveRecord, 'versionRecord' => $versionRecord); $_funcRef = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['workspaces']['viewSingleRecord']; $null = null; $viewUrl = GeneralUtility::callUserFunction($_funcRef, $_params, $null); } return $viewUrl; }
/** * Entry method * * @return array As defined in initializeResultArray() of AbstractNode */ public function render() { $languageService = $this->getLanguageService(); $this->inlineData = $this->data['inlineData']; /** @var InlineStackProcessor $inlineStackProcessor */ $inlineStackProcessor = GeneralUtility::makeInstance(InlineStackProcessor::class); $this->inlineStackProcessor = $inlineStackProcessor; $inlineStackProcessor->initializeByGivenStructure($this->data['inlineStructure']); $table = $this->data['tableName']; $row = $this->data['databaseRow']; $field = $this->data['fieldName']; $parameterArray = $this->data['parameterArray']; $resultArray = $this->initializeResultArray(); $config = $parameterArray['fieldConf']['config']; $foreign_table = $config['foreign_table']; $language = 0; if (BackendUtility::isTableLocalizable($table)) { $language = (int) $row[$GLOBALS['TCA'][$table]['ctrl']['languageField']]; } // Add the current inline job to the structure stack $newStructureItem = array('table' => $table, 'uid' => $row['uid'], 'field' => $field, 'config' => $config, 'localizationMode' => BackendUtility::getInlineLocalizationMode($table, $config)); // Extract FlexForm parts (if any) from element name, e.g. array('vDEF', 'lDEF', 'FlexField', 'vDEF') if (!empty($parameterArray['itemFormElName'])) { $flexFormParts = $this->extractFlexFormParts($parameterArray['itemFormElName']); if ($flexFormParts !== null) { $newStructureItem['flexform'] = $flexFormParts; } } $inlineStackProcessor->pushStableStructureItem($newStructureItem); // e.g. data[<table>][<uid>][<field>] $nameForm = $inlineStackProcessor->getCurrentStructureFormPrefix(); // e.g. data-<pid>-<table1>-<uid1>-<field1>-<table2>-<uid2>-<field2> $nameObject = $inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($this->data['inlineFirstPid']); $config['inline']['first'] = false; // @todo: This initialization shouldn't be required data provider should take care this is set? if (!is_array($this->data['parameterArray']['fieldConf']['children'])) { $this->data['parameterArray']['fieldConf']['children'] = array(); } $firstChild = reset($this->data['parameterArray']['fieldConf']['children']); if (isset($firstChild['databaseRow']['uid'])) { $config['inline']['first'] = $firstChild['databaseRow']['uid']; } $config['inline']['last'] = false; $lastChild = end($this->data['parameterArray']['fieldConf']['children']); if (isset($lastChild['databaseRow']['uid'])) { $config['inline']['last'] = $lastChild['databaseRow']['uid']; } $top = $inlineStackProcessor->getStructureLevel(0); $this->inlineData['config'][$nameObject] = array('table' => $foreign_table, 'md5' => md5($nameObject)); $this->inlineData['config'][$nameObject . '-' . $foreign_table] = array('min' => $config['minitems'], 'max' => $config['maxitems'], 'sortable' => $config['appearance']['useSortable'], 'top' => array('table' => $top['table'], 'uid' => $top['uid']), 'context' => array('config' => $config, 'hmac' => GeneralUtility::hmac(serialize($config)))); $this->inlineData['nested'][$nameObject] = $this->data['tabAndInlineStack']; // If relations are required to be unique, get the uids that have already been used on the foreign side of the relation $uniqueMax = 0; $possibleRecords = []; $uniqueIds = []; if ($config['foreign_unique']) { // If uniqueness *and* selector are set, they should point to the same field - so, get the configuration of one: $selConfig = FormEngineUtility::getInlinePossibleRecordsSelectorConfig($config, $config['foreign_unique']); // Get the used unique ids: $uniqueIds = $this->getUniqueIds($this->data['parameterArray']['fieldConf']['children'], $config, $selConfig['type'] == 'groupdb'); $possibleRecords = $this->getPossibleRecords($table, $field, $row, $config, 'foreign_unique'); $uniqueMax = $config['appearance']['useCombination'] || $possibleRecords === false ? -1 : count($possibleRecords); $this->inlineData['unique'][$nameObject . '-' . $foreign_table] = array('max' => $uniqueMax, 'used' => $uniqueIds, 'type' => $selConfig['type'], 'table' => $config['foreign_table'], 'elTable' => $selConfig['table'], 'field' => $config['foreign_unique'], 'selector' => $selConfig['selector'], 'possible' => $this->getPossibleRecordsFlat($possibleRecords)); } $resultArray['inlineData'] = $this->inlineData; // Render the localization links $localizationLinks = ''; if ($language > 0 && $row[$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']] > 0 && MathUtility::canBeInterpretedAsInteger($row['uid'])) { // Add the "Localize all records" link before all child records: if (isset($config['appearance']['showAllLocalizationLink']) && $config['appearance']['showAllLocalizationLink']) { $localizationLinks .= ' ' . $this->getLevelInteractionLink('localize', $nameObject . '-' . $foreign_table, $config); } // Add the "Synchronize with default language" link before all child records: if (isset($config['appearance']['showSynchronizationLink']) && $config['appearance']['showSynchronizationLink']) { $localizationLinks .= ' ' . $this->getLevelInteractionLink('synchronize', $nameObject . '-' . $foreign_table, $config); } } $numberOfFullChildren = 0; foreach ($this->data['parameterArray']['fieldConf']['children'] as $child) { if (!$child['inlineIsDefaultLanguage']) { $numberOfFullChildren++; } } // Define how to show the "Create new record" link - if there are more than maxitems, hide it if ($numberOfFullChildren >= $config['maxitems'] || $uniqueMax > 0 && $numberOfFullChildren >= $uniqueMax) { $config['inline']['inlineNewButtonStyle'] = 'display: none;'; $config['inline']['inlineNewRelationButtonStyle'] = 'display: none;'; } // Render the level links (create new record): $levelLinks = $this->getLevelInteractionLink('newRecord', $nameObject . '-' . $foreign_table, $config); // Wrap all inline fields of a record with a <div> (like a container) $html = '<div class="form-group" id="' . $nameObject . '">'; // Add the level links before all child records: if ($config['appearance']['levelLinksPosition'] === 'both' || $config['appearance']['levelLinksPosition'] === 'top') { $html .= '<div class="form-group t3js-formengine-validation-marker">' . $levelLinks . $localizationLinks . '</div>'; } // If it's required to select from possible child records (reusable children), add a selector box if ($config['foreign_selector'] && $config['appearance']['showPossibleRecordsSelector'] !== false) { // If not already set by the foreign_unique, set the possibleRecords here and the uniqueIds to an empty array if (!$config['foreign_unique']) { $possibleRecords = $this->getPossibleRecords($table, $field, $row, $config); $uniqueIds = array(); } $selectorBox = $this->renderPossibleRecordsSelector($possibleRecords, $config, $uniqueIds); $html .= $selectorBox . $localizationLinks; } $title = $languageService->sL($parameterArray['fieldConf']['label']); $html .= '<div class="panel-group panel-hover" data-title="' . htmlspecialchars($title) . '" id="' . $nameObject . '_records">'; $sortableRecordUids = []; foreach ($this->data['parameterArray']['fieldConf']['children'] as $options) { $options['inlineParentUid'] = $row['uid']; // @todo: this can be removed if this container no longer sets additional info to $config $options['inlineParentConfig'] = $config; $options['inlineData'] = $this->inlineData; $options['inlineStructure'] = $inlineStackProcessor->getStructure(); $options['inlineExpandCollapseStateArray'] = $this->data['inlineExpandCollapseStateArray']; $options['renderType'] = 'inlineRecordContainer'; $childResult = $this->nodeFactory->create($options)->render(); $html .= $childResult['html']; $childArray['html'] = ''; $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $childResult); if (!$options['inlineIsDefaultLanguage']) { // Don't add record to list of "valid" uids if it is only the default // language record of a not yet localized child $sortableRecordUids[] = $options['databaseRow']['uid']; } } $html .= '</div>'; // Add the level links after all child records: if ($config['appearance']['levelLinksPosition'] === 'both' || $config['appearance']['levelLinksPosition'] === 'bottom') { $html .= $levelLinks . $localizationLinks; } if (is_array($config['customControls'])) { $html .= '<div id="' . $nameObject . '_customControls">'; foreach ($config['customControls'] as $customControlConfig) { $parameters = array('table' => $table, 'field' => $field, 'row' => $row, 'nameObject' => $nameObject, 'nameForm' => $nameForm, 'config' => $config); $html .= GeneralUtility::callUserFunction($customControlConfig, $parameters, $this); } $html .= '</div>'; } // Add Drag&Drop functions for sorting to FormEngine::$additionalJS_post if (count($sortableRecordUids) > 1 && $config['appearance']['useSortable']) { $resultArray['additionalJavaScriptPost'][] = 'inline.createDragAndDropSorting("' . $nameObject . '_records' . '");'; } // Publish the uids of the child records in the given order to the browser $html .= '<input type="hidden" name="' . $nameForm . '" value="' . implode(',', $sortableRecordUids) . '" ' . $this->getValidationDataAsDataAttribute(array('type' => 'inline', 'minitems' => $config['minitems'], 'maxitems' => $config['maxitems'])) . ' class="inlineRecord" />'; // Close the wrap for all inline fields (container) $html .= '</div>'; $resultArray['html'] = $html; return $resultArray; }
/** * Entry method * * @return array As defined in initializeResultArray() of AbstractNode */ public function render() { $languageService = $this->getLanguageService(); $this->inlineData = $this->data['inlineData']; /** @var InlineStackProcessor $inlineStackProcessor */ $inlineStackProcessor = GeneralUtility::makeInstance(InlineStackProcessor::class); $this->inlineStackProcessor = $inlineStackProcessor; $inlineStackProcessor->initializeByGivenStructure($this->data['inlineStructure']); $table = $this->data['tableName']; $row = $this->data['databaseRow']; $field = $this->data['fieldName']; $parameterArray = $this->data['parameterArray']; $resultArray = $this->initializeResultArray(); $config = $parameterArray['fieldConf']['config']; $foreign_table = $config['foreign_table']; $language = 0; if (BackendUtility::isTableLocalizable($table)) { $language = (int) $row[$GLOBALS['TCA'][$table]['ctrl']['languageField']]; } // Add the current inline job to the structure stack $newStructureItem = array('table' => $table, 'uid' => $row['uid'], 'field' => $field, 'config' => $config, 'localizationMode' => BackendUtility::getInlineLocalizationMode($table, $config)); // Extract FlexForm parts (if any) from element name, e.g. array('vDEF', 'lDEF', 'FlexField', 'vDEF') if (!empty($parameterArray['itemFormElName'])) { $flexFormParts = $this->extractFlexFormParts($parameterArray['itemFormElName']); if ($flexFormParts !== null) { $newStructureItem['flexform'] = $flexFormParts; } } $inlineStackProcessor->pushStableStructureItem($newStructureItem); // Transport the flexform DS identifier fields to the FormAjaxInlineController if (!empty($newStructureItem['flexform']) && isset($this->data['processedTca']['columns'][$field]['config']['ds']['meta']['dataStructurePointers'])) { $config['flexDataStructurePointers'] = $this->data['processedTca']['columns'][$field]['config']['ds']['meta']['dataStructurePointers']; } // e.g. data[<table>][<uid>][<field>] $nameForm = $inlineStackProcessor->getCurrentStructureFormPrefix(); // e.g. data-<pid>-<table1>-<uid1>-<field1>-<table2>-<uid2>-<field2> $nameObject = $inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($this->data['inlineFirstPid']); $config['inline']['first'] = false; // @todo: This initialization shouldn't be required data provider should take care this is set? if (!is_array($this->data['parameterArray']['fieldConf']['children'])) { $this->data['parameterArray']['fieldConf']['children'] = array(); } $firstChild = reset($this->data['parameterArray']['fieldConf']['children']); if (isset($firstChild['databaseRow']['uid'])) { $config['inline']['first'] = $firstChild['databaseRow']['uid']; } $config['inline']['last'] = false; $lastChild = end($this->data['parameterArray']['fieldConf']['children']); if (isset($lastChild['databaseRow']['uid'])) { $config['inline']['last'] = $lastChild['databaseRow']['uid']; } $top = $inlineStackProcessor->getStructureLevel(0); $this->inlineData['config'][$nameObject] = array('table' => $foreign_table, 'md5' => md5($nameObject)); $this->inlineData['config'][$nameObject . '-' . $foreign_table] = array('min' => $config['minitems'], 'max' => $config['maxitems'], 'sortable' => $config['appearance']['useSortable'], 'top' => array('table' => $top['table'], 'uid' => $top['uid']), 'context' => array('config' => $config, 'hmac' => GeneralUtility::hmac(serialize($config)))); $this->inlineData['nested'][$nameObject] = $this->data['tabAndInlineStack']; $uniqueMax = 0; $uniqueIds = []; if ($config['foreign_unique']) { // Add inlineData['unique'] with JS unique configuration $type = $config['selectorOrUniqueConfiguration']['config']['type'] === 'select' ? 'select' : 'groupdb'; foreach ($parameterArray['fieldConf']['children'] as $child) { // Determine used unique ids, skip not localized records if (!$child['isInlineDefaultLanguageRecordInLocalizedParentContext']) { $value = $child['databaseRow'][$config['foreign_unique']]; // We're assuming there is only one connected value here for both select and group if ($type === 'select') { // A resolved select field is an array - take first value $value = $value['0']; } else { // A group field is still a list with pipe separated uid|tableName $valueParts = GeneralUtility::trimExplode('|', $value); $itemParts = explode('_', $valueParts[0]); $value = array('uid' => array_pop($itemParts), 'table' => implode('_', $itemParts)); } // @todo: This is weird, $value has different structure for group and select fields? $uniqueIds[$child['databaseRow']['uid']] = $value; } } $possibleRecords = $config['selectorOrUniquePossibleRecords']; $possibleRecordsUidToTitle = []; foreach ($possibleRecords as $possibleRecord) { $possibleRecordsUidToTitle[$possibleRecord[1]] = $possibleRecord[0]; } $uniqueMax = $config['appearance']['useCombination'] || empty($possibleRecords) ? -1 : count($possibleRecords); $this->inlineData['unique'][$nameObject . '-' . $foreign_table] = array('max' => $uniqueMax, 'used' => $uniqueIds, 'type' => $type, 'table' => $foreign_table, 'elTable' => $config['selectorOrUniqueConfiguration']['foreignTable'], 'field' => $config['foreign_unique'], 'selector' => $config['selectorOrUniqueConfiguration']['isSelector'] ? $type : false, 'possible' => $possibleRecordsUidToTitle); } $resultArray['inlineData'] = $this->inlineData; // @todo: It might be a good idea to have something like "isLocalizedRecord" or similar set by a data provider $isLocalizedParent = $language > 0 && $row[$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']][0] > 0 && MathUtility::canBeInterpretedAsInteger($row['uid']); $numberOfFullLocalizedChildren = 0; $numberOfNotYetLocalizedChildren = 0; foreach ($this->data['parameterArray']['fieldConf']['children'] as $child) { if (!$child['isInlineDefaultLanguageRecordInLocalizedParentContext']) { $numberOfFullLocalizedChildren++; } if ($isLocalizedParent && $child['isInlineDefaultLanguageRecordInLocalizedParentContext']) { $numberOfNotYetLocalizedChildren++; } } // Render the localization links if needed $localizationLinks = ''; if ($numberOfNotYetLocalizedChildren) { // Add the "Localize all records" link before all child records: if (isset($config['appearance']['showAllLocalizationLink']) && $config['appearance']['showAllLocalizationLink']) { $localizationLinks = ' ' . $this->getLevelInteractionLink('localize', $nameObject . '-' . $foreign_table, $config); } // Add the "Synchronize with default language" link before all child records: if (isset($config['appearance']['showSynchronizationLink']) && $config['appearance']['showSynchronizationLink']) { $localizationLinks .= ' ' . $this->getLevelInteractionLink('synchronize', $nameObject . '-' . $foreign_table, $config); } } // Define how to show the "Create new record" link - if there are more than maxitems, hide it if ($numberOfFullLocalizedChildren >= $config['maxitems'] || $uniqueMax > 0 && $numberOfFullLocalizedChildren >= $uniqueMax) { $config['inline']['inlineNewButtonStyle'] = 'display: none;'; $config['inline']['inlineNewRelationButtonStyle'] = 'display: none;'; $config['inline']['inlineOnlineMediaAddButtonStyle'] = 'display: none;'; } // Render the level links (create new record): $levelLinks = ''; if (!empty($config['appearance']['enabledControls']['new'])) { $levelLinks = $this->getLevelInteractionLink('newRecord', $nameObject . '-' . $foreign_table, $config); } // Wrap all inline fields of a record with a <div> (like a container) $html = '<div class="form-group" id="' . $nameObject . '">'; // Add the level links before all child records: if ($config['appearance']['levelLinksPosition'] === 'both' || $config['appearance']['levelLinksPosition'] === 'top') { $html .= '<div class="form-group t3js-formengine-validation-marker">' . $levelLinks . $localizationLinks . '</div>'; } // If it's required to select from possible child records (reusable children), add a selector box if ($config['foreign_selector'] && $config['appearance']['showPossibleRecordsSelector'] !== false) { if ($config['selectorOrUniqueConfiguration']['config']['type'] === 'select') { $selectorBox = $this->renderPossibleRecordsSelectorTypeSelect($config, $uniqueIds); } else { $selectorBox = $this->renderPossibleRecordsSelectorTypeGroupDB($config); } $html .= $selectorBox . $localizationLinks; } $title = $languageService->sL(trim($parameterArray['fieldConf']['label'])); $html .= '<div class="panel-group panel-hover" data-title="' . htmlspecialchars($title) . '" id="' . $nameObject . '_records">'; $sortableRecordUids = []; foreach ($this->data['parameterArray']['fieldConf']['children'] as $options) { $options['inlineParentUid'] = $row['uid']; $options['inlineFirstPid'] = $this->data['inlineFirstPid']; // @todo: this can be removed if this container no longer sets additional info to $config $options['inlineParentConfig'] = $config; $options['inlineData'] = $this->inlineData; $options['inlineStructure'] = $inlineStackProcessor->getStructure(); $options['inlineExpandCollapseStateArray'] = $this->data['inlineExpandCollapseStateArray']; $options['renderType'] = 'inlineRecordContainer'; $childResult = $this->nodeFactory->create($options)->render(); $html .= $childResult['html']; $childArray['html'] = ''; $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $childResult); if (!$options['isInlineDefaultLanguageRecordInLocalizedParentContext']) { // Don't add record to list of "valid" uids if it is only the default // language record of a not yet localized child $sortableRecordUids[] = $options['databaseRow']['uid']; } } $html .= '</div>'; // Add the level links after all child records: if ($config['appearance']['levelLinksPosition'] === 'both' || $config['appearance']['levelLinksPosition'] === 'bottom') { $html .= $levelLinks . $localizationLinks; } if (is_array($config['customControls'])) { $html .= '<div id="' . $nameObject . '_customControls">'; foreach ($config['customControls'] as $customControlConfig) { $parameters = array('table' => $table, 'field' => $field, 'row' => $row, 'nameObject' => $nameObject, 'nameForm' => $nameForm, 'config' => $config); $html .= GeneralUtility::callUserFunction($customControlConfig, $parameters, $this); } $html .= '</div>'; } // Add Drag&Drop functions for sorting to FormEngine::$additionalJS_post if (count($sortableRecordUids) > 1 && $config['appearance']['useSortable']) { $resultArray['additionalJavaScriptPost'][] = 'inline.createDragAndDropSorting("' . $nameObject . '_records' . '");'; } $resultArray['requireJsModules'] = array_merge($resultArray['requireJsModules'], $this->requireJsModules); // Publish the uids of the child records in the given order to the browser $html .= '<input type="hidden" name="' . $nameForm . '" value="' . implode(',', $sortableRecordUids) . '" ' . $this->getValidationDataAsDataAttribute(array('type' => 'inline', 'minitems' => $config['minitems'], 'maxitems' => $config['maxitems'])) . ' class="inlineRecord" />'; // Close the wrap for all inline fields (container) $html .= '</div>'; $resultArray['html'] = $html; return $resultArray; }
/** * Generation of TCEform elements of the type "inline" * This will render inline-relational-record sets. Relations. * * @param string $table The table name of the record * @param string $field The field name which this element is supposed to edit * @param array $row The record data array where the value(s) for the field can be found * @param array $PA An array with additional configuration options. * @return string The HTML code for the TCEform field * @todo Define visibility */ public function getSingleField_typeInline($table, $field, $row, &$PA) { // Check the TCA configuration - if FALSE is returned, something was wrong if ($this->checkConfiguration($PA['fieldConf']['config']) === FALSE) { return FALSE; } $item = ''; $levelLinks = ''; $localizationLinks = ''; // Count the number of processed inline elements $this->inlineCount++; // Init: $config = $PA['fieldConf']['config']; $foreign_table = $config['foreign_table']; $language = 0; if (BackendUtility::isTableLocalizable($table)) { $language = (int) $row[$GLOBALS['TCA'][$table]['ctrl']['languageField']]; } $minitems = MathUtility::forceIntegerInRange($config['minitems'], 0); $maxitems = MathUtility::forceIntegerInRange($config['maxitems'], 0); if (!$maxitems) { $maxitems = 100000; } // Register the required number of elements: $this->fObj->requiredElements[$PA['itemFormElName']] = array($minitems, $maxitems, 'imgName' => $table . '_' . $row['uid'] . '_' . $field); // Remember the page id (pid of record) where inline editing started first // We need that pid for ajax calls, so that they would know where the action takes place on the page structure if (!isset($this->inlineFirstPid)) { // If this record is not new, try to fetch the inlineView states // @TODO: Add checking/cleaning for unused tables, records, etc. to save space in uc-field if (MathUtility::canBeInterpretedAsInteger($row['uid'])) { $inlineView = unserialize($GLOBALS['BE_USER']->uc['inlineView']); $this->inlineView = $inlineView[$table][$row['uid']]; } // If the parent is a page, use the uid(!) of the (new?) page as pid for the child records: if ($table == 'pages') { $liveVersionId = BackendUtility::getLiveVersionIdOfRecord('pages', $row['uid']); $this->inlineFirstPid = is_null($liveVersionId) ? $row['uid'] : $liveVersionId; } elseif ($row['pid'] < 0) { $prevRec = BackendUtility::getRecord($table, abs($row['pid'])); $this->inlineFirstPid = $prevRec['pid']; } else { $this->inlineFirstPid = $row['pid']; } } // Add the current inline job to the structure stack $this->pushStructure($table, $row['uid'], $field, $config, $PA); // e.g. data[<table>][<uid>][<field>] $nameForm = $this->inlineNames['form']; // e.g. data-<pid>-<table1>-<uid1>-<field1>-<table2>-<uid2>-<field2> $nameObject = $this->inlineNames['object']; // Get the records related to this inline record $relatedRecords = $this->getRelatedRecords($table, $field, $row, $PA, $config); // Set the first and last record to the config array $relatedRecordsUids = array_keys($relatedRecords['records']); $config['inline']['first'] = reset($relatedRecordsUids); $config['inline']['last'] = end($relatedRecordsUids); // Tell the browser what we have (using JSON later): $top = $this->getStructureLevel(0); $this->inlineData['config'][$nameObject] = array('table' => $foreign_table, 'md5' => md5($nameObject)); $this->inlineData['config'][$nameObject . self::Structure_Separator . $foreign_table] = array('min' => $minitems, 'max' => $maxitems, 'sortable' => $config['appearance']['useSortable'], 'top' => array('table' => $top['table'], 'uid' => $top['uid']), 'context' => array('config' => $config, 'hmac' => GeneralUtility::hmac(serialize($config)))); // Set a hint for nested IRRE and tab elements: $this->inlineData['nested'][$nameObject] = $this->fObj->getDynNestedStack(FALSE, $this->isAjaxCall); // If relations are required to be unique, get the uids that have already been used on the foreign side of the relation if ($config['foreign_unique']) { // If uniqueness *and* selector are set, they should point to the same field - so, get the configuration of one: $selConfig = $this->getPossibleRecordsSelectorConfig($config, $config['foreign_unique']); // Get the used unique ids: $uniqueIds = $this->getUniqueIds($relatedRecords['records'], $config, $selConfig['type'] == 'groupdb'); $possibleRecords = $this->getPossibleRecords($table, $field, $row, $config, 'foreign_unique'); $uniqueMax = $config['appearance']['useCombination'] || $possibleRecords === FALSE ? -1 : count($possibleRecords); $this->inlineData['unique'][$nameObject . self::Structure_Separator . $foreign_table] = array('max' => $uniqueMax, 'used' => $uniqueIds, 'type' => $selConfig['type'], 'table' => $config['foreign_table'], 'elTable' => $selConfig['table'], 'field' => $config['foreign_unique'], 'selector' => $selConfig['selector'], 'possible' => $this->getPossibleRecordsFlat($possibleRecords)); } // Render the localization links if ($language > 0 && $row[$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']] > 0 && MathUtility::canBeInterpretedAsInteger($row['uid'])) { // Add the "Localize all records" link before all child records: if (isset($config['appearance']['showAllLocalizationLink']) && $config['appearance']['showAllLocalizationLink']) { $localizationLinks .= ' ' . $this->getLevelInteractionLink('localize', $nameObject . self::Structure_Separator . $foreign_table, $config); } // Add the "Synchronize with default language" link before all child records: if (isset($config['appearance']['showSynchronizationLink']) && $config['appearance']['showSynchronizationLink']) { $localizationLinks .= ' ' . $this->getLevelInteractionLink('synchronize', $nameObject . self::Structure_Separator . $foreign_table, $config); } } // If it's required to select from possible child records (reusable children), add a selector box if ($config['foreign_selector'] && $config['appearance']['showPossibleRecordsSelector'] !== FALSE) { // If not already set by the foreign_unique, set the possibleRecords here and the uniqueIds to an empty array if (!$config['foreign_unique']) { $possibleRecords = $this->getPossibleRecords($table, $field, $row, $config); $uniqueIds = array(); } $selectorBox = $this->renderPossibleRecordsSelector($possibleRecords, $config, $uniqueIds); $item .= $selectorBox . $localizationLinks; // Render the level links (create new record): } else { $levelLinks = $this->getLevelInteractionLink('newRecord', $nameObject . self::Structure_Separator . $foreign_table, $config); } // Wrap all inline fields of a record with a <div> (like a container) $item .= '<div id="' . $nameObject . '">'; // Define how to show the "Create new record" link - if there are more than maxitems, hide it if ($relatedRecords['count'] >= $maxitems || $uniqueMax > 0 && $relatedRecords['count'] >= $uniqueMax) { $config['inline']['inlineNewButtonStyle'] = 'display: none;'; } // Add the level links before all child records: if (in_array($config['appearance']['levelLinksPosition'], array('both', 'top'))) { $item .= $levelLinks . $localizationLinks; } $item .= '<div id="' . $nameObject . '_records">'; $relationList = array(); if (count($relatedRecords['records'])) { foreach ($relatedRecords['records'] as $rec) { $item .= $this->renderForeignRecord($row['uid'], $rec, $config); if (!isset($rec['__virtual']) || !$rec['__virtual']) { $relationList[] = $rec['uid']; } } } $item .= '</div>'; // Add the level links after all child records: if (in_array($config['appearance']['levelLinksPosition'], array('both', 'bottom'))) { $item .= $levelLinks . $localizationLinks; } if (is_array($config['customControls'])) { $item .= '<div id="' . $nameObject . '_customControls">'; foreach ($config['customControls'] as $customControlConfig) { $parameters = array('table' => $table, 'field' => $field, 'row' => $row, 'nameObject' => $nameObject, 'nameForm' => $nameForm, 'config' => $config); $item .= GeneralUtility::callUserFunction($customControlConfig, $parameters, $this); } $item .= '</div>'; } // Add Drag&Drop functions for sorting to TCEforms::$additionalJS_post if (count($relationList) > 1 && $config['appearance']['useSortable']) { $this->addJavaScriptSortable($nameObject . '_records'); } // Publish the uids of the child records in the given order to the browser $item .= '<input type="hidden" name="' . $nameForm . '" value="' . implode(',', $relationList) . '" class="inlineRecord" />'; // Close the wrap for all inline fields (container) $item .= '</div>'; // On finishing this section, remove the last item from the structure stack $this->popStructure(); // If this was the first call to the inline type, restore the values if (!$this->getStructureDepth()) { unset($this->inlineFirstPid); } return $item; }
/** * Fetches the localization for a given record. * * @FIXME: This method is a clone of BackendUtility::getRecordLocalization, using origUid instead of transOrigPointerField * * @param string $table Table name present in $GLOBALS['TCA'] * @param int $uid The uid of the record * @param int $language The uid of the language record in sys_language * @param string $andWhereClause Optional additional WHERE clause (default: '') * @return mixed Multidimensional array with selected records; if none exist, FALSE is returned */ public function getRecordLocalization($table, $uid, $language, $andWhereClause = '') { $recordLocalization = false; // Check if translations are stored in other table if (isset($GLOBALS['TCA'][$table]['ctrl']['transForeignTable'])) { $table = $GLOBALS['TCA'][$table]['ctrl']['transForeignTable']; } if (BackendUtility::isTableLocalizable($table)) { $tcaCtrl = $GLOBALS['TCA'][$table]['ctrl']; if (isset($tcaCtrl['origUid'])) { $recordLocalization = BackendUtility::getRecordsByField($table, $tcaCtrl['origUid'], $uid, 'AND ' . $tcaCtrl['languageField'] . '=' . (int) $language . ($andWhereClause ? ' ' . $andWhereClause : ''), '', '', '1'); } } return $recordLocalization; }