/** * Post-processing to define which control items to show. Possibly own icons can be added here. * * @param string $parentUid The uid of the parent (embedding) record (uid or NEW...) * @param string $foreignTable The table (foreign_table) we create control-icons for * @param array $childRecord The current record of that foreign_table * @param array $childConfig TCA configuration of the current field of the child record * @param boolean $isVirtual Defines whether the current records is only virtually shown and not physically part of the parent record * @param array $controlItems (reference) Associative array with the currently available control items * * @return void */ public function renderForeignRecordHeaderControl_postProcess($parentUid, $foreignTable, array $childRecord, array $childConfig, $isVirtual, array &$controlItems) { if ($foreignTable != 'sys_file_reference') { return; } if (!GeneralUtility::isFirstPartOfStr($childRecord['uid_local'], 'sys_file_')) { return; } $parts = BackendUtility::splitTable_Uid($childRecord['uid_local']); if (!isset($parts[1])) { return; } $table = $childRecord['tablenames']; $uid = $parentUid; $arguments = GeneralUtility::_GET(); if ($this->isValidRecord($table, $uid) && isset($arguments['edit'])) { $returnUrl = ['edit' => $arguments['edit'], 'returnUrl' => $arguments['returnUrl']]; if (GeneralUtility::compat_version('7.0')) { $returnUrlGenerated = BackendUtility::getModuleUrl('record_edit', $returnUrl); } else { $returnUrlGenerated = 'alt_doc.php?' . ltrim(GeneralUtility::implodeArrayForUrl('', $returnUrl, '', true, true), '&') . BackendUtility::getUrlToken('editRecord'); } $wizardArguments = ['P' => ['referenceUid' => $childRecord['uid'], 'returnUrl' => $returnUrlGenerated]]; $wizardUri = BackendUtility::getModuleUrl('focuspoint', $wizardArguments); } else { $wizardUri = 'javascript:alert(\'Please save the base record first, because open this wizard will not save the changes in the current form!\');'; } /** @var WizardService $wizardService */ $wizardService = GeneralUtility::makeInstance('HDNET\\Focuspoint\\Service\\WizardService'); $this->arrayUnshiftAssoc($controlItems, 'focuspoint', $wizardService->getWizardButton($wizardUri)); }
/** * Get the user function label for the file_reference table * * @param array $params * @return void */ public function getInlineLabel(array &$params) { $sysFileFields = isset($params['options']['sys_file']) && is_array($params['options']['sys_file']) ? $params['options']['sys_file'] : []; if (empty($sysFileFields)) { // Nothing to do $params['title'] = $params['row']['uid']; return; } // In case of a group field uid_local has the table_uid|label syntax $tableAndUid = array_shift(GeneralUtility::trimExplode('|', $params['row']['uid_local'], true, 2)); $fileInfo = BackendUtility::splitTable_Uid($tableAndUid); $fileRecord = BackendUtility::getRecord($fileInfo[0], $fileInfo[1]); // Configuration $title = []; foreach ($sysFileFields as $field) { $value = ''; if ($field === 'title') { if (isset($params['row']['title'])) { $fullTitle = $params['row']['title']; } else { try { $metaDataRepository = GeneralUtility::makeInstance(MetaDataRepository::class); $metaData = $metaDataRepository->findByFileUid($fileRecord['uid']); $fullTitle = $metaData['title']; } catch (InvalidUidException $e) { /** * We just catch the exception here * Reasoning: There is nothing an editor or even admin could do */ $fullTitle = ''; } } $value = BackendUtility::getRecordTitlePrep(htmlspecialchars($fullTitle)); } else { if (isset($params['row'][$field])) { $value = htmlspecialchars($params['row'][$field]); } elseif (isset($fileRecord[$field])) { $value = BackendUtility::getRecordTitlePrep($fileRecord[$field]); } } if ((string) $value === '') { continue; } $labelText = LocalizationUtility::translate('LLL:EXT:lang/Resources/Private/Language/locallang_tca.xlf:sys_file.' . $field, 'lang'); $title[] = '<dt>' . htmlspecialchars($labelText) . '</dt>' . '<dd>' . $value . '</dd>'; } $params['title'] = '<dl>' . implode('', $title) . '</dl>'; }
/** * Post-processing to define which control items to show. Possibly own icons can be added here. * * @param string $parentUid The uid of the parent (embedding) record (uid or NEW...) * @param string $foreignTable The table (foreign_table) we create control-icons for * @param array $childRecord The current record of that foreign_table * @param array $childConfig TCA configuration of the current field of the child record * @param boolean $isVirtual Defines whether the current records is only virtually shown and not physically part of the parent record * @param array $controlItems (reference) Associative array with the currently available control items * * @return void */ public function renderForeignRecordHeaderControl_postProcess($parentUid, $foreignTable, array $childRecord, array $childConfig, $isVirtual, array &$controlItems) { if ($foreignTable != 'sys_file_reference') { return; } if (!GeneralUtility::isFirstPartOfStr($childRecord['uid_local'], 'sys_file_')) { return; } $parts = BackendUtility::splitTable_Uid($childRecord['uid_local']); if (!isset($parts[1])) { return; } $wizardArguments = array('P' => array('uid' => $this->getMetaDataUidByFileUid($parts[1]), 'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI'))); $wizardUri = BackendUtility::getModuleUrl('focuspoint', $wizardArguments); $configuration = '<a href="' . $wizardUri . '" class="btn btn-default">' . IconUtility::getSpriteIcon('extensions-focuspoint-focuspoint') . '</a>'; $this->arrayUnshiftAssoc($controlItems, 'focuspoint', $configuration); }
/** * Get the user function label for the file_reference table * * @param array $params * @return void */ public function getInlineLabel(array &$params) { $sysFileFields = isset($params['options']['sys_file']) && is_array($params['options']['sys_file']) ? $params['options']['sys_file'] : array(); if (!count($sysFileFields)) { // Nothing to do $params['title'] = $params['row']['uid']; return; } $fileInfo = BackendUtility::splitTable_Uid($params['row']['uid_local'], 2); $fileRecord = BackendUtility::getRecord($fileInfo[0], $fileInfo[1]); // Configuration $title = array(); foreach ($sysFileFields as $field) { $value = ''; if ($field === 'title') { if (isset($params['row']['title'])) { $fullTitle = $params['row']['title']; } else { try { $metaDataRepository = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\Index\\MetaDataRepository'); $metaData = $metaDataRepository->findByFileUid($fileRecord['uid']); $fullTitle = $metaData['title']; } catch (\TYPO3\CMS\Core\Resource\Exception\InvalidUidException $e) { /** * We just catch the exception here * Reasoning: There is nothing an editor or even admin could do */ } } $value = BackendUtility::getRecordTitlePrep(htmlspecialchars($fullTitle)); } else { if (isset($params['row'][$field])) { $value = htmlspecialchars($params['row'][$field]); } elseif (isset($fileRecord[$field])) { $value = BackendUtility::getRecordTitlePrep($fileRecord[$field]); } } if (!strlen($value)) { continue; } $labelText = LocalizationUtility::translate('LLL:EXT:lang/locallang_tca.xlf:sys_file.' . $field, 'lang'); $title[] = '<dt>' . htmlspecialchars($labelText) . '</dt>' . '<dd>' . $value . '</dd>'; } $params['title'] = '<dl>' . implode('', $title) . '</dl>'; }
/** * Fetches tt_content records and returns them in an array * * @param string $recordList List of uids to fetch * @return array Array of tt_content records */ public function render($recordList) { $records = array(); $recordList = GeneralUtility::TrimExplode(',', $recordList); if (is_array($recordList) && count($recordList) > 0) { foreach ($recordList as $recordIdentifier) { $split = BackendUtility::splitTable_Uid($recordIdentifier); $tableName = empty($split[0]) ? 'tt_content' : $split[0]; $shortcutRecord = BackendUtility::getRecord($tableName, $split[1]); if (is_array($shortcutRecord)) { if ($this->arguments['flexform2array']) { // parse flexform $flexformService = GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\Service\\FlexFormService'); $shortcutRecord['pi_flexform'] = $flexformService->convertFlexFormContentToArray($shortcutRecord['pi_flexform']); } $records[] = $shortcutRecord; } } } return $records; }
/** * Return uids of related records for group type fields. Uids consisting of * multiple parts like [table]_[uid]|[title] will be reduced to integers and * validated against the allowed table. Uids without a table prefix are * accepted in any case. * * @param array $fieldConfig TCA "config" section for the group type field. * @param string $value A comma separated list of records * @return array */ protected function getRelatedGroupFieldUids(array $fieldConfig, $value) { $relatedUids = []; $allowedTable = $this->getAllowedTableForGroupField($fieldConfig); // Skip if it's not a database relation with a resolvable foreign table if ($fieldConfig['internal_type'] !== 'db' || $allowedTable === false) { return $relatedUids; } $values = GeneralUtility::trimExplode(',', $value, true); foreach ($values as $groupValue) { list($foreignIdentifier, $_) = GeneralUtility::trimExplode('|', $groupValue); list($recordForeignTable, $foreignUid) = BackendUtility::splitTable_Uid($foreignIdentifier); // skip records that do not match the allowed table if (!empty($recordForeignTable) && $recordForeignTable !== $allowedTable) { continue; } $relatedUids[] = $foreignUid; } return $relatedUids; }
/** * Renders the HTML header for a foreign record, such as the title, toggle-function, drag'n'drop, etc. * Later on the command-icons are inserted here. * * @param string $parentUid The uid of the parent (embedding) record (uid or NEW...) * @param string $foreign_table The foreign_table we create a header for * @param array $rec The current record of that foreign_table * @param array $config content of $PA['fieldConf']['config'] * @param boolean $isVirtualRecord * @return string The HTML code of the header * @todo Define visibility */ public function renderForeignRecordHeader($parentUid, $foreign_table, $rec, $config, $isVirtualRecord = FALSE) { // Init: $objectId = $this->inlineNames['object'] . self::Structure_Separator . $foreign_table . self::Structure_Separator . $rec['uid']; // We need the returnUrl of the main script when loading the fields via AJAX-call (to correct wizard code, so include it as 3rd parameter) // Pre-Processing: $isOnSymmetricSide = RelationHandler::isOnSymmetricSide($parentUid, $config, $rec); $hasForeignLabel = !$isOnSymmetricSide && $config['foreign_label'] ? TRUE : FALSE; $hasSymmetricLabel = $isOnSymmetricSide && $config['symmetric_label'] ? TRUE : FALSE; // Get the record title/label for a record: // Try using a self-defined user function only for formatted labels if (isset($GLOBALS['TCA'][$foreign_table]['ctrl']['formattedLabel_userFunc'])) { $params = array('table' => $foreign_table, 'row' => $rec, 'title' => '', 'isOnSymmetricSide' => $isOnSymmetricSide, 'options' => isset($GLOBALS['TCA'][$foreign_table]['ctrl']['formattedLabel_userFunc_options']) ? $GLOBALS['TCA'][$foreign_table]['ctrl']['formattedLabel_userFunc_options'] : array(), 'parent' => array('uid' => $parentUid, 'config' => $config)); // callUserFunction requires a third parameter, but we don't want to give $this as reference! $null = NULL; GeneralUtility::callUserFunction($GLOBALS['TCA'][$foreign_table]['ctrl']['formattedLabel_userFunc'], $params, $null); $recTitle = $params['title']; // Try using a normal self-defined user function } elseif (isset($GLOBALS['TCA'][$foreign_table]['ctrl']['label_userFunc'])) { $params = array('table' => $foreign_table, 'row' => $rec, 'title' => '', 'isOnSymmetricSide' => $isOnSymmetricSide, 'parent' => array('uid' => $parentUid, 'config' => $config)); // callUserFunction requires a third parameter, but we don't want to give $this as reference! $null = NULL; GeneralUtility::callUserFunction($GLOBALS['TCA'][$foreign_table]['ctrl']['label_userFunc'], $params, $null); $recTitle = $params['title']; } elseif ($hasForeignLabel || $hasSymmetricLabel) { $titleCol = $hasForeignLabel ? $config['foreign_label'] : $config['symmetric_label']; $foreignConfig = $this->getPossibleRecordsSelectorConfig($config, $titleCol); // Render title for everything else than group/db: if ($foreignConfig['type'] != 'groupdb') { $recTitle = BackendUtility::getProcessedValueExtra($foreign_table, $titleCol, $rec[$titleCol], 0, 0, FALSE); } else { // $recTitle could be something like: "tx_table_123|...", $valueParts = GeneralUtility::trimExplode('|', $rec[$titleCol]); $itemParts = GeneralUtility::revExplode('_', $valueParts[0], 2); $recTemp = BackendUtility::getRecordWSOL($itemParts[0], $itemParts[1]); $recTitle = BackendUtility::getRecordTitle($itemParts[0], $recTemp, FALSE); } $recTitle = BackendUtility::getRecordTitlePrep($recTitle); if (trim($recTitle) === '') { $recTitle = BackendUtility::getNoRecordTitle(TRUE); } } else { $recTitle = BackendUtility::getRecordTitle($foreign_table, $rec, TRUE); } $altText = BackendUtility::getRecordIconAltText($rec, $foreign_table); $iconImg = IconUtility::getSpriteIconForRecord($foreign_table, $rec, array('title' => htmlspecialchars($altText), 'id' => $objectId . '_icon')); $label = '<span id="' . $objectId . '_label">' . $recTitle . '</span>'; $ctrl = $this->renderForeignRecordHeaderControl($parentUid, $foreign_table, $rec, $config, $isVirtualRecord); $thumbnail = FALSE; // Renders a thumbnail for the header if (!empty($config['appearance']['headerThumbnail']['field'])) { $fieldValue = $rec[$config['appearance']['headerThumbnail']['field']]; $firstElement = array_shift(GeneralUtility::trimExplode(',', $fieldValue)); $fileUid = array_pop(BackendUtility::splitTable_Uid($firstElement)); if (!empty($fileUid)) { $fileObject = \TYPO3\CMS\Core\Resource\ResourceFactory::getInstance()->getFileObject($fileUid); if ($fileObject && $fileObject->isMissing()) { $flashMessage = \TYPO3\CMS\Core\Resource\Utility\BackendUtility::getFlashMessageForMissingFile($fileObject); $thumbnail = $flashMessage->render(); } elseif ($fileObject) { $imageSetup = $config['appearance']['headerThumbnail']; unset($imageSetup['field']); $imageSetup = array_merge(array('width' => '45', 'height' => '45c'), $imageSetup); $processedImage = $fileObject->process(\TYPO3\CMS\Core\Resource\ProcessedFile::CONTEXT_IMAGECROPSCALEMASK, $imageSetup); // Only use a thumbnail if the processing was successful. if (!$processedImage->usesOriginalFile()) { $imageUrl = $processedImage->getPublicUrl(TRUE); $thumbnail = '<img class="t3-form-field-header-inline-thumbnail-image" src="' . $imageUrl . '" alt="' . htmlspecialchars($altText) . '" title="' . htmlspecialchars($altText) . '">'; } } } } if (!empty($config['appearance']['headerThumbnail']['field']) && $thumbnail) { $headerClasses = ' t3-form-field-header-inline-has-thumbnail'; $mediaContainer = '<div class="t3-form-field-header-inline-thumbnail" id="' . $objectId . '_thumbnailcontainer">' . $thumbnail . '</div>'; } else { $headerClasses = ' t3-form-field-header-inline-has-icon'; $mediaContainer = '<div class="t3-form-field-header-inline-icon" id="' . $objectId . '_iconcontainer">' . $iconImg . '</div>'; } $header = '<div class="t3-form-field-header-inline-wrap' . $headerClasses . '">' . '<div class="t3-form-field-header-inline-ctrl">' . $ctrl . '</div>' . '<div class="t3-form-field-header-inline-body">' . $mediaContainer . '<div class="t3-form-field-header-inline-summary">' . $label . '</div>' . '</div>' . '</div>'; return $header; }
/** * This will render a selector box into which elements from either * the file system or database can be inserted. Relations. * * @return array As defined in initializeResultArray() of AbstractNode */ public function render() { $table = $this->globalOptions['table']; $fieldName = $this->globalOptions['fieldName']; $row = $this->globalOptions['databaseRow']; $parameterArray = $this->globalOptions['parameterArray']; $config = $parameterArray['fieldConf']['config']; $show_thumbs = $config['show_thumbs']; $resultArray = $this->initializeResultArray(); $size = isset($config['size']) ? (int)$config['size'] : $this->minimumInputWidth; $maxitems = MathUtility::forceIntegerInRange($config['maxitems'], 0); if (!$maxitems) { $maxitems = 100000; } $minitems = MathUtility::forceIntegerInRange($config['minitems'], 0); $thumbnails = array(); $allowed = GeneralUtility::trimExplode(',', $config['allowed'], TRUE); $disallowed = GeneralUtility::trimExplode(',', $config['disallowed'], TRUE); $disabled = ($this->isGlobalReadonly() || $config['readOnly']); $info = array(); $parameterArray['itemFormElID_file'] = $parameterArray['itemFormElID'] . '_files'; // whether the list and delete controls should be disabled $noList = isset($config['disable_controls']) && GeneralUtility::inList($config['disable_controls'], 'list'); $noDelete = isset($config['disable_controls']) && GeneralUtility::inList($config['disable_controls'], 'delete'); // "Extra" configuration; Returns configuration for the field based on settings found in the "types" fieldlist. $specConf = BackendUtility::getSpecConfParts($parameterArray['fieldConf']['defaultExtras']); // Register properties in requiredElements $resultArray['requiredElements'][$parameterArray['itemFormElName']] = array( $minitems, $maxitems, 'imgName' => $table . '_' . $row['uid'] . '_' . $fieldName ); $tabAndInlineStack = $this->globalOptions['tabAndInlineStack']; if (!empty($tabAndInlineStack) && preg_match('/^(.+\\])\\[(\\w+)\\]$/', $parameterArray['itemFormElName'], $match)) { array_shift($match); $resultArray['requiredNested'][$parameterArray['itemFormElName']] = array( 'parts' => $match, 'level' => $tabAndInlineStack, ); } // If maxitems==1 then automatically replace the current item (in list and file selector) if ($maxitems === 1) { $resultArray['additionalJavaScriptPost'][] = 'TBE_EDITOR.clearBeforeSettingFormValueFromBrowseWin[' . GeneralUtility::quoteJSvalue($parameterArray['itemFormElName']) . '] = { itemFormElID_file: ' . GeneralUtility::quoteJSvalue($parameterArray['itemFormElID_file']) . ' }'; $parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged'] = 'setFormValueManipulate(' . GeneralUtility::quoteJSvalue($parameterArray['itemFormElName']) . ', \'Remove\'); ' . $parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged']; } elseif ($noList) { // If the list controls have been removed and the maximum number is reached, remove the first entry to avoid "write once" field $parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged'] = 'setFormValueManipulate(' . GeneralUtility::quoteJSvalue($parameterArray['itemFormElName']) . ', \'RemoveFirstIfFull\', ' . GeneralUtility::quoteJSvalue($maxitems) . '); ' . $parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged']; } $html = '<input type="hidden" name="' . $parameterArray['itemFormElName'] . '_mul" value="' . ($config['multiple'] ? 1 : 0) . '"' . $disabled . ' />'; // Acting according to either "file" or "db" type: switch ((string)$config['internal_type']) { case 'file_reference': $config['uploadfolder'] = ''; // Fall through case 'file': // Creating string showing allowed types: if (!count($allowed)) { $allowed = array('*'); } // Making the array of file items: $itemArray = GeneralUtility::trimExplode(',', $parameterArray['itemFormElValue'], TRUE); $fileFactory = ResourceFactory::getInstance(); // Correct the filename for the FAL items foreach ($itemArray as &$fileItem) { list($fileUid, $fileLabel) = explode('|', $fileItem); if (MathUtility::canBeInterpretedAsInteger($fileUid)) { $fileObject = $fileFactory->getFileObject($fileUid); $fileLabel = $fileObject->getName(); } $fileItem = $fileUid . '|' . $fileLabel; } // Showing thumbnails: if ($show_thumbs) { foreach ($itemArray as $imgRead) { $imgP = explode('|', $imgRead); $imgPath = rawurldecode($imgP[0]); // FAL icon production if (MathUtility::canBeInterpretedAsInteger($imgP[0])) { $fileObject = $fileFactory->getFileObject($imgP[0]); if ($fileObject->isMissing()) { $thumbnails[] = array( 'message' => \TYPO3\CMS\Core\Resource\Utility\BackendUtility::getFlashMessageForMissingFile($fileObject)->render() ); } elseif (GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], $fileObject->getExtension())) { $thumbnails[] = array( 'name' => htmlspecialchars($fileObject->getName()), 'image' => $fileObject->process(ProcessedFile::CONTEXT_IMAGEPREVIEW, array())->getPublicUrl(TRUE) ); } else { // Icon $thumbnails[] = array( 'name' => htmlspecialchars($fileObject->getName()), 'image' => IconUtility::getSpriteIconForResource($fileObject, array('title' => $fileObject->getName())) ); } } else { $rowCopy = array(); $rowCopy[$fieldName] = $imgPath; try { $thumbnails[] = array( 'name' => $imgPath, 'image' => BackendUtility::thumbCode( $rowCopy, $table, $fieldName, '', '', $config['uploadfolder'], 0, ' align="middle"' ) ); } catch (\Exception $exception) { /** @var $flashMessage FlashMessage */ $message = $exception->getMessage(); $flashMessage = GeneralUtility::makeInstance( FlashMessage::class, htmlspecialchars($message), '', FlashMessage::ERROR, TRUE ); /** @var $flashMessageService FlashMessageService */ $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class); $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier(); $defaultFlashMessageQueue->enqueue($flashMessage); $logMessage = $message . ' (' . $table . ':' . $row['uid'] . ')'; GeneralUtility::sysLog($logMessage, 'core', GeneralUtility::SYSLOG_SEVERITY_WARNING); } } } } // Creating the element: $params = array( 'size' => $size, 'allowed' => $allowed, 'disallowed' => $disallowed, 'dontShowMoveIcons' => $maxitems <= 1, 'autoSizeMax' => MathUtility::forceIntegerInRange($config['autoSizeMax'], 0), 'maxitems' => $maxitems, 'style' => isset($config['selectedListStyle']) ? ' style="' . htmlspecialchars($config['selectedListStyle']) . '"' : '', 'thumbnails' => $thumbnails, 'readOnly' => $disabled, 'noBrowser' => $noList || isset($config['disable_controls']) && GeneralUtility::inList($config['disable_controls'], 'browser'), 'noList' => $noList, 'noDelete' => $noDelete ); $html .= $this->dbFileIcons( $parameterArray['itemFormElName'], 'file', implode(',', $allowed), $itemArray, '', $params, $parameterArray['onFocus'], '', '', '', $config); if (!$disabled && !(isset($config['disable_controls']) && GeneralUtility::inList($config['disable_controls'], 'upload'))) { // Adding the upload field: $isDirectFileUploadEnabled = (bool)$this->getBackendUserAuthentication()->uc['edit_docModuleUpload']; if ($isDirectFileUploadEnabled && $config['uploadfolder']) { // Insert the multiple attribute to enable HTML5 multiple file upload $multipleAttribute = ''; $multipleFilenameSuffix = ''; if (isset($config['maxitems']) && $config['maxitems'] > 1) { $multipleAttribute = ' multiple="multiple"'; $multipleFilenameSuffix = '[]'; } $html .= ' <div id="' . $parameterArray['itemFormElID_file'] . '"> <input type="file"' . $multipleAttribute . ' name="data_files' . $this->globalOptions['elementBaseName'] . $multipleFilenameSuffix . '" size="35" onchange="' . implode('', $parameterArray['fieldChangeFunc']) . '" /> </div>'; } } break; case 'folder': // If the element is of the internal type "folder": // Array of folder items: $itemArray = GeneralUtility::trimExplode(',', $parameterArray['itemFormElValue'], TRUE); // Creating the element: $params = array( 'size' => $size, 'dontShowMoveIcons' => $maxitems <= 1, 'autoSizeMax' => MathUtility::forceIntegerInRange($config['autoSizeMax'], 0), 'maxitems' => $maxitems, 'style' => isset($config['selectedListStyle']) ? ' style="' . htmlspecialchars($config['selectedListStyle']) . '"' : '', 'readOnly' => $disabled, 'noBrowser' => $noList || isset($config['disable_controls']) && GeneralUtility::inList($config['disable_controls'], 'browser'), 'noList' => $noList ); $html .= $this->dbFileIcons( $parameterArray['itemFormElName'], 'folder', '', $itemArray, '', $params, $parameterArray['onFocus'] ); break; case 'db': // If the element is of the internal type "db": // Creating string showing allowed types: $onlySingleTableAllowed = FALSE; $languageService = $this->getLanguageService(); $allowedTables = array(); if ($allowed[0] === '*') { $allowedTables = array( 'name' => htmlspecialchars($languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.allTables')) ); } elseif ($allowed) { $onlySingleTableAllowed = count($allowed) == 1; foreach ($allowed as $allowedTable) { $allowedTables[] = array( 'name' => htmlspecialchars($languageService->sL($GLOBALS['TCA'][$allowedTable]['ctrl']['title'])), 'icon' => IconUtility::getSpriteIconForRecord($allowedTable, array()), 'onClick' => 'setFormValueOpenBrowser(\'db\', ' . GeneralUtility::quoteJSvalue($parameterArray['itemFormElName'] . '|||' . $allowedTable) . '); return false;' ); } } $perms_clause = $this->getBackendUserAuthentication()->getPagePermsClause(1); $itemArray = array(); // Thumbnails: $temp_itemArray = GeneralUtility::trimExplode(',', $parameterArray['itemFormElValue'], TRUE); foreach ($temp_itemArray as $dbRead) { $recordParts = explode('|', $dbRead); list($this_table, $this_uid) = BackendUtility::splitTable_Uid($recordParts[0]); // For the case that no table was found and only a single table is defined to be allowed, use that one: if (!$this_table && $onlySingleTableAllowed) { $this_table = $allowed; } $itemArray[] = array('table' => $this_table, 'id' => $this_uid); if (!$disabled && $show_thumbs) { $rr = BackendUtility::getRecordWSOL($this_table, $this_uid); $thumbnails[] = array( 'name' => BackendUtility::getRecordTitle($this_table, $rr, TRUE), 'image' => IconUtility::getSpriteIconForRecord($this_table, $rr), 'path' => BackendUtility::getRecordPath($rr['pid'], $perms_clause, 15), 'uid' => $rr['uid'], 'table' => $this_table ); } } // Creating the element: $params = array( 'size' => $size, 'dontShowMoveIcons' => $maxitems <= 1, 'autoSizeMax' => MathUtility::forceIntegerInRange($config['autoSizeMax'], 0), 'maxitems' => $maxitems, 'style' => isset($config['selectedListStyle']) ? ' style="' . htmlspecialchars($config['selectedListStyle']) . '"' : '', 'info' => $info, 'allowedTables' => $allowedTables, 'thumbnails' => $thumbnails, 'readOnly' => $disabled, 'noBrowser' => $noList || isset($config['disable_controls']) && GeneralUtility::inList($config['disable_controls'], 'browser'), 'noList' => $noList ); $html .= $this->dbFileIcons( $parameterArray['itemFormElName'], 'db', implode(',', $allowed), $itemArray, '', $params, $parameterArray['onFocus'], $table, $fieldName, $row['uid'], $config ); break; } // Wizards: $altItem = '<input type="hidden" name="' . $parameterArray['itemFormElName'] . '" value="' . htmlspecialchars($parameterArray['itemFormElValue']) . '" />'; if (!$disabled) { $html = $this->renderWizards( array( $html, $altItem ), $config['wizards'], $table, $row, $fieldName, $parameterArray, $parameterArray['itemFormElName'], $specConf ); } $resultArray['html'] = $html; return $resultArray; }
/** * Renders the HTML header for a foreign record, such as the title, toggle-function, drag'n'drop, etc. * Later on the command-icons are inserted here. * * @param array $data Current data * @return string The HTML code of the header */ protected function renderForeignRecordHeader(array $data) { $languageService = $this->getLanguageService(); $inlineConfig = $data['inlineParentConfig']; $foreignTable = $inlineConfig['foreign_table']; $rec = $data['databaseRow']; // Init: $domObjectId = $this->inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($data['inlineFirstPid']); $objectId = $domObjectId . '-' . $foreignTable . '-' . $rec['uid']; $recordTitle = $data['recordTitle']; if (empty($recordTitle)) { $recordTitle = '<em>[' . $languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.no_title', true) . ']</em>'; } $altText = BackendUtility::getRecordIconAltText($rec, $foreignTable); $iconImg = '<span title="' . $altText . '" id="' . htmlspecialchars($objectId) . '_icon' . '">' . $this->iconFactory->getIconForRecord($foreignTable, $rec, Icon::SIZE_SMALL)->render() . '</span>'; $label = '<span id="' . $objectId . '_label">' . $recordTitle . '</span>'; $ctrl = $this->renderForeignRecordHeaderControl($data); $thumbnail = false; // Renders a thumbnail for the header if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['thumbnails'] && !empty($inlineConfig['appearance']['headerThumbnail']['field'])) { $fieldValue = $rec[$inlineConfig['appearance']['headerThumbnail']['field']]; $firstElement = array_shift(GeneralUtility::trimExplode('|', array_shift(GeneralUtility::trimExplode(',', $fieldValue)))); $fileUid = array_pop(BackendUtility::splitTable_Uid($firstElement)); if (!empty($fileUid)) { try { $fileObject = ResourceFactory::getInstance()->getFileObject($fileUid); } catch (\InvalidArgumentException $e) { $fileObject = null; } if ($fileObject && $fileObject->isMissing()) { $thumbnail .= '<span class="label label-danger">' . htmlspecialchars(static::getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:warning.file_missing')) . '</span> ' . htmlspecialchars($fileObject->getName()) . '<br />'; } elseif ($fileObject) { $imageSetup = $inlineConfig['appearance']['headerThumbnail']; unset($imageSetup['field']); if (!empty($rec['crop'])) { $imageSetup['crop'] = $rec['crop']; } $imageSetup = array_merge(array('width' => '45', 'height' => '45c'), $imageSetup); $processedImage = $fileObject->process(ProcessedFile::CONTEXT_IMAGEPREVIEW, $imageSetup); // Only use a thumbnail if the processing process was successful by checking if image width is set if ($processedImage->getProperty('width')) { $imageUrl = $processedImage->getPublicUrl(true); $thumbnail = '<img src="' . $imageUrl . '" ' . 'width="' . $processedImage->getProperty('width') . '" ' . 'height="' . $processedImage->getProperty('height') . '" ' . 'alt="' . htmlspecialchars($altText) . '" ' . 'title="' . htmlspecialchars($altText) . '">'; } } } } if (!empty($inlineConfig['appearance']['headerThumbnail']['field']) && $thumbnail) { $mediaContainer = '<div class="form-irre-header-cell form-irre-header-thumbnail" id="' . $objectId . '_thumbnailcontainer">' . $thumbnail . '</div>'; } else { $mediaContainer = '<div class="form-irre-header-cell form-irre-header-icon" id="' . $objectId . '_iconcontainer">' . $iconImg . '</div>'; } $header = $mediaContainer . ' <div class="form-irre-header-cell form-irre-header-body">' . $label . '</div> <div class="form-irre-header-cell form-irre-header-control t3js-formengine-irre-control">' . $ctrl . '</div>'; return $header; }
/** * Draws the preview content for a content element * * @param string $row Content element * @param boolean $isRTE Set if the RTE link can be created. * @return string HTML * @throws \UnexpectedValueException * @todo Define visibility */ public function tt_content_drawItem($row, $isRTE = FALSE) { $out = ''; $outHeader = ''; // Make header: if ($row['header']) { $infoArr = array(); $this->getProcessedValue('tt_content', 'header_position,header_layout,header_link', $row, $infoArr); $hiddenHeaderNote = ''; // If header layout is set to 'hidden', display an accordant note: if ($row['header_layout'] == 100) { $hiddenHeaderNote = ' <em>[' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.hidden', TRUE) . ']</em>'; } $outHeader = $row['date'] ? htmlspecialchars($this->itemLabels['date'] . ' ' . BackendUtility::date($row['date'])) . '<br />' : ''; $outHeader .= '<strong>' . $this->linkEditContent($this->renderText($row['header']), $row) . $hiddenHeaderNote . '</strong><br />'; } // Make content: $infoArr = array(); $drawItem = TRUE; // Hook: Render an own preview of a record $drawItemHooks =& $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/class.tx_cms_layout.php']['tt_content_drawItem']; if (is_array($drawItemHooks)) { foreach ($drawItemHooks as $hookClass) { $hookObject = GeneralUtility::getUserObj($hookClass); if (!$hookObject instanceof PageLayoutViewDrawItemHookInterface) { throw new \UnexpectedValueException('$hookObject must implement interface TYPO3\\CMS\\Backend\\View\\PageLayoutViewDrawItemHookInterface', 1218547409); } $hookObject->preProcess($this, $drawItem, $outHeader, $out, $row); } } // Draw preview of the item depending on its CType (if not disabled by previous hook): if ($drawItem) { switch ($row['CType']) { case 'header': if ($row['subheader']) { $out .= $this->linkEditContent($this->renderText($row['subheader']), $row) . '<br />'; } break; case 'text': case 'textpic': case 'image': if ($row['CType'] == 'text' || $row['CType'] == 'textpic') { if ($row['bodytext']) { $out .= $this->linkEditContent($this->renderText($row['bodytext']), $row) . '<br />'; } } if ($row['CType'] == 'textpic' || $row['CType'] == 'image') { if ($row['image']) { $out .= $this->thumbCode($row, 'tt_content', 'image') . '<br />'; if ($row['imagecaption']) { $out .= $this->linkEditContent($this->renderText($row['imagecaption']), $row) . '<br />'; } } } break; case 'bullets': case 'table': case 'mailform': if ($row['bodytext']) { $out .= $this->linkEditContent($this->renderText($row['bodytext']), $row) . '<br />'; } break; case 'uploads': if ($row['media']) { $out .= $this->thumbCode($row, 'tt_content', 'media') . '<br />'; } break; case 'multimedia': if ($row['multimedia']) { $out .= $this->renderText($row['multimedia']) . '<br />'; $out .= $this->renderText($row['parameters']) . '<br />'; } break; case 'menu': if ($row['pages']) { $out .= $this->linkEditContent($row['pages'], $row) . '<br />'; } break; case 'shortcut': if (!empty($row['records'])) { $shortcutContent = array(); $recordList = explode(',', $row['records']); foreach ($recordList as $recordIdentifier) { $split = BackendUtility::splitTable_Uid($recordIdentifier); $tableName = empty($split[0]) ? 'tt_content' : $split[0]; $shortcutRecord = BackendUtility::getRecord($tableName, $split[1]); if (is_array($shortcutRecord)) { $icon = IconUtility::getSpriteIconForRecord($tableName, $shortcutRecord); $onClick = $this->getPageLayoutController()->doc->wrapClickMenuOnIcon($icon, $tableName, $shortcutRecord['uid'], 1, '', '+copy,info,edit,view', TRUE); $shortcutContent[] = '<a href="#" onclick="' . htmlspecialchars($onClick) . '">' . $icon . '</a>' . htmlspecialchars(BackendUtility::getRecordTitle($tableName, $shortcutRecord)); } } $out .= implode('<br />', $shortcutContent) . '<br />'; } break; case 'list': $hookArr = array(); $hookOut = ''; if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/class.tx_cms_layout.php']['list_type_Info'][$row['list_type']])) { $hookArr = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/class.tx_cms_layout.php']['list_type_Info'][$row['list_type']]; } elseif (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/class.tx_cms_layout.php']['list_type_Info']['_DEFAULT'])) { $hookArr = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/class.tx_cms_layout.php']['list_type_Info']['_DEFAULT']; } if (count($hookArr) > 0) { $_params = array('pObj' => &$this, 'row' => $row, 'infoArr' => $infoArr); foreach ($hookArr as $_funcRef) { $hookOut .= GeneralUtility::callUserFunction($_funcRef, $_params, $this); } } if ((string) $hookOut !== '') { $out .= $hookOut; } elseif (!empty($row['list_type'])) { $label = BackendUtility::getLabelFromItemlist('tt_content', 'list_type', $row['list_type']); if (!empty($label)) { $out .= '<strong>' . $this->getLanguageService()->sL($label, TRUE) . '</strong><br />'; } else { $message = sprintf($this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.noMatchingValue'), $row['list_type']); $out .= GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Messaging\\FlashMessage', htmlspecialchars($message), '', FlashMessage::WARNING)->render(); } } elseif (!empty($row['select_key'])) { $out .= $this->getLanguageService()->sL(BackendUtility::getItemLabel('tt_content', 'select_key'), TRUE) . ' ' . $row['select_key'] . '<br />'; } else { $out .= '<strong>' . $this->getLanguageService()->getLL('noPluginSelected') . '</strong>'; } $out .= $this->getLanguageService()->sL(BackendUtility::getLabelFromItemlist('tt_content', 'pages', $row['pages']), TRUE) . '<br />'; break; case 'script': $out .= $this->getLanguageService()->sL(BackendUtility::getItemLabel('tt_content', 'select_key'), TRUE) . ' ' . $row['select_key'] . '<br />'; $out .= '<br />' . $this->linkEditContent($this->renderText($row['bodytext']), $row) . '<br />'; $out .= '<br />' . $this->linkEditContent($this->renderText($row['imagecaption']), $row) . '<br />'; break; default: $contentType = $this->CType_labels[$row['CType']]; if (isset($contentType)) { $out .= '<strong>' . htmlspecialchars($contentType) . '</strong><br />'; if ($row['bodytext']) { $out .= $this->linkEditContent($this->renderText($row['bodytext']), $row) . '<br />'; } if ($row['image']) { $out .= $this->thumbCode($row, 'tt_content', 'image') . '<br />'; } } else { $message = sprintf($this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.noMatchingValue'), $row['CType']); $out .= GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Messaging\\FlashMessage', htmlspecialchars($message), '', FlashMessage::WARNING)->render(); } } } // Wrap span-tags: $out = ' <span class="exampleContent">' . $out . '</span>'; // Add header: $out = $outHeader . $out; // Add RTE button: if ($isRTE) { $out .= $this->linkRTEbutton($row); } // Return values: if ($this->isDisabled('tt_content', $row)) { return $this->getDocumentTemplate()->dfw($out); } else { return $out; } }
/** * Generation of TCEform elements of the type "group" * This will render a selectorbox into which elements from either the file system or database can be inserted. 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_typeGroup($table, $field, $row, &$PA) { // Init: $config = $PA['fieldConf']['config']; $show_thumbs = $config['show_thumbs']; $size = isset($config['size']) ? (int) $config['size'] : 5; $maxitems = MathUtility::forceIntegerInRange($config['maxitems'], 0); if (!$maxitems) { $maxitems = 100000; } $minitems = MathUtility::forceIntegerInRange($config['minitems'], 0); $allowed = trim($config['allowed']); $disallowed = trim($config['disallowed']); $item = ''; $disabled = ''; if ($this->renderReadonly || $config['readOnly']) { $disabled = ' disabled="disabled"'; } $item .= '<input type="hidden" name="' . $PA['itemFormElName'] . '_mul" value="' . ($config['multiple'] ? 1 : 0) . '"' . $disabled . ' />'; $this->registerRequiredProperty('range', $PA['itemFormElName'], array($minitems, $maxitems, 'imgName' => $table . '_' . $row['uid'] . '_' . $field)); $info = ''; // "Extra" configuration; Returns configuration for the field based on settings found in the "types" fieldlist. $specConf = $this->getSpecConfFromString($PA['extra'], $PA['fieldConf']['defaultExtras']); $PA['itemFormElID_file'] = $PA['itemFormElID'] . '_files'; // whether the list and delete controls should be disabled $noList = isset($config['disable_controls']) && GeneralUtility::inList($config['disable_controls'], 'list'); $noDelete = isset($config['disable_controls']) && GeneralUtility::inList($config['disable_controls'], 'delete'); // if maxitems==1 then automatically replace the current item (in list and file selector) if ($maxitems === 1) { $this->additionalJS_post[] = 'TBE_EDITOR.clearBeforeSettingFormValueFromBrowseWin[\'' . $PA['itemFormElName'] . '\'] = { itemFormElID_file: \'' . $PA['itemFormElID_file'] . '\' }'; $PA['fieldChangeFunc']['TBE_EDITOR_fieldChanged'] = 'setFormValueManipulate(\'' . $PA['itemFormElName'] . '\', \'Remove\'); ' . $PA['fieldChangeFunc']['TBE_EDITOR_fieldChanged']; } elseif ($noList) { // If the list controls have been removed and the maximum number is reached, remove the first entry to avoid "write once" field $PA['fieldChangeFunc']['TBE_EDITOR_fieldChanged'] = 'setFormValueManipulate(\'' . $PA['itemFormElName'] . '\', \'RemoveFirstIfFull\', \'' . $maxitems . '\'); ' . $PA['fieldChangeFunc']['TBE_EDITOR_fieldChanged']; } // Acting according to either "file" or "db" type: switch ((string) $config['internal_type']) { case 'file_reference': $config['uploadfolder'] = ''; // Fall through // Fall through case 'file': // Creating string showing allowed types: $tempFT = GeneralUtility::trimExplode(',', $allowed, TRUE); if (!count($tempFT)) { $info .= '*'; } foreach ($tempFT as $ext) { if ($ext) { $info .= strtoupper($ext) . ' '; } } // Creating string, showing disallowed types: $tempFT_dis = GeneralUtility::trimExplode(',', $disallowed, TRUE); if (count($tempFT_dis)) { $info .= '<br />'; } foreach ($tempFT_dis as $ext) { if ($ext) { $info .= '-' . strtoupper($ext) . ' '; } } // Making the array of file items: $itemArray = GeneralUtility::trimExplode(',', $PA['itemFormElValue'], TRUE); $fileFactory = ResourceFactory::getInstance(); // Correct the filename for the FAL items foreach ($itemArray as &$fileItem) { list($fileUid, $fileLabel) = explode('|', $fileItem); if (MathUtility::canBeInterpretedAsInteger($fileUid)) { $fileObject = $fileFactory->getFileObject($fileUid); $fileLabel = $fileObject->getName(); } $fileItem = $fileUid . '|' . $fileLabel; } // Showing thumbnails: $thumbsnail = ''; if ($show_thumbs) { $imgs = array(); foreach ($itemArray as $imgRead) { $imgP = explode('|', $imgRead); $imgPath = rawurldecode($imgP[0]); // FAL icon production if (MathUtility::canBeInterpretedAsInteger($imgP[0])) { $fileObject = $fileFactory->getFileObject($imgP[0]); if ($fileObject->isMissing()) { $flashMessage = \TYPO3\CMS\Core\Resource\Utility\BackendUtility::getFlashMessageForMissingFile($fileObject); $imgs[] = $flashMessage->render(); } elseif (GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], $fileObject->getExtension())) { $imageUrl = $fileObject->process(ProcessedFile::CONTEXT_IMAGEPREVIEW, array())->getPublicUrl(TRUE); $imgTag = '<img src="' . $imageUrl . '" alt="' . htmlspecialchars($fileObject->getName()) . '" />'; $imgs[] = '<span class="nobr">' . $imgTag . htmlspecialchars($fileObject->getName()) . '</span>'; } else { // Icon $imgTag = IconUtility::getSpriteIconForResource($fileObject, array('title' => $fileObject->getName())); $imgs[] = '<span class="nobr">' . $imgTag . htmlspecialchars($fileObject->getName()) . '</span>'; } } else { $rowCopy = array(); $rowCopy[$field] = $imgPath; $thumbnailCode = ''; try { $thumbnailCode = BackendUtility::thumbCode($rowCopy, $table, $field, $this->backPath, 'thumbs.php', $config['uploadfolder'], 0, ' align="middle"'); $thumbnailCode = '<span class="nobr">' . $thumbnailCode . $imgPath . '</span>'; } catch (\Exception $exception) { /** @var $flashMessage FlashMessage */ $message = $exception->getMessage(); $flashMessage = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Messaging\\FlashMessage', htmlspecialchars($message), '', FlashMessage::ERROR, TRUE); $class = 'TYPO3\\CMS\\Core\\Messaging\\FlashMessageService'; /** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */ $flashMessageService = GeneralUtility::makeInstance($class); $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier(); $defaultFlashMessageQueue->enqueue($flashMessage); $logMessage = $message . ' (' . $table . ':' . $row['uid'] . ')'; GeneralUtility::sysLog($logMessage, 'core', GeneralUtility::SYSLOG_SEVERITY_WARNING); } $imgs[] = $thumbnailCode; } } $thumbsnail = implode('<br />', $imgs); } // Creating the element: $params = array('size' => $size, 'dontShowMoveIcons' => $maxitems <= 1, 'autoSizeMax' => MathUtility::forceIntegerInRange($config['autoSizeMax'], 0), 'maxitems' => $maxitems, 'style' => isset($config['selectedListStyle']) ? ' style="' . htmlspecialchars($config['selectedListStyle']) . '"' : ' style="' . $this->defaultMultipleSelectorStyle . '"', 'info' => $info, 'thumbnails' => $thumbsnail, 'readOnly' => $disabled, 'noBrowser' => $noList || isset($config['disable_controls']) && GeneralUtility::inList($config['disable_controls'], 'browser'), 'noList' => $noList, 'noDelete' => $noDelete); $item .= $this->dbFileIcons($PA['itemFormElName'], 'file', implode(',', $tempFT), $itemArray, '', $params, $PA['onFocus'], '', '', '', $config); if (!$disabled && !(isset($config['disable_controls']) && GeneralUtility::inList($config['disable_controls'], 'upload'))) { // Adding the upload field: if ($this->edit_docModuleUpload && $config['uploadfolder']) { // Insert the multiple attribute to enable HTML5 multiple file upload $multipleAttribute = ''; $multipleFilenameSuffix = ''; if (isset($config['maxitems']) && $config['maxitems'] > 1) { $multipleAttribute = ' multiple="multiple"'; $multipleFilenameSuffix = '[]'; } $item .= '<div id="' . $PA['itemFormElID_file'] . '"><input type="file"' . $multipleAttribute . ' name="' . $PA['itemFormElName_file'] . $multipleFilenameSuffix . '" size="35" onchange="' . implode('', $PA['fieldChangeFunc']) . '" /></div>'; } } break; case 'folder': // If the element is of the internal type "folder": // Array of folder items: $itemArray = GeneralUtility::trimExplode(',', $PA['itemFormElValue'], TRUE); // Creating the element: $params = array('size' => $size, 'dontShowMoveIcons' => $maxitems <= 1, 'autoSizeMax' => MathUtility::forceIntegerInRange($config['autoSizeMax'], 0), 'maxitems' => $maxitems, 'style' => isset($config['selectedListStyle']) ? ' style="' . htmlspecialchars($config['selectedListStyle']) . '"' : ' style="' . $this->defaultMultipleSelectorStyle . '"', 'info' => $info, 'readOnly' => $disabled, 'noBrowser' => $noList || isset($config['disable_controls']) && GeneralUtility::inList($config['disable_controls'], 'browser'), 'noList' => $noList); $item .= $this->dbFileIcons($PA['itemFormElName'], 'folder', '', $itemArray, '', $params, $PA['onFocus']); break; case 'db': // If the element is of the internal type "db": // Creating string showing allowed types: $tempFT = GeneralUtility::trimExplode(',', $allowed, TRUE); $onlySingleTableAllowed = FALSE; if (trim($tempFT[0]) === '*') { $info .= '<span class="nobr">' . htmlspecialchars($this->getLL('l_allTables')) . '</span><br />'; } elseif ($tempFT) { $onlySingleTableAllowed = count($tempFT) == 1; foreach ($tempFT as $theT) { $aOnClick = 'setFormValueOpenBrowser(\'db\', \'' . ($PA['itemFormElName'] . '|||' . $theT) . '\'); return false;'; $info .= '<span class="nobr"> <a href="#" onclick="' . htmlspecialchars($aOnClick) . '">' . IconUtility::getSpriteIconForRecord($theT, array()) . htmlspecialchars($this->sL($GLOBALS['TCA'][$theT]['ctrl']['title'])) . '</a></span><br />'; } } $perms_clause = $this->getBackendUserAuthentication()->getPagePermsClause(1); $itemArray = array(); $imgs = array(); // Thumbnails: $temp_itemArray = GeneralUtility::trimExplode(',', $PA['itemFormElValue'], TRUE); foreach ($temp_itemArray as $dbRead) { $recordParts = explode('|', $dbRead); list($this_table, $this_uid) = BackendUtility::splitTable_Uid($recordParts[0]); // For the case that no table was found and only a single table is defined to be allowed, use that one: if (!$this_table && $onlySingleTableAllowed) { $this_table = $allowed; } $itemArray[] = array('table' => $this_table, 'id' => $this_uid); if (!$disabled && $show_thumbs) { $rr = BackendUtility::getRecordWSOL($this_table, $this_uid); $imgs[] = '<span class="nobr">' . $this->getClickMenu(IconUtility::getSpriteIconForRecord($this_table, $rr, array('style' => 'vertical-align:top', 'title' => htmlspecialchars(BackendUtility::getRecordPath($rr['pid'], $perms_clause, 15) . ' [UID: ' . $rr['uid'] . ']'))), $this_table, $this_uid) . ' ' . BackendUtility::getRecordTitle($this_table, $rr, TRUE) . ' <span class="typo3-dimmed"><em>[' . $rr['uid'] . ']</em></span>' . '</span>'; } } $thumbsnail = ''; if (!$disabled && $show_thumbs) { $thumbsnail = implode('<br />', $imgs); } // Creating the element: $params = array('size' => $size, 'dontShowMoveIcons' => $maxitems <= 1, 'autoSizeMax' => MathUtility::forceIntegerInRange($config['autoSizeMax'], 0), 'maxitems' => $maxitems, 'style' => isset($config['selectedListStyle']) ? ' style="' . htmlspecialchars($config['selectedListStyle']) . '"' : ' style="' . $this->defaultMultipleSelectorStyle . '"', 'info' => $info, 'thumbnails' => $thumbsnail, 'readOnly' => $disabled, 'noBrowser' => $noList || isset($config['disable_controls']) && GeneralUtility::inList($config['disable_controls'], 'browser'), 'noList' => $noList); $item .= $this->dbFileIcons($PA['itemFormElName'], 'db', implode(',', $tempFT), $itemArray, '', $params, $PA['onFocus'], $table, $field, $row['uid'], $config); break; } // Wizards: $altItem = '<input type="hidden" name="' . $PA['itemFormElName'] . '" value="' . htmlspecialchars($PA['itemFormElValue']) . '" />'; if (!$disabled) { $item = $this->renderWizards(array($item, $altItem), $config['wizards'], $table, $row, $field, $PA, $PA['itemFormElName'], $specConf); } return $item; }
/** * Determine and get the value for the placeholder and return the placeholder attribute * * @param string $table * @param string $field * @param array $config * @param array $row * @return string */ protected function getPlaceholderAttribute($table, $field, array $config, array $row) { $value = trim($config['placeholder']); if (!$value) { return ''; } // Check if we have a reference to another field value from the current record if (substr($value, 0, 6) === '__row|') { $keySegments = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode('|', substr($value, 6)); if (isset($row[$keySegments[0]])) { // First segment (fieldname) exists in the current row $value = $row[$keySegments[0]]; $fieldConf = $GLOBALS['TCA'][$table]['columns'][$keySegments[0]]; if ($fieldConf['config']['type'] === 'group' && $fieldConf['config']['internal_type'] === 'db') { // The field is a relation to another record list($foreignIdentifier, $foreignTitle) = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode('|', $value); // Use the foreign title $value = $foreignTitle; if (!empty($keySegments[1])) { // Use any field in the foreign record list($foreignTable, $foreignUid) = \TYPO3\CMS\Backend\Utility\BackendUtility::splitTable_Uid($foreignIdentifier); $foreignRecord = \t3lib_befunc::getRecord($foreignTable, $foreignUid); if (isset($foreignRecord[$keySegments[1]])) { $value = $foreignRecord[$keySegments[1]]; } } } elseif (!empty($keySegments[1]) && isset($row[$keySegments[0]][$keySegments[1]])) { $value = $row[$keySegments[0]][$keySegments[1]]; } } } // Cleanup the string and support 'LLL:' $value = htmlspecialchars(trim($this->sL($value))); return empty($value) ? '' : ' placeholder="' . $value . '" '; }
/** * Renders the HTML header for a foreign record, such as the title, toggle-function, drag'n'drop, etc. * Later on the command-icons are inserted here. * * @param string $parentUid The uid of the parent (embedding) record (uid or NEW...) * @param string $foreign_table The foreign_table we create a header for * @param array $rec The current record of that foreign_table * @param array $config content of $PA['fieldConf']['config'] * @param boolean $isVirtualRecord * @return string The HTML code of the header * @todo Define visibility */ public function renderForeignRecordHeader($parentUid, $foreign_table, $rec, $config, $isVirtualRecord = FALSE) { // Init: $objectId = $this->inlineNames['object'] . self::Structure_Separator . $foreign_table . self::Structure_Separator . $rec['uid']; // We need the returnUrl of the main script when loading the fields via AJAX-call (to correct wizard code, so include it as 3rd parameter) // Pre-Processing: $isOnSymmetricSide = \TYPO3\CMS\Core\Database\RelationHandler::isOnSymmetricSide($parentUid, $config, $rec); $hasForeignLabel = !$isOnSymmetricSide && $config['foreign_label'] ? TRUE : FALSE; $hasSymmetricLabel = $isOnSymmetricSide && $config['symmetric_label'] ? TRUE : FALSE; // Get the record title/label for a record: // render using a self-defined user function if ($GLOBALS['TCA'][$foreign_table]['ctrl']['label_userFunc']) { $params = array('table' => $foreign_table, 'row' => $rec, 'title' => '', 'isOnSymmetricSide' => $isOnSymmetricSide, 'parent' => array('uid' => $parentUid, 'config' => $config)); // callUserFunction requires a third parameter, but we don't want to give $this as reference! $null = NULL; \TYPO3\CMS\Core\Utility\GeneralUtility::callUserFunction($GLOBALS['TCA'][$foreign_table]['ctrl']['label_userFunc'], $params, $null); $recTitle = $params['title']; } elseif ($hasForeignLabel || $hasSymmetricLabel) { $titleCol = $hasForeignLabel ? $config['foreign_label'] : $config['symmetric_label']; $foreignConfig = $this->getPossibleRecordsSelectorConfig($config, $titleCol); // Render title for everything else than group/db: if ($foreignConfig['type'] != 'groupdb') { $recTitle = \TYPO3\CMS\Backend\Utility\BackendUtility::getProcessedValueExtra($foreign_table, $titleCol, $rec[$titleCol], 0, 0, FALSE); } else { // $recTitle could be something like: "tx_table_123|...", $valueParts = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode('|', $rec[$titleCol]); $itemParts = \TYPO3\CMS\Core\Utility\GeneralUtility::revExplode('_', $valueParts[0], 2); $recTemp = \t3lib_befunc::getRecordWSOL($itemParts[0], $itemParts[1]); $recTitle = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecordTitle($itemParts[0], $recTemp, FALSE); } $recTitle = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecordTitlePrep($recTitle); if (!strcmp(trim($recTitle), '')) { $recTitle = \TYPO3\CMS\Backend\Utility\BackendUtility::getNoRecordTitle(TRUE); } } else { $recTitle = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecordTitle($foreign_table, $rec, TRUE); } // Renders a thumbnail for the header if (!empty($config['appearance']['headerThumbnail']['field'])) { $fieldValue = $rec[$config['appearance']['headerThumbnail']['field']]; $firstElement = array_shift(\TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $fieldValue)); $fileUid = array_pop(\TYPO3\CMS\Backend\Utility\BackendUtility::splitTable_Uid($firstElement)); if (!empty($fileUid)) { $fileObject = \TYPO3\CMS\Core\Resource\ResourceFactory::getInstance()->getFileObject($fileUid); if ($fileObject) { $imageSetup = $config['appearance']['headerThumbnail']; unset($imageSetup['field']); $imageSetup = array_merge(array('width' => 64, 'height' => 64), $imageSetup); $imageUrl = $fileObject->process(\TYPO3\CMS\Core\Resource\ProcessedFile::CONTEXT_IMAGEPREVIEW, $imageSetup)->getPublicUrl(TRUE); $thumbnail = '<img src="' . $imageUrl . '" alt="' . htmlspecialchars($recTitle) . '">'; } else { $thumbnail = FALSE; } } } $altText = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecordIconAltText($rec, $foreign_table); $iconImg = \TYPO3\CMS\Backend\Utility\IconUtility::getSpriteIconForRecord($foreign_table, $rec, array('title' => htmlspecialchars($altText), 'id' => $objectId . '_icon')); $label = '<span id="' . $objectId . '_label">' . $recTitle . '</span>'; $ctrl = $this->renderForeignRecordHeaderControl($parentUid, $foreign_table, $rec, $config, $isVirtualRecord); $header = '<table>' . '<tr>' . (!empty($config['appearance']['headerThumbnail']['field']) && $thumbnail ? '<td class="t3-form-field-header-inline-thumbnail" id="' . $objectId . '_thumbnailcontainer">' . $thumbnail . '</td>' : '<td class="t3-form-field-header-inline-icon" id="' . $objectId . '_iconcontainer">' . $iconImg . '</td>') . '<td class="t3-form-field-header-inline-summary">' . $label . '</td>' . '<td clasS="t3-form-field-header-inline-ctrl">' . $ctrl . '</td>' . '</tr>' . '</table>'; return $header; }
/** * Tries to get the related UIDs of a group field. * * @param array $fieldConfig "config" section from the TCA for the current field. * @param string $value The current value (normally a comma separated record list, possibly consisting of multiple parts [table]_[uid]|[title]). * @return array Array of related UIDs. */ protected function getRelatedGroupFieldUids(array $fieldConfig, $value) { $relatedUids = array(); $allowedTable = $this->getAllowedTableForGroupField($fieldConfig); if ($fieldConfig['internal_type'] !== 'db' || $allowedTable === FALSE) { return $relatedUids; } $values = GeneralUtility::trimExplode(',', $value, TRUE); foreach ($values as $groupValue) { list($foreignIdentifier, $foreignTitle) = GeneralUtility::trimExplode('|', $groupValue); list($recordForeignTable, $foreignUid) = BackendUtility::splitTable_Uid($foreignIdentifier); // skip records that do not match the allowed table if (!empty($recordForeignTable) && $recordForeignTable !== $allowedTable) { continue; } if (!empty($foreignTitle)) { $this->alternativeFieldValue = rawurldecode($foreignTitle); } $relatedUids[] = $foreignUid; } if (count($relatedUids) > 0) { $this->currentTable = $allowedTable; } return $relatedUids; }
/** * Renders the HTML header for a foreign record, such as the title, toggle-function, drag'n'drop, etc. * Later on the command-icons are inserted here. * * @param string $parentUid The uid of the parent (embedding) record (uid or NEW...) * @param string $foreign_table The foreign_table we create a header for * @param array $data Current data * @param array $config content of $PA['fieldConf']['config'] * @param bool $isVirtualRecord * @return string The HTML code of the header */ protected function renderForeignRecordHeader($parentUid, $foreign_table, $data, $config, $isVirtualRecord = false) { $rec = $data['databaseRow']; // Init: $domObjectId = $this->inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($this->data['inlineFirstPid']); $objectId = $domObjectId . '-' . $foreign_table . '-' . $rec['uid']; // We need the returnUrl of the main script when loading the fields via AJAX-call (to correct wizard code, so include it as 3rd parameter) // Pre-Processing: $isOnSymmetricSide = RelationHandler::isOnSymmetricSide($parentUid, $config, $rec); $hasForeignLabel = (bool) (!$isOnSymmetricSide && $config['foreign_label']); $hasSymmetricLabel = (bool) $isOnSymmetricSide && $config['symmetric_label']; // Get the record title/label for a record: // Try using a self-defined user function only for formatted labels if (isset($GLOBALS['TCA'][$foreign_table]['ctrl']['formattedLabel_userFunc'])) { $params = array('table' => $foreign_table, 'row' => $rec, 'title' => '', 'isOnSymmetricSide' => $isOnSymmetricSide, 'options' => isset($GLOBALS['TCA'][$foreign_table]['ctrl']['formattedLabel_userFunc_options']) ? $GLOBALS['TCA'][$foreign_table]['ctrl']['formattedLabel_userFunc_options'] : array(), 'parent' => array('uid' => $parentUid, 'config' => $config)); // callUserFunction requires a third parameter, but we don't want to give $this as reference! $null = null; GeneralUtility::callUserFunction($GLOBALS['TCA'][$foreign_table]['ctrl']['formattedLabel_userFunc'], $params, $null); $recTitle = $params['title']; // Try using a normal self-defined user function } elseif (isset($GLOBALS['TCA'][$foreign_table]['ctrl']['label_userFunc'])) { $recTitle = $data['recordTitle']; } elseif ($hasForeignLabel || $hasSymmetricLabel) { $titleCol = $hasForeignLabel ? $config['foreign_label'] : $config['symmetric_label']; // Render title for everything else than group/db: if (isset($this->data['processedTca']['columns'][$titleCol]['config']['type']) && $this->data['processedTca']['columns'][$titleCol]['config']['type'] === 'group' && isset($this->data['processedTca']['columns'][$titleCol]['config']['internal_type']) && $this->data['processedTca']['columns'][$titleCol]['config']['internal_type'] === 'db') { $recTitle = BackendUtility::getProcessedValueExtra($foreign_table, $titleCol, $rec[$titleCol], 0, 0, false); } else { // $recTitle could be something like: "tx_table_123|...", $valueParts = GeneralUtility::trimExplode('|', $rec[$titleCol]); $itemParts = GeneralUtility::revExplode('_', $valueParts[0], 2); $recTemp = BackendUtility::getRecordWSOL($itemParts[0], $itemParts[1]); $recTitle = BackendUtility::getRecordTitle($itemParts[0], $recTemp, false); } $recTitle = BackendUtility::getRecordTitlePrep($recTitle); if (trim($recTitle) === '') { $recTitle = BackendUtility::getNoRecordTitle(true); } } else { $recTitle = BackendUtility::getRecordTitle($foreign_table, FormEngineUtility::databaseRowCompatibility($rec), true); } $altText = BackendUtility::getRecordIconAltText($rec, $foreign_table); $iconImg = '<span title="' . $altText . '" id="' . htmlspecialchars($objectId) . '_icon' . '">' . $this->iconFactory->getIconForRecord($foreign_table, $rec, Icon::SIZE_SMALL)->render() . '</span>'; $label = '<span id="' . $objectId . '_label">' . $recTitle . '</span>'; $ctrl = $this->renderForeignRecordHeaderControl($parentUid, $foreign_table, $data, $config, $isVirtualRecord); $thumbnail = false; // Renders a thumbnail for the header if (!empty($config['appearance']['headerThumbnail']['field'])) { $fieldValue = $rec[$config['appearance']['headerThumbnail']['field']]; $firstElement = array_shift(GeneralUtility::trimExplode('|', array_shift(GeneralUtility::trimExplode(',', $fieldValue)))); $fileUid = array_pop(BackendUtility::splitTable_Uid($firstElement)); if (!empty($fileUid)) { $fileObject = ResourceFactory::getInstance()->getFileObject($fileUid); if ($fileObject && $fileObject->isMissing()) { $flashMessage = \TYPO3\CMS\Core\Resource\Utility\BackendUtility::getFlashMessageForMissingFile($fileObject); $thumbnail = $flashMessage->render(); } elseif ($fileObject) { $imageSetup = $config['appearance']['headerThumbnail']; unset($imageSetup['field']); if (!empty($rec['crop'])) { $imageSetup['crop'] = $rec['crop']; } $imageSetup = array_merge(array('width' => '45', 'height' => '45c'), $imageSetup); $processedImage = $fileObject->process(ProcessedFile::CONTEXT_IMAGECROPSCALEMASK, $imageSetup); // Only use a thumbnail if the processing process was successful by checking if image width is set if ($processedImage->getProperty('width')) { $imageUrl = $processedImage->getPublicUrl(true); $thumbnail = '<img src="' . $imageUrl . '" ' . 'width="' . $processedImage->getProperty('width') . '" ' . 'height="' . $processedImage->getProperty('height') . '" ' . 'alt="' . htmlspecialchars($altText) . '" ' . 'title="' . htmlspecialchars($altText) . '">'; } } } } if (!empty($config['appearance']['headerThumbnail']['field']) && $thumbnail) { $mediaContainer = '<div class="form-irre-header-cell form-irre-header-thumbnail" id="' . $objectId . '_thumbnailcontainer">' . $thumbnail . '</div>'; } else { $mediaContainer = '<div class="form-irre-header-cell form-irre-header-icon" id="' . $objectId . '_iconcontainer">' . $iconImg . '</div>'; } $header = $mediaContainer . ' <div class="form-irre-header-cell form-irre-header-body">' . $label . '</div> <div class="form-irre-header-cell form-irre-header-control t3js-formengine-irre-control">' . $ctrl . '</div>'; return $header; }
/** * Draws the preview content for a content element * * @param array $row Content element * @return string HTML * @throws \UnexpectedValueException */ public function tt_content_drawItem($row) { $out = ''; $outHeader = ''; // Make header: if ($row['header']) { $infoArr = array(); $this->getProcessedValue('tt_content', 'header_position,header_layout,header_link', $row, $infoArr); $hiddenHeaderNote = ''; // If header layout is set to 'hidden', display an accordant note: if ($row['header_layout'] == 100) { $hiddenHeaderNote = ' <em>[' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.hidden', true) . ']</em>'; } $outHeader = $row['date'] ? htmlspecialchars($this->itemLabels['date'] . ' ' . BackendUtility::date($row['date'])) . '<br />' : ''; $outHeader .= '<strong>' . $this->linkEditContent($this->renderText($row['header']), $row) . $hiddenHeaderNote . '</strong><br />'; } // Make content: $infoArr = array(); $drawItem = true; // Hook: Render an own preview of a record $drawItemHooks =& $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/class.tx_cms_layout.php']['tt_content_drawItem']; if (is_array($drawItemHooks)) { foreach ($drawItemHooks as $hookClass) { $hookObject = GeneralUtility::getUserObj($hookClass); if (!$hookObject instanceof PageLayoutViewDrawItemHookInterface) { throw new \UnexpectedValueException('$hookObject must implement interface ' . \TYPO3\CMS\Backend\View\PageLayoutViewDrawItemHookInterface::class, 1218547409); } $hookObject->preProcess($this, $drawItem, $outHeader, $out, $row); } } // If the previous hook did not render something, // then check if a Fluid-based preview template was defined for this CType // and render it via Fluid. Possible option: // mod.web_layout.tt_content.preview.media = EXT:site_mysite/Resources/Private/Templates/Preview/Media.html if ($drawItem) { $tsConfig = BackendUtility::getModTSconfig($row['pid'], 'mod.web_layout.tt_content.preview'); if (!empty($tsConfig['properties'][$row['CType']])) { $fluidTemplateFile = $tsConfig['properties'][$row['CType']]; $fluidTemplateFile = GeneralUtility::getFileAbsFileName($fluidTemplateFile); if ($fluidTemplateFile) { try { /** @var StandaloneView $view */ $view = GeneralUtility::makeInstance(StandaloneView::class); $view->setTemplatePathAndFilename($fluidTemplateFile); $view->assignMultiple($row); if (!empty($row['pi_flexform'])) { /** @var FlexFormService $flexFormService */ $flexFormService = GeneralUtility::makeInstance(FlexFormService::class); $view->assign('pi_flexform_transformed', $flexFormService->convertFlexFormContentToArray($row['pi_flexform'])); } $out = $view->render(); $drawItem = false; } catch (\Exception $e) { // Catch any exception to avoid breaking the view } } } } // Draw preview of the item depending on its CType (if not disabled by previous hook): if ($drawItem) { switch ($row['CType']) { case 'header': if ($row['subheader']) { $out .= $this->linkEditContent($this->renderText($row['subheader']), $row) . '<br />'; } break; case 'bullets': case 'table': if ($row['bodytext']) { $out .= $this->linkEditContent($this->renderText($row['bodytext']), $row) . '<br />'; } break; case 'uploads': if ($row['media']) { $out .= $this->linkEditContent($this->getThumbCodeUnlinked($row, 'tt_content', 'media'), $row) . '<br />'; } break; case 'menu': $contentType = $this->CType_labels[$row['CType']]; $out .= $this->linkEditContent('<strong>' . htmlspecialchars($contentType) . '</strong>', $row) . '<br />'; // Add Menu Type $menuTypeLabel = $this->getLanguageService()->sL(BackendUtility::getLabelFromItemListMerged($row['pid'], 'tt_content', 'menu_type', $row['menu_type'])); $menuTypeLabel = $menuTypeLabel ?: 'invalid menu type'; $out .= $this->linkEditContent($menuTypeLabel, $row); if ($row['menu_type'] !== '2' && ($row['pages'] || $row['selected_categories'])) { // Show pages if menu type is not "Sitemap" $out .= ':' . $this->linkEditContent($this->generateListForCTypeMenu($row), $row) . '<br />'; } break; case 'shortcut': if (!empty($row['records'])) { $shortcutContent = array(); $recordList = explode(',', $row['records']); foreach ($recordList as $recordIdentifier) { $split = BackendUtility::splitTable_Uid($recordIdentifier); $tableName = empty($split[0]) ? 'tt_content' : $split[0]; $shortcutRecord = BackendUtility::getRecord($tableName, $split[1]); if (is_array($shortcutRecord)) { $icon = $this->iconFactory->getIconForRecord($tableName, $shortcutRecord, Icon::SIZE_SMALL)->render(); $icon = BackendUtility::wrapClickMenuOnIcon($icon, $tableName, $shortcutRecord['uid'], 1, '', '+copy,info,edit,view'); $shortcutContent[] = $icon . htmlspecialchars(BackendUtility::getRecordTitle($tableName, $shortcutRecord)); } } $out .= implode('<br />', $shortcutContent) . '<br />'; } break; case 'list': $hookArr = array(); $hookOut = ''; if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/class.tx_cms_layout.php']['list_type_Info'][$row['list_type']])) { $hookArr = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/class.tx_cms_layout.php']['list_type_Info'][$row['list_type']]; } elseif (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/class.tx_cms_layout.php']['list_type_Info']['_DEFAULT'])) { $hookArr = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/class.tx_cms_layout.php']['list_type_Info']['_DEFAULT']; } if (!empty($hookArr)) { $_params = array('pObj' => &$this, 'row' => $row, 'infoArr' => $infoArr); foreach ($hookArr as $_funcRef) { $hookOut .= GeneralUtility::callUserFunction($_funcRef, $_params, $this); } } if ((string) $hookOut !== '') { $out .= $hookOut; } elseif (!empty($row['list_type'])) { $label = BackendUtility::getLabelFromItemListMerged($row['pid'], 'tt_content', 'list_type', $row['list_type']); if (!empty($label)) { $out .= $this->linkEditContent('<strong>' . $this->getLanguageService()->sL($label, true) . '</strong>', $row) . '<br />'; } else { $message = sprintf($this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.noMatchingValue'), $row['list_type']); $out .= GeneralUtility::makeInstance(FlashMessage::class, htmlspecialchars($message), '', FlashMessage::WARNING)->render(); } } elseif (!empty($row['select_key'])) { $out .= $this->getLanguageService()->sL(BackendUtility::getItemLabel('tt_content', 'select_key'), true) . ' ' . $row['select_key'] . '<br />'; } else { $out .= '<strong>' . $this->getLanguageService()->getLL('noPluginSelected') . '</strong>'; } $out .= $this->getLanguageService()->sL(BackendUtility::getLabelFromItemlist('tt_content', 'pages', $row['pages']), true) . '<br />'; break; default: $contentType = $this->CType_labels[$row['CType']]; if (isset($contentType)) { $out .= $this->linkEditContent('<strong>' . htmlspecialchars($contentType) . '</strong>', $row) . '<br />'; if ($row['bodytext']) { $out .= $this->linkEditContent($this->renderText($row['bodytext']), $row) . '<br />'; } if ($row['image']) { $out .= $this->linkEditContent($this->getThumbCodeUnlinked($row, 'tt_content', 'image'), $row) . '<br />'; } } else { $message = sprintf($this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.noMatchingValue'), $row['CType']); $out .= GeneralUtility::makeInstance(FlashMessage::class, htmlspecialchars($message), '', FlashMessage::WARNING)->render(); } } } // Wrap span-tags: $out = ' <span class="exampleContent">' . $out . '</span>'; // Add header: $out = $outHeader . $out; // Return values: if ($this->isDisabled('tt_content', $row)) { return '<span class="text-muted">' . $out . '</span>'; } else { return $out; } }
/** * Entry method * * @return array As defined in initializeResultArray() of AbstractNode */ public function render() { $backendUser = $this->getBackendUserAuthentication(); $languageService = $this->getLanguageService(); $resultArray = $this->initializeResultArray(); $table = $this->data['tableName']; $row = $this->data['databaseRow']; $fieldName = $this->data['fieldName']; // @todo: it should be safe at this point, this array exists ... if (!is_array($this->data['processedTca']['columns'][$fieldName])) { return $resultArray; } $parameterArray = []; $parameterArray['fieldConf'] = $this->data['processedTca']['columns'][$fieldName]; $isOverlay = false; // This field decides whether the current record is an overlay (as opposed to being a standalone record) // Based on this decision we need to trigger field exclusion or special rendering (like readOnly) if (isset($this->data['processedTca']['ctrl']['transOrigPointerField']) && is_array($this->data['processedTca']['columns'][$this->data['processedTca']['ctrl']['transOrigPointerField']])) { $parentValue = $row[$this->data['processedTca']['ctrl']['transOrigPointerField']]; if (MathUtility::canBeInterpretedAsInteger($parentValue)) { $isOverlay = (bool) $parentValue; } elseif (is_array($parentValue)) { // This case may apply if the value has been converted to an array by the select data provider $isOverlay = !empty($parentValue) ? (bool) $parentValue[0] : false; } elseif (is_string($parentValue) && $parentValue !== '') { // This case may apply if a group definition is used in TCA and the group provider builds a weird string $recordsReferencedInField = GeneralUtility::trimExplode(',', $parentValue); // Pick the first record because if you set multiple records you're in trouble anyways $recordIdentifierParts = GeneralUtility::trimExplode('|', $recordsReferencedInField[0]); list(, $refUid) = BackendUtility::splitTable_Uid($recordIdentifierParts[0]); $isOverlay = MathUtility::canBeInterpretedAsInteger($refUid) ? (bool) $refUid : false; } else { throw new \InvalidArgumentException('The given value for the original language field ' . $this->data['processedTca']['ctrl']['transOrigPointerField'] . ' of table ' . $table . ' contains an invalid value.', 1470742770); } } // A couple of early returns in case the field should not be rendered // Check if this field is configured and editable according to exclude fields and other configuration if ($parameterArray['fieldConf']['exclude'] && !$backendUser->check('non_exclude_fields', $table . ':' . $fieldName) || $parameterArray['fieldConf']['config']['type'] === 'passthrough' || $isOverlay && empty($parameterArray['fieldConf']['l10n_display']) && $parameterArray['fieldConf']['l10n_mode'] === 'exclude' || $isOverlay && $this->data['localizationMode'] && $this->data['localizationMode'] !== $parameterArray['fieldConf']['l10n_cat'] || $this->inlineFieldShouldBeSkipped()) { return $resultArray; } $parameterArray['fieldTSConfig'] = []; if (isset($this->data['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']) && is_array($this->data['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.'])) { $parameterArray['fieldTSConfig'] = $this->data['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']; } if ($parameterArray['fieldTSConfig']['disabled']) { return $resultArray; } // Override fieldConf by fieldTSconfig: $parameterArray['fieldConf']['config'] = FormEngineUtility::overrideFieldConf($parameterArray['fieldConf']['config'], $parameterArray['fieldTSConfig']); $parameterArray['itemFormElName'] = 'data[' . $table . '][' . $row['uid'] . '][' . $fieldName . ']'; $parameterArray['itemFormElID'] = 'data_' . $table . '_' . $row['uid'] . '_' . $fieldName; $newElementBaseName = $this->data['elementBaseName'] . '[' . $table . '][' . $row['uid'] . '][' . $fieldName . ']'; // The value to show in the form field. $parameterArray['itemFormElValue'] = $row[$fieldName]; // Set field to read-only if configured for translated records to show default language content as readonly if ($parameterArray['fieldConf']['l10n_display'] && GeneralUtility::inList($parameterArray['fieldConf']['l10n_display'], 'defaultAsReadonly') && $isOverlay) { $parameterArray['fieldConf']['config']['readOnly'] = true; $parameterArray['itemFormElValue'] = $this->data['defaultLanguageRow'][$fieldName]; } if (strpos($this->data['processedTca']['ctrl']['type'], ':') === false) { $typeField = $this->data['processedTca']['ctrl']['type']; } else { $typeField = substr($this->data['processedTca']['ctrl']['type'], 0, strpos($this->data['processedTca']['ctrl']['type'], ':')); } // Create a JavaScript code line which will ask the user to save/update the form due to changing the element. // This is used for eg. "type" fields and others configured with "requestUpdate" if (!empty($this->data['processedTca']['ctrl']['type']) && $fieldName === $typeField || !empty($this->data['processedTca']['ctrl']['requestUpdate']) && GeneralUtility::inList(str_replace(' ', '', $this->data['processedTca']['ctrl']['requestUpdate']), $fieldName)) { if ($backendUser->jsConfirmation(JsConfirmation::TYPE_CHANGE)) { $alertMsgOnChange = 'top.TYPO3.Modal.confirm(TYPO3.lang["FormEngine.refreshRequiredTitle"], TYPO3.lang["FormEngine.refreshRequiredContent"]).on("button.clicked", function(e) { if (e.target.name == "ok" && TBE_EDITOR.checkSubmit(-1)) { TBE_EDITOR.submitForm() } top.TYPO3.Modal.dismiss(); });'; } else { $alertMsgOnChange = 'if (TBE_EDITOR.checkSubmit(-1)){ TBE_EDITOR.submitForm() };'; } } else { $alertMsgOnChange = ''; } // JavaScript code for event handlers: $parameterArray['fieldChangeFunc'] = []; $parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged'] = 'TBE_EDITOR.fieldChanged(' . GeneralUtility::quoteJSvalue($table) . ',' . GeneralUtility::quoteJSvalue($row['uid']) . ',' . GeneralUtility::quoteJSvalue($fieldName) . ',' . GeneralUtility::quoteJSvalue($parameterArray['itemFormElName']) . ');'; if ($alertMsgOnChange) { $parameterArray['fieldChangeFunc']['alert'] = $alertMsgOnChange; } // If this is the child of an inline type and it is the field creating the label if ($this->isInlineChildAndLabelField($table, $fieldName)) { /** @var InlineStackProcessor $inlineStackProcessor */ $inlineStackProcessor = GeneralUtility::makeInstance(InlineStackProcessor::class); $inlineStackProcessor->initializeByGivenStructure($this->data['inlineStructure']); $inlineDomObjectId = $inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($this->data['inlineFirstPid']); $inlineObjectId = implode('-', [$inlineDomObjectId, $table, $row['uid']]); $parameterArray['fieldChangeFunc']['inline'] = 'inline.handleChangedField(' . GeneralUtility::quoteJSvalue($parameterArray['itemFormElName']) . ',' . GeneralUtility::quoteJSvalue($inlineObjectId) . ');'; } // Based on the type of the item, call a render function on a child element $options = $this->data; $options['parameterArray'] = $parameterArray; $options['elementBaseName'] = $newElementBaseName; if (!empty($parameterArray['fieldConf']['config']['renderType'])) { $options['renderType'] = $parameterArray['fieldConf']['config']['renderType']; } else { // Fallback to type if no renderType is given $options['renderType'] = $parameterArray['fieldConf']['config']['type']; } $resultArray = $this->nodeFactory->create($options)->render(); // If output is empty stop further processing. // This means there was internal processing only and we don't need to add additional information if (empty($resultArray['html'])) { return $resultArray; } $html = $resultArray['html']; // @todo: the language handling, the null and the placeholder stuff should be embedded in the single // @todo: element classes. Basically, this method should return here and have the element classes // @todo: decide on language stuff and other wraps already. // Add language + diff $renderLanguageDiff = true; if ($parameterArray['fieldConf']['l10n_display'] && (GeneralUtility::inList($parameterArray['fieldConf']['l10n_display'], 'hideDiff') || GeneralUtility::inList($parameterArray['fieldConf']['l10n_display'], 'defaultAsReadonly'))) { $renderLanguageDiff = false; } if ($renderLanguageDiff) { $html = $this->renderDefaultLanguageContent($table, $fieldName, $row, $html); $html = $this->renderDefaultLanguageDiff($table, $fieldName, $row, $html); } $fieldItemClasses = ['t3js-formengine-field-item']; // NULL value and placeholder handling $nullControlNameAttribute = ' name="' . htmlspecialchars('control[active][' . $table . '][' . $row['uid'] . '][' . $fieldName . ']') . '"'; if (!empty($parameterArray['fieldConf']['config']['eval']) && GeneralUtility::inList($parameterArray['fieldConf']['config']['eval'], 'null') && (empty($parameterArray['fieldConf']['config']['mode']) || $parameterArray['fieldConf']['config']['mode'] !== 'useOrOverridePlaceholder')) { // This field has eval=null set, but has no useOverridePlaceholder defined. // Goal is to have a field that can distinct between NULL and empty string in the database. // A checkbox and an additional hidden field will be created, both with the same name // and prefixed with "control[active]". If the checkbox is set (value 1), the value from the casual // input field will be written to the database. If the checkbox is not set, the hidden field // transfers value=0 to DataHandler, the value of the input field will then be reset to NULL by the // DataHandler at an early point in processing, so NULL will be written to DB as field value. // If the value of the field *is* NULL at the moment, an additional class is set // @todo: This does not work well at the moment, but is kept for now. see input_14 of ext:styleguide as example $checked = ' checked="checked"'; if ($this->data['databaseRow'][$fieldName] === null) { $fieldItemClasses[] = 'disabled'; $checked = ''; } $formElementName = 'data[' . $table . '][' . $row['uid'] . '][' . $fieldName . ']'; $onChange = htmlspecialchars('typo3form.fieldSetNull(' . GeneralUtility::quoteJSvalue($formElementName) . ', !this.checked)'); $nullValueWrap = []; $nullValueWrap[] = '<div class="' . implode(' ', $fieldItemClasses) . '">'; $nullValueWrap[] = '<div class="t3-form-field-disable"></div>'; $nullValueWrap[] = '<div class="checkbox t3-form-field-eval-null-checkbox">'; $nullValueWrap[] = '<label>'; $nullValueWrap[] = '<input type="hidden"' . $nullControlNameAttribute . ' value="0" />'; $nullValueWrap[] = '<input type="checkbox"' . $nullControlNameAttribute . ' value="1" onchange="' . $onChange . '"' . $checked . ' /> '; $nullValueWrap[] = '</label>'; $nullValueWrap[] = '</div>'; $nullValueWrap[] = $html; $nullValueWrap[] = '</div>'; $html = implode(LF, $nullValueWrap); } elseif (isset($parameterArray['fieldConf']['config']['mode']) && $parameterArray['fieldConf']['config']['mode'] === 'useOrOverridePlaceholder') { // This field has useOverridePlaceholder set. // Here, a value from a deeper DB structure can be "fetched up" as value, and can also be overridden by a // local value. This is used in FAL, where eg. the "title" field can have the default value from sys_file_metadata, // the title field of sys_file_reference is then set to NULL. Or the "override" checkbox is set, and a string // or an empty string is then written to the field of sys_file_reference. // The situation is similar to the NULL handling above, but additionally a "default" value should be shown. // To achieve this, again a hidden control[hidden] field is added together with a checkbox with the same name // to transfer the information whether the default value should be used or not: Checkbox checked transfers 1 as // value in control[active], meaning the overridden value should be used. // Additionally to the casual input field, a second field is added containing the "placeholder" value. This // field has no name attribute and is not transferred at all. Those two are then hidden / shown depending // on the state of the above checkbox in via JS. $placeholder = empty($parameterArray['fieldConf']['config']['placeholder']) ? '' : $parameterArray['fieldConf']['config']['placeholder']; $onChange = 'typo3form.fieldTogglePlaceholder(' . GeneralUtility::quoteJSvalue($parameterArray['itemFormElName']) . ', !this.checked)'; $checked = $parameterArray['itemFormElValue'] === null ? '' : ' checked="checked"'; $disabled = ''; $fallbackValue = 0; if (strlen(BackendUtility::getRecordTitlePrep($placeholder, 20)) > 0) { $overrideLabel = sprintf($languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.placeholder.override'), BackendUtility::getRecordTitlePrep($placeholder, 20)); } else { $fallbackValue = 1; $checked = ' checked="checked"'; $disabled = ' disabled="disabled"'; $overrideLabel = sprintf($languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.placeholder.override_not_available'), BackendUtility::getRecordTitlePrep($placeholder, 20)); } $resultArray['additionalJavaScriptPost'][] = 'typo3form.fieldTogglePlaceholder(' . GeneralUtility::quoteJSvalue($parameterArray['itemFormElName']) . ', ' . ($checked ? 'false' : 'true') . ');'; // Renders an input or textarea field depending on type of "parent" $options = []; $options['databaseRow'] = []; $options['table'] = ''; $options['parameterArray'] = $parameterArray; $options['parameterArray']['itemFormElValue'] = GeneralUtility::fixed_lgd_cs($placeholder, 30); $options['renderType'] = 'none'; $noneElementResult = $this->nodeFactory->create($options)->render(); $noneElementHtml = $noneElementResult['html']; $placeholderWrap = []; $placeholderWrap[] = '<div class="' . implode(' ', $fieldItemClasses) . '">'; $placeholderWrap[] = '<div class="t3-form-field-disable"></div>'; $placeholderWrap[] = '<div class="checkbox">'; $placeholderWrap[] = '<label>'; $placeholderWrap[] = '<input type="hidden"' . $nullControlNameAttribute . ' value="' . $fallbackValue . '" />'; $placeholderWrap[] = '<input type="checkbox"' . $nullControlNameAttribute . ' value="1" id="tce-forms-textfield-use-override-' . $fieldName . '-' . $row['uid'] . '" onchange="' . htmlspecialchars($onChange) . '"' . $checked . $disabled . ' />'; $placeholderWrap[] = $overrideLabel; $placeholderWrap[] = '</label>'; $placeholderWrap[] = '</div>'; $placeholderWrap[] = '<div class="t3js-formengine-placeholder-placeholder">'; $placeholderWrap[] = $noneElementHtml; $placeholderWrap[] = '</div>'; $placeholderWrap[] = '<div class="t3js-formengine-placeholder-formfield">'; $placeholderWrap[] = $html; $placeholderWrap[] = '</div>'; $placeholderWrap[] = '</div>'; $html = implode(LF, $placeholderWrap); } elseif ($parameterArray['fieldConf']['config']['type'] !== 'user' || empty($parameterArray['fieldConf']['config']['noTableWrapping'])) { // Add a casual wrap if the field is not of type user with no wrap requested. $standardWrap = []; $standardWrap[] = '<div class="' . implode(' ', $fieldItemClasses) . '">'; $standardWrap[] = '<div class="t3-form-field-disable"></div>'; $standardWrap[] = $html; $standardWrap[] = '</div>'; $html = implode(LF, $standardWrap); } $resultArray['html'] = $html; return $resultArray; }