/** * Gets the record uid of the live default record. If already * pointing to the live record, the submitted record uid is returned. * * @param string $tableName * @param int $id * @return int */ protected function getLiveDefaultId($tableName, $id) { $liveDefaultId = BackendUtility::getLiveVersionIdOfRecord($tableName, $id); if ($liveDefaultId === null) { $liveDefaultId = $id; } return (int) $liveDefaultId; }
/** * Return parent page array * * @param array $page * @return array|boolean */ protected function getPageParent($page) { // try to get the original page $live = BackendUtility::getLiveVersionIdOfRecord('pages', intval($page['uid'])); $live = NULL === $live ? $page : $live; return $this->getPage($live['pid']); }
/** * 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; }
/** * Performs remapping of old UID values to NEW uid values for an inline field. * * @param array $conf TCA field config * @param string $value Field value * @param int $uid The uid of the ORIGINAL record * @param string $table Table name * @return void */ public function remapListedDBRecords_procInline($conf, $value, $uid, $table) { $theUidToUpdate = $this->copyMappingArray_merged[$table][$uid]; if ($conf['foreign_table']) { $inlineType = $this->getInlineFieldType($conf); if ($inlineType == 'mm') { $this->remapListedDBRecords_procDBRefs($conf, $value, $theUidToUpdate, $table); } elseif ($inlineType !== false) { /** @var $dbAnalysis RelationHandler */ $dbAnalysis = $this->createRelationHandlerInstance(); $dbAnalysis->start($value, $conf['foreign_table'], '', 0, $table, $conf); // Keep original (live) item array and update values for specific versioned records $originalItemArray = $dbAnalysis->itemArray; foreach ($dbAnalysis->itemArray as &$item) { $versionedId = $this->getAutoVersionId($item['table'], $item['id']); if (!empty($versionedId)) { $item['id'] = $versionedId; } } // Update child records if using pointer fields ('foreign_field'): if ($inlineType == 'field') { $dbAnalysis->writeForeignField($conf, $uid, $theUidToUpdate); } $thePidToUpdate = null; // If the current field is set on a page record, update the pid of related child records: if ($table == 'pages') { $thePidToUpdate = $theUidToUpdate; } elseif (isset($this->registerDBPids[$table][$uid])) { $thePidToUpdate = $this->registerDBPids[$table][$uid]; $thePidToUpdate = $this->copyMappingArray_merged['pages'][$thePidToUpdate]; } // Update child records if change to pid is required (only if the current record is not on a workspace): if ($thePidToUpdate) { $updateValues = array('pid' => $thePidToUpdate); foreach ($originalItemArray as $v) { if ($v['id'] && $v['table'] && is_null(BackendUtility::getLiveVersionIdOfRecord($v['table'], $v['id']))) { $this->databaseConnection->exec_UPDATEquery($v['table'], 'uid=' . (int) $v['id'], $updateValues); } } } } } }
/** * 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; }
/** * Performs remapping of old UID values to NEW uid values for a inline field. * * @param array $conf TCA field config * @param string $value Field value * @param integer $uid The uid of the ORIGINAL record * @param string $table Table name * @return void * @todo Define visibility */ public function remapListedDBRecords_procInline($conf, $value, $uid, $table) { $theUidToUpdate = $this->copyMappingArray_merged[$table][$uid]; if ($conf['foreign_table']) { $inlineType = $this->getInlineFieldType($conf); if ($inlineType == 'mm') { $this->remapListedDBRecords_procDBRefs($conf, $value, $theUidToUpdate, $table); } elseif ($inlineType !== FALSE) { /** @var $dbAnalysis \TYPO3\CMS\Core\Database\RelationHandler */ $dbAnalysis = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Database\\RelationHandler'); $dbAnalysis->start($value, $conf['foreign_table'], '', 0, $table, $conf); // Update child records if using pointer fields ('foreign_field'): if ($inlineType == 'field') { $dbAnalysis->writeForeignField($conf, $uid, $theUidToUpdate); } // If the current field is set on a page record, update the pid of related child records: if ($table == 'pages') { $thePidToUpdate = $theUidToUpdate; } elseif (isset($this->registerDBPids[$table][$uid])) { $thePidToUpdate = $this->registerDBPids[$table][$uid]; $thePidToUpdate = $this->copyMappingArray_merged['pages'][$thePidToUpdate]; } // Update child records if change to pid is required (only if the current record is not on a workspace): if ($thePidToUpdate) { $updateValues = array('pid' => $thePidToUpdate); foreach ($dbAnalysis->itemArray as $v) { if ($v['id'] && $v['table'] && is_null(\TYPO3\CMS\Backend\Utility\BackendUtility::getLiveVersionIdOfRecord($v['table'], $v['id']))) { $GLOBALS['TYPO3_DB']->exec_UPDATEquery($v['table'], 'uid=' . intval($v['id']), $updateValues); } } } } } }
/** * Generates a view link for a page. * * @static * @param string $table Table to be used * @param integer $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) { $viewUrl = ''; if ($table == 'pages') { $viewUrl = BackendUtility::viewOnClick(BackendUtility::getLiveVersionIdOfRecord('pages', $uid)); } elseif ($table === 'pages_language_overlay' || $table === 'tt_content') { 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'); } $previewPageId = empty($movePlaceholder['pid']) ? $liveRecord['pid'] : $movePlaceholder['pid']; $additionalParameters = '&tx_workspaces_web_workspacesworkspaces[previewWS]=' . $versionRecord['t3ver_wsid']; $languageField = $GLOBALS['TCA'][$table]['ctrl']['languageField']; if ($versionRecord[$languageField] > 0) { $additionalParameters .= '&L=' . $versionRecord[$languageField]; } $viewUrl = BackendUtility::viewOnClick($previewPageId, '', '', '', '', $additionalParameters); } else { if (isset($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 = \TYPO3\CMS\Core\Utility\GeneralUtility::callUserFunction($_funcRef, $_params, $null); } } return $viewUrl; }
/** * The "entry" pid for inline records. Nested inline records can potentially hang around on different * pid's, but the entry pid is needed for AJAX calls, so that they would know where the action takes place on the page structure. * * @return integer */ protected function getInlineFirstPid() { $table = $this->table; $row = $this->databaseRow; // 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']); $pid = is_null($liveVersionId) ? $row['uid'] : $liveVersionId; } elseif ($row['pid'] < 0) { $prevRec = BackendUtility::getRecord($table, abs($row['pid'])); $pid = $prevRec['pid']; } else { $pid = $row['pid']; } return $pid; }