Commit b524fc86 authored by Christian Kuhn's avatar Christian Kuhn
Browse files

[!!!][TASK] The FormEngine is dead, long live the FormEngine!

tl;dr: This patch makes FormEngine insanely flexible, extensions
however should not rely on structures for now, since class names
and array content will change.

The patch applies a separation of concerns to the FormEngine
class structure by extracting the data processing from rendering.

As a main goal the render part consisting of container and element
classes routed through the flexible NodeFactory only works on data
created by the new FormDataCompiler class construct. This makes the
FormEngine much more flexible and opens ways to not only use the render
part in the context of database driven data, but on anything that is
fed to it.

This patch creates the main structure for this. The FormDataCompiler
class returns a defined array container and elements can work on it.
Data is added by single FormDataProvider, which are combined in
FormDataGroups. FormDataProvider may depend on each other and a
FormDataGroup "knows" its providers and calls them in a dedicated order.

For instance, the "FullDatabaseRecord" FormDataGroup first calls a
provider that fetches the record defined by uid and table name and
a later called provider determines the given record type this record
is assigned to, so another provider can then work on TCA to determine
the list of record fields to be shown. The FormDataProvider used
for the main FormDataGroup are defined in TYPO3_CONF_VARS, so
extensions can add and remove their own providers to add or change
certain data if needed. This is highly flexible and extensions are
able to hook in at a specific position within the provider chain for
the main data groups.

This construct obsoletes the DataPreprocessor as well as several
other side classes.

With this patch the main architecture is created and lots of data
preparation is transfered already, supported by a high unit test
coverage.

The FormEngine class itself is removed: The inline ajax entry point
is moved to an own controller class, the getMainFields() and friends
methods are substituted with FormDataCompiler / NodeFactory combinations
and the data gathering is for now parked in a FormResultCompiler class.

However, this process is not yet finished and lots of @todo
statements are added to the code base to document open ends and to
further separate the data handling from the render engine. Especially
the IRRE data handling is currently still located within the render
engine and makes the whole thing much more complicated than it should
be. Lots of detail patches need to follow to bring this code
to a level where it belongs to be.

Warning: While this patch is already insanely huge touching more than
22 thousands lines of code, lots of loose ends need to be tackled and
the API is not final yet. The arrays will be reduced and sharpened
during the next weeks, class names may change and structures will
change.

Change-Id: Ief1769f478373cc26d1bf6c49114258f0dae8355
Resolves: #69568
Releases: master
Reviewed-on: http://review.typo3.org/41933

Reviewed-by: Anja Leichsenring's avatarAnja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: Anja Leichsenring's avatarAnja Leichsenring <aleichsenring@ab-softlab.de>
Reviewed-by: default avatarFlorian Peters <fpeters1392@googlemail.com>
Reviewed-by: Mathias Schreiber's avatarMathias Schreiber <mathias.schreiber@wmdb.de>
Tested-by: Mathias Schreiber's avatarMathias Schreiber <mathias.schreiber@wmdb.de>
Reviewed-by: Alexander Opitz's avatarAlexander Opitz <opitz.alexander@googlemail.com>
Tested-by: Alexander Opitz's avatarAlexander Opitz <opitz.alexander@googlemail.com>
Reviewed-by: Wouter Wolters's avatarWouter Wolters <typo3@wouterwolters.nl>
Tested-by: Wouter Wolters's avatarWouter Wolters <typo3@wouterwolters.nl>
Reviewed-by: Christian Kuhn's avatarChristian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn's avatarChristian Kuhn <lolli@schwarzbu.ch>
parent bbe40115
......@@ -74,6 +74,7 @@ class TranslationConfigurationProvider {
$languageRecords = $this->getDatabaseConnection()->exec_SELECTgetRows('*', 'sys_language', '');
foreach ($languageRecords as $languageRecord) {
$languages[$languageRecord['uid']] = $languageRecord;
// @todo: this should probably resolve language_isocode too and throw a deprecation if not filled
if ($languageRecord['static_lang_isocode'] && ExtensionManagementUtility::isLoaded('static_info_tables')) {
$staticLangRow = BackendUtility::getRecord('static_languages', $languageRecord['static_lang_isocode'], 'lg_iso_2');
if ($staticLangRow['lg_iso_2']) {
......
......@@ -163,9 +163,6 @@ class ElementInformationController implements \TYPO3\CMS\Core\Http\ControllerInt
$this->access = is_array($this->pageInfo) ? 1 : 0;
}
}
/** @var $treatData \TYPO3\CMS\Backend\Form\DataPreprocessor */
$treatData = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Form\DataPreprocessor::class);
$treatData->renderRecord($this->table, $this->uid, 0, $this->row);
}
}
......
......@@ -15,8 +15,8 @@ namespace TYPO3\CMS\Backend\Controller;
*/
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Form\DataPreprocessor;
use TYPO3\CMS\Backend\Form\FormEngine;
use TYPO3\CMS\Backend\Form\Exception\AccessDeniedException;
use TYPO3\CMS\Backend\Form\FormResultCompiler;
use TYPO3\CMS\Backend\Form\Utility\FormEngineUtility;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Backend\Utility\IconUtility;
......@@ -30,10 +30,12 @@ use TYPO3\CMS\Core\Page\PageRenderer;
use TYPO3\CMS\Core\Type\Bitmask\Permission;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\HttpUtility;
use TYPO3\CMS\Core\Service\MarkerBasedTemplateService;
use TYPO3\CMS\Core\Utility\MathUtility;
use TYPO3\CMS\Extbase\SignalSlot\Dispatcher;
use TYPO3\CMS\Frontend\Page\PageRepository;
use TYPO3\CMS\Backend\Form\FormDataCompiler;
use TYPO3\CMS\Backend\Form\NodeFactory;
use TYPO3\CMS\Backend\Form\FormDataGroup\TcaDatabaseRecord;
/**
* Script Class: Drawing the editing form for editing records in TYPO3.
......@@ -207,13 +209,6 @@ class EditDocumentController implements \TYPO3\CMS\Core\Http\ControllerInterface
*/
public $returnEditConf;
/**
* localization mode for TCEforms (eg. "text")
*
* @var string
*/
public $localizationMode;
/**
* Workspace used for the editing action.
*
......@@ -291,7 +286,7 @@ class EditDocumentController implements \TYPO3\CMS\Core\Http\ControllerInterface
*
* @var string
*/
public $storeTitle;
public $storeTitle = '';
/**
* Contains an array with key/value pairs of GET parameters needed to reach the
......@@ -383,25 +378,16 @@ class EditDocumentController implements \TYPO3\CMS\Core\Http\ControllerInterface
public $modTSconfig;
/**
* instance of TCEforms class
*
* @var \TYPO3\CMS\Backend\Form\FormEngine
*/
public $tceforms;
/**
* Contains the root-line path of the currently edited record(s) - for display.
*
* @var string
* @var FormResultCompiler
*/
public $generalPathOfForm;
protected $formResultCompiler;
/**
* Used internally to disable the storage of the document reference (eg. new records)
*
* @var bool
*/
public $dontStoreDocumentRef;
public $dontStoreDocumentRef = 0;
/**
* @var \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
......@@ -485,9 +471,6 @@ class EditDocumentController implements \TYPO3\CMS\Core\Http\ControllerInterface
$this->R_URL_getvars['edit'] = $this->editconf;
// MAKE url for storing
$this->compileStoreDat();
// Initialize more variables.
$this->dontStoreDocumentRef = 0;
$this->storeTitle = '';
// Get session data for the module:
$this->docDat = $this->getBackendUser()->getModuleData('FormEngine', 'ses');
$this->docHandler = $this->docDat[0];
......@@ -861,12 +844,9 @@ class EditDocumentController implements \TYPO3\CMS\Core\Http\ControllerInterface
$body = '';
// Begin edit:
if (is_array($this->editconf)) {
// Initialize TCEforms (rendering the forms)
$this->tceforms = GeneralUtility::makeInstance(FormEngine::class);
$this->tceforms->doSaveFieldName = 'doSave';
$this->tceforms->localizationMode = GeneralUtility::inList('text,media', $this->localizationMode) ? $this->localizationMode : '';
// text,media is keywords defined in TYPO3 Core API..., see "l10n_cat"
$this->tceforms->returnUrl = $this->R_URI;
/** @var FormResultCompiler formResultCompiler */
$this->formResultCompiler = GeneralUtility::makeInstance(FormResultCompiler::class);
if ($this->editRegularContentFromId) {
$this->editRegularContentFromId();
}
......@@ -882,9 +862,9 @@ class EditDocumentController implements \TYPO3\CMS\Core\Http\ControllerInterface
}
// Module configuration
$this->modTSconfig = $this->viewId ? BackendUtility::getModTSconfig($this->viewId, 'mod.xMOD_alt_doc') : array();
$body = $this->tceforms->printNeededJSFunctions_top();
$body = $this->formResultCompiler->JStop();
$body .= $this->compileForm($editForm);
$body .= $this->tceforms->printNeededJSFunctions();
$body .= $this->formResultCompiler->printNeededJSFunctions();
}
}
// Access check...
......@@ -894,7 +874,6 @@ class EditDocumentController implements \TYPO3\CMS\Core\Http\ControllerInterface
$docHeaderButtons = $this->getButtons();
$markers = array(
'LANGSELECTOR' => $this->langSelector(),
'EXTRAHEADER' => $this->extraFormHeaders(),
'CSH' => $docHeaderButtons['csh'],
'CONTENT' => $body
);
......@@ -922,7 +901,7 @@ class EditDocumentController implements \TYPO3\CMS\Core\Http\ControllerInterface
*
***************************/
/**
* Creates the editing form with TCEforms, based on the input from GPvars.
* Creates the editing form with FormEnigne, based on the input from GPvars.
*
* @return string HTML form elements wrapped in tables
*/
......@@ -931,7 +910,6 @@ class EditDocumentController implements \TYPO3\CMS\Core\Http\ControllerInterface
$this->elementsData = array();
$this->errorC = 0;
$this->newC = 0;
$thePrevUid = '';
$editForm = '';
$trData = NULL;
$beUser = $this->getBackendUser();
......@@ -940,164 +918,154 @@ class EditDocumentController implements \TYPO3\CMS\Core\Http\ControllerInterface
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 => $cmd) {
if ($cmd == 'edit' || $cmd == 'new') {
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) {
// Checking if the user has permissions? (Only working as a precaution,
// because the final permission check is always down in TCE. But it's
// good to notify the user on beforehand...)
// First, resetting flags.
$hasAccess = 1;
$deniedAccessReason = '';
$deleteAccess = 0;
$this->viewId = 0;
// If the command is to create a NEW record...:
if ($cmd == 'new') {
// NOTICE: the id values in this case points to the page uid onto which the
// record should be create OR (if the id is negativ) to a record from the
// same table AFTER which to create the record.
if ((int)$theUid) {
// Find parent page on which the new record reside
// Less than zero - find parent page
if ($theUid < 0) {
$calcPRec = BackendUtility::getRecord($table, abs($theUid));
$calcPRec = BackendUtility::getRecord('pages', $calcPRec['pid']);
} else {
// always a page
$calcPRec = BackendUtility::getRecord('pages', abs($theUid));
// Don't save this document title in the document selector if the document is new.
if ($command === 'new') {
$this->dontStoreDocumentRef = 1;
}
// Now, calculate whether the user has access to creating new records on this position:
if (is_array($calcPRec)) {
// Permissions for the parent page
$CALC_PERMS = $beUser->calcPerms($calcPRec);
if ($table == 'pages') {
// If pages:
$hasAccess = $CALC_PERMS & Permission::PAGE_NEW ? 1 : 0;
/** @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,
];
$formData = $formDataCompiler->compile($formDataCompilerInput);
// Set this->viewId if possible
if ($command === 'new' && $table !== 'pages' && !empty($formData['parentPageRow']['uid'])) {
$this->viewId = $formData['parentPageRow']['uid'];
} else {
$hasAccess = $CALC_PERMS & Permission::CONTENT_EDIT ? 1 : 0;
$this->viewId = $calcPRec['uid'];
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['vanillaTableTca']['ctrl']['languageField'])
&& is_array($formData['databaseRow'][$formData['vanillaTableTca']['ctrl']['languageField']])
&& $formData['databaseRow'][$formData['vanillaTableTca']['ctrl']['languageField']][0] > 0
) {
$this->viewId_addParams = '&L=' . $formData['databaseRow'][$formData['vanillaTableTca']['ctrl']['languageField']][0];
}
}
}
// Don't save this document title in the document selector if the document is new.
$this->dontStoreDocumentRef = 1;
} else {
// Edit:
$calcPRec = BackendUtility::getRecord($table, $theUid);
BackendUtility::fixVersioningPid($table, $calcPRec);
if (is_array($calcPRec)) {
if ($table == 'pages') { // If pages:
$CALC_PERMS = $beUser->calcPerms($calcPRec);
$hasAccess = $CALC_PERMS & Permission::PAGE_EDIT ? 1 : 0;
$deleteAccess = $CALC_PERMS & Permission::PAGE_DELETE ? 1 : 0;
$this->viewId = $calcPRec['uid'];
// 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 {
// Fetching pid-record first
$CALC_PERMS = $beUser->calcPerms(BackendUtility::getRecord('pages', $calcPRec['pid']));
$hasAccess = $CALC_PERMS & Permission::CONTENT_EDIT ? 1 : 0;
$deleteAccess = $CALC_PERMS & Permission::CONTENT_EDIT ? 1 : 0;
$this->viewId = $calcPRec['pid'];
// Adding "&L=xx" if the record being edited has a languageField with a value larger than zero!
if ($GLOBALS['TCA'][$table]['ctrl']['languageField'] && $calcPRec[$GLOBALS['TCA'][$table]['ctrl']['languageField']] > 0) {
$this->viewId_addParams = '&L=' . $calcPRec[$GLOBALS['TCA'][$table]['ctrl']['languageField']];
$deleteAccess = $permission & Permission::CONTENT_EDIT ? TRUE : FALSE;
}
}
// Check internals regarding access:
$isRootLevelRestrictionIgnored = BackendUtility::isRootLevelRestrictionIgnored($table);
if ($hasAccess || (int)$calcPRec['pid'] === 0 && $isRootLevelRestrictionIgnored) {
$hasAccess = $beUser->recordEditAccessInternals($table, $calcPRec);
$deniedAccessReason = $beUser->errorMsg;
}
} else {
$hasAccess = 0;
// 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 \TYPO3\CMS\Core\Messaging\FlashMessageQueue */
$defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
$defaultFlashMessageQueue->enqueue($flashMessage);
}
}
if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/alt_doc.php']['makeEditForm_accessCheck'])) {
foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/alt_doc.php']['makeEditForm_accessCheck'] as $_funcRef) {
$_params = array(
'table' => $table,
'uid' => $theUid,
'cmd' => $cmd,
'hasAccess' => $hasAccess
);
$hasAccess = GeneralUtility::callUserFunction($_funcRef, $_params, $this);
}
}
// AT THIS POINT we have checked the access status of the editing/creation of
// records and we can now proceed with creating the form elements:
if ($hasAccess) {
/** @var DataPreprocessor $trData */
$prevPageID = is_object($trData) ? $trData->prevPageID : '';
$trData = GeneralUtility::makeInstance(DataPreprocessor::class);
$trData->addRawData = TRUE;
$trData->defVals = $this->defVals;
$trData->lockRecords = 1;
$trData->prevPageID = $prevPageID;
// 'new'
$trData->fetchRecord($table, $theUid, $cmd == 'new' ? 'new' : '');
$rec = reset($trData->regTableItems_data);
$rec['uid'] = $cmd == 'new' ? uniqid('NEW', TRUE) : $theUid;
if ($cmd == 'new') {
$rec['pid'] = $theUid == 'prev' ? $thePrevUid : $theUid;
// Record title
if (!$this->storeTitle) {
$this->storeTitle = $this->recTitle
? htmlspecialchars($this->recTitle)
: BackendUtility::getRecordTitle($table, $formData['databaseRow'], TRUE);
}
$this->elementsData[] = array(
'table' => $table,
'uid' => $rec['uid'],
'pid' => $rec['pid'],
'cmd' => $cmd,
'uid' => $formData['databaseRow']['uid'],
'pid' => $formData['databaseRow']['pid'],
'cmd' => $command,
'deleteAccess' => $deleteAccess
);
// Now, render the form:
if (is_array($rec)) {
// Setting visual path / title of form:
$this->generalPathOfForm = $this->tceforms->getRecordPath($table, $rec);
if (!$this->storeTitle) {
$this->storeTitle = $this->recTitle ? htmlspecialchars($this->recTitle) : BackendUtility::getRecordTitle($table, $rec, TRUE);
}
// Setting variables in TCEforms object:
// Set additional FormData
// @todo: This is a hack and should be done differently
if (is_array($this->overrideVals) && is_array($this->overrideVals[$table])) {
$this->tceforms->hiddenFieldListArr = array_keys($this->overrideVals[$table]);
$formData['hiddenFieldListArray'] = array_keys($this->overrideVals[$table]);
}
if ($command !== 'new') {
BackendUtility::lockRecords($table, $formData['databaseRow']['uid'], $table === 'tt_content' ? $formData['databaseRow']['pid'] : 0);
}
// Create form for the record (either specific list of fields or the whole record):
$panel = '';
// 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)) {
$panel .= $this->tceforms->getListedFields($table, $rec, $this->columnsOnly[$table]);
$formData['fieldListToRender'] = $this->columnsOnly[$table];
} else {
$panel .= $this->tceforms->getListedFields($table, $rec, $this->columnsOnly);
$formData['fieldListToRender'] = $this->columnsOnly;
}
} else {
$panel .= $this->tceforms->getMainFields($table, $rec);
}
$panel = $this->tceforms->wrapTotal($panel, $rec, $table);
// Setting the pid value for new records:
if ($cmd == 'new') {
$panel .= '<input type="hidden" name="data[' . $table . '][' . $rec['uid'] . '][pid]" value="' . $rec['pid'] . '" />';
$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++;
}
// Display "is-locked" message:
if ($lockInfo = BackendUtility::isRecordLocked($table, $rec['uid'])) {
/** @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 \TYPO3\CMS\Core\Messaging\FlashMessageQueue */
$defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
$defaultFlashMessageQueue->enqueue($flashMessage);
}
// Combine it all:
$editForm .= $panel;
}
$thePrevUid = $rec['uid'];
} else {
$editForm .= $html;
} catch (AccessDeniedException $e) {
$this->errorC++;
$editForm .= $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.noEditPermission', TRUE) . '<br /><br />' . ($deniedAccessReason ? 'Reason: ' . htmlspecialchars($deniedAccessReason) . '<br /><br />' : '');
// 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
}
}
}
......@@ -1238,22 +1206,6 @@ class EditDocumentController implements \TYPO3\CMS\Core\Http\ControllerInterface
return $langSelector;
}
/**
* Compiles the extra form headers if the tceforms
*
* @return string The HTML
*/
public function extraFormHeaders() {
/** @var MarkerBasedTemplateService $templateService */
$templateService = GeneralUtility::makeInstance(MarkerBasedTemplateService::class);
$extraTemplate = '';
if (is_array($this->tceforms->extraFormHeaders)) {
$extraTemplate = $templateService->getSubpart($this->doc->moduleTemplate, '###DOCHEADER_EXTRAHEADER###');
$extraTemplate = $templateService->substituteMarker($extraTemplate, '###EXTRAHEADER###', implode(LF, $this->tceforms->extraFormHeaders));
}
return $extraTemplate;
}
/**
* Put together the various elements (buttons, selectors, form) into a table
*
......
......@@ -145,7 +145,6 @@ class CreateFolderController implements \TYPO3\CMS\Core\Http\ControllerInterface
$this->content .= $this->doc->startPage($lang->sL('LLL:EXT:lang/locallang_core.xlf:file_newfolder.php.pagetitle'));
// Make page header:
$pageContent = $this->doc->header($lang->sL('LLL:EXT:lang/locallang_core.xlf:file_newfolder.php.pagetitle'));
if ($this->folderObject->checkActionPermission('add')) {
$code = '<form role="form" action="' . htmlspecialchars(BackendUtility::getModuleUrl('tce_file')) . '" method="post" name="editform">';
// Making the selector box for the number of concurrent folder-creations
......
......@@ -14,6 +14,11 @@ namespace TYPO3\CMS\Backend\Controller;
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Backend\Form\Exception\AccessDeniedException;
use TYPO3\CMS\Backend\Form\FormDataCompiler;
use TYPO3\CMS\Backend\Form\FormDataGroup\TcaDatabaseRecord;
use TYPO3\CMS\Backend\Form\FormResultCompiler;
use TYPO3\CMS\Backend\Form\NodeFactory;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Backend\Utility\IconUtility;
use TYPO3\CMS\Core\Imaging\Icon;
......@@ -21,7 +26,6 @@ use TYPO3\CMS\Core\Imaging\IconFactory;
use TYPO3\CMS\Core\Page\PageRenderer;
use TYPO3\CMS\Core\Type\Bitmask\Permission;
use TYPO3\CMS\Core\Messaging\FlashMessage;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\HttpUtility;
use TYPO3\CMS\Core\Utility\MathUtility;
......@@ -33,8 +37,6 @@ use TYPO3\CMS\Frontend\Page\PageRepository;
use TYPO3\CMS\Backend\Module\ModuleLoader;
use TYPO3\CMS\Backend\Template\DocumentTemplate;
use TYPO3\CMS\Backend\View\BackendLayoutView;
use TYPO3\CMS\Backend\Form\DataPreprocessor;
use TYPO3\CMS\Backend\Form\FormEngine;
use TYPO3\CMS\Core\Messaging\FlashMessageService;
use TYPO3\CMS\Backend\View\PageLayoutView;
use TYPO3\CMS\Backend\Tree\View\ContentLayoutPagePositionMap;
......@@ -816,11 +818,12 @@ class PageLayoutController {
}
// Splitting the edit-record cmd value into table/uid:
$this->eRParts = explode(':', $edit_record);
$tableName = $this->eRParts[0];
// Delete-button flag?
$this->deleteButton = MathUtility::canBeInterpretedAsInteger($this->eRParts[1]) && $edit_record && ($this->eRParts[0] != 'pages' && $this->EDIT_CONTENT || $this->eRParts[0] == 'pages' && $this->CALC_PERMS & Permission::PAGE_DELETE);
$this->deleteButton = MathUtility::canBeInterpretedAsInteger($this->eRParts[1]) && $edit_record && ($tableName !== 'pages' && $this->EDIT_CONTENT || $tableName === 'pages' && $this->CALC_PERMS & Permission::PAGE_DELETE);
// If undo-button should be rendered (depends on available items in sys_history)
$this->undoButton = FALSE;
$undoRes = $databaseConnection->exec_SELECTquery('tstamp', 'sys_history', 'tablename=' . $databaseConnection->fullQuoteStr($this->eRParts[0], 'sys_history') . ' AND recuid=' . (int)$this->eRParts[1], '', 'tstamp DESC', '1');
$undoRes = $databaseConnection->exec_SELECTquery('tstamp', 'sys_history', 'tablename=' . $databaseConnection->fullQuoteStr($tableName, 'sys_history') . ' AND recuid=' . (int)$this->eRParts[1], '', 'tstamp DESC', '1');
if ($this->undoButtonR = $databaseConnection->sql_fetch_assoc($undoRes)) {
$this->undoButton = TRUE;
}
......@@ -843,58 +846,77 @@ class PageLayoutController {
$this->editSelect = '<select name="edit_record" onchange="' . htmlspecialchars('jumpToUrl(' . GeneralUtility::quoteJSvalue(
BackendUtility::getModuleUrl('web_layout') . '&id=' . $this->id . '&edit_record='
) . '+escape(this.options[this.selectedIndex].value)' . $retUrlStr . ',this);') . '">' . implode('', $opt) . '</select>';
$content = '';
// Creating editing form:
if ($beUser->check('tables_modify', $this->eRParts[0]) && $edit_record && ($this->eRParts[0] !== 'pages' && $this->EDIT_CONTENT || $this->eRParts[0] === 'pages' && $this->CALC_PERMS & Permission::PAGE_SHOW)) {
$content = '';
if ($edit_record) {
// Splitting uid parts for special features, if new:
list($uidVal, $ex_pid, $ex_colPos) = explode('/', $this->eRParts[1]);
list($uidVal, $neighborRecordUid, $ex_colPos) = explode('/', $this->eRParts[1]);
if ($uidVal === 'new') {
$command = 'new';
// Page id of this new record
$theUid = $this->id;
if ($neighborRecordUid) {
$theUid = $neighborRecordUid;
}
} else {
$command = 'edit';
$theUid = $uidVal;
// Convert $uidVal to workspace version if any:
if ($uidVal != 'new') {
if ($draftRecord = BackendUtility::getWorkspaceVersionOfRecord($beUser->workspace, $this->eRParts[0], $uidVal, 'uid')) {
$uidVal = $draftRecord['uid'];
$draftRecord = BackendUtility::getWorkspaceVersionOfRecord($beUser->workspace, $tableName, $theUid, 'uid');
if ($draftRecord) {
$theUid = $draftRecord['uid'];
}
}
// Initializing transfer-data object:
$trData = GeneralUtility::makeInstance(DataPreprocessor::class);
$trData->addRawData = TRUE;
$trData->defVals[$this->eRParts[0]] = array(
// @todo: Hack because DatabaseInitializeNewRow reads from _GP directly
$GLOBALS['_GET']['defVals'][$tableName] = array(
'colPos' => (int)$ex_colPos,
'sys_language_uid' => (int)$this->current_sys_language
);
$trData->lockRecords = 1;
// 'new'