/** * @test */ public function databaseRowCompatibilityImplodesSelectArrayWithValuesAtSecondPosition() { $input = ['uid' => 42, 'simpleArray' => [0 => [0 => 'aLabel', 1 => 'aValue'], 1 => [0 => 'anotherLabel', 1 => 'anotherValue']]]; $expected = $input; $expected['simpleArray'] = 'aValue,anotherValue'; $this->assertEquals($expected, FormEngineUtility::databaseRowCompatibility($input)); }
/** * Creates the editing form with FormEnigne, based on the input from GPvars. * * @return string HTML form elements wrapped in tables */ public function makeEditForm() { // Initialize variables: $this->elementsData = array(); $this->errorC = 0; $this->newC = 0; $editForm = ''; $trData = null; $beUser = $this->getBackendUser(); // Traverse the GPvar edit array // Tables: foreach ($this->editconf as $table => $conf) { if (is_array($conf) && $GLOBALS['TCA'][$table] && $beUser->check('tables_modify', $table)) { // Traverse the keys/comments of each table (keys can be a commalist of uids) foreach ($conf as $cKey => $command) { if ($command == 'edit' || $command == 'new') { // Get the ids: $ids = GeneralUtility::trimExplode(',', $cKey, true); // Traverse the ids: foreach ($ids as $theUid) { // Don't save this document title in the document selector if the document is new. if ($command === 'new') { $this->dontStoreDocumentRef = 1; } /** @var TcaDatabaseRecord $formDataGroup */ $formDataGroup = GeneralUtility::makeInstance(TcaDatabaseRecord::class); /** @var FormDataCompiler $formDataCompiler */ $formDataCompiler = GeneralUtility::makeInstance(FormDataCompiler::class, $formDataGroup); /** @var NodeFactory $nodeFactory */ $nodeFactory = GeneralUtility::makeInstance(NodeFactory::class); try { // Reset viewId - it should hold data of last entry only $this->viewId = 0; $this->viewId_addParams = ''; $formDataCompilerInput = ['tableName' => $table, 'vanillaUid' => (int) $theUid, 'command' => $command, 'returnUrl' => $this->R_URI]; if (is_array($this->overrideVals) && is_array($this->overrideVals[$table])) { $formDataCompilerInput['overrideValues'] = $this->overrideVals[$table]; } $formData = $formDataCompiler->compile($formDataCompilerInput); // Set this->viewId if possible if ($command === 'new' && $table !== 'pages' && !empty($formData['parentPageRow']['uid'])) { $this->viewId = $formData['parentPageRow']['uid']; } else { if ($table == 'pages') { $this->viewId = $formData['databaseRow']['uid']; } elseif (!empty($formData['parentPageRow']['uid'])) { $this->viewId = $formData['parentPageRow']['uid']; // Adding "&L=xx" if the record being edited has a languageField with a value larger than zero! if (!empty($formData['processedTca']['ctrl']['languageField']) && is_array($formData['databaseRow'][$formData['processedTca']['ctrl']['languageField']]) && $formData['databaseRow'][$formData['processedTca']['ctrl']['languageField']][0] > 0) { $this->viewId_addParams = '&L=' . $formData['databaseRow'][$formData['processedTca']['ctrl']['languageField']][0]; } } } // Determine if delete button can be shown $deleteAccess = false; if ($command === 'edit') { $permission = $formData['userPermissionOnPage']; if ($formData['tableName'] === 'pages') { $deleteAccess = $permission & Permission::PAGE_DELETE ? true : false; } else { $deleteAccess = $permission & Permission::CONTENT_EDIT ? true : false; } } // Display "is-locked" message: if ($command === 'edit') { $lockInfo = BackendUtility::isRecordLocked($table, $formData['databaseRow']['uid']); if ($lockInfo) { /** @var $flashMessage \TYPO3\CMS\Core\Messaging\FlashMessage */ $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, htmlspecialchars($lockInfo['msg']), '', FlashMessage::WARNING); /** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */ $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class); /** @var $defaultFlashMessageQueue FlashMessageQueue */ $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier(); $defaultFlashMessageQueue->enqueue($flashMessage); } } // Record title if (!$this->storeTitle) { $this->storeTitle = $this->recTitle ? htmlspecialchars($this->recTitle) : BackendUtility::getRecordTitle($table, FormEngineUtility::databaseRowCompatibility($formData['databaseRow']), true); } $this->elementsData[] = array('table' => $table, 'uid' => $formData['databaseRow']['uid'], 'pid' => $formData['databaseRow']['pid'], 'cmd' => $command, 'deleteAccess' => $deleteAccess); if ($command !== 'new') { BackendUtility::lockRecords($table, $formData['databaseRow']['uid'], $table === 'tt_content' ? $formData['databaseRow']['pid'] : 0); } // Set list if only specific fields should be rendered. This will trigger // ListOfFieldsContainer instead of FullRecordContainer in OuterWrapContainer if ($this->columnsOnly) { if (is_array($this->columnsOnly)) { $formData['fieldListToRender'] = $this->columnsOnly[$table]; } else { $formData['fieldListToRender'] = $this->columnsOnly; } } $formData['renderType'] = 'outerWrapContainer'; $formResult = $nodeFactory->create($formData)->render(); $html = $formResult['html']; $formResult['html'] = ''; $formResult['doSaveFieldName'] = 'doSave'; // @todo: Put all the stuff into FormEngine as final "compiler" class // @todo: This is done here for now to not rewrite JStop() // @todo: and printNeededJSFunctions() now $this->formResultCompiler->mergeResult($formResult); // Seems the pid is set as hidden field (again) at end?! if ($command == 'new') { // @todo: looks ugly $html .= LF . '<input type="hidden"' . ' name="data[' . $table . '][' . $formData['databaseRow']['uid'] . '][pid]"' . ' value="' . $formData['databaseRow']['pid'] . '" />'; $this->newC++; } $editForm .= $html; } catch (AccessDeniedException $e) { $this->errorC++; // Try to fetch error message from "recordInternals" be user object // @todo: This construct should be logged and localized and de-uglified $message = $beUser->errorMsg; if (empty($message)) { // Create message from exception. $message = $e->getMessage() . ' ' . $e->getCode(); } $editForm .= $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.noEditPermission', true) . '<br /><br />' . htmlspecialchars($message) . '<br /><br />'; } } // End of for each uid } } } } return $editForm; }
/** * 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; }