/** * @param string $headerContent * @param string $itemContent * @param array $row * @param boolean $drawItem * @return NULL */ public function renderPreview(&$headerContent, &$itemContent, array &$row, &$drawItem) { $fieldName = NULL; // every provider for tt_content will be asked to get a preview if ('shortcut' === $row['CType'] && FALSE === strpos($row['records'], ',')) { $itemContent = $this->createShortcutIcon($row) . $itemContent; } else { $itemContent = '<a name="c' . $row['uid'] . '"></a>' . $itemContent; } $providers = $this->configurationService->resolveConfigurationProviders('tt_content', $fieldName, $row); foreach ($providers as $provider) { /** @var ProviderInterface $provider */ list($previewHeader, $previewContent, $continueDrawing) = $provider->getPreview($row); if (FALSE === empty($previewHeader)) { $headerContent = $previewHeader . (FALSE === empty($headerContent) ? ': ' . $headerContent : ''); $drawItem = FALSE; } if (FALSE === empty($previewContent)) { $itemContent .= $previewContent; $drawItem = FALSE; } if (FALSE === $continueDrawing) { break; } } $this->attachAssets(); return NULL; }
/** * Render method * @param string $table * @param string $field * @param integer $uid * @param array $record * @param string $as * @return array * @throws Exception */ public function render($table, $field, $uid = NULL, $record = NULL, $as = NULL) { if (NULL === $record && NULL === $as) { $record = $this->renderChildren(); } if (NULL === $uid && NULL !== $record && TRUE === isset($record['uid'])) { $uid = $record['uid']; } if (TRUE === isset($GLOBALS['TCA'][$table]) && TRUE === isset($GLOBALS['TCA'][$table]['columns'][$field])) { if (NULL === $record) { $record = $this->recordService->getSingle($table, 'uid,' . $field, $uid); } if (NULL === $record) { throw new Exception(sprintf('Either table "%s", field "%s" or record with uid %d do not exist and you did not manually ' . 'provide the "record" attribute.', $table, $field, $uid), 1358679983); } $providers = $this->configurationService->resolveConfigurationProviders($table, $field, $record); $dataArray = $this->readDataArrayFromProvidersOrUsingDefaultMethod($providers, $record, $field); } else { throw new Exception('Invalid table:field "' . $table . ':' . $field . '" - does not exist in TYPO3 TCA.', 1387049117); } if (NULL !== $as) { if ($this->templateVariableContainer->exists($as)) { $backupVariable = $this->templateVariableContainer->get($as); $this->templateVariableContainer->remove($as); } $this->templateVariableContainer->add($as, $dataArray); $content = $this->renderChildren(); $this->templateVariableContainer->remove($as); if (TRUE === isset($backupVariable)) { $this->templateVariableContainer->add($as, $backupVariable); } return $content; } return $dataArray; }
/** * Hook for generating dynamic FlexForm source code * * @param array $dataStructArray * @param array $conf * @param array $row * @param string $table * @param string $fieldName * @return void */ public function getFlexFormDS_postProcessDS(&$dataStructArray, $conf, &$row, $table, $fieldName) { if (empty($fieldName) === TRUE) { // Cast NULL if an empty but not-NULL field name was passed. This has significance to the Flux internals in // respect to which ConfigurationProvider(s) are returned. $fieldName = NULL; } $providers = $this->configurationService->resolveConfigurationProviders($table, $fieldName, $row); foreach ($providers as $provider) { $provider->postProcessDataStructure($row, $dataStructArray, $conf); } }
/** * @param integer $pageUid * @param integer $columnPosition * @param integer $relativeUid * @return array */ protected function getWhiteAndBlackListsFromPageAndContentColumn($pageUid, $columnPosition, $relativeUid) { $whitelist = array(); $blacklist = array(); // if a Provider is registered for the "pages" table, try to get a Grid from it. If the Grid // returned contains a Column which matches the desired colPos value, attempt to read a list // of allowed/denied content element types from it. $pageRecord = (array) $this->recordService->getSingle('pages', '*', $pageUid); $pageProviders = $this->configurationService->resolveConfigurationProviders('pages', NULL, $pageRecord); $this->appendToWhiteAndBlacklistFromProviders($pageProviders, $pageRecord, $whitelist, $blacklist, $columnPosition); // Detect what was clicked in order to create the new content element; decide restrictions // based on this. Returned parent UID and area name is either non-zero and string, or zero // and NULL when record is NOT inserted as child. list($parentRecordUid, $fluxAreaName) = $this->getAreaNameAndParentFromRelativeRecordOrDefaults($relativeUid); // if these variables now indicate that we are inserting content elements into a Flux-enabled content // area inside another content element, attempt to read allowed/denied content types from the // Grid returned by the Provider that applies to the parent element's type and configuration // (admitted, that's quite a mouthful - but it's not that different from reading the values from // a page template like above; it's the same principle). if (0 < $parentRecordUid && FALSE === empty($fluxAreaName)) { $parentRecord = (array) $this->recordService->getSingle('tt_content', '*', $parentRecordUid); $contentProviders = $this->configurationService->resolveConfigurationProviders('tt_content', NULL, $parentRecord); $this->appendToWhiteAndBlacklistFromProviders($contentProviders, $parentRecord, $whitelist, $blacklist, NULL, $fluxAreaName); } // White/blacklist filtering. If whitelist contains elements, filter the list // of possible types by whitelist first. Then apply the blacklist, removing // any element types recorded herein. $whitelist = array_unique($whitelist); $blacklist = array_unique($blacklist); return array($whitelist, $blacklist); }
/** * Render method * @param string $table * @param string $field * @param integer $uid * @param array $record * @param string $as * @return array * @throws Exception */ public function render($table, $field, $uid = NULL, $record = NULL, $as = NULL) { if (NULL === $uid && NULL !== $record && TRUE === isset($record['uid'])) { $uid = $record['uid']; } if (TRUE === isset(self::$dataCache[$uid . $table . $field])) { $dataArray = self::$dataCache[$uid . $table . $field]; } elseif (TRUE === isset($GLOBALS['TCA'][$table]) && TRUE === isset($GLOBALS['TCA'][$table]['columns'][$field])) { if (NULL === $record) { $record = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('uid,' . $field, $table, sprintf('uid=%d', $uid)); } if (FALSE === $record) { throw new Exception(sprintf('Either table "%s", field "%s" or record with uid %d do not exist and you did not manually ' . 'provide the "row" attribute.', $table, $field, $uid), 1358679983); } $providers = $this->configurationService->resolveConfigurationProviders($table, $field, $record); if (0 === count($providers)) { $dataArray = $this->configurationService->convertFlexFormContentToArray($record[$field]); } else { $dataArray = array(); foreach ($providers as $provider) { $data = (array) $provider->getFlexFormValues($record); $dataArray = RecursiveArrayUtility::merge($dataArray, $data); } } self::$dataCache[$uid . $table . $field] = $dataArray; } else { throw new Exception('Invalid table:field "' . $table . ':' . $field . '" - does not exist in TYPO3 TCA.', 1387049117); } if (NULL !== $as) { if ($this->templateVariableContainer->exists($as)) { $backupVariable = $this->templateVariableContainer->get($as); $this->templateVariableContainer->remove($as); } $this->templateVariableContainer->add($as, $dataArray); $content = $this->renderChildren(); $this->templateVariableContainer->remove($as); if (TRUE === isset($backupVariable)) { $this->templateVariableContainer->add($as, $backupVariable); } return $content; } return $dataArray; }
/** * Perform various cleanup operations upon clearing cache * * @param string $command * @return void */ public function clearCacheCommand($command) { if (TRUE === self::$cachesCleared) { return; } $tables = array_keys($GLOBALS['TCA']); foreach ($tables as $table) { $providers = $this->configurationService->resolveConfigurationProviders($table, NULL); foreach ($providers as $provider) { /** @var $provider ProviderInterface */ $provider->clearCacheCommand($command); } } self::$cachesCleared = TRUE; }
/** * Hook for generating dynamic FlexForm source code. * * NOTE: patches data structure resolving in a way that solves * a regression in the TYPO3 core when dealing with IRRE AJAX * requests (in which the database record is no longer fetched * by the controller). This patches not only data structure * resolving for Flux data structures but indeed any data * structure built using hooks or involving user functions which * require the entire record (but when using hooks, supports * only extensions which are loaded AFTER or depend on Flux). * * @param array $dataStructArray * @param array $conf * @param array $row * @param string $table * @param string $fieldName * @return void */ public function getFlexFormDS_postProcessDS(&$dataStructArray, $conf, &$row, $table, $fieldName) { if (empty($fieldName) === TRUE) { // Cast NULL if an empty but not-NULL field name was passed. This has significance to the Flux internals in // respect to which ConfigurationProvider(s) are returned. $fieldName = NULL; } if (!empty($fieldName) && !isset($row[$fieldName])) { // Patch required (possibly temporary). Due to changes in TYPO3 in the new FormEngine we must fetch the // database record at this point when the record is incomplete, which happens when attempting to render // IRRE records. The reason is that the controller that creates the HTML does not fetch the record any // more - and that the AJAX request contains only the UID. So, we fetch the record here to ensure it // contains the necessary fields. DOES NOT WORK FOR NEW RECORDS - SEE COMMENTS BELOW. $row = $this->recordService->getSingle($table, '*', $row['uid']); } $defaultDataSourceCacheIdentifier = $table . '_' . $fieldName . '_' . sha1(serialize($conf)); if (!$row) { // In the case that the database record cannot be fetched we are dealing with a new or otherwise deleted // or unidentifiable record. This happens primarily when AJAX requests are made to render IRRE records // without the parent record having been saved first. To accommodate this case we have to be slightly // creative and store a "default" data source definition which is identified based on a checksum of the // configuration provided. Whenever we are then unable to fetch a record, we can at least attempt to // locate a default data source in previously cached content. NB: we enforce a VERY high cache lifetime // and continually refresh it every time it is possible to render a new DS that can serve as default. $dataStructArray = (array) $this->cache->get($defaultDataSourceCacheIdentifier); } else { if (FALSE === is_array($dataStructArray)) { $dataStructArray = array(); } $providers = $this->configurationService->resolveConfigurationProviders($table, $fieldName, $row); foreach ($providers as $provider) { $provider->postProcessDataStructure($row, $dataStructArray, $conf); } if (empty($dataStructArray)) { $dataStructArray = array('ROOT' => array('el' => array())); } $evaluationParameters = array(); $this->cache->set($defaultDataSourceCacheIdentifier, $this->recursivelyEvaluateClosures($dataStructArray, $evaluationParameters), array(), time() + 31536000); } // Trigger TCEforms dimension patching only if required by TYPO3 version according to CompatibilityRegistry. if (CompatibilityRegistry::get('FluidTYPO3\\Flux\\Backend\\DynamicFlexForm::NEEDS_TCEFORMS_WRAPPER')) { $dataStructArray = $this->patchTceformsWrapper($dataStructArray); } }
/** * @param integer $relativeRecordUid * @param string $fluxAreaName * @param array $whitelist * @param array $blacklist * @return array */ protected function readWhitelistAndBlacklistFromColumn($relativeRecordUid, $fluxAreaName, $whitelist, $blacklist) { $relativeRecord = $this->recordService->getSingle('tt_content', '*', (int) $relativeRecordUid); $contentProviders = $this->configurationService->resolveConfigurationProviders('tt_content', NULL, $relativeRecord); foreach ($contentProviders as $contentProvider) { $grid = $contentProvider->getGrid($relativeRecord); if (NULL === $grid) { continue; } foreach ($grid->getRows() as $row) { foreach ($row->getColumns() as $column) { if ($column->getName() === $fluxAreaName) { list($whitelist, $blacklist) = $this->appendToWhiteAndBlacklistFromComponent($column, $whitelist, $blacklist); } } } } return array($whitelist, $blacklist); }
/** * @param string $headerContent * @param string $itemContent * @param array $row * @param boolean $drawItem * @return NULL */ public function renderPreview(&$headerContent, &$itemContent, array &$row, &$drawItem) { $children = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid,tx_flux_column', 'tt_content', "tx_flux_parent = '" . $row['uid'] . "'"); $checksum = sha1(json_encode($row)) . '-' . sha1(json_encode($children)); $cacheFilePathAndfilenameHeader = GeneralUtility::getFileAbsFileName('typo3temp/flux-preview-' . $checksum . '-header.tmp'); $cacheFilePathAndfilenameContent = GeneralUtility::getFileAbsFileName('typo3temp/flux-preview-' . $checksum . '-content.tmp'); $drawItem = TRUE; if (TRUE === file_exists($cacheFilePathAndfilenameHeader) && TRUE === file_exists($cacheFilePathAndfilenameContent)) { $itemContent = file_get_contents($cacheFilePathAndfilenameContent); $headerContent = file_get_contents($cacheFilePathAndfilenameHeader); $drawItem = FALSE; $this->attachStyle(); return NULL; } $fieldName = NULL; // every provider for tt_content will be asked to get a preview if ('shortcut' === $row['CType'] && FALSE === strpos($row['records'], ',')) { $itemContent = $this->createShortcutIcon($row) . $itemContent; } else { $itemContent = '<a name="c' . $row['uid'] . '"></a>' . $itemContent; } $providers = $this->configurationService->resolveConfigurationProviders('tt_content', $fieldName, $row); foreach ($providers as $provider) { /** @var ProviderInterface $provider */ list($previewHeader, $previewContent, $continueDrawing) = $provider->getPreview($row); if (FALSE === empty($previewHeader)) { $headerContent = $previewHeader . (FALSE === empty($headerContent) ? ': ' . $headerContent : ''); $drawItem = FALSE; GeneralUtility::writeFile($cacheFilePathAndfilenameHeader, $headerContent); } if (FALSE === empty($previewContent)) { $itemContent .= $previewContent; $drawItem = FALSE; GeneralUtility::writeFile($cacheFilePathAndfilenameContent, $itemContent); } if (FALSE === $continueDrawing) { break; } } $this->attachStyle(); return NULL; }
/** * @param integer $uid * @return array */ public function getContentAreasDefinedInContentElement($uid) { $uid = (int) $uid; $record = $this->recordService->getSingle('tt_content', '*', $uid); /** @var $providers ProviderInterface[] */ $providers = $this->fluxService->resolveConfigurationProviders('tt_content', NULL, $record); $columns = array(); foreach ($providers as $provider) { $grid = $provider->getGrid($record); if (TRUE === empty($grid)) { continue; } $gridConfiguration = $grid->build(); foreach ($gridConfiguration['rows'] as $row) { foreach ($row['columns'] as $column) { array_push($columns, array($column['label'] . ' (' . $column['name'] . ')', $column['name'])); } } } return array_unique($columns, SORT_REGULAR); }
/** * @param integer $uid * @return array */ public function getContentAreasDefinedInContentElement($uid) { $uid = (int) $uid; $record = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('*', 'tt_content', "uid = '" . $uid . "'"); /** @var $providers ProviderInterface[] */ $providers = $this->fluxService->resolveConfigurationProviders('tt_content', NULL, $record); $columns = array(); foreach ($providers as $provider) { $grid = $provider->getGrid($record); if (TRUE === empty($grid)) { continue; } $gridConfiguration = $grid->build(); foreach ($gridConfiguration['rows'] as $row) { foreach ($row['columns'] as $column) { foreach ($column['areas'] as $area) { array_push($columns, array($area['label'] . ' (' . $area['name'] . ')', $area['name'])); } } } } return array_unique($columns); }
/** * @param array $items * @param \TYPO3\CMS\Backend\Controller\ContentElement\NewContentElementController * @return void */ public function manipulateWizardItems(&$items, &$parentObject) { $whitelist = array(); $blacklist = array(); // if a Provider is registered for the "pages" table, try to get a Grid from it. If the Grid // returned contains a Column which matches the desired colPos value, attempt to read a list // of allowed/denied content element types from it. $pageRecord = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('*', 'pages', "uid = '" . $parentObject->id . "'"); $pageProviders = $this->configurationService->resolveConfigurationProviders('pages', NULL, $pageRecord); foreach ($pageProviders as $pageProvider) { $grid = $pageProvider->getGrid($pageRecord); if (NULL === $grid) { continue; } foreach ($grid->getRows() as $row) { foreach ($row->getColumns() as $column) { if ($column->getColumnPosition() === $parentObject->colPos) { list($whitelist, $blacklist) = $this->appendToWhiteAndBlacklistFromComponent($column, $whitelist, $blacklist); } } } } // Detect what was clicked in order to create the new content element; decide restrictions // based on this. $defaultValues = GeneralUtility::_GET('defVals'); if (0 > $parentObject->uid_pid) { // pasting after another element means we should try to resolve the Flux content relation // from that element instead of GET parameters (clicked: "create new" icon after other element) $relativeRecordUid = abs($parentObject->uid_pid); $relativeRecord = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('*', 'tt_content', "uid = '" . $relativeRecordUid . "'"); $fluxAreaName = $relativeRecord['tx_flux_column']; } elseif (TRUE === isset($defaultValues['tt_content']['tx_flux_column'])) { // attempt to read the target Flux content area from GET parameters (clicked: "create new" icon // in top of nested Flux content area $fluxAreaName = $defaultValues['tt_content']['tx_flux_column']; $relativeRecordUid = $defaultValues['tt_content']['tx_flux_parent']; } // if these variables now indicate that we are inserting content elements into a Flux-enabled content // area inside another content element, attempt to read allowed/denied content types from the // Grid returned by the Provider that applies to the parent element's type and configuration // (admitted, that's quite a mouthful - but it's not that different from reading the values from // a page template like above; it's the same principle). if (0 < $relativeRecordUid && FALSE === empty($fluxAreaName)) { $relativeRecord = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('*', 'tt_content', "uid = '" . $relativeRecordUid . "'"); $contentProviders = $this->configurationService->resolveConfigurationProviders('tt_content', NULL, $relativeRecord); foreach ($contentProviders as $contentProvider) { $grid = $contentProvider->getGrid($relativeRecord); if (NULL === $grid) { continue; } foreach ($grid->getRows() as $row) { foreach ($row->getColumns() as $column) { foreach ($column->getAreas() as $area) { if ($area->getName() === $fluxAreaName) { list($whitelist, $blacklist) = $this->appendToWhiteAndBlacklistFromComponent($area, $whitelist, $blacklist); } } } } } } // White/blacklist filtering. If whitelist contains elements, filter the list // of possible types by whitelist first. Then apply the blacklist, removing // any element types recorded herein. $whitelist = array_unique($whitelist); $blacklist = array_unique($blacklist); if (0 < count($whitelist)) { foreach ($items as $name => $item) { if (FALSE !== strpos($name, '_') && FALSE === in_array($item['tt_content_defValues']['CType'], $whitelist)) { unset($items[$name]); } } } if (0 < count($blacklist)) { foreach ($blacklist as $contentElementType) { foreach ($items as $name => $item) { if ($item['tt_content_defValues']['CType'] === $contentElementType) { unset($items[$name]); } } } } // Finally, loop through the items list and clean up any tabs with zero element types inside. $preserveHeaders = array(); foreach ($items as $name => $item) { if (FALSE !== strpos($name, '_')) { array_push($preserveHeaders, reset(explode('_', $name))); } } foreach ($items as $name => $item) { if (FALSE === strpos($name, '_') && FALSE === in_array($name, $preserveHeaders)) { unset($items[$name]); } } }