public function renderHtml($name, $value, $options) { $width = $options['width']; // TODO: das Feld beachten! $maxlength = $options['maxlength']; $attributes = array(); // for data-formengine-input-params $paramsList = array('field' => $name, 'evalList' => 'int', 'is_in' => ''); $attributes['id'] = StringUtility::getUniqueId('formengine-input-'); $attributes['value'] = ''; $attributes['data-formengine-validation-rules'] = json_encode(array('type' => 'int')); $attributes['data-formengine-input-params'] = json_encode($paramsList); $attributes['data-formengine-input-name'] = htmlspecialchars($name); $attributeString = ''; foreach ($attributes as $attributeName => $attributeValue) { $attributeString .= ' ' . $attributeName . '="' . htmlspecialchars($attributeValue) . '"'; } //$width = (int)$this->formMaxWidth($size); $width = $GLOBALS['TBE_TEMPLATE']->formWidth($width); $html = ' <input type="text"' . $attributeString . $width . ' />'; // This is the ACTUAL form field - values from the EDITABLE field must be transferred to this field which is the one that is written to the database. $html .= '<input type="hidden" name="' . $name . '" value="' . htmlspecialchars($value) . '" />'; // Den Wrap lassen wir weg, weil es zu einem Zeilenumbruch kommt // $html = '<div class="form-control-wrap"' . $width . '>' . $html . '</div>'; return $html; }
/** * Type fetching method, based on the type that softRefParserObj returns * * @param array $value Reference properties * @param string $type Current type * @param string $key Validator hook name * @return string fetched type */ public function fetchType($value, $type, $key) { if (StringUtility::beginswith(strtolower($value['tokenValue']), 'file:')) { $type = 'file'; } return $type; }
/** * Handler for unknown types. * * @return array As defined in initializeResultArray() of AbstractNode */ public function render() { $resultArray = $this->initializeResultArray(); $languageService = $this->getLanguageService(); $row = $this->data['databaseRow']; $parameterArray = $this->data['parameterArray']; // If ratios are set do not add default options if (isset($parameterArray['fieldConf']['config']['ratios'])) { unset($this->defaultConfig['ratios']); } $config = ArrayUtility::arrayMergeRecursiveOverrule($this->defaultConfig, $parameterArray['fieldConf']['config']); // By default we allow all image extensions that can be handled by the GFX functionality if ($config['allowedExtensions'] === null) { $config['allowedExtensions'] = $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']; } if ($config['readOnly']) { $options = array(); $options['parameterArray'] = array('fieldConf' => array('config' => $config), 'itemFormElValue' => $parameterArray['itemFormElValue']); $options['renderType'] = 'none'; return $this->nodeFactory->create($options)->render(); } $file = $this->getFile($row, $config['file_field']); if (!$file) { return $resultArray; } $content = ''; $preview = ''; if (GeneralUtility::inList(mb_strtolower($config['allowedExtensions']), mb_strtolower($file->getExtension()))) { // Get preview $preview = $this->getPreview($file, $parameterArray['itemFormElValue']); // Check if ratio labels hold translation strings foreach ((array) $config['ratios'] as $ratio => $label) { $config['ratios'][$ratio] = $languageService->sL($label, true); } $formFieldId = StringUtility::getUniqueId('formengine-image-manipulation-'); $wizardData = array('zoom' => $config['enableZoom'] ? '1' : '0', 'ratios' => json_encode($config['ratios']), 'file' => $file->getUid()); $wizardData['token'] = GeneralUtility::hmac(implode('|', $wizardData), 'ImageManipulationWizard'); $buttonAttributes = array('data-url' => BackendUtility::getAjaxUrl('wizard_image_manipulation', $wizardData), 'data-severity' => 'notice', 'data-image-name' => $file->getNameWithoutExtension(), 'data-image-uid' => $file->getUid(), 'data-file-field' => $config['file_field'], 'data-field' => $formFieldId); $button = '<button class="btn btn-default t3js-image-manipulation-trigger"'; foreach ($buttonAttributes as $key => $value) { $button .= ' ' . $key . '="' . htmlspecialchars($value) . '"'; } $button .= '><span class="t3-icon fa fa-crop"></span>'; $button .= $languageService->sL('LLL:EXT:lang/locallang_wizards.xlf:imwizard.open-editor', true); $button .= '</button>'; $inputField = '<input type="hidden" ' . 'id="' . $formFieldId . '" ' . 'name="' . $parameterArray['itemFormElName'] . '" ' . 'value="' . htmlspecialchars($parameterArray['itemFormElValue']) . '" />'; $content .= $inputField . $button; $content .= $this->getImageManipulationInfoTable($parameterArray['itemFormElValue']); $resultArray['requireJsModules'][] = array('TYPO3/CMS/Backend/ImageManipulation' => 'function(ImageManipulation){ImageManipulation.initializeTrigger()}'); } $content .= '<p class="text-muted"><em>' . $languageService->sL('LLL:EXT:lang/locallang_wizards.xlf:imwizard.supported-types-message', true) . '<br />'; $content .= mb_strtoupper(implode(', ', GeneralUtility::trimExplode(',', $config['allowedExtensions']))); $content .= '</em></p>'; $item = '<div class="media">'; $item .= $preview; $item .= '<div class="media-body">' . $content . '</div>'; $item .= '</div>'; $resultArray['html'] = $item; return $resultArray; }
/** * Unset all additional properties of test classes to help PHP * garbage collection. This reduces memory footprint with lots * of tests. * * If owerwriting tearDown() in test classes, please call * parent::tearDown() at the end. Unsetting of own properties * is not needed this way. * * @throws \RuntimeException * @return void */ protected function tearDown() { // Unset properties of test classes to safe memory $reflection = new \ReflectionObject($this); foreach ($reflection->getProperties() as $property) { $declaringClass = $property->getDeclaringClass()->getName(); if (!$property->isStatic() && $declaringClass !== \TYPO3\CMS\Core\Tests\UnitTestCase::class && $declaringClass !== \TYPO3\CMS\Core\Tests\BaseTestCase::class && strpos($property->getDeclaringClass()->getName(), 'PHPUnit_') !== 0) { $propertyName = $property->getName(); unset($this->{$propertyName}); } } unset($reflection); // Delete registered test files and directories foreach ($this->testFilesToDelete as $absoluteFileName) { $absoluteFileName = GeneralUtility::fixWindowsFilePath(PathUtility::getCanonicalPath($absoluteFileName)); if (!GeneralUtility::validPathStr($absoluteFileName)) { throw new \RuntimeException('tearDown() cleanup: Filename contains illegal characters', 1410633087); } if (!StringUtility::beginsWith($absoluteFileName, PATH_site . 'typo3temp/')) { throw new \RuntimeException('tearDown() cleanup: Files to delete must be within typo3temp/', 1410633412); } // file_exists returns false for links pointing to not existing targets, so handle links before next check. if (@is_link($absoluteFileName) || @is_file($absoluteFileName)) { unlink($absoluteFileName); } elseif (@is_dir($absoluteFileName)) { GeneralUtility::rmdir($absoluteFileName, true); } else { throw new \RuntimeException('tearDown() cleanup: File, link or directory does not exist', 1410633510); } } $this->testFilesToDelete = array(); }
/** * @param array|NULL $backendUser * @param int $size * @param bool $showIcon * @return string */ public function render(array $backendUser = NULL, $size = 32, $showIcon = FALSE) { $size = (int) $size; if (!is_array($backendUser)) { $backendUser = $this->getBackendUser()->user; } $image = parent::render($backendUser, $size, $showIcon); if (!StringUtility::beginsWith($image, '<span class="avatar"><span class="avatar-image"></span>') || empty($backendUser['email'])) { return $image; } $cachedFilePath = PATH_site . 'typo3temp/t3gravatar/'; $cachedFileName = sha1($backendUser['email'] . $size) . '.jpg'; if (!file_exists($cachedFilePath . $cachedFileName)) { $gravatar = 'https://www.gravatar.com/avatar/' . md5(strtolower($backendUser['email'])) . '?s=' . $size . '&d=404'; $gravatarImage = GeneralUtility::getUrl($gravatar); if (empty($gravatarImage)) { return $image; } GeneralUtility::writeFileToTypo3tempDir($cachedFileName, $gravatarImage); } // Icon $icon = ''; if ($showIcon) { $icon = '<span class="avatar-icon">' . IconUtility::getSpriteIconForRecord('be_users', $backendUser) . '</span>'; } $relativeFilePath = PathUtility::getRelativePath(PATH_typo3, $cachedFilePath); return '<span class="avatar"><span class="avatar-image">' . '<img src="' . $relativeFilePath . $cachedFileName . '" width="' . $size . '" height="' . $size . '" /></span>' . $icon . '</span>'; }
/** * Resolve placeholders for input/text fields. Placeholders that are simple * strings will be returned unmodified. Placeholders beginning with __row are * being resolved, possibly traversing multiple tables. * * @param array $result * @return array */ public function addData(array $result) { foreach ($result['processedTca']['columns'] as $fieldName => $fieldConfig) { // Placeholders are only valid for input and text type fields if ($fieldConfig['config']['type'] !== 'input' && $fieldConfig['config']['type'] !== 'text' || !isset($fieldConfig['config']['placeholder'])) { continue; } // Resolve __row|field type placeholders if (StringUtility::beginsWith($fieldConfig['config']['placeholder'], '__row|')) { // split field names into array and remove the __row indicator $fieldNameArray = array_slice(GeneralUtility::trimExplode('|', $fieldConfig['config']['placeholder'], true), 1); $result['processedTca']['columns'][$fieldName]['config']['placeholder'] = $this->getPlaceholderValue($fieldNameArray, $result); } // Resolve placeholders from language files if (StringUtility::beginsWith($fieldConfig['config']['placeholder'], 'LLL:')) { $result['processedTca']['columns'][$fieldName]['config']['placeholder'] = $this->getLanguageService()->sl($fieldConfig['config']['placeholder']); } // Remove empty placeholders if (empty($fieldConfig['config']['placeholder'])) { unset($result['processedTca']['columns'][$fieldName]['config']['placeholder']); continue; } } return $result; }
/** * Rendering the cObject, QTOBJECT * * @param array $conf Array of TypoScript properties * @return string Output */ public function render($conf = array()) { $params = $prefix = ''; if ($GLOBALS['TSFE']->baseUrl) { $prefix = $GLOBALS['TSFE']->baseUrl; } if ($GLOBALS['TSFE']->absRefPrefix) { $prefix = $GLOBALS['TSFE']->absRefPrefix; } $type = isset($conf['type.']) ? $this->cObj->stdWrap($conf['type'], $conf['type.']) : $conf['type']; // If file is audio and an explicit path has not been set, // take path from audio fallback property if ($type == 'audio' && empty($conf['file'])) { $conf['file'] = $conf['audioFallback']; } $filename = isset($conf['file.']) ? $this->cObj->stdWrap($conf['file'], $conf['file.']) : $conf['file']; $typeConf = $conf[$type . '.']; // Add QTobject js-file $this->getPageRenderer()->addJsFile($this->getPathToLibrary('flashmedia/qtobject/qtobject.js')); $replaceElementIdString = StringUtility::getUniqueId('mmqt'); $GLOBALS['TSFE']->register['MMQTID'] = $replaceElementIdString; $qtObject = 'QTObject' . $replaceElementIdString; // Merge with default parameters $conf['params.'] = array_merge((array) $typeConf['default.']['params.'], (array) $conf['params.']); if (is_array($conf['params.']) && is_array($typeConf['mapping.']['params.'])) { ArrayUtility::remapArrayKeys($conf['params.'], $typeConf['mapping.']['params.']); foreach ($conf['params.'] as $key => $value) { $params .= $qtObject . '.addParam("' . $key . '", "' . $value . '");' . LF; } } $params = ($params ? substr($params, 0, -2) : '') . LF . $qtObject . '.write("' . $replaceElementIdString . '");'; $alternativeContent = isset($conf['alternativeContent.']) ? $this->cObj->stdWrap($conf['alternativeContent'], $conf['alternativeContent.']) : $conf['alternativeContent']; $layout = str_replace(array('###ID###', '###QTOBJECT###'), array($replaceElementIdString, '<div id="' . $replaceElementIdString . '">' . $alternativeContent . '</div>'), isset($conf['layout.']) ? $this->cObj->stdWrap($conf['layout'], $conf['layout.']) : $conf['layout']); $width = isset($conf['width.']) ? $this->cObj->stdWrap($conf['width'], $conf['width.']) : $conf['width']; if (!$width) { $width = $conf[$type . '.']['defaultWidth']; } $height = isset($conf['height.']) ? $this->cObj->stdWrap($conf['height'], $conf['height.']) : $conf['height']; if (!$height) { $height = $conf[$type . '.']['defaultHeight']; } $fullFilename = $filename; // If the file name doesn't contain a scheme, prefix with appropriate data if (strpos($filename, '://') === false && !empty($prefix)) { $fullFilename = $prefix . $filename; } $embed = 'var ' . $qtObject . ' = new QTObject("' . $fullFilename . '", "' . $replaceElementIdString . '", "' . $width . '", "' . $height . '");'; $content = $layout . ' <script type="text/javascript"> ' . $embed . ' ' . $params . ' </script>'; if (isset($conf['stdWrap.'])) { $content = $this->cObj->stdWrap($content, $conf['stdWrap.']); } return $content; }
/** * @param Icon $icon * @param array $options * @return string * @throws \InvalidArgumentException */ protected function generateInlineMarkup(Icon $icon, array $options) { if (empty($options['source'])) { throw new \InvalidArgumentException('The option "source" is required and must not be empty', 1440754980); } $source = $options['source']; if (StringUtility::beginsWith($source, 'EXT:') || !StringUtility::beginsWith($source, '/')) { $source = GeneralUtility::getFileAbsFileName($source); } return $this->getInlineSvg($source); }
/** * @param Icon $icon * @param array $options * @return string * @throws \InvalidArgumentException */ protected function generateMarkup(Icon $icon, array $options) { if (empty($options['source'])) { throw new \InvalidArgumentException('[' . $icon->getIdentifier() . '] The option "source" is required and must not be empty', 1440754980); } $source = $options['source']; if (StringUtility::beginsWith($source, 'EXT:') || !StringUtility::beginsWith($source, '/')) { $source = GeneralUtility::getFileAbsFileName($source); } $source = PathUtility::getAbsoluteWebPath($source); return '<img src="' . htmlspecialchars($source) . '" width="' . $icon->getDimension()->getWidth() . '" height="' . $icon->getDimension()->getHeight() . '" />'; }
/** * Initialize new row with unique uid * * @param array $result * @return array * @throws \InvalidArgumentException */ public function addData(array $result) { if ($result['command'] !== 'new') { return $result; } // Throw exception if uid is already set if (isset($result['databaseRow']['uid'])) { throw new \InvalidArgumentException('uid is already set to ' . $result['databaseRow']['uid'], 1437991120); } $result['databaseRow']['uid'] = StringUtility::getUniqueId('NEW'); return $result; }
/** * Add the given icon to the TCA table type * * @param string $table * @param string $type * @param string $icon */ public static function addTcaTypeIcon($table, $type, $icon) { if (GeneralUtility::compat_version('7.0')) { $fullIconPath = substr(PathUtility::getAbsoluteWebPath($icon), 1); if (StringUtility::endsWith(strtolower($fullIconPath), 'svg')) { $iconProviderClass = 'TYPO3\\CMS\\Core\\Imaging\\IconProvider\\SvgIconProvider'; } else { $iconProviderClass = 'TYPO3\\CMS\\Core\\Imaging\\IconProvider\\BitmapIconProvider'; } /** @var IconRegistry $iconRegistry */ $iconRegistry = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Imaging\\IconRegistry'); $iconIdentifier = 'tcarecords-' . $table . '-' . $type; $iconRegistry->registerIcon($iconIdentifier, $iconProviderClass, ['source' => $fullIconPath]); $GLOBALS['TCA']['tt_content']['ctrl']['typeicon_classes'][$type] = $iconIdentifier; } else { $fullIconPath = str_replace('../typo3/', '', $icon); SpriteManager::addTcaTypeIcon('tt_content', $type, $fullIconPath); } }
/** * Return the default value of a field formatted to match the native MySQL SQL dialect * * @param array $fieldDefinition * @return mixed */ protected function getNativeDefaultValue($fieldDefinition) { if (!$fieldDefinition['has_default']) { $returnValue = null; } elseif ($fieldDefinition['type'] === 'SERIAL' && substr($fieldDefinition['default_value'], 0, 7) === 'nextval') { $returnValue = null; } elseif ($fieldDefinition['type'] === 'varchar') { // Strip character class and unquote string if (StringUtility::beginsWith($fieldDefinition['default_value'], 'NULL::')) { $returnValue = null; } else { $returnValue = str_replace("\\'", "'", preg_replace('/\'(.*)\'(::(?:character\\svarying|varchar|character|char|text)(?:\\(\\d+\\))?)?\\z/', '\\1', $fieldDefinition['default_value'])); } } elseif (substr($fieldDefinition['type'], 0, 3) === 'int') { $returnValue = (int) preg_replace('/^\\(?(\\-?\\d+)\\)?$/', '\\1', $fieldDefinition['default_value']); } else { $returnValue = $fieldDefinition['default_value']; } return $returnValue; }
/** * @param array $data * @return array */ protected function decryptDataArray(array $data) { foreach ($data as $key => $value) { if (empty($value)) { continue; } if (is_array($value)) { $data[$key] = $this->decryptDataArray($value); continue; } if (!StringUtility::beginsWith($value, 'rsa:')) { continue; } $decryptedValue = $this->getBackend()->decrypt($this->getKey(), substr($value, 4)); if ($decryptedValue !== null) { $data[$key] = $decryptedValue; } } return $data; }
/** * Determine which fields are required to render the placeholders and * add those to the list of columns that must be processed by the next * data providers. * * @param array $result * @return array */ public function addData(array $result) { foreach ($result['processedTca']['columns'] as $fieldName => $fieldConfig) { // Placeholders are only valid for input and text type fields if ($fieldConfig['config']['type'] !== 'input' && $fieldConfig['config']['type'] !== 'text' || !isset($fieldConfig['config']['placeholder'])) { continue; } // Process __row|field type placeholders if (StringUtility::beginsWith($fieldConfig['config']['placeholder'], '__row|')) { // split field names into array and remove the __row indicator $fieldNameArray = array_slice(GeneralUtility::trimExplode('|', $fieldConfig['config']['placeholder'], true), 1); // only the first field is required to be processed as it's the one referring to // the current record. All other columns will be resolved in a later pass through // the related records. if (!empty($fieldNameArray[0])) { $result['columnsToProcess'][] = $fieldNameArray[0]; } } } return $result; }
/** * Generates the JumpURL for the given parameters. * * @see \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::processUrlModifiers() * @param string $context The context in which the URL is generated (e.g. "typolink"). * @param string $url The URL that should be processed. * @param array $configuration The link configuration. * @param \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer $contentObjectRenderer The calling content object renderer. * @param bool $keepProcessing If this is set to FALSE no further hooks will be processed after the current one. * @return string */ public function process($context, $url, array $configuration, ContentObjectRenderer $contentObjectRenderer, &$keepProcessing) { if (!$this->isEnabled($context, $configuration)) { return $url; } $this->contentObjectRenderer = $contentObjectRenderer; // Strip the absRefPrefix from the URLs. $urlPrefix = (string) $this->getTypoScriptFrontendController()->absRefPrefix; if ($urlPrefix !== '' && StringUtility::beginsWith($url, $urlPrefix)) { $url = substr($url, strlen($urlPrefix)); } // Make sure the slashes in the file URL are not encoded. if ($context === UrlProcessorInterface::CONTEXT_FILE) { $url = str_replace('%2F', '/', rawurlencode(rawurldecode($url))); } $url = $this->build($url, isset($configuration['jumpurl.']) ? $configuration['jumpurl.'] : array()); // Now add the prefix again if it was not added by a typolink call already. if ($urlPrefix !== '' && !StringUtility::beginsWith($url, $urlPrefix)) { $url = $urlPrefix . $url; } return $url; }
/** * @param string $status * @param string $table * @param int $id * @param array $fieldArray * @param DataHandler $parentObject */ public function processDatamap_afterDatabaseOperations($status, $table, $id, $fieldArray, $parentObject) { if ($table !== 'be_groups' || $GLOBALS['TYPO3_CONF_VARS']['BE']['explicitADmode'] !== 'explicitAllow') { return; } $backendUserGroup = BackendUtility::getRecord($table, $id, 'explicit_allowdeny'); $explicitAllowDenyFields = GeneralUtility::trimExplode(',', $backendUserGroup['explicit_allowdeny']); foreach ($explicitAllowDenyFields as $value) { if (StringUtility::beginsWith($value, 'tt_content:list_type:')) { if (!in_array('tt_content:CType:list:ALLOW', $explicitAllowDenyFields, true)) { /** @var $flashMessage FlashMessage */ $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, $this->getLanguageService()->sl('LLL:EXT:lang/locallang_core.xlf:error.backendUserGroupListTypeError.message'), $this->getLanguageService()->sl('LLL:EXT:lang/locallang_core.xlf:error.backendUserGroupListTypeError.header'), FlashMessage::WARNING, true); /** @var $flashMessageService FlashMessageService */ $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class); /** @var $defaultFlashMessageQueue FlashMessageQueue */ $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier(); $defaultFlashMessageQueue->enqueue($flashMessage); } return; } } }
/** * Checks if this is the handler for the given link * * The handler may store this information locally for later usage. * * @param array $linkParts Link parts as returned from TypoLinkCodecService * * @return bool */ public function canHandleLink(array $linkParts) { if (!$linkParts['url']) { return false; } $url = rawurldecode($linkParts['url']); if (StringUtility::beginsWith($url, 'file:') && !StringUtility::beginsWith($url, 'file://')) { $rel = substr($url, 5); try { // resolve FAL-api "file:UID-of-sys_file-record" and "file:combined-identifier" $fileOrFolderObject = ResourceFactory::getInstance()->retrieveFileOrFolderObject($rel); if (is_a($fileOrFolderObject, $this->expectedClass)) { $this->linkParts = $linkParts; $this->linkParts['url'] = $rel; $this->linkParts['name'] = $fileOrFolderObject->getName(); return true; } } catch (FileDoesNotExistException $e) { } } return false; }
/** * At the end of the import process all file and DB relations should be set properly (that is relations * to imported records are all re-created so imported records are correctly related again) * Relations in flexform fields are processed in setFlexFormRelations() after this function * * @return void * @see setFlexFormRelations() */ public function setRelations() { $updateData = array(); // import_newId contains a register of all records that was in the import memorys "records" key foreach ($this->import_newId as $nId => $dat) { $table = $dat['table']; $uid = $dat['uid']; // original UID - NOT the new one! // If the record has been written and received a new id, then proceed: if (is_array($this->import_mapId[$table]) && isset($this->import_mapId[$table][$uid])) { $thisNewUid = BackendUtility::wsMapId($table, $this->import_mapId[$table][$uid]); if (is_array($this->dat['records'][$table . ':' . $uid]['rels'])) { $thisNewPageUid = 0; if ($this->legacyImport) { if ($table != 'pages') { $oldPid = $this->dat['records'][$table . ':' . $uid]['data']['pid']; $thisNewPageUid = BackendUtility::wsMapId($table, $this->import_mapId['pages'][$oldPid]); } else { $thisNewPageUid = $thisNewUid; } } // Traverse relation fields of each record foreach ($this->dat['records'][$table . ':' . $uid]['rels'] as $field => $config) { // uid_local of sys_file_reference needs no update because the correct reference uid was already written // @see ImportExport::fixUidLocalInSysFileReferenceRecords() if ($table === 'sys_file_reference' && $field === 'uid_local') { continue; } switch ((string) $config['type']) { case 'db': if (is_array($config['itemArray']) && !empty($config['itemArray'])) { $itemConfig = $GLOBALS['TCA'][$table]['columns'][$field]['config']; $valArray = $this->setRelations_db($config['itemArray'], $itemConfig); $updateData[$table][$thisNewUid][$field] = implode(',', $valArray); } break; case 'file': if (is_array($config['newValueFiles']) && !empty($config['newValueFiles'])) { $valArr = array(); foreach ($config['newValueFiles'] as $fI) { $valArr[] = $this->import_addFileNameToBeCopied($fI); } if ($this->legacyImport && $this->legacyImportFolder === null && isset($this->legacyImportMigrationTables[$table][$field])) { // Do nothing - the legacy import folder is missing } elseif ($this->legacyImport && $this->legacyImportFolder !== null && isset($this->legacyImportMigrationTables[$table][$field])) { $refIds = array(); foreach ($valArr as $tempFile) { $fileName = $this->alternativeFileName[$tempFile]; $fileObject = null; try { // check, if there is alreay the same file in the folder if ($this->legacyImportFolder->hasFile($fileName)) { $fileStorage = $this->legacyImportFolder->getStorage(); $file = $fileStorage->getFile($this->legacyImportFolder->getIdentifier() . $fileName); if ($file->getSha1() === sha1_file($tempFile)) { $fileObject = $file; } } } catch (Exception $e) { } if ($fileObject === null) { try { $fileObject = $this->legacyImportFolder->addFile($tempFile, $fileName, DuplicationBehavior::RENAME); } catch (Exception $e) { $this->error('Error: no file could be added to the storage for file name' . $this->alternativeFileName[$tempFile]); } } if ($fileObject !== null) { $refId = StringUtility::getUniqueId('NEW'); $refIds[] = $refId; $updateData['sys_file_reference'][$refId] = array('uid_local' => $fileObject->getUid(), 'uid_foreign' => $thisNewUid, 'tablenames' => $table, 'fieldname' => $field, 'pid' => $thisNewPageUid, 'table_local' => 'sys_file'); } } $updateData[$table][$thisNewUid][$field] = implode(',', $refIds); if (!empty($this->legacyImportMigrationTables[$table][$field])) { $this->legacyImportMigrationRecords[$table][$thisNewUid][$field] = $refIds; } } else { $updateData[$table][$thisNewUid][$field] = implode(',', $valArr); } } break; } } } else { $this->error('Error: no record was found in data array!'); } } else { $this->error('Error: this records is NOT created it seems! (' . $table . ':' . $uid . ')'); } } if (!empty($updateData)) { $tce = $this->getNewTCE(); $tce->isImporting = true; $this->callHook('before_setRelation', array('tce' => &$tce, 'data' => &$updateData)); $tce->start($updateData, array()); $tce->process_datamap(); // Replace the temporary "NEW" ids with the final ones. foreach ($this->legacyImportMigrationRecords as $table => $records) { foreach ($records as $uid => $fields) { foreach ($fields as $field => $referenceIds) { foreach ($referenceIds as $key => $referenceId) { $this->legacyImportMigrationRecords[$table][$uid][$field][$key] = $tce->substNEWwithIDs[$referenceId]; } } } } $this->callHook('after_setRelations', array('tce' => &$tce)); } }
/** * Check if an update is possible at all * * @param string $version The target version number * @return bool TRUE on success * @throws \TYPO3\CMS\Install\Status\Exception */ public function checkPreConditions($version) { $success = true; $messages = array(); /** @var StatusUtility $statusUtility */ $statusUtility = $this->objectManager->get(StatusUtility::class); // Folder structure test: Update can be done only if folder structure returns no errors /** @var $folderStructureFacade \TYPO3\CMS\Install\FolderStructure\StructureFacade */ $folderStructureFacade = $this->objectManager->get(DefaultFactory::class)->getStructure(); $folderStructureErrors = $statusUtility->filterBySeverity($folderStructureFacade->getStatus(), 'error'); $folderStructureWarnings = $statusUtility->filterBySeverity($folderStructureFacade->getStatus(), 'warning'); if (!empty($folderStructureErrors) || !empty($folderStructureWarnings)) { $success = false; /** @var $message StatusInterface */ $message = $this->objectManager->get(ErrorStatus::class); $message->setTitle('Automatic TYPO3 CMS core update not possible: Folder structure has errors or warnings'); $message->setMessage('To perform an update, the folder structure of this TYPO3 CMS instance must' . ' stick to the conventions, or the update process could lead to unexpected' . ' results and may be hazardous to your system'); $messages[] = $message; } // No core update on windows if (TYPO3_OS === 'WIN') { $success = false; /** @var $message StatusInterface */ $message = $this->objectManager->get(ErrorStatus::class); $message->setTitle('Automatic TYPO3 CMS core update not possible: Update not supported on Windows OS'); $messages[] = $message; } if ($success) { // Explicit write check to document root $file = PATH_site . StringUtility::getUniqueId('install-core-update-test-'); $result = @touch($file); if (!$result) { $success = false; /** @var $message StatusInterface */ $message = $this->objectManager->get(ErrorStatus::class); $message->setTitle('Automatic TYPO3 CMS core update not possible: No write access to document root'); $message->setMessage('Could not write a file in path "' . PATH_site . '"!'); $messages[] = $message; } else { unlink($file); } if (!$this->checkCoreFilesAvailable($version)) { // Explicit write check to upper directory of current core location $coreLocation = @realPath($this->symlinkToCoreFiles . '/../'); $file = $coreLocation . '/' . StringUtility::getUniqueId('install-core-update-test-'); $result = @touch($file); if (!$result) { $success = false; /** @var $message StatusInterface */ $message = $this->objectManager->get(ErrorStatus::class); $message->setTitle('Automatic TYPO3 CMS core update not possible: No write access to TYPO3 CMS core location'); $message->setMessage('New TYPO3 CMS core should be installed in "' . $coreLocation . '", but this directory is not writable!'); $messages[] = $message; } else { unlink($file); } } } if ($success && !$this->coreVersionService->isInstalledVersionAReleasedVersion()) { $success = false; /** @var $message StatusInterface */ $message = $this->objectManager->get(ErrorStatus::class); $message->setTitle('Automatic TYPO3 CMS core update not possible: You are running a development version of TYPO3'); $message->setMessage('Your current version is specified as ' . $this->coreVersionService->getInstalledVersion() . '.' . ' This is a development version and can not be updated automatically. If this is a "git"' . ' checkout, please update using git directly.'); $messages[] = $message; } $this->messages = $messages; return $success; }
/** * Renders Content Elements from the tt_content table from page id * * @param int $id Page id * @return string HTML for the listing */ public function getTable_tt_content($id) { $backendUser = $this->getBackendUser(); $this->pageinfo = BackendUtility::readPageAccess($this->id, $backendUser->getPagePermsClause($this->ext_CALC_PERMS)); $this->initializeLanguages(); $this->initializeClipboard(); $pageTitleParamForAltDoc = '&recTitle=' . rawurlencode(BackendUtility::getRecordTitle('pages', BackendUtility::getRecordWSOL('pages', $id), true)); /** @var $pageRenderer PageRenderer */ $pageRenderer = GeneralUtility::makeInstance(PageRenderer::class); $pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/LayoutModule/DragDrop'); $pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Modal'); $userCanEditPage = $this->ext_CALC_PERMS & Permission::PAGE_EDIT && !empty($this->id) && ($backendUser->isAdmin() || (int) $this->pageinfo['editlock'] === 0); if ($this->tt_contentConfig['languageColsPointer'] > 0) { $userCanEditPage = $this->getBackendUser()->check('tables_modify', 'pages_language_overlay'); } if ($userCanEditPage) { $pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/PageActions', 'function(PageActions) { PageActions.setPageId(' . (int) $this->id . '); PageActions.setLanguageOverlayId(' . $this->tt_contentConfig['languageColsPointer'] . '); PageActions.initializePageTitleRenaming(); }'); } // Get labels for CTypes and tt_content element fields in general: $this->CType_labels = array(); foreach ($GLOBALS['TCA']['tt_content']['columns']['CType']['config']['items'] as $val) { $this->CType_labels[$val[1]] = $this->getLanguageService()->sL($val[0]); } $this->itemLabels = array(); foreach ($GLOBALS['TCA']['tt_content']['columns'] as $name => $val) { $this->itemLabels[$name] = $this->getLanguageService()->sL($val['label']); } $languageColumn = array(); $out = ''; // Setting language list: $langList = $this->tt_contentConfig['sys_language_uid']; if ($this->tt_contentConfig['languageMode']) { if ($this->tt_contentConfig['languageColsPointer']) { $langList = '0,' . $this->tt_contentConfig['languageColsPointer']; } else { $langList = implode(',', array_keys($this->tt_contentConfig['languageCols'])); } $languageColumn = array(); } $langListArr = GeneralUtility::intExplode(',', $langList); $defLanguageCount = array(); $defLangBinding = array(); // For each languages... : // If not languageMode, then we'll only be through this once. foreach ($langListArr as $lP) { $lP = (int) $lP; if (!isset($this->contentElementCache[$lP])) { $this->contentElementCache[$lP] = array(); } if (count($langListArr) === 1 || $lP === 0) { $showLanguage = ' AND sys_language_uid IN (' . $lP . ',-1)'; } else { $showLanguage = ' AND sys_language_uid=' . $lP; } $cList = explode(',', $this->tt_contentConfig['cols']); $content = array(); $head = array(); // Select content records per column $contentRecordsPerColumn = $this->getContentRecordsPerColumn('table', $id, array_values($cList), $showLanguage); // For each column, render the content into a variable: foreach ($cList as $key) { if (!isset($this->contentElementCache[$lP][$key])) { $this->contentElementCache[$lP][$key] = array(); } if (!$lP) { $defLanguageCount[$key] = array(); } // Start wrapping div $content[$key] .= '<div data-colpos="' . $key . '" data-language-uid="' . $lP . '" class="t3js-sortable t3js-sortable-lang t3js-sortable-lang-' . $lP . ' t3-page-ce-wrapper'; if (empty($contentRecordsPerColumn[$key])) { $content[$key] .= ' t3-page-ce-empty'; } $content[$key] .= '">'; // Add new content at the top most position $link = ''; if ($this->getPageLayoutController()->pageIsNotLockedForEditors() && $this->getBackendUser()->doesUserHaveAccess($this->pageinfo, Permission::CONTENT_EDIT)) { $link = '<a href="#" onclick="' . htmlspecialchars($this->newContentElementOnClick($id, $key, $lP)) . '" title="' . $this->getLanguageService()->getLL('newContentElement', true) . '" class="btn btn-default btn-sm">' . $this->iconFactory->getIcon('actions-document-new', Icon::SIZE_SMALL)->render() . ' ' . $this->getLanguageService()->getLL('content', true) . '</a>'; } $content[$key] .= ' <div class="t3-page-ce t3js-page-ce" data-page="' . (int) $id . '" id="' . StringUtility::getUniqueId() . '"> <div class="t3js-page-new-ce t3-page-ce-wrapper-new-ce" id="colpos-' . $key . '-' . 'page-' . $id . '-' . StringUtility::getUniqueId() . '">' . $link . '</div> <div class="t3-page-ce-dropzone-available t3js-page-ce-dropzone-available"></div> </div> '; $editUidList = ''; $rowArr = $contentRecordsPerColumn[$key]; $this->generateTtContentDataArray($rowArr); foreach ((array) $rowArr as $rKey => $row) { $this->contentElementCache[$lP][$key][$row['uid']] = $row; if ($this->tt_contentConfig['languageMode']) { $languageColumn[$key][$lP] = $head[$key] . $content[$key]; if (!$this->defLangBinding) { $languageColumn[$key][$lP] .= $this->newLanguageButton($this->getNonTranslatedTTcontentUids($defLanguageCount[$key], $id, $lP), $lP, $key); } } if (is_array($row) && !VersionState::cast($row['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)) { $singleElementHTML = ''; if (!$lP && ($this->defLangBinding || $row['sys_language_uid'] != -1)) { $defLanguageCount[$key][] = isset($row['_ORIG_uid']) ? $row['_ORIG_uid'] : $row['uid']; } $editUidList .= $row['uid'] . ','; $disableMoveAndNewButtons = $this->defLangBinding && $lP > 0; if (!$this->tt_contentConfig['languageMode']) { $singleElementHTML .= '<div class="t3-page-ce-dragitem" id="' . StringUtility::getUniqueId() . '">'; } $singleElementHTML .= $this->tt_content_drawHeader($row, $this->tt_contentConfig['showInfo'] ? 15 : 5, $disableMoveAndNewButtons, !$this->tt_contentConfig['languageMode'], $this->getBackendUser()->doesUserHaveAccess($this->pageinfo, Permission::CONTENT_EDIT)); $innerContent = '<div ' . ($row['_ORIG_uid'] ? ' class="ver-element"' : '') . '>' . $this->tt_content_drawItem($row) . '</div>'; $singleElementHTML .= '<div class="t3-page-ce-body-inner">' . $innerContent . '</div>' . $this->tt_content_drawFooter($row); $isDisabled = $this->isDisabled('tt_content', $row); $statusHidden = $isDisabled ? ' t3-page-ce-hidden t3js-hidden-record' : ''; $displayNone = !$this->tt_contentConfig['showHidden'] && $isDisabled ? ' style="display: none;"' : ''; $singleElementHTML = '<div class="t3-page-ce t3js-page-ce t3js-page-ce-sortable ' . $statusHidden . '" id="element-tt_content-' . $row['uid'] . '" data-table="tt_content" data-uid="' . $row['uid'] . '"' . $displayNone . '>' . $singleElementHTML . '</div>'; if ($this->tt_contentConfig['languageMode']) { $singleElementHTML .= '<div class="t3-page-ce t3js-page-ce">'; } $singleElementHTML .= '<div class="t3js-page-new-ce t3-page-ce-wrapper-new-ce" id="colpos-' . $key . '-' . 'page-' . $id . '-' . StringUtility::getUniqueId() . '">'; // Add icon "new content element below" if (!$disableMoveAndNewButtons && $this->getPageLayoutController()->pageIsNotLockedForEditors() && $this->getBackendUser()->doesUserHaveAccess($this->pageinfo, Permission::CONTENT_EDIT)) { // New content element: if ($this->option_newWizard) { $onClick = 'window.location.href=' . GeneralUtility::quoteJSvalue(BackendUtility::getModuleUrl('new_content_element') . '&id=' . $row['pid'] . '&sys_language_uid=' . $row['sys_language_uid'] . '&colPos=' . $row['colPos'] . '&uid_pid=' . -$row['uid'] . '&returnUrl=' . rawurlencode(GeneralUtility::getIndpEnv('REQUEST_URI'))) . ';'; } else { $params = '&edit[tt_content][' . -$row['uid'] . ']=new'; $onClick = BackendUtility::editOnClick($params); } $singleElementHTML .= ' <a href="#" onclick="' . htmlspecialchars($onClick) . '" title="' . $this->getLanguageService()->getLL('newContentElement', true) . '" class="btn btn-default btn-sm">' . $this->iconFactory->getIcon('actions-document-new', Icon::SIZE_SMALL)->render() . ' ' . $this->getLanguageService()->getLL('content', true) . '</a> '; } $singleElementHTML .= '</div></div><div class="t3-page-ce-dropzone-available t3js-page-ce-dropzone-available"></div></div>'; if ($this->defLangBinding && $this->tt_contentConfig['languageMode']) { $defLangBinding[$key][$lP][$row[$lP ? 'l18n_parent' : 'uid']] = $singleElementHTML; } else { $content[$key] .= $singleElementHTML; } } else { unset($rowArr[$rKey]); } } $content[$key] .= '</div>'; // Add new-icon link, header: $newP = $this->newContentElementOnClick($id, $key, $lP); $colTitle = BackendUtility::getProcessedValue('tt_content', 'colPos', $key); $tcaItems = GeneralUtility::callUserFunction(\TYPO3\CMS\Backend\View\BackendLayoutView::class . '->getColPosListItemsParsed', $id, $this); foreach ($tcaItems as $item) { if ($item[1] == $key) { $colTitle = $this->getLanguageService()->sL($item[0]); } } $pasteP = array('colPos' => $key, 'sys_language_uid' => $lP); $editParam = $this->doEdit && !empty($rowArr) ? '&edit[tt_content][' . $editUidList . ']=edit' . $pageTitleParamForAltDoc : ''; $head[$key] .= $this->tt_content_drawColHeader($colTitle, $editParam, $newP, $pasteP); } // For each column, fit the rendered content into a table cell: $out = ''; if ($this->tt_contentConfig['languageMode']) { // in language mode process the content elements, but only fill $languageColumn. output will be generated later $sortedLanguageColumn = array(); foreach ($cList as $key) { $languageColumn[$key][$lP] = $head[$key] . $content[$key]; if (!$this->defLangBinding) { $languageColumn[$key][$lP] .= $this->newLanguageButton($this->getNonTranslatedTTcontentUids($defLanguageCount[$key], $id, $lP), $lP, $key); } // We sort $languageColumn again according to $cList as it may contain data already from above. $sortedLanguageColumn[$key] = $languageColumn[$key]; } $languageColumn = $sortedLanguageColumn; } else { $backendLayout = $this->getBackendLayoutView()->getSelectedBackendLayout($this->id); // GRID VIEW: $grid = '<div class="t3-grid-container"><table border="0" cellspacing="0" cellpadding="0" width="100%" class="t3-page-columns t3-grid-table t3js-page-columns">'; // Add colgroups $colCount = (int) $backendLayout['__config']['backend_layout.']['colCount']; $rowCount = (int) $backendLayout['__config']['backend_layout.']['rowCount']; $grid .= '<colgroup>'; for ($i = 0; $i < $colCount; $i++) { $grid .= '<col style="width:' . 100 / $colCount . '%"></col>'; } $grid .= '</colgroup>'; // Cycle through rows for ($row = 1; $row <= $rowCount; $row++) { $rowConfig = $backendLayout['__config']['backend_layout.']['rows.'][$row . '.']; if (!isset($rowConfig)) { continue; } $grid .= '<tr>'; for ($col = 1; $col <= $colCount; $col++) { $columnConfig = $rowConfig['columns.'][$col . '.']; if (!isset($columnConfig)) { continue; } // Which tt_content colPos should be displayed inside this cell $columnKey = (int) $columnConfig['colPos']; // Render the grid cell $colSpan = (int) $columnConfig['colspan']; $rowSpan = (int) $columnConfig['rowspan']; $grid .= '<td valign="top"' . ($colSpan > 0 ? ' colspan="' . $colSpan . '"' : '') . ($rowSpan > 0 ? ' rowspan="' . $rowSpan . '"' : '') . ' data-colpos="' . (int) $columnConfig['colPos'] . '" data-language-uid="' . $lP . '" class="t3js-page-lang-column-' . $lP . ' t3js-page-column t3-grid-cell t3-page-column t3-page-column-' . $columnKey . (!isset($columnConfig['colPos']) || $columnConfig['colPos'] === '' ? ' t3-grid-cell-unassigned' : '') . (isset($columnConfig['colPos']) && $columnConfig['colPos'] !== '' && !$head[$columnKey] || !GeneralUtility::inList($this->tt_contentConfig['activeCols'], $columnConfig['colPos']) ? ' t3-grid-cell-restricted' : '') . ($colSpan > 0 ? ' t3-gridCell-width' . $colSpan : '') . ($rowSpan > 0 ? ' t3-gridCell-height' . $rowSpan : '') . '">'; // Draw the pre-generated header with edit and new buttons if a colPos is assigned. // If not, a new header without any buttons will be generated. if (isset($columnConfig['colPos']) && $columnConfig['colPos'] !== '' && $head[$columnKey] && GeneralUtility::inList($this->tt_contentConfig['activeCols'], $columnConfig['colPos'])) { $grid .= $head[$columnKey] . $content[$columnKey]; } elseif (isset($columnConfig['colPos']) && $columnConfig['colPos'] !== '' && GeneralUtility::inList($this->tt_contentConfig['activeCols'], $columnConfig['colPos'])) { $grid .= $this->tt_content_drawColHeader($this->getLanguageService()->getLL('noAccess'), '', ''); } elseif (isset($columnConfig['colPos']) && $columnConfig['colPos'] !== '' && !GeneralUtility::inList($this->tt_contentConfig['activeCols'], $columnConfig['colPos'])) { $grid .= $this->tt_content_drawColHeader($this->getLanguageService()->sL($columnConfig['name']) . ' (' . $this->getLanguageService()->getLL('noAccess') . ')', '', ''); } elseif (isset($columnConfig['name']) && $columnConfig['name'] !== '') { $grid .= $this->tt_content_drawColHeader($this->getLanguageService()->sL($columnConfig['name']) . ' (' . $this->getLanguageService()->getLL('notAssigned') . ')', '', ''); } else { $grid .= $this->tt_content_drawColHeader($this->getLanguageService()->getLL('notAssigned'), '', ''); } $grid .= '</td>'; } $grid .= '</tr>'; } $out .= $grid . '</table></div>'; } // CSH: $out .= BackendUtility::cshItem($this->descrTable, 'columns_multi'); } // If language mode, then make another presentation: // Notice that THIS presentation will override the value of $out! // But it needs the code above to execute since $languageColumn is filled with content we need! if ($this->tt_contentConfig['languageMode']) { // Get language selector: $languageSelector = $this->languageSelector($id); // Reset out - we will make new content here: $out = ''; // Traverse languages found on the page and build up the table displaying them side by side: $cCont = array(); $sCont = array(); foreach ($langListArr as $lP) { // Header: $lP = (int) $lP; $cCont[$lP] = ' <td valign="top" class="t3-page-column" data-language-uid="' . $lP . '"> <h2>' . htmlspecialchars($this->tt_contentConfig['languageCols'][$lP]) . '</h2> </td>'; // "View page" icon is added: $viewLink = ''; if (!VersionState::cast($this->getPageLayoutController()->pageinfo['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)) { $onClick = BackendUtility::viewOnClick($this->id, '', BackendUtility::BEgetRootLine($this->id), '', '', '&L=' . $lP); $viewLink = '<a href="#" class="btn btn-default btn-sm" onclick="' . htmlspecialchars($onClick) . '" title="' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.showPage', true) . '">' . $this->iconFactory->getIcon('actions-view', Icon::SIZE_SMALL)->render() . '</a>'; } // Language overlay page header: if ($lP) { list($lpRecord) = BackendUtility::getRecordsByField('pages_language_overlay', 'pid', $id, 'AND sys_language_uid=' . $lP); BackendUtility::workspaceOL('pages_language_overlay', $lpRecord); $params = '&edit[pages_language_overlay][' . $lpRecord['uid'] . ']=edit&overrideVals[pages_language_overlay][sys_language_uid]=' . $lP; $recordIcon = BackendUtility::wrapClickMenuOnIcon($this->iconFactory->getIconForRecord('pages_language_overlay', $lpRecord, Icon::SIZE_SMALL)->render(), 'pages_language_overlay', $lpRecord['uid']); $editLink = $this->getBackendUser()->check('tables_modify', 'pages_language_overlay') ? '<a href="#" class="btn btn-default btn-sm" onclick="' . htmlspecialchars(BackendUtility::editOnClick($params)) . '" title="' . $this->getLanguageService()->getLL('edit', true) . '">' . $this->iconFactory->getIcon('actions-open', Icon::SIZE_SMALL)->render() . '</a>' : ''; $lPLabel = '<div class="btn-group">' . $viewLink . $editLink . '</div>' . ' ' . $recordIcon . ' ' . htmlspecialchars(GeneralUtility::fixed_lgd_cs($lpRecord['title'], 20)); } else { $params = '&edit[pages][' . $this->id . ']=edit'; $recordIcon = BackendUtility::wrapClickMenuOnIcon($this->iconFactory->getIconForRecord('pages', $this->pageRecord, Icon::SIZE_SMALL)->render(), 'pages', $this->id); $editLink = $this->getBackendUser()->check('tables_modify', 'pages_language_overlay') ? '<a href="#" class="btn btn-default btn-sm" onclick="' . htmlspecialchars(BackendUtility::editOnClick($params)) . '" title="' . $this->getLanguageService()->getLL('edit', true) . '">' . $this->iconFactory->getIcon('actions-open', Icon::SIZE_SMALL)->render() . '</a>' : ''; $lPLabel = '<div class="btn-group">' . $viewLink . $editLink . '</div>' . ' ' . $recordIcon . ' ' . htmlspecialchars(GeneralUtility::fixed_lgd_cs($this->pageRecord['title'], 20)); } $sCont[$lP] = ' <td nowrap="nowrap" class="t3-page-column t3-page-lang-label">' . $lPLabel . '</td>'; } // Add headers: $out .= '<tr>' . implode($cCont) . '</tr>'; $out .= '<tr>' . implode($sCont) . '</tr>'; unset($cCont, $sCont); // Traverse previously built content for the columns: foreach ($languageColumn as $cKey => $cCont) { $out .= '<tr>'; foreach ($cCont as $languageId => $columnContent) { $out .= '<td valign="top" class="t3-grid-cell t3-page-column t3js-page-column t3js-page-lang-column t3js-page-lang-column-' . $languageId . '">' . $columnContent . '</td>'; } $out .= '</tr>'; if ($this->defLangBinding) { // "defLangBinding" mode foreach ($defLanguageCount[$cKey] as $defUid) { $cCont = array(); foreach ($langListArr as $lP) { $cCont[] = $defLangBinding[$cKey][$lP][$defUid] . $this->newLanguageButton($this->getNonTranslatedTTcontentUids(array($defUid), $id, $lP), $lP, $cKey); } $out .= ' <tr> <td valign="top" class="t3-grid-cell">' . implode('</td>' . ' <td valign="top" class="t3-grid-cell">', $cCont) . '</td> </tr>'; } } } // Finally, wrap it all in a table and add the language selector on top of it: $out = $languageSelector . ' <div class="t3-grid-container"> <table cellpadding="0" cellspacing="0" class="t3-page-columns t3-grid-table t3js-page-columns"> ' . $out . ' </table> </div>'; // CSH: $out .= BackendUtility::cshItem($this->descrTable, 'language_list'); } return $out; }
/** * Adds a module path to $GLOBALS['TBE_MODULES'] for used with the module dispatcher, index.php * Used only for modules that are not placed in the main/sub menu hierarchy by the traditional mechanism of addModule() * Examples for this is context menu functionality (like import/export) which runs as an independent module through index.php * FOR USE IN ext_tables.php FILES * Example: \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addModulePath('xMOD_tximpexp', \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath($_EXTKEY).'app/'); * * @param string $name The name of the module, refer to conf.php of the module. * @param string $path The absolute path to the module directory inside of which "index.php" and "conf.php" is found. * @return void * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8, use routeTarget or dispatched modules instead. */ public static function addModulePath($name, $path) { GeneralUtility::logDeprecatedFunction(); if (StringUtility::beginsWith($path, 'EXT:')) { list($extensionKey, $relativePath) = explode('/', substr($path, 4), 2); $path = ExtensionManagementUtility::extPath($extensionKey) . $relativePath; } $GLOBALS['TBE_MODULES']['_PATHS'][$name] = $path; }
/** * This will render a single-line input form field, possibly with various control/validation features * * @return array As defined in initializeResultArray() of AbstractNode */ public function render() { $languageService = $this->getLanguageService(); $table = $this->data['tableName']; $fieldName = $this->data['fieldName']; $row = $this->data['databaseRow']; $parameterArray = $this->data['parameterArray']; $resultArray = $this->initializeResultArray(); $resultArray['requireJsModules'] = array('TYPO3/CMS/Rsaauth/RsaEncryptionModule'); $config = $parameterArray['fieldConf']['config']; $specConf = BackendUtility::getSpecConfParts($parameterArray['fieldConf']['defaultExtras']); $size = MathUtility::forceIntegerInRange($config['size'] ?: $this->defaultInputWidth, $this->minimumInputWidth, $this->maxInputWidth); $evalList = GeneralUtility::trimExplode(',', $config['eval'], true); $classes = array(); $attributes = array('type' => 'text', 'value' => ''); if ($config['readOnly']) { $itemFormElValue = $parameterArray['itemFormElValue']; $options = $this->data; $options['parameterArray'] = array('fieldConf' => array('config' => $config), 'itemFormElValue' => $itemFormElValue); $options['renderType'] = 'none'; return $this->nodeFactory->create($options)->render(); } // @todo: The whole eval handling is a mess and needs refactoring foreach ($evalList as $func) { switch ($func) { case 'required': $attributes['data-formengine-validation-rules'] = $this->getValidationDataAsJsonString(array('required' => true)); break; case 'password': $attributes['type'] = 'password'; $attributes['value'] = '********'; $attributes['autocomplete'] = 'off'; break; default: // @todo: This is ugly: The code should find out on it's own whether a eval definition is a // @todo: keyword like "date", or a class reference. The global registration could be dropped then // Pair hook to the one in \TYPO3\CMS\Core\DataHandling\DataHandler::checkValue_input_Eval() if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tce']['formevals'][$func])) { if (class_exists($func)) { $evalObj = GeneralUtility::makeInstance($func); if (method_exists($evalObj, 'deevaluateFieldValue')) { $_params = array('value' => $parameterArray['itemFormElValue']); $parameterArray['itemFormElValue'] = $evalObj->deevaluateFieldValue($_params); } } } } } $evalList = array_filter($evalList, function ($value) { return $value !== 'password'; }); $paramsList = array('field' => $parameterArray['itemFormElName'], 'evalList' => implode(',', $evalList), 'is_in' => trim($config['is_in'])); // set classes $classes[] = 'form-control'; $classes[] = 't3js-clearable'; $classes[] = 'hasDefaultValue'; // calculate attributes $attributes['data-formengine-validation-rules'] = $this->getValidationDataAsJsonString($config); $attributes['data-formengine-input-params'] = json_encode($paramsList); $attributes['data-formengine-input-name'] = htmlspecialchars($parameterArray['itemFormElName']); $attributes['id'] = StringUtility::getUniqueId('formengine-input-'); if (isset($config['max']) && (int) $config['max'] > 0) { $attributes['maxlength'] = (int) $config['max']; } if (!empty($classes)) { $attributes['class'] = implode(' ', $classes); } if (isset($config['max']) && (int) $config['max'] > 0) { $attributes['maxlength'] = (int) $config['max']; } // This is the EDITABLE form field. if (!empty($config['placeholder'])) { $attributes['placeholder'] = trim($config['placeholder']); } if (isset($config['autocomplete'])) { $attributes['autocomplete'] = empty($config['autocomplete']) ? 'off' : 'on'; } // Build the attribute string $attributeString = ''; foreach ($attributes as $attributeName => $attributeValue) { $attributeString .= ' ' . $attributeName . '="' . htmlspecialchars($attributeValue) . '"'; } $html = ' <input' . $attributeString . $parameterArray['onFocus'] . ' />'; // This is the ACTUAL form field - values from the EDITABLE field must be transferred to this field which is the one that is written to the database. $html .= '<input type="hidden" data-rsa-encryption="" id="' . $parameterArray['itemFormElID'] . '_hidden" name="' . $parameterArray['itemFormElName'] . '" value="' . htmlspecialchars($parameterArray['itemFormElValue']) . '" />'; // Going through all custom evaluations configured for this field // @todo: Similar to above code! foreach ($evalList as $evalData) { if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tce']['formevals'][$evalData])) { if (class_exists($evalData)) { $evalObj = GeneralUtility::makeInstance($evalData); if (method_exists($evalObj, 'returnFieldJS')) { $resultArray['extJSCODE'] .= LF . 'TBE_EDITOR.customEvalFunctions[' . GeneralUtility::quoteJSvalue($evalData) . '] = function(value) {' . $evalObj->returnFieldJS() . '}'; } } } } // Wrap a wizard around the item? $html = $this->renderWizards(array($html), $config['wizards'], $table, $row, $fieldName, $parameterArray, $parameterArray['itemFormElName'], $specConf); // Add a wrapper to remain maximum width $width = (int) $this->formMaxWidth($size); $html = '<div class="form-control-wrap"' . ($width ? ' style="max-width: ' . $width . 'px"' : '') . '>' . $html . '</div>'; $resultArray['html'] = $html; return $resultArray; }
/** * This will render a single-line input form field, possibly with various control/validation features * * @return array As defined in initializeResultArray() of AbstractNode */ public function render() { /** @var IconFactory $iconFactory */ $iconFactory = GeneralUtility::makeInstance(IconFactory::class); $languageService = $this->getLanguageService(); $table = $this->data['tableName']; $fieldName = $this->data['fieldName']; $row = $this->data['databaseRow']; $parameterArray = $this->data['parameterArray']; $resultArray = $this->initializeResultArray(); $isDateField = false; $config = $parameterArray['fieldConf']['config']; $specConf = BackendUtility::getSpecConfParts($parameterArray['fieldConf']['defaultExtras']); $size = MathUtility::forceIntegerInRange($config['size'] ?: $this->defaultInputWidth, $this->minimumInputWidth, $this->maxInputWidth); $evalList = GeneralUtility::trimExplode(',', $config['eval'], true); $classes = array(); $attributes = array(); if (!isset($config['checkbox'])) { $config['checkbox'] = '0'; $checkboxIsset = false; } else { $checkboxIsset = true; } // set all date times available $dateFormats = array('date' => '%d-%m-%Y', 'year' => '%Y', 'time' => '%H:%M', 'timesec' => '%H:%M:%S'); if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['USdateFormat']) { $dateFormats['date'] = '%m-%d-%Y'; } $dateFormats['datetime'] = $dateFormats['time'] . ' ' . $dateFormats['date']; $dateFormats['datetimesec'] = $dateFormats['timesec'] . ' ' . $dateFormats['date']; // readonly if ($config['readOnly']) { $itemFormElValue = $parameterArray['itemFormElValue']; if (in_array('date', $evalList)) { $config['format'] = 'date'; } elseif (in_array('datetime', $evalList)) { $config['format'] = 'datetime'; } elseif (in_array('time', $evalList)) { $config['format'] = 'time'; } if (in_array('password', $evalList)) { $itemFormElValue = $itemFormElValue ? '*********' : ''; } $options = $this->data; $options['parameterArray'] = array('fieldConf' => array('config' => $config), 'itemFormElValue' => $itemFormElValue); $options['renderType'] = 'none'; return $this->nodeFactory->create($options)->render(); } if (in_array('datetime', $evalList, true) || in_array('date', $evalList)) { $classes[] = 't3js-datetimepicker'; $isDateField = true; if (in_array('datetime', $evalList)) { $attributes['data-date-type'] = 'datetime'; $dateFormat = $dateFormats['datetime']; } elseif (in_array('date', $evalList)) { $attributes['data-date-type'] = 'date'; $dateFormat = $dateFormats['date']; } if ($parameterArray['itemFormElValue'] > 0) { $parameterArray['itemFormElValue'] += date('Z', $parameterArray['itemFormElValue']); } if (isset($config['range']['lower'])) { $attributes['data-date-minDate'] = (int) $config['range']['lower']; } if (isset($config['range']['upper'])) { $attributes['data-date-maxDate'] = (int) $config['range']['upper']; } } elseif (in_array('time', $evalList)) { $dateFormat = $dateFormats['time']; $isDateField = true; $classes[] = 't3js-datetimepicker'; $attributes['data-date-type'] = 'time'; } elseif (in_array('timesec', $evalList)) { $dateFormat = $dateFormats['timesec']; $isDateField = true; $classes[] = 't3js-datetimepicker'; $attributes['data-date-type'] = 'timesec'; } else { if ($checkboxIsset === false) { $config['checkbox'] = ''; } } // @todo: The whole eval handling is a mess and needs refactoring foreach ($evalList as $func) { switch ($func) { case 'required': $attributes['data-formengine-validation-rules'] = $this->getValidationDataAsJsonString(array('required' => true)); break; default: // @todo: This is ugly: The code should find out on it's own whether a eval definition is a // @todo: keyword like "date", or a class reference. The global registration could be dropped then // Pair hook to the one in \TYPO3\CMS\Core\DataHandling\DataHandler::checkValue_input_Eval() if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tce']['formevals'][$func])) { if (class_exists($func)) { $evalObj = GeneralUtility::makeInstance($func); if (method_exists($evalObj, 'deevaluateFieldValue')) { $_params = array('value' => $parameterArray['itemFormElValue']); $parameterArray['itemFormElValue'] = $evalObj->deevaluateFieldValue($_params); } } } } } $paramsList = array('field' => $parameterArray['itemFormElName'], 'evalList' => implode(',', $evalList), 'is_in' => trim($config['is_in']), 'checkbox' => $config['checkbox'] ? 1 : 0, 'checkboxValue' => $config['checkbox']); // set classes $classes[] = 'form-control'; $classes[] = 't3js-clearable'; $classes[] = 'hasDefaultValue'; // calculate attributes $attributes['data-formengine-validation-rules'] = $this->getValidationDataAsJsonString($config); $attributes['data-formengine-input-params'] = json_encode($paramsList); $attributes['data-formengine-input-name'] = htmlspecialchars($parameterArray['itemFormElName']); $attributes['id'] = StringUtility::getUniqueId('formengine-input-'); $attributes['value'] = ''; if (isset($config['max']) && (int) $config['max'] > 0) { $attributes['maxlength'] = (int) $config['max']; } if (!empty($classes)) { $attributes['class'] = implode(' ', $classes); } // This is the EDITABLE form field. if (!empty($config['placeholder'])) { $attributes['placeholder'] = trim($config['placeholder']); } if (isset($config['autocomplete'])) { $attributes['autocomplete'] = empty($config['autocomplete']) ? 'off' : 'on'; } // Build the attribute string $attributeString = ''; foreach ($attributes as $attributeName => $attributeValue) { $attributeString .= ' ' . $attributeName . '="' . htmlspecialchars($attributeValue) . '"'; } $html = ' <input type="text"' . $attributeString . $parameterArray['onFocus'] . ' />'; // This is the ACTUAL form field - values from the EDITABLE field must be transferred to this field which is the one that is written to the database. $html .= '<input type="hidden" name="' . $parameterArray['itemFormElName'] . '" value="' . htmlspecialchars($parameterArray['itemFormElValue']) . '" />'; // Going through all custom evaluations configured for this field // @todo: Similar to above code! foreach ($evalList as $evalData) { if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tce']['formevals'][$evalData])) { if (class_exists($evalData)) { $evalObj = GeneralUtility::makeInstance($evalData); if (method_exists($evalObj, 'returnFieldJS')) { $resultArray['extJSCODE'] .= LF . 'TBE_EDITOR.customEvalFunctions[' . GeneralUtility::quoteJSvalue($evalData) . '] = function(value) {' . $evalObj->returnFieldJS() . '}'; } } } } // add HTML wrapper if ($isDateField) { $html = ' <div class="input-group"> ' . $html . ' <span class="input-group-btn"> <label class="btn btn-default" for="' . $attributes['id'] . '"> ' . $iconFactory->getIcon('actions-edit-pick-date', Icon::SIZE_SMALL)->render() . ' </label> </span> </div>'; } // Wrap a wizard around the item? $html = $this->renderWizards(array($html), $config['wizards'], $table, $row, $fieldName, $parameterArray, $parameterArray['itemFormElName'], $specConf); // Add a wrapper to remain maximum width $width = (int) $this->formMaxWidth($size); $html = '<div class="form-control-wrap"' . ($width ? ' style="max-width: ' . $width . 'px"' : '') . '>' . $html . '</div>'; $resultArray['html'] = $html; return $resultArray; }
/** * Determines whether the URL is on the current host and belongs to the * current TYPO3 installation. The scheme part is ignored in the comparison. * * @param string $url URL to be checked * @return bool Whether the URL belongs to the current TYPO3 installation */ protected function isInCurrentDomain($url) { $urlWithoutSchema = preg_replace('#^https?://#', '', $url); $siteUrlWithoutSchema = preg_replace('#^https?://#', '', GeneralUtility::getIndpEnv('TYPO3_SITE_URL')); return StringUtility::beginsWith($urlWithoutSchema . '/', GeneralUtility::getIndpEnv('HTTP_HOST') . '/') && StringUtility::beginsWith($urlWithoutSchema, $siteUrlWithoutSchema); }
/** * Creating the module output. * * @throws \UnexpectedValueException * @return void */ public function main() { $lang = $this->getLanguageService(); $this->content .= '<form action="" name="editForm" id="NewContentElementController"><input type="hidden" name="defValues" value="" />'; if ($this->id && $this->access) { // Init position map object: $posMap = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Tree\View\ContentCreationPagePositionMap::class); $posMap->cur_sys_language = $this->sys_language; // If a column is pre-set: if (isset($this->colPos)) { if ($this->uid_pid < 0) { $row = array(); $row['uid'] = abs($this->uid_pid); } else { $row = ''; } $this->onClickEvent = $posMap->onClickInsertRecord($row, $this->colPos, '', $this->uid_pid, $this->sys_language); } else { $this->onClickEvent = ''; } // *************************** // Creating content // *************************** $this->content .= '<h1>' . $lang->getLL('newContentElement') . '</h1>'; // Wizard $wizardItems = $this->wizardArray(); // Wrapper for wizards $this->elementWrapper['section'] = array('', ''); // Hook for manipulating wizardItems, wrapper, onClickEvent etc. if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms']['db_new_content_el']['wizardItemsHook'])) { foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms']['db_new_content_el']['wizardItemsHook'] as $classData) { $hookObject = GeneralUtility::getUserObj($classData); if (!$hookObject instanceof NewContentElementWizardHookInterface) { throw new \UnexpectedValueException('$hookObject must implement interface ' . NewContentElementWizardHookInterface::class, 1227834741); } $hookObject->manipulateWizardItems($wizardItems, $this); } } // Add document inline javascript $this->moduleTemplate->addJavaScriptCode('NewContentElementWizardInlineJavascript', ' function goToalt_doc() { // ' . $this->onClickEvent . ' } if(top.refreshMenu) { top.refreshMenu(); } else { top.TYPO3ModuleMenu.refreshMenu(); }'); $iconRegistry = GeneralUtility::makeInstance(IconRegistry::class); // Traverse items for the wizard. // An item is either a header or an item rendered with a radio button and title/description and icon: $cc = $key = 0; $menuItems = array(); foreach ($wizardItems as $k => $wInfo) { if ($wInfo['header']) { $menuItems[] = array('label' => htmlspecialchars($wInfo['header']), 'content' => $this->elementWrapper['section'][0]); $key = count($menuItems) - 1; } else { $content = ''; if (!$this->onClickEvent) { // Radio button: $oC = 'document.editForm.defValues.value=unescape(' . GeneralUtility::quoteJSvalue(rawurlencode($wInfo['params'])) . ');goToalt_doc();' . (!$this->onClickEvent ? 'window.location.hash=\'#sel2\';' : ''); $content .= '<div class="media-left"><input type="radio" name="tempB" value="' . htmlspecialchars($k) . '" onclick="' . htmlspecialchars($oC) . '" /></div>'; // Onclick action for icon/title: $aOnClick = 'document.getElementsByName(\'tempB\')[' . $cc . '].checked=1;' . $oC . 'return false;'; } else { $aOnClick = "document.editForm.defValues.value=unescape('" . rawurlencode($wInfo['params']) . "');goToalt_doc();" . (!$this->onClickEvent ? "window.location.hash='#sel2';" : ''); } if (isset($wInfo['icon'])) { GeneralUtility::deprecationLog('The PageTS-Config: mod.wizards.newContentElement.wizardItems.*.elements.*.icon' . ' is deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8.' . ' Register your icon in IconRegistry::registerIcon and use the new setting:' . ' mod.wizards.newContentElement.wizardItems.*.elements.*.iconIdentifier'); $wInfo['iconIdentifier'] = 'content-' . $k; $icon = $wInfo['icon']; if (StringUtility::beginsWith($icon, '../typo3conf/ext/')) { $icon = str_replace('../typo3conf/ext/', 'EXT:', $icon); } if (!StringUtility::beginsWith($icon, 'EXT:') && strpos($icon, '/') !== false) { $icon = TYPO3_mainDir . GeneralUtility::resolveBackPath($wInfo['icon']); } $iconRegistry->registerIcon($wInfo['iconIdentifier'], BitmapIconProvider::class, array('source' => $icon)); } $icon = $this->moduleTemplate->getIconFactory()->getIcon($wInfo['iconIdentifier'])->render(); $menuItems[$key]['content'] .= ' <div class="media"> <a href="#" onclick="' . htmlspecialchars($aOnClick) . '"> ' . $content . ' <div class="media-left"> ' . $icon . ' </div> <div class="media-body"> <strong>' . htmlspecialchars($wInfo['title']) . '</strong>' . '<br />' . nl2br(htmlspecialchars(trim($wInfo['description']))) . '</div> </a> </div>'; $cc++; } } // Add closing section-tag foreach ($menuItems as $key => $val) { $menuItems[$key]['content'] .= $this->elementWrapper['section'][1]; } // Add the wizard table to the content, wrapped in tabs $code = '<p>' . $lang->getLL('sel1', 1) . '</p>' . $this->moduleTemplate->getDynamicTabMenu($menuItems, 'new-content-element-wizard'); $this->content .= !$this->onClickEvent ? '<h2>' . $lang->getLL('1_selectType', true) . '</h2>' : ''; $this->content .= '<div>' . $code . '</div>'; // If the user must also select a column: if (!$this->onClickEvent) { // Add anchor "sel2" $this->content .= '<div><a name="sel2"></a></div>'; // Select position $code = '<p>' . $lang->getLL('sel2', 1) . '</p>'; // Load SHARED page-TSconfig settings and retrieve column list from there, if applicable: $colPosArray = GeneralUtility::callUserFunction(BackendLayoutView::class . '->getColPosListItemsParsed', $this->id, $this); $colPosIds = array_column($colPosArray, 1); // Removing duplicates, if any $colPosList = implode(',', array_unique(array_map('intval', $colPosIds))); // Finally, add the content of the column selector to the content: $code .= $posMap->printContentElementColumns($this->id, 0, $colPosList, 1, $this->R_URI); $this->content .= '<h2>' . $lang->getLL('2_selectPosition', true) . '</h2><div>' . $code . '</div>'; } } else { // In case of no access: $this->content = ''; $this->content .= '<h1>' . $lang->getLL('newContentElement') . '</h1>'; } $this->content .= '</form>'; // Setting up the buttons and markers for docheader $this->getButtons(); }
/** * Detects if a control button (up/down/around/delete) has been pressed for an item and accordingly it will * manipulate the internal TABLECFG array * * @return void * @internal */ public function changeFunc() { if ($this->TABLECFG['col_remove']) { $kk = key($this->TABLECFG['col_remove']); $cmd = 'col_remove'; } elseif ($this->TABLECFG['col_add']) { $kk = key($this->TABLECFG['col_add']); $cmd = 'col_add'; } elseif ($this->TABLECFG['col_start']) { $kk = key($this->TABLECFG['col_start']); $cmd = 'col_start'; } elseif ($this->TABLECFG['col_end']) { $kk = key($this->TABLECFG['col_end']); $cmd = 'col_end'; } elseif ($this->TABLECFG['col_left']) { $kk = key($this->TABLECFG['col_left']); $cmd = 'col_left'; } elseif ($this->TABLECFG['col_right']) { $kk = key($this->TABLECFG['col_right']); $cmd = 'col_right'; } elseif ($this->TABLECFG['row_remove']) { $kk = key($this->TABLECFG['row_remove']); $cmd = 'row_remove'; } elseif ($this->TABLECFG['row_add']) { $kk = key($this->TABLECFG['row_add']); $cmd = 'row_add'; } elseif ($this->TABLECFG['row_top']) { $kk = key($this->TABLECFG['row_top']); $cmd = 'row_top'; } elseif ($this->TABLECFG['row_bottom']) { $kk = key($this->TABLECFG['row_bottom']); $cmd = 'row_bottom'; } elseif ($this->TABLECFG['row_up']) { $kk = key($this->TABLECFG['row_up']); $cmd = 'row_up'; } elseif ($this->TABLECFG['row_down']) { $kk = key($this->TABLECFG['row_down']); $cmd = 'row_down'; } else { $kk = ''; $cmd = ''; } if ($cmd && MathUtility::canBeInterpretedAsInteger($kk)) { if (StringUtility::beginsWith($cmd, 'row_')) { switch ($cmd) { case 'row_remove': unset($this->TABLECFG['c'][$kk]); break; case 'row_add': for ($a = 1; $a <= $this->numNewRows; $a++) { // Checking if set: The point is that any new row between existing rows // will be TRUE after one row is added while if rows are added in the bottom // of the table there will be no existing rows to stop the addition of new rows // which means it will add up to $this->numNewRows rows then. if (!isset($this->TABLECFG['c'][$kk + $a])) { $this->TABLECFG['c'][$kk + $a] = array(); } else { break; } } break; case 'row_top': $this->TABLECFG['c'][1] = $this->TABLECFG['c'][$kk]; unset($this->TABLECFG['c'][$kk]); break; case 'row_bottom': $this->TABLECFG['c'][10000000] = $this->TABLECFG['c'][$kk]; unset($this->TABLECFG['c'][$kk]); break; case 'row_up': $this->TABLECFG['c'][$kk - 3] = $this->TABLECFG['c'][$kk]; unset($this->TABLECFG['c'][$kk]); break; case 'row_down': $this->TABLECFG['c'][$kk + 3] = $this->TABLECFG['c'][$kk]; unset($this->TABLECFG['c'][$kk]); break; } ksort($this->TABLECFG['c']); } if (StringUtility::beginsWith($cmd, 'col_')) { foreach ($this->TABLECFG['c'] as $cAK => $value) { switch ($cmd) { case 'col_remove': unset($this->TABLECFG['c'][$cAK][$kk]); break; case 'col_add': $this->TABLECFG['c'][$cAK][$kk + 1] = ''; break; case 'col_start': $this->TABLECFG['c'][$cAK][1] = $this->TABLECFG['c'][$cAK][$kk]; unset($this->TABLECFG['c'][$cAK][$kk]); break; case 'col_end': $this->TABLECFG['c'][$cAK][1000000] = $this->TABLECFG['c'][$cAK][$kk]; unset($this->TABLECFG['c'][$cAK][$kk]); break; case 'col_left': $this->TABLECFG['c'][$cAK][$kk - 3] = $this->TABLECFG['c'][$cAK][$kk]; unset($this->TABLECFG['c'][$cAK][$kk]); break; case 'col_right': $this->TABLECFG['c'][$cAK][$kk + 3] = $this->TABLECFG['c'][$cAK][$kk]; unset($this->TABLECFG['c'][$cAK][$kk]); break; } ksort($this->TABLECFG['c'][$cAK]); } } } // Convert line breaks to <br /> tags: foreach ($this->TABLECFG['c'] as $a => $value) { foreach ($this->TABLECFG['c'][$a] as $b => $value2) { $this->TABLECFG['c'][$a][$b] = str_replace(LF, '<br />', str_replace(CR, '', $this->TABLECFG['c'][$a][$b])); } } }
/** * This will render a <textarea> * * @return array As defined in initializeResultArray() of AbstractNode */ public function render() { $languageService = $this->getLanguageService(); $table = $this->data['tableName']; $fieldName = $this->data['fieldName']; $row = $this->data['databaseRow']; $parameterArray = $this->data['parameterArray']; $resultArray = $this->initializeResultArray(); $backendUser = $this->getBackendUserAuthentication(); $config = $parameterArray['fieldConf']['config']; // Setting columns number $cols = MathUtility::forceIntegerInRange($config['cols'] ?: $this->defaultInputWidth, $this->minimumInputWidth, $this->maxInputWidth); // Setting number of rows $rows = MathUtility::forceIntegerInRange($config['rows'] ?: 5, 1, 20); $originalRows = $rows; $itemFormElementValueLength = strlen($parameterArray['itemFormElValue']); if ($itemFormElementValueLength > $this->charactersPerRow * 2) { $cols = $this->maxInputWidth; $rows = MathUtility::forceIntegerInRange(round($itemFormElementValueLength / $this->charactersPerRow), count(explode(LF, $parameterArray['itemFormElValue'])), 20); if ($rows < $originalRows) { $rows = $originalRows; } } // must be called after the cols and rows calculation, so the parameters are applied // to read-only fields as well. // @todo: Same as in InputTextElement ... if ($config['readOnly']) { $config['cols'] = $cols; $config['rows'] = $rows; $options = $this->data; $options['parameterArray'] = array('fieldConf' => array('config' => $config), 'itemFormElValue' => $parameterArray['itemFormElValue']); $options['renderType'] = 'none'; return $this->nodeFactory->create($options)->render(); } $evalList = GeneralUtility::trimExplode(',', $config['eval'], true); // "Extra" configuration; Returns configuration for the field based on settings found in the "types" fieldlist. Traditionally, this is where RTE configuration has been found. $specialConfiguration = BackendUtility::getSpecConfParts($parameterArray['fieldConf']['defaultExtras']); $html = ''; // Show message, if no RTE (field can only be edited with RTE!) if ($specialConfiguration['rte_only']) { $html = '<p><em>' . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.noRTEfound')) . '</em></p>'; } else { $attributes = array(); // validation foreach ($evalList as $func) { if ($func === 'required') { $attributes['data-formengine-validation-rules'] = $this->getValidationDataAsJsonString(array('required' => true)); } else { // @todo: This is ugly: The code should find out on it's own whether a eval definition is a // @todo: keyword like "date", or a class reference. The global registration could be dropped then // Pair hook to the one in \TYPO3\CMS\Core\DataHandling\DataHandler::checkValue_input_Eval() // There is a similar hook for "evaluateFieldValue" in DataHandler and InputTextElement if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tce']['formevals'][$func])) { if (class_exists($func)) { $evalObj = GeneralUtility::makeInstance($func); if (method_exists($evalObj, 'deevaluateFieldValue')) { $_params = array('value' => $parameterArray['itemFormElValue']); $parameterArray['itemFormElValue'] = $evalObj->deevaluateFieldValue($_params); } } } } } // calculate classes $classes = array(); $classes[] = 'form-control'; $classes[] = 't3js-formengine-textarea'; $classes[] = 'formengine-textarea'; if ($specialConfiguration['fixed-font']) { $classes[] = 'text-monospace'; } if ($specialConfiguration['enable-tab']) { $classes[] = 't3js-enable-tab'; } // calculate styles $styles = array(); // add the max-height from the users' preference to it $maximumHeight = (int) $backendUser->uc['resizeTextareas_MaxHeight']; if ($maximumHeight > 0) { $styles[] = 'max-height: ' . $maximumHeight . 'px'; } // calculate attributes $attributes['id'] = StringUtility::getUniqueId('formengine-textarea-'); $attributes['name'] = htmlspecialchars($parameterArray['itemFormElName']); $attributes['data-formengine-input-name'] = htmlspecialchars($parameterArray['itemFormElName']); if (!empty($styles)) { $attributes['style'] = implode(' ', $styles); } if (!empty($classes)) { $attributes['class'] = implode(' ', $classes); } $attributes['rows'] = $rows; $attributes['wrap'] = $specialConfiguration['nowrap'] ? 'off' : ($config['wrap'] ?: 'virtual'); $attributes['onChange'] = implode('', $parameterArray['fieldChangeFunc']); if (isset($config['max']) && (int) $config['max'] > 0) { $attributes['maxlength'] = (int) $config['max']; } $attributeString = ''; foreach ($attributes as $attributeName => $attributeValue) { $attributeString .= ' ' . $attributeName . '="' . htmlspecialchars($attributeValue) . '"'; } // Build the textarea $placeholderAttribute = ''; if (!empty($config['placeholder'])) { $placeholderAttribute = ' placeholder="' . htmlspecialchars(trim($config['placeholder'])) . '" '; } $html .= '<textarea' . $attributeString . $placeholderAttribute . '>' . htmlspecialchars($parameterArray['itemFormElValue']) . '</textarea>'; // Wrap a wizard around the item? $html = $this->renderWizards(array($html), $config['wizards'], $table, $row, $fieldName, $parameterArray, $parameterArray['itemFormElName'], $specialConfiguration, false); $maximumWidth = (int) $this->formMaxWidth($cols); $html = '<div class="form-control-wrap"' . ($maximumWidth ? ' style="max-width: ' . $maximumWidth . 'px"' : '') . '>' . $html . '</div>'; } $resultArray['html'] = $html; return $resultArray; }
/** * called from the typoLink() function * * does the magic to split the full "typolink" string like "15,13 _blank myclass &more=1" * into separate parts * * @param string $linkText The string (text) to link * @param string $mixedLinkParameter destination data like "15,13 _blank myclass &more=1" used to create the link * @param array $configuration TypoScript configuration * @return array | string * @see typoLink() */ protected function resolveMixedLinkParameter($linkText, $mixedLinkParameter, &$configuration = array()) { $linkParameter = null; // Link parameter value = first part $linkParameterParts = GeneralUtility::makeInstance(TypoLinkCodecService::class)->decode($mixedLinkParameter); // Check for link-handler keyword: list($linkHandlerKeyword, $linkHandlerValue) = explode(':', $linkParameterParts['url'], 2); if ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['typolinkLinkHandler'][$linkHandlerKeyword] && (string) $linkHandlerValue !== '') { $linkHandlerObj = GeneralUtility::getUserObj($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['typolinkLinkHandler'][$linkHandlerKeyword]); if (method_exists($linkHandlerObj, 'main')) { return $linkHandlerObj->main($linkText, $configuration, $linkHandlerKeyword, $linkHandlerValue, $mixedLinkParameter, $this); } } // Resolve FAL-api "file:UID-of-sys_file-record" and "file:combined-identifier" if ($linkHandlerKeyword === 'file' && !StringUtility::beginsWith($linkParameterParts['url'], 'file://')) { try { $fileOrFolderObject = $this->getResourceFactory()->retrieveFileOrFolderObject($linkHandlerValue); // Link to a folder or file if ($fileOrFolderObject instanceof File || $fileOrFolderObject instanceof Folder) { $linkParameter = $fileOrFolderObject->getPublicUrl(); } else { $linkParameter = null; } } catch (\RuntimeException $e) { // Element wasn't found $linkParameter = null; } catch (ResourceDoesNotExistException $e) { // Resource was not found return $linkText; } // Disallow direct javascript: links } elseif (strtolower(trim($linkHandlerKeyword)) === 'javascript') { return $linkText; } else { $linkParameter = $linkParameterParts['url']; } // additional parameters that need to be set if ($linkParameterParts['additionalParams'] !== '') { $forceParams = $linkParameterParts['additionalParams']; // params value $configuration['additionalParams'] .= $forceParams[0] === '&' ? $forceParams : '&' . $forceParams; } return array('href' => $linkParameter, 'target' => $linkParameterParts['target'], 'class' => $linkParameterParts['class'], 'title' => $linkParameterParts['title']); }
/** * Flushes a directory by first moving to a temporary resource, and then * triggering the remove process. This way directories can be flushed faster * to prevent race conditions on concurrent processes accessing the same directory. * * @param string $directory The directory to be renamed and flushed * @param bool $keepOriginalDirectory Whether to only empty the directory and not remove it * @param bool $flushOpcodeCache Also flush the opcode cache right after renaming the directory. * @return bool Whether the action was successful */ public static function flushDirectory($directory, $keepOriginalDirectory = false, $flushOpcodeCache = false) { $result = false; if (is_dir($directory)) { $temporaryDirectory = rtrim($directory, '/') . '.' . StringUtility::getUniqueId('remove') . '/'; if (rename($directory, $temporaryDirectory)) { if ($flushOpcodeCache) { self::makeInstance(OpcodeCacheService::class)->clearAllActive($directory); } if ($keepOriginalDirectory) { static::mkdir($directory); } clearstatcache(); $result = static::rmdir($temporaryDirectory, true); } } return $result; }
/** * Creates SELECT query components for selecting fields ($select) from two/three tables joined * Use $mm_table together with $local_table or $foreign_table to select over two tables. Or use all three tables to select the full MM-relation. * The JOIN is done with [$local_table].uid <--> [$mm_table].uid_local / [$mm_table].uid_foreign <--> [$foreign_table].uid * The function is very useful for selecting MM-relations between tables adhering to the MM-format used by TCE (TYPO3 Core Engine). See the section on $GLOBALS['TCA'] in Inside TYPO3 for more details. * * @param string $select See exec_SELECT_mm_query() * @param string $local_table See exec_SELECT_mm_query() * @param string $mm_table See exec_SELECT_mm_query() * @param string $foreign_table See exec_SELECT_mm_query() * @param string $whereClause See exec_SELECT_mm_query() * @param string $groupBy See exec_SELECT_mm_query() * @param string $orderBy See exec_SELECT_mm_query() * @param string $limit See exec_SELECT_mm_query() * @return array SQL query components */ protected function getSelectMmQueryParts($select, $local_table, $mm_table, $foreign_table, $whereClause = '', $groupBy = '', $orderBy = '', $limit = '') { $foreign_table_as = $foreign_table == $local_table ? $foreign_table . StringUtility::getUniqueId('_join') : ''; $mmWhere = $local_table ? $local_table . '.uid=' . $mm_table . '.uid_local' : ''; $mmWhere .= ($local_table and $foreign_table) ? ' AND ' : ''; $tables = ($local_table ? $local_table . ',' : '') . $mm_table; if ($foreign_table) { $mmWhere .= ($foreign_table_as ?: $foreign_table) . '.uid=' . $mm_table . '.uid_foreign'; $tables .= ',' . $foreign_table . ($foreign_table_as ? ' AS ' . $foreign_table_as : ''); } return array('SELECT' => $select, 'FROM' => $tables, 'WHERE' => $mmWhere . ' ' . $whereClause, 'GROUPBY' => $groupBy, 'ORDERBY' => $orderBy, 'LIMIT' => $limit); }