first commit
This commit is contained in:
commit
1218dd1517
12716 files changed
+989233
No files matched your search
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Backend\Controller\Wizard;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use TYPO3\CMS\Backend\Template\DocumentTemplate;
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
|
||||
use TYPO3\CMS\Core\Localization\LanguageService;
|
||||
use TYPO3\CMS\Core\Type\Bitmask\Permission;
|
||||
|
||||
/**
|
||||
* Class AbstractWizardController
|
||||
* @internal This class is a specific Backend controller implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class AbstractWizardController
|
||||
{
|
||||
/**
|
||||
* Checks access for element
|
||||
*
|
||||
* @param string $table Table name
|
||||
* @param int $uid Record uid
|
||||
* @return bool
|
||||
*/
|
||||
protected function checkEditAccess($table, $uid)
|
||||
{
|
||||
$record = BackendUtility::getRecord($table, $uid);
|
||||
BackendUtility::fixVersioningPid($table, $record);
|
||||
if (is_array($record)) {
|
||||
// If pages:
|
||||
if ($table === 'pages') {
|
||||
$calculatedPermissions = $this->getBackendUserAuthentication()->calcPerms($record);
|
||||
$hasAccess = $calculatedPermissions & Permission::PAGE_EDIT;
|
||||
} else {
|
||||
// Fetching pid-record first.
|
||||
$calculatedPermissions = $this->getBackendUserAuthentication()->calcPerms(
|
||||
BackendUtility::getRecord('pages', $record['pid'])
|
||||
);
|
||||
$hasAccess = $calculatedPermissions & Permission::CONTENT_EDIT;
|
||||
}
|
||||
// Check internals regarding access:
|
||||
if ($hasAccess) {
|
||||
$hasAccess = $this->getBackendUserAuthentication()->recordEditAccessInternals($table, $record);
|
||||
}
|
||||
} else {
|
||||
$hasAccess = false;
|
||||
}
|
||||
return (bool)$hasAccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of BackendUserAuthentication
|
||||
*
|
||||
* @return BackendUserAuthentication
|
||||
*/
|
||||
protected function getBackendUserAuthentication()
|
||||
{
|
||||
return $GLOBALS['BE_USER'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of LanguageService
|
||||
*
|
||||
* @return LanguageService
|
||||
*/
|
||||
protected function getLanguageService()
|
||||
{
|
||||
return $GLOBALS['LANG'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of DocumentTemplate
|
||||
*
|
||||
* @return DocumentTemplate
|
||||
*/
|
||||
protected function getDocumentTemplate()
|
||||
{
|
||||
return $GLOBALS['TBE_TEMPLATE'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,342 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
namespace TYPO3\CMS\Backend\Controller\Wizard;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Backend\Form\FormDataCompiler;
|
||||
use TYPO3\CMS\Backend\Form\FormDataGroup\TcaDatabaseRecord;
|
||||
use TYPO3\CMS\Backend\Routing\UriBuilder;
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
use TYPO3\CMS\Core\Compatibility\PublicPropertyDeprecationTrait;
|
||||
use TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools;
|
||||
use TYPO3\CMS\Core\DataHandling\DataHandler;
|
||||
use TYPO3\CMS\Core\Http\RedirectResponse;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\HttpUtility;
|
||||
use TYPO3\CMS\Core\Utility\MathUtility;
|
||||
|
||||
/**
|
||||
* Script Class for adding new items to a group/select field. Performs proper redirection as needed.
|
||||
* Script is typically called after new child record was added and then adds the new child to select value of parent.
|
||||
* @internal This class is a specific Backend controller implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class AddController extends AbstractWizardController
|
||||
{
|
||||
use PublicPropertyDeprecationTrait;
|
||||
|
||||
/**
|
||||
* Properties which have been moved to protected status from public
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $deprecatedPublicProperties = [
|
||||
'content' => 'Using $content of class AddController from the outside is discouraged, as this variable is only used for internal storage.',
|
||||
'processDataFlag' => 'Using $processDataFlag of class AddController from the outside is discouraged, as this variable is only used for internal storage.',
|
||||
'pid' => 'Using $pid of class AddController from the outside is discouraged, as this variable is only used for internal storage.',
|
||||
'table' => 'Using $table of class AddController from the outside is discouraged, as this variable is only used for internal storage.',
|
||||
'id' => 'Using $id of class AddController from the outside is discouraged, as this variable is only used for internal storage.',
|
||||
'P' => 'Using $P of class AddController from the outside is discouraged, as this variable is only used for internal storage.',
|
||||
'returnEditConf' => 'Using $returnEditConf of class AddController from the outside is discouraged, as this variable is only used for internal storage.',
|
||||
];
|
||||
/**
|
||||
* Content accumulation for the module.
|
||||
*
|
||||
* @var string
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0, unused
|
||||
*/
|
||||
protected $content;
|
||||
|
||||
/**
|
||||
* If set, the DataHandler class is loaded and used to add the returning ID to the parent record.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $processDataFlag = 0;
|
||||
|
||||
/**
|
||||
* Create new record -pid (pos/neg). If blank, return immediately
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $pid;
|
||||
|
||||
/**
|
||||
* The parent table we are working on.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table;
|
||||
|
||||
/**
|
||||
* Loaded with the created id of a record FormEngine returns ...
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* Wizard parameters, coming from TCEforms linking to the wizard.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $P;
|
||||
|
||||
/**
|
||||
* Information coming back from the FormEngine script, telling what the table/id was of the newly created record.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $returnEditConf;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->getLanguageService()->includeLLFile('EXT:core/Resources/Private/Language/locallang_wizards.xlf');
|
||||
// @deprecated since TYPO3 v9, will be moved out of __construct() in TYPO3 v10.0
|
||||
$this->init($GLOBALS['TYPO3_REQUEST']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects the request object for the current request or subrequest
|
||||
* As this controller goes only through the main() method, it is rather simple for now
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function mainAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
return $this->processRequest($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function
|
||||
* Will issue a location-header, redirecting either BACK or to a new FormEngine instance...
|
||||
*
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
|
||||
*/
|
||||
public function main()
|
||||
{
|
||||
trigger_error('AddController->main() will be replaced by protected method processRequest() in TYPO3 v10.0. Do not call from other extension.', E_USER_DEPRECATED);
|
||||
|
||||
$response = $this->processRequest($GLOBALS['TYPO3_REQUEST']);
|
||||
HttpUtility::redirect($response->getHeaders()['location'][0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialization of the class.
|
||||
* @param ServerRequestInterface $request
|
||||
*/
|
||||
protected function init(ServerRequestInterface $request): void
|
||||
{
|
||||
$parsedBody = $request->getParsedBody();
|
||||
$queryParams = $request->getQueryParams();
|
||||
// Init GPvars:
|
||||
$this->P = $parsedBody['P'] ?? $queryParams['P'] ?? [];
|
||||
$this->returnEditConf = $parsedBody['returnEditConf'] ?? $queryParams['returnEditConf'] ?? null;
|
||||
// Get this record
|
||||
$record = BackendUtility::getRecord($this->P['table'], $this->P['uid']);
|
||||
// Set table:
|
||||
$this->table = $this->P['params']['table'];
|
||||
// Get TSconfig for it.
|
||||
$TSconfig = BackendUtility::getTCEFORM_TSconfig(
|
||||
$this->P['table'],
|
||||
is_array($record) ? $record : ['pid' => $this->P['pid']]
|
||||
);
|
||||
// Set [params][pid]
|
||||
if (strpos($this->P['params']['pid'], '###') === 0 && substr($this->P['params']['pid'], -3) === '###') {
|
||||
$keyword = substr($this->P['params']['pid'], 3, -3);
|
||||
if (strpos($keyword, 'PAGE_TSCONFIG_') === 0) {
|
||||
$this->pid = (int)$TSconfig[$this->P['field']][$keyword];
|
||||
} else {
|
||||
$this->pid = (int)$TSconfig['_' . $keyword];
|
||||
}
|
||||
} else {
|
||||
$this->pid = (int)$this->P['params']['pid'];
|
||||
}
|
||||
// Return if new record as parent (not possibly/allowed)
|
||||
if ($this->pid === '') {
|
||||
// HTTP Redirect is performed by processRequest()
|
||||
return;
|
||||
}
|
||||
// Else proceed:
|
||||
// If a new id has returned from a newly created record...
|
||||
if ($this->returnEditConf) {
|
||||
$editConfiguration = json_decode($this->returnEditConf, true);
|
||||
if (is_array($editConfiguration[$this->table]) && MathUtility::canBeInterpretedAsInteger($this->P['uid'])) {
|
||||
// Getting id and cmd from returning editConf array.
|
||||
reset($editConfiguration[$this->table]);
|
||||
$this->id = (int)key($editConfiguration[$this->table]);
|
||||
$cmd = current($editConfiguration[$this->table]);
|
||||
// ... and if everything seems OK we will register some classes for inclusion and instruct the object
|
||||
// to perform processing later.
|
||||
if ($this->P['params']['setValue']
|
||||
&& $cmd === 'edit'
|
||||
&& $this->id
|
||||
&& $this->P['table']
|
||||
&& $this->P['field'] && $this->P['uid']
|
||||
) {
|
||||
$liveRecord = BackendUtility::getLiveVersionOfRecord($this->table, $this->id, 'uid');
|
||||
if ($liveRecord) {
|
||||
$this->id = $liveRecord['uid'];
|
||||
}
|
||||
$this->processDataFlag = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function
|
||||
* Will issue a location-header, redirecting either BACK or to a new FormEngine instance...
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
protected function processRequest(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
// Return if new record as parent (not possibly/allowed)
|
||||
if ($this->pid === '') {
|
||||
return new RedirectResponse(GeneralUtility::sanitizeLocalUrl($this->P['returnUrl']));
|
||||
}
|
||||
|
||||
if ($this->returnEditConf) {
|
||||
if ($this->processDataFlag) {
|
||||
// Because OnTheFly can't handle MM relations with intermediate tables we use TcaDatabaseRecord here
|
||||
// Otherwise already stored relations are overwritten with the new entry
|
||||
/** @var TcaDatabaseRecord $formDataGroup */
|
||||
$formDataGroup = GeneralUtility::makeInstance(TcaDatabaseRecord::class);
|
||||
/** @var FormDataCompiler $formDataCompiler */
|
||||
$formDataCompiler = GeneralUtility::makeInstance(FormDataCompiler::class, $formDataGroup);
|
||||
$input = [
|
||||
'tableName' => $this->P['table'],
|
||||
'vanillaUid' => (int)$this->P['uid'],
|
||||
'command' => 'edit',
|
||||
];
|
||||
$result = $formDataCompiler->compile($input);
|
||||
$currentParentRow = $result['databaseRow'];
|
||||
|
||||
// If that record was found (should absolutely be...), then init DataHandler and set, prepend or append
|
||||
// the record
|
||||
if (is_array($currentParentRow)) {
|
||||
/** @var DataHandler $dataHandler */
|
||||
$dataHandler = GeneralUtility::makeInstance(DataHandler::class);
|
||||
$data = [];
|
||||
$recordId = $this->table . '_' . $this->id;
|
||||
// Setting the new field data:
|
||||
// If the field is a flexForm field, work with the XML structure instead:
|
||||
if ($this->P['flexFormPath']) {
|
||||
// Current value of flexForm path:
|
||||
$currentFlexFormData = $currentParentRow[$this->P['field']];
|
||||
/** @var FlexFormTools $flexFormTools */
|
||||
$flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
|
||||
$currentFlexFormValueByPath = $flexFormTools->getArrayValueByPath(
|
||||
$this->P['flexFormPath'],
|
||||
$currentFlexFormData
|
||||
);
|
||||
|
||||
// Compile currentFlexFormData to functional string
|
||||
$currentFlexFormValues = [];
|
||||
foreach ($currentFlexFormValueByPath as $value) {
|
||||
if (is_array($value)) {
|
||||
// group fields are always resolved to array
|
||||
$currentFlexFormValues[] = $value['table'] . '_' . $value['uid'];
|
||||
} else {
|
||||
// but select fields may be uids only
|
||||
$currentFlexFormValues[] = $value;
|
||||
}
|
||||
}
|
||||
$currentFlexFormValue = implode(',', $currentFlexFormValues);
|
||||
|
||||
$insertValue = '';
|
||||
switch ((string)$this->P['params']['setValue']) {
|
||||
case 'set':
|
||||
$insertValue = $recordId;
|
||||
break;
|
||||
case 'append':
|
||||
$insertValue = $currentFlexFormValue . ',' . $recordId;
|
||||
break;
|
||||
case 'prepend':
|
||||
$insertValue = $recordId . ',' . $currentFlexFormValue;
|
||||
break;
|
||||
}
|
||||
$insertValue = implode(',', GeneralUtility::trimExplode(',', $insertValue, true));
|
||||
$data[$this->P['table']][$this->P['uid']][$this->P['field']] = [];
|
||||
$flexFormTools->setArrayValueByPath(
|
||||
$this->P['flexFormPath'],
|
||||
$data[$this->P['table']][$this->P['uid']][$this->P['field']],
|
||||
$insertValue
|
||||
);
|
||||
} else {
|
||||
$currentValue = $currentParentRow[$this->P['field']];
|
||||
|
||||
// Normalize CSV values
|
||||
if (!is_array($currentValue)) {
|
||||
$currentValue = GeneralUtility::trimExplode(',', $currentValue, true);
|
||||
}
|
||||
|
||||
// Normalize all items to "<table>_<uid>" format
|
||||
$currentValue = array_map(function ($item) {
|
||||
// Handle per-item table for "group" elements
|
||||
if (is_array($item)) {
|
||||
$item = $item['table'] . '_' . $item['uid'];
|
||||
} else {
|
||||
$item = $this->table . '_' . $item;
|
||||
}
|
||||
|
||||
return $item;
|
||||
}, $currentValue);
|
||||
|
||||
switch ((string)$this->P['params']['setValue']) {
|
||||
case 'set':
|
||||
$currentValue = [$recordId];
|
||||
break;
|
||||
case 'append':
|
||||
$currentValue[] = $recordId;
|
||||
break;
|
||||
case 'prepend':
|
||||
array_unshift($currentValue, $recordId);
|
||||
break;
|
||||
}
|
||||
|
||||
$data[$this->P['table']][$this->P['uid']][$this->P['field']] = implode(',', $currentValue);
|
||||
}
|
||||
// Submit the data:
|
||||
$dataHandler->start($data, []);
|
||||
$dataHandler->process_datamap();
|
||||
}
|
||||
}
|
||||
// Return to the parent FormEngine record editing session:
|
||||
return new RedirectResponse(GeneralUtility::sanitizeLocalUrl($this->P['returnUrl']));
|
||||
}
|
||||
|
||||
// Redirecting to FormEngine with instructions to create a new record
|
||||
// AND when closing to return back with information about that records ID etc.
|
||||
/** @var \TYPO3\CMS\Core\Http\NormalizedParams */
|
||||
$normalizedParams = $request->getAttribute('normalizedParams');
|
||||
/** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
|
||||
$uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
|
||||
$redirectUrl = (string)$uriBuilder->buildUriFromRoute('record_edit', [
|
||||
'returnEditConf' => 1,
|
||||
'edit[' . $this->P['params']['table'] . '][' . $this->pid . ']' => 'new',
|
||||
'returnUrl' => $normalizedParams->getRequestUri(),
|
||||
]);
|
||||
|
||||
return new RedirectResponse($redirectUrl);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
namespace TYPO3\CMS\Backend\Controller\Wizard;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Core\Compatibility\PublicPropertyDeprecationTrait;
|
||||
use TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools;
|
||||
use TYPO3\CMS\Core\Database\RelationHandler;
|
||||
use TYPO3\CMS\Core\Http\HtmlResponse;
|
||||
use TYPO3\CMS\Core\Http\RedirectResponse;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\HttpUtility;
|
||||
use TYPO3\CMS\Core\Utility\MathUtility;
|
||||
|
||||
/**
|
||||
* Script Class for redirecting a backend user to the editing form when an "Edit wizard" link was clicked in FormEngine somewhere
|
||||
* @internal This class is a specific Backend controller implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class EditController extends AbstractWizardController
|
||||
{
|
||||
use PublicPropertyDeprecationTrait;
|
||||
|
||||
/**
|
||||
* Properties which have been moved to protected status from public
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $deprecatedPublicProperties = [
|
||||
'P' => 'Using $P of class EditController from the outside is discouraged, as this variable is only used for internal storage.',
|
||||
'doClose' => 'Using $doClose of class EditController from the outside is discouraged, as this variable is only used for internal storage.',
|
||||
];
|
||||
|
||||
/**
|
||||
* Wizard parameters, coming from FormEngine linking to the wizard.
|
||||
*
|
||||
* Contains the following parts:
|
||||
* - table
|
||||
* - field
|
||||
* - formName
|
||||
* - hmac
|
||||
* - fieldChangeFunc
|
||||
* - fieldChangeFuncHash
|
||||
* - currentValue
|
||||
* - currentSelectedValues
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $P;
|
||||
|
||||
/**
|
||||
* Boolean; if set, the window will be closed by JavaScript
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $doClose;
|
||||
|
||||
/**
|
||||
* A little JavaScript to close the open window.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $closeWindow = '<script language="javascript" type="text/javascript">close();</script>';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->getLanguageService()->includeLLFile('EXT:core/Resources/Private/Language/locallang_wizards.xlf');
|
||||
|
||||
// @deprecated since TYPO3 v9, will be moved out of __construct() in TYPO3 v10.0
|
||||
$this->init($GLOBALS['TYPO3_REQUEST']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialization of the script
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
*/
|
||||
protected function init(ServerRequestInterface $request)
|
||||
{
|
||||
$parsedBody = $request->getParsedBody();
|
||||
$queryParams = $request->getQueryParams();
|
||||
|
||||
$this->P = $parsedBody['P'] ?? $queryParams['P'] ?? [];
|
||||
|
||||
// Used for the return URL to FormEngine so that we can close the window.
|
||||
$this->doClose = $parsedBody['doClose'] ?? $queryParams['doClose'] ?? 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects the request object for the current request or subrequest
|
||||
* As this controller goes only through the main() method, it is rather simple for now
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function mainAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$content = $this->processRequest($request);
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function
|
||||
* Makes a header-location redirect to an edit form IF POSSIBLE from the passed data - otherwise the window will
|
||||
* just close.
|
||||
*
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
|
||||
* @return string
|
||||
*/
|
||||
public function main()
|
||||
{
|
||||
trigger_error('EditController->main() will be set to protected in TYPO3 v10.0. Do not call from other extension.', E_USER_DEPRECATED);
|
||||
$request = $GLOBALS['TYPO3_REQUEST'];
|
||||
|
||||
$response = $this->processRequest($request);
|
||||
|
||||
if ($response instanceof RedirectResponse) {
|
||||
HttpUtility::redirect($response->getHeaders()['location'][0]);
|
||||
} else {
|
||||
return $response->getBody()->getContents();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process request function
|
||||
* Makes a header-location redirect to an edit form IF POSSIBLE from the passed data - otherwise the window will
|
||||
* just close.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
protected function processRequest(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
if ($this->doClose) {
|
||||
return new HtmlResponse($this->closeWindow);
|
||||
}
|
||||
// Initialize:
|
||||
$table = $this->P['table'];
|
||||
$field = $this->P['field'];
|
||||
|
||||
if (empty($this->P['flexFormDataStructureIdentifier'])) {
|
||||
// If there is not flex data structure identifier, field config is found in globals
|
||||
$config = $GLOBALS['TCA'][$table]['columns'][$field]['config'];
|
||||
} else {
|
||||
// If there is a flex data structure identifier, parse that data structure and
|
||||
// fetch config defined by given flex path
|
||||
$flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
|
||||
$dataStructure = $flexFormTools->parseDataStructureByIdentifier($this->P['flexFormDataStructureIdentifier']);
|
||||
$config = $flexFormTools->getArrayValueByPath($this->P['flexFormDataStructurePath'], $dataStructure);
|
||||
if (!is_array($config)) {
|
||||
throw new \RuntimeException(
|
||||
'Something went wrong finding flex path ' . $this->P['flexFormDataStructurePath']
|
||||
. ' in data structure identified by ' . $this->P['flexFormDataStructureIdentifier'],
|
||||
1537356346
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
|
||||
$urlParameters = [
|
||||
'returnUrl' => (string)$uriBuilder->buildUriFromRoute('wizard_edit', ['doClose' => 1])
|
||||
];
|
||||
|
||||
// Detecting the various allowed field type setups and acting accordingly.
|
||||
if (is_array($config)
|
||||
&& $config['type'] === 'select'
|
||||
&& !$config['MM']
|
||||
&& $config['maxitems'] <= 1
|
||||
&& MathUtility::canBeInterpretedAsInteger($this->P['currentValue'])
|
||||
&& $this->P['currentValue']
|
||||
&& $config['foreign_table']
|
||||
) {
|
||||
// SINGLE value
|
||||
$urlParameters['edit[' . $config['foreign_table'] . '][' . $this->P['currentValue'] . ']'] = 'edit';
|
||||
// Redirect to FormEngine
|
||||
$url = (string)$uriBuilder->buildUriFromRoute('record_edit', $urlParameters);
|
||||
return new RedirectResponse($url);
|
||||
}
|
||||
|
||||
if (is_array($config)
|
||||
&& $this->P['currentSelectedValues']
|
||||
&& (
|
||||
$config['type'] === 'select'
|
||||
&& $config['foreign_table']
|
||||
|| $config['type'] === 'group'
|
||||
&& $config['internal_type'] === 'db'
|
||||
)
|
||||
) {
|
||||
// MULTIPLE VALUES:
|
||||
// Init settings:
|
||||
$allowedTables = $config['type'] === 'group' ? $config['allowed'] : $config['foreign_table'];
|
||||
$prependName = 1;
|
||||
// Selecting selected values into an array:
|
||||
$relationHandler = GeneralUtility::makeInstance(RelationHandler::class);
|
||||
$relationHandler->start($this->P['currentSelectedValues'], $allowedTables);
|
||||
$value = $relationHandler->getValueArray($prependName);
|
||||
// Traverse that array and make parameters for FormEngine
|
||||
foreach ($value as $rec) {
|
||||
$recTableUidParts = GeneralUtility::revExplode('_', $rec, 2);
|
||||
$urlParameters['edit[' . $recTableUidParts[0] . '][' . $recTableUidParts[1] . ']'] = 'edit';
|
||||
}
|
||||
// Redirect to FormEngine
|
||||
$url = (string)$uriBuilder->buildUriFromRoute('record_edit', $urlParameters);
|
||||
|
||||
return new RedirectResponse($url);
|
||||
}
|
||||
return new HtmlResponse($this->closeWindow);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
namespace TYPO3\CMS\Backend\Controller\Wizard;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Core\Http\HtmlResponse;
|
||||
use TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException;
|
||||
use TYPO3\CMS\Core\Resource\ResourceFactory;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\MathUtility;
|
||||
use TYPO3\CMS\Fluid\View\StandaloneView;
|
||||
|
||||
/**
|
||||
* Wizard for rendering image manipulation view
|
||||
* @internal This class is a specific Backend controller implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class ImageManipulationController
|
||||
{
|
||||
/**
|
||||
* @var StandaloneView
|
||||
*/
|
||||
private $templateView;
|
||||
|
||||
/**
|
||||
* @param StandaloneView $templateView
|
||||
*/
|
||||
public function __construct(StandaloneView $templateView = null)
|
||||
{
|
||||
if (!$templateView) {
|
||||
$templateView = GeneralUtility::makeInstance(StandaloneView::class);
|
||||
$templateView->setLayoutRootPaths([GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Layouts/')]);
|
||||
$templateView->setPartialRootPaths([GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Partials/ImageManipulation/')]);
|
||||
$templateView->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates/ImageManipulation/ImageManipulationWizard.html'));
|
||||
}
|
||||
$this->templateView = $templateView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the HTML for the wizard inside the modal
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface $response
|
||||
*/
|
||||
public function getWizardContent(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
if ($this->isSignatureValid($request)) {
|
||||
$parsedBody = json_decode($request->getParsedBody()['arguments'], true);
|
||||
$fileUid = $parsedBody['image'];
|
||||
$image = null;
|
||||
if (MathUtility::canBeInterpretedAsInteger($fileUid)) {
|
||||
try {
|
||||
$image = ResourceFactory::getInstance()->getFileObject($fileUid);
|
||||
} catch (FileDoesNotExistException $e) {
|
||||
}
|
||||
}
|
||||
$viewData = [
|
||||
'image' => $image,
|
||||
'cropVariants' => $parsedBody['cropVariants']
|
||||
];
|
||||
$content = $this->templateView->renderSection('Main', $viewData);
|
||||
return new HtmlResponse($content);
|
||||
}
|
||||
return new HtmlResponse('', 403);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if hmac signature is correct
|
||||
*
|
||||
* @param ServerRequestInterface $request the request with the POST parameters
|
||||
* @return bool
|
||||
*/
|
||||
protected function isSignatureValid(ServerRequestInterface $request): bool
|
||||
{
|
||||
$token = GeneralUtility::hmac($request->getParsedBody()['arguments'], 'ajax_wizard_image_manipulation');
|
||||
return hash_equals($token, $request->getParsedBody()['signature']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
namespace TYPO3\CMS\Backend\Controller\Wizard;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Backend\Routing\UriBuilder;
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
use TYPO3\CMS\Core\Compatibility\PublicPropertyDeprecationTrait;
|
||||
use TYPO3\CMS\Core\Http\HtmlResponse;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\HttpUtility;
|
||||
|
||||
/**
|
||||
* Script Class for redirecting the user to the Web > List module if a wizard-link has been clicked in FormEngine
|
||||
* @internal This class is a specific Backend controller implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class ListController extends AbstractWizardController
|
||||
{
|
||||
use PublicPropertyDeprecationTrait;
|
||||
|
||||
/**
|
||||
* Properties which have been moved to protected status from public
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $deprecatedPublicProperties = [
|
||||
'pid' => 'Using $pid of class ListController from the outside is discouraged, as this variable is only used for internal storage.',
|
||||
'P' => 'Using $P of class ListController from the outside is discouraged, as this variable is only used for internal storage.',
|
||||
'table' => 'Using $table of class ListController from the outside is discouraged, as this variable is only used for internal storage.',
|
||||
'id' => 'Using $id of class ListController from the outside is discouraged, as this variable is only used for internal storage.',
|
||||
];
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $pid;
|
||||
|
||||
/**
|
||||
* Wizard parameters, coming from FormEngine linking to the wizard.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $P;
|
||||
|
||||
/**
|
||||
* Table to show, if none, then all tables are listed in list module.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table;
|
||||
|
||||
/**
|
||||
* Page id to list.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* Initialization of the class, setting GPvars.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->getLanguageService()->includeLLFile('EXT:core/Resources/Private/Language/locallang_wizards.xlf');
|
||||
|
||||
// @deprecated since TYPO3 v9, will be obsolete in TYPO3 v10.0
|
||||
$request = $GLOBALS['TYPO3_REQUEST'];
|
||||
$parsedBody = $request->getParsedBody();
|
||||
$queryParams = $request->getQueryParams();
|
||||
|
||||
$this->P = $parsedBody['P'] ?? $queryParams['P'] ?? null;
|
||||
$this->table = $parsedBody['table'] ?? $queryParams['table'] ?? null;
|
||||
$this->id = $parsedBody['id'] ?? $queryParams['id'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects the request object for the current request or subrequest
|
||||
* As this controller goes only through the main() method, it is rather simple for now
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function mainAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$this->processRequest($request);
|
||||
return new HtmlResponse('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function
|
||||
* Will issue a location-header, redirecting either BACK or to a new FormEngine instance...
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
|
||||
*/
|
||||
public function main(): void
|
||||
{
|
||||
trigger_error('ListController->main() will be replaced by protected method processRequest() in TYPO3 v10.0. Do not call from other extension.', E_USER_DEPRECATED);
|
||||
$this->processRequest($GLOBALS['TYPO3_REQUEST']);
|
||||
}
|
||||
/**
|
||||
* Main function
|
||||
* Will issue a location-header, redirecting either BACK or to a new FormEngine instance...
|
||||
* @param ServerRequestInterface $request
|
||||
*/
|
||||
protected function processRequest(ServerRequestInterface $request): void
|
||||
{
|
||||
// Get this record
|
||||
$origRow = BackendUtility::getRecord($this->P['table'], $this->P['uid']);
|
||||
// Get TSconfig for it.
|
||||
$TSconfig = BackendUtility::getTCEFORM_TSconfig(
|
||||
$this->table,
|
||||
is_array($origRow) ? $origRow : ['pid' => $this->P['pid']]
|
||||
);
|
||||
// Set [params][pid]
|
||||
if (strpos($this->P['params']['pid'], '###') === 0 && substr($this->P['params']['pid'], -3) === '###') {
|
||||
$keyword = substr($this->P['params']['pid'], 3, -3);
|
||||
if (strpos($keyword, 'PAGE_TSCONFIG_') === 0) {
|
||||
$this->pid = (int)$TSconfig[$this->P['field']][$keyword];
|
||||
} else {
|
||||
$this->pid = (int)$TSconfig['_' . $keyword];
|
||||
}
|
||||
} else {
|
||||
$this->pid = (int)$this->P['params']['pid'];
|
||||
}
|
||||
// Make redirect:
|
||||
// If pid is blank OR if id is set, then return...
|
||||
if ((string)$this->id !== '') {
|
||||
$redirectUrl = GeneralUtility::sanitizeLocalUrl($this->P['returnUrl']);
|
||||
} else {
|
||||
/** @var UriBuilder $uriBuilder */
|
||||
$uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
|
||||
$normalizedParams = $request->getAttribute('normalizedParams');
|
||||
$requestUri = $normalizedParams->getRequestUri();
|
||||
// Otherwise, show the list:
|
||||
$urlParameters = [];
|
||||
$urlParameters['id'] = $this->pid;
|
||||
$urlParameters['table'] = $this->P['params']['table'];
|
||||
$urlParameters['returnUrl'] = !empty($this->P['returnUrl'])
|
||||
? GeneralUtility::sanitizeLocalUrl($this->P['returnUrl'])
|
||||
: $requestUri;
|
||||
$redirectUrl = (string)$uriBuilder->buildUriFromRoute('web_list', $urlParameters);
|
||||
}
|
||||
HttpUtility::redirect($redirectUrl);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,305 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Backend\Controller\Wizard;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Backend\Form\Wizard\SuggestWizardDefaultReceiver;
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
use TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Http\JsonResponse;
|
||||
use TYPO3\CMS\Core\Utility\ArrayUtility;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Receives ajax request from FormEngine suggest wizard and creates suggest answer as json result
|
||||
* @internal This class is a specific Backend controller implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class SuggestWizardController
|
||||
{
|
||||
/**
|
||||
* Ajax handler for the "suggest" feature in FormEngine.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @throws \RuntimeException for incomplete or invalid arguments
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function searchAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$parsedBody = $request->getParsedBody();
|
||||
|
||||
$search = $parsedBody['value'] ?? null;
|
||||
$tableName = $parsedBody['tableName'] ?? null;
|
||||
$fieldName = $parsedBody['fieldName'] ?? null;
|
||||
$uid = $parsedBody['uid'] ?? null;
|
||||
$pid = isset($parsedBody['pid']) ? (int)$parsedBody['pid'] : 0;
|
||||
$dataStructureIdentifier = '' ?? null;
|
||||
if (!empty($parsedBody['dataStructureIdentifier'])) {
|
||||
$dataStructureIdentifier = $parsedBody['dataStructureIdentifier'];
|
||||
}
|
||||
$flexFormSheetName = $parsedBody['flexFormSheetName'] ?? null;
|
||||
$flexFormFieldName = $parsedBody['flexFormFieldName'] ?? null;
|
||||
$flexFormContainerName = $parsedBody['flexFormContainerName'] ?? null;
|
||||
$flexFormContainerFieldName = $parsedBody['flexFormContainerFieldName'] ?? null;
|
||||
|
||||
// Determine TCA config of field
|
||||
if (empty($dataStructureIdentifier)) {
|
||||
// Normal columns field
|
||||
$fieldConfig = $GLOBALS['TCA'][$tableName]['columns'][$fieldName]['config'];
|
||||
$fieldNameInPageTsConfig = $fieldName;
|
||||
} else {
|
||||
// A flex flex form field
|
||||
$flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
|
||||
$dataStructure = $flexFormTools->parseDataStructureByIdentifier($dataStructureIdentifier);
|
||||
if (empty($flexFormContainerFieldName)) {
|
||||
// @todo: See if a path in pageTsConfig like "TCEForm.tableName.theContainerFieldName =" is useful and works with other pageTs, too.
|
||||
$fieldNameInPageTsConfig = $flexFormFieldName;
|
||||
if (!isset($dataStructure['sheets'][$flexFormSheetName]['ROOT']
|
||||
['el'][$flexFormFieldName]['TCEforms']['config'])
|
||||
) {
|
||||
throw new \RuntimeException(
|
||||
'Specified path ' . $flexFormFieldName . ' not found in flex form data structure',
|
||||
1480609491
|
||||
);
|
||||
}
|
||||
$fieldConfig = $dataStructure['sheets'][$flexFormSheetName]['ROOT']
|
||||
['el'][$flexFormFieldName]['TCEforms']['config'];
|
||||
} else {
|
||||
$fieldNameInPageTsConfig = $flexFormContainerFieldName;
|
||||
if (!isset($dataStructure['sheets'][$flexFormSheetName]['ROOT']
|
||||
['el'][$flexFormFieldName]
|
||||
['el'][$flexFormContainerName]
|
||||
['el'][$flexFormContainerFieldName]['TCEforms']['config'])
|
||||
) {
|
||||
throw new \RuntimeException(
|
||||
'Specified path ' . $flexFormContainerName . ' not found in flex form section container data structure',
|
||||
1480611208
|
||||
);
|
||||
}
|
||||
$fieldConfig = $dataStructure['sheets'][$flexFormSheetName]['ROOT']
|
||||
['el'][$flexFormFieldName]
|
||||
['el'][$flexFormContainerName]
|
||||
['el'][$flexFormContainerFieldName]['TCEforms']['config'];
|
||||
}
|
||||
}
|
||||
|
||||
$pageTsConfig = BackendUtility::getPagesTSconfig($pid);
|
||||
|
||||
$wizardConfig = $fieldConfig['suggestOptions'] ?? [];
|
||||
|
||||
$queryTables = $this->getTablesToQueryFromFieldConfiguration($fieldConfig);
|
||||
$whereClause = $this->getWhereClause($fieldConfig);
|
||||
|
||||
$resultRows = [];
|
||||
|
||||
// fetch the records for each query table. A query table is a table from which records are allowed to
|
||||
// be added to the TCEForm selector, originally fetched from the "allowed" config option in the TCA
|
||||
foreach ($queryTables as $queryTable) {
|
||||
// if the table does not exist, skip it
|
||||
if (!is_array($GLOBALS['TCA'][$queryTable]) || empty($GLOBALS['TCA'][$queryTable])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$config = $this->getConfigurationForTable($queryTable, $wizardConfig, $pageTsConfig, $tableName, $fieldNameInPageTsConfig);
|
||||
|
||||
// process addWhere
|
||||
if (!isset($config['addWhere']) && $whereClause) {
|
||||
$config['addWhere'] = $whereClause;
|
||||
}
|
||||
if (isset($config['addWhere'])) {
|
||||
$replacement = [
|
||||
'###THIS_UID###' => (int)$uid,
|
||||
'###CURRENT_PID###' => (int)$pid
|
||||
];
|
||||
if (isset($pageTsConfig['TCEFORM.'][$tableName . '.'][$fieldNameInPageTsConfig . '.'])) {
|
||||
$fieldTSconfig = $pageTsConfig['TCEFORM.'][$tableName . '.'][$fieldNameInPageTsConfig . '.'];
|
||||
if (isset($fieldTSconfig['PAGE_TSCONFIG_ID'])) {
|
||||
$replacement['###PAGE_TSCONFIG_ID###'] = (int)$fieldTSconfig['PAGE_TSCONFIG_ID'];
|
||||
}
|
||||
if (isset($fieldTSconfig['PAGE_TSCONFIG_IDLIST'])) {
|
||||
$replacement['###PAGE_TSCONFIG_IDLIST###'] = implode(',', GeneralUtility::intExplode(',', $fieldTSconfig['PAGE_TSCONFIG_IDLIST']));
|
||||
}
|
||||
if (isset($fieldTSconfig['PAGE_TSCONFIG_STR'])) {
|
||||
$connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($fieldConfig['foreign_table']);
|
||||
// nasty hack, but it's currently not possible to just quote anything "inside" the value but not escaping
|
||||
// the whole field as it is not known where it is used in the WHERE clause
|
||||
$replacement['###PAGE_TSCONFIG_STR###'] = trim($connection->quote($fieldTSconfig['PAGE_TSCONFIG_STR']), '\'');
|
||||
}
|
||||
}
|
||||
$config['addWhere'] = strtr(' ' . $config['addWhere'], $replacement);
|
||||
}
|
||||
|
||||
// instantiate the class that should fetch the records for this $queryTable
|
||||
$receiverClassName = $config['receiverClass'];
|
||||
if (!class_exists($receiverClassName)) {
|
||||
$receiverClassName = SuggestWizardDefaultReceiver::class;
|
||||
}
|
||||
$receiverObj = GeneralUtility::makeInstance($receiverClassName, $queryTable, $config);
|
||||
$params = [
|
||||
'value' => $search,
|
||||
'uid' => $uid,
|
||||
];
|
||||
$rows = $receiverObj->queryTable($params);
|
||||
if (empty($rows)) {
|
||||
continue;
|
||||
}
|
||||
$resultRows = $rows + $resultRows;
|
||||
unset($rows);
|
||||
}
|
||||
|
||||
// Limit the number of items in the result list
|
||||
$maxItems = $config['maxItemsInResultList'] ?? 10;
|
||||
$maxItems = min(count($resultRows), $maxItems);
|
||||
|
||||
array_splice($resultRows, $maxItems);
|
||||
return (new JsonResponse())->setPayload(array_values($resultRows));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns TRUE if a table has been marked as hidden in the configuration
|
||||
*
|
||||
* @param array $tableConfig
|
||||
* @return bool
|
||||
*/
|
||||
protected function isTableHidden(array $tableConfig)
|
||||
{
|
||||
return (bool)$tableConfig['ctrl']['hideTable'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current backend user is allowed to access the given table, based on the ctrl-section of the
|
||||
* table's configuration array (TCA) entry.
|
||||
*
|
||||
* @param array $tableConfig
|
||||
* @return bool
|
||||
*/
|
||||
protected function currentBackendUserMayAccessTable(array $tableConfig)
|
||||
{
|
||||
if ($this->getBackendUser()->isAdmin()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the user is no admin, they may not access admin-only tables
|
||||
if ($tableConfig['ctrl']['adminOnly']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// allow access to root level pages if security restrictions should be bypassed
|
||||
return !$tableConfig['ctrl']['rootLevel'] || $tableConfig['ctrl']['security']['ignoreRootLevelRestriction'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the configuration for the suggest wizard for the given table. This does multiple overlays from the
|
||||
* TSconfig.
|
||||
*
|
||||
* @param string $queryTable The table to query
|
||||
* @param array $wizardConfig The configuration for the wizard as configured in the data structure
|
||||
* @param array $TSconfig The TSconfig array of the current page
|
||||
* @param string $table The table where the wizard is used
|
||||
* @param string $field The field where the wizard is used
|
||||
* @return array
|
||||
*/
|
||||
protected function getConfigurationForTable($queryTable, array $wizardConfig, array $TSconfig, $table, $field)
|
||||
{
|
||||
$config = (array)$wizardConfig['default'];
|
||||
|
||||
if (is_array($wizardConfig[$queryTable])) {
|
||||
ArrayUtility::mergeRecursiveWithOverrule($config, $wizardConfig[$queryTable]);
|
||||
}
|
||||
$globalSuggestTsConfig = $TSconfig['TCEFORM.']['suggest.'];
|
||||
$currentFieldSuggestTsConfig = $TSconfig['TCEFORM.'][$table . '.'][$field . '.']['suggest.'];
|
||||
|
||||
// merge the configurations of different "levels" to get the working configuration for this table and
|
||||
// field (i.e., go from the most general to the most special configuration)
|
||||
if (is_array($globalSuggestTsConfig['default.'])) {
|
||||
ArrayUtility::mergeRecursiveWithOverrule($config, $globalSuggestTsConfig['default.']);
|
||||
}
|
||||
|
||||
if (is_array($globalSuggestTsConfig[$queryTable . '.'])) {
|
||||
ArrayUtility::mergeRecursiveWithOverrule($config, $globalSuggestTsConfig[$queryTable . '.']);
|
||||
}
|
||||
|
||||
// use $table instead of $queryTable here because we overlay a config
|
||||
// for the input-field here, not for the queried table
|
||||
if (is_array($currentFieldSuggestTsConfig['default.'])) {
|
||||
ArrayUtility::mergeRecursiveWithOverrule($config, $currentFieldSuggestTsConfig['default.']);
|
||||
}
|
||||
|
||||
if (is_array($currentFieldSuggestTsConfig[$queryTable . '.'])) {
|
||||
ArrayUtility::mergeRecursiveWithOverrule($config, $currentFieldSuggestTsConfig[$queryTable . '.']);
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the given field configuration for the tables that should be used for querying and returns them as an
|
||||
* array.
|
||||
*
|
||||
* @param array $fieldConfig
|
||||
* @return array
|
||||
*/
|
||||
protected function getTablesToQueryFromFieldConfiguration(array $fieldConfig)
|
||||
{
|
||||
$queryTables = [];
|
||||
|
||||
if (isset($fieldConfig['allowed'])) {
|
||||
if ($fieldConfig['allowed'] !== '*') {
|
||||
// list of allowed tables
|
||||
$queryTables = GeneralUtility::trimExplode(',', $fieldConfig['allowed']);
|
||||
} else {
|
||||
// all tables are allowed, if the user can access them
|
||||
foreach ($GLOBALS['TCA'] as $tableName => $tableConfig) {
|
||||
if (!$this->isTableHidden($tableConfig) && $this->currentBackendUserMayAccessTable($tableConfig)) {
|
||||
$queryTables[] = $tableName;
|
||||
}
|
||||
}
|
||||
unset($tableName, $tableConfig);
|
||||
}
|
||||
} elseif (isset($fieldConfig['foreign_table'])) {
|
||||
// use the foreign table
|
||||
$queryTables = [$fieldConfig['foreign_table']];
|
||||
}
|
||||
|
||||
return $queryTables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SQL WHERE clause to use for querying records. This is currently only relevant if a foreign_table
|
||||
* is configured and should be used; it could e.g. be used to limit to a certain subset of records from the
|
||||
* foreign table
|
||||
*
|
||||
* @param array $fieldConfig
|
||||
* @return string
|
||||
*/
|
||||
protected function getWhereClause(array $fieldConfig)
|
||||
{
|
||||
if (!isset($fieldConfig['foreign_table'])) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// strip ORDER BY clause
|
||||
return trim(preg_replace('/ORDER[[:space:]]+BY.*/i', '', $fieldConfig['foreign_table_where']));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
|
||||
*/
|
||||
protected function getBackendUser()
|
||||
{
|
||||
return $GLOBALS['BE_USER'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,792 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
namespace TYPO3\CMS\Backend\Controller\Wizard;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Backend\Template\Components\ButtonBar;
|
||||
use TYPO3\CMS\Backend\Template\ModuleTemplate;
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
use TYPO3\CMS\Core\Compatibility\PublicPropertyDeprecationTrait;
|
||||
use TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools;
|
||||
use TYPO3\CMS\Core\DataHandling\DataHandler;
|
||||
use TYPO3\CMS\Core\Http\HtmlResponse;
|
||||
use TYPO3\CMS\Core\Http\RedirectResponse;
|
||||
use TYPO3\CMS\Core\Imaging\Icon;
|
||||
use TYPO3\CMS\Core\Imaging\IconFactory;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\HttpUtility;
|
||||
use TYPO3\CMS\Core\Utility\MathUtility;
|
||||
|
||||
/**
|
||||
* Script Class for rendering the Table Wizard
|
||||
* @internal This class is a specific Backend controller implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class TableController extends AbstractWizardController
|
||||
{
|
||||
use PublicPropertyDeprecationTrait;
|
||||
|
||||
/**
|
||||
* Properties which have been moved to protected status from public
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $deprecatedPublicProperties = [
|
||||
'content' => 'Using $content of class TableController from the outside is discouraged, as this variable is only used for internal storage.',
|
||||
'inputStyle' => 'Using $inputStyle of class TableController from the outside is discouraged, as this variable is only used for internal storage.',
|
||||
'xmlStorage' => 'Using $xmlStorage of class TableController from the outside is discouraged, as this variable is only used for internal storage.',
|
||||
'numNewRows' => 'Using $numNewRows of class TableController from the outside is discouraged, as this variable is only used for internal storage.',
|
||||
'colsFieldName' => 'Using $colsFieldName of class TableController from the outside is discouraged, as this variable is only used for internal storage.',
|
||||
'P' => 'Using $P of class TableController from the outside is discouraged, as this variable is only used for internal storage.',
|
||||
'TABLECFG' => 'Using $TABLECFG of class TableController from the outside is discouraged, as this variable is only used for internal storage.',
|
||||
'tableParsing_quote' => 'Using $tableParsing_quote of class TableController from the outside is discouraged, as this variable is only used for internal storage.',
|
||||
'tableParsing_delimiter' => 'Using $tableParsing_delimiter of class TableController from the outside is discouraged, as this variable is only used for internal storage.',
|
||||
];
|
||||
|
||||
/**
|
||||
* Content accumulation for the module.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $content;
|
||||
|
||||
/**
|
||||
* If TRUE, <input> fields are shown instead of textareas.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $inputStyle = false;
|
||||
|
||||
/**
|
||||
* If set, the string version of the content is interpreted/written as XML
|
||||
* instead of the original line-based kind. This variable still needs binding
|
||||
* to the wizard parameters - but support is ready!
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $xmlStorage = 0;
|
||||
|
||||
/**
|
||||
* Number of new rows to add in bottom of wizard
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $numNewRows = 1;
|
||||
|
||||
/**
|
||||
* Name of field in parent record which MAY contain the number of columns for the table
|
||||
* here hardcoded to the value of tt_content. Should be set by FormEngine parameters (from P)
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $colsFieldName = 'cols';
|
||||
|
||||
/**
|
||||
* Wizard parameters, coming from FormEngine linking to the wizard.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $P;
|
||||
|
||||
/**
|
||||
* The array which is constantly submitted by the multidimensional form of this wizard.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $TABLECFG;
|
||||
|
||||
/**
|
||||
* Table parsing
|
||||
* quoting of table cells
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $tableParsing_quote;
|
||||
|
||||
/**
|
||||
* delimiter between table cells
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $tableParsing_delimiter;
|
||||
|
||||
/**
|
||||
* @var IconFactory
|
||||
*/
|
||||
protected $iconFactory;
|
||||
|
||||
/**
|
||||
* ModuleTemplate object
|
||||
*
|
||||
* @var ModuleTemplate
|
||||
*/
|
||||
protected $moduleTemplate;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
|
||||
$this->getLanguageService()->includeLLFile('EXT:core/Resources/Private/Language/locallang_wizards.xlf');
|
||||
|
||||
// @deprecated since TYPO3 v9, will be moved out of __construct() in TYPO3 v10.0
|
||||
$this->init($GLOBALS['TYPO3_REQUEST']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects the request object for the current request or subrequest
|
||||
* As this controller goes only through the main() method, it is rather simple for now
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function mainAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$response = $this->renderContent($request);
|
||||
|
||||
if (empty($response)) {
|
||||
$response = new HtmlResponse($this->moduleTemplate->renderContent());
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function, rendering the table wizard
|
||||
*
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
|
||||
*/
|
||||
public function main()
|
||||
{
|
||||
trigger_error('TableController->main() will be replaced by protected method renderContent() in TYPO3 v10.0. Do not call from other extensions.', E_USER_DEPRECATED);
|
||||
|
||||
$response = $this->renderContent($GLOBALS['TYPO3_REQUEST']);
|
||||
|
||||
if ($response instanceof RedirectResponse) {
|
||||
HttpUtility::redirect($response->getHeaders()['location'][0]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the table wizard content
|
||||
*
|
||||
* @return string HTML content for the form.
|
||||
* @throws \RuntimeException
|
||||
*
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
|
||||
*/
|
||||
public function tableWizard()
|
||||
{
|
||||
trigger_error('TableController->tableWizard() will be replaced by protected method renderTableWizard() in TYPO3 v10.0. Do not call from other extensions.', E_USER_DEPRECATED);
|
||||
|
||||
$result = $this->renderTableWizard($GLOBALS['TYPO3_REQUEST']);
|
||||
|
||||
if ($result instanceof RedirectResponse) {
|
||||
HttpUtility::redirect($result->getHeaders()['location'][0]);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will get and return the configuration code string
|
||||
* Will also save (and possibly redirect/exit) the content if a save button has been pressed
|
||||
*
|
||||
* @param array $row Current parent record row
|
||||
* @return array Table config code in an array
|
||||
*
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
|
||||
*/
|
||||
public function getConfigCode($row)
|
||||
{
|
||||
trigger_error('TableController->getConfigCode() will be replaced by protected method getConfiguration() in TYPO3 v10.0. Do not call from other extensions.', E_USER_DEPRECATED);
|
||||
|
||||
$result = $this->getConfiguration($row, $GLOBALS['TYPO3_REQUEST']);
|
||||
|
||||
if ($result instanceof RedirectResponse) {
|
||||
HttpUtility::redirect($result->getHeaders()['location'][0]);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the HTML for the Table Wizard:
|
||||
*
|
||||
* @param array $configuration Table config array
|
||||
* @return string HTML for the table wizard
|
||||
* @internal
|
||||
*
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
|
||||
*/
|
||||
public function getTableHTML($configuration)
|
||||
{
|
||||
trigger_error('TableController->getTableHTML() will be replaced by protected method getTableWizard() in TYPO3 v10.0. Do not call from other extensions.', E_USER_DEPRECATED);
|
||||
return $this->getTableWizard($configuration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects if a control button (up/down/around/delete) has been pressed for an item and accordingly it will
|
||||
* manipulate the internal TABLECFG array
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
|
||||
*/
|
||||
public function changeFunc()
|
||||
{
|
||||
trigger_error('TableController->changeFunc() will be replaced by protected method manipulateTable() in TYPO3 v10.0. Do not call from other extensions.', E_USER_DEPRECATED);
|
||||
$this->manipulateTable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the input array to a configuration code string
|
||||
*
|
||||
* @param array $cfgArr Array of table configuration (follows the input structure from the table wizard POST form)
|
||||
* @return string The array converted into a string with line-based configuration.
|
||||
* @see cfgString2CfgArray()
|
||||
*
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
|
||||
*/
|
||||
public function cfgArray2CfgString($cfgArr)
|
||||
{
|
||||
trigger_error('TableController->cfgArray2CfgString() will be replaced by protected method configurationArrayToString() in TYPO3 v10.0. Do not call from other extensions.', E_USER_DEPRECATED);
|
||||
return $this->configurationArrayToString($cfgArr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the input configuration code string into an array
|
||||
*
|
||||
* @param string $configurationCode Configuration code
|
||||
* @param int $columns Default number of columns
|
||||
* @return array Configuration array
|
||||
* @see cfgArray2CfgString()
|
||||
*
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
|
||||
*/
|
||||
public function cfgString2CfgArray($configurationCode, $columns)
|
||||
{
|
||||
trigger_error('TableController->cfgString2CfgArray() will be replaced by protected method configurationStringToArray() in TYPO3 v10.0. Do not call from other extensions.', E_USER_DEPRECATED);
|
||||
return $this->configurationStringToArray($configurationCode, $columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialization of the class
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
*/
|
||||
protected function init(ServerRequestInterface $request): void
|
||||
{
|
||||
$parsedBody = $request->getParsedBody();
|
||||
$queryParams = $request->getQueryParams();
|
||||
// GPvars:
|
||||
$this->P = $parsedBody['P'] ?? $queryParams['P'] ?? null;
|
||||
$this->TABLECFG = $parsedBody['TABLE'] ?? $queryParams['TABLE'] ?? null;
|
||||
// Setting options:
|
||||
$this->xmlStorage = $this->P['params']['xmlOutput'];
|
||||
$this->numNewRows = MathUtility::forceIntegerInRange($this->P['params']['numNewRows'], 1, 50, 5);
|
||||
// Textareas or input fields:
|
||||
$this->inputStyle = isset($this->TABLECFG['textFields']) ? (bool)$this->TABLECFG['textFields'] : true;
|
||||
$this->tableParsing_delimiter = '|';
|
||||
$this->tableParsing_quote = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function, rendering the table wizard
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface|null
|
||||
*/
|
||||
protected function renderContent(ServerRequestInterface $request): ?ResponseInterface
|
||||
{
|
||||
$normalizedParams = $request->getAttribute('normalizedParams');
|
||||
$requestUri = $normalizedParams->getRequestUri();
|
||||
list($rUri) = explode('#', $requestUri);
|
||||
$this->content .= '<form action="' . htmlspecialchars($rUri) . '" method="post" id="TableController" name="wizardForm">';
|
||||
if ($this->P['table'] && $this->P['field'] && $this->P['uid']) {
|
||||
$tableWizard = $this->renderTableWizard($request);
|
||||
|
||||
if ($tableWizard instanceof RedirectResponse) {
|
||||
return $tableWizard;
|
||||
}
|
||||
|
||||
$this->content .= '<h2>' . htmlspecialchars($this->getLanguageService()->getLL('table_title')) . '</h2>'
|
||||
. '<div>' . $tableWizard . '</div>';
|
||||
} else {
|
||||
$this->content .= '<h2>' . htmlspecialchars($this->getLanguageService()->getLL('table_title')) . '</h2>'
|
||||
. '<div><span class="text-danger">' . htmlspecialchars($this->getLanguageService()->getLL('table_noData')) . '</span></div>';
|
||||
}
|
||||
$this->content .= '</form>';
|
||||
// Setting up the buttons and markers for docHeader
|
||||
$this->getButtons();
|
||||
// Build the <body> for the module
|
||||
$this->moduleTemplate->setContent($this->content);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the panel of buttons for submitting the form or otherwise perform operations.
|
||||
*/
|
||||
protected function getButtons(): void
|
||||
{
|
||||
$buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
|
||||
if ($this->P['table'] && $this->P['field'] && $this->P['uid']) {
|
||||
// CSH
|
||||
$cshButton = $buttonBar->makeHelpButton()
|
||||
->setModuleName('xMOD_csh_corebe')
|
||||
->setFieldName('wizard_table_wiz');
|
||||
$buttonBar->addButton($cshButton);
|
||||
// Close
|
||||
$closeButton = $buttonBar->makeLinkButton()
|
||||
->setHref($this->P['returnUrl'])
|
||||
->setIcon($this->moduleTemplate->getIconFactory()->getIcon('actions-close', Icon::SIZE_SMALL))
|
||||
->setTitle($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:rm.closeDoc'))
|
||||
->setShowLabelText(true);
|
||||
$buttonBar->addButton($closeButton, ButtonBar::BUTTON_POSITION_LEFT, 1);
|
||||
// Save
|
||||
$saveButton = $buttonBar->makeInputButton()
|
||||
->setName('_savedok')
|
||||
->setValue('1')
|
||||
->setForm('TableController')
|
||||
->setIcon($this->moduleTemplate->getIconFactory()->getIcon('actions-document-save', Icon::SIZE_SMALL))
|
||||
->setTitle($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:rm.saveDoc'))
|
||||
->setShowLabelText(true);
|
||||
$buttonBar->addButton($saveButton, ButtonBar::BUTTON_POSITION_LEFT, 2);
|
||||
// Reload
|
||||
$reloadButton = $buttonBar->makeInputButton()
|
||||
->setName('_refresh')
|
||||
->setValue('1')
|
||||
->setForm('TableController')
|
||||
->setIcon($this->moduleTemplate->getIconFactory()->getIcon('actions-refresh', Icon::SIZE_SMALL))
|
||||
->setTitle($this->getLanguageService()->getLL('forms_refresh'));
|
||||
$buttonBar->addButton($reloadButton, ButtonBar::BUTTON_POSITION_RIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the table wizard content
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return string|ResponseInterface HTML content for the form.
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function renderTableWizard(ServerRequestInterface $request)
|
||||
{
|
||||
if (!$this->checkEditAccess($this->P['table'], $this->P['uid'])) {
|
||||
throw new \RuntimeException('Wizard Error: No access', 1349692692);
|
||||
}
|
||||
// First, check the references by selecting the record:
|
||||
$row = BackendUtility::getRecord($this->P['table'], $this->P['uid']);
|
||||
if (!is_array($row)) {
|
||||
throw new \RuntimeException('Wizard Error: No reference to record', 1294587125);
|
||||
}
|
||||
// This will get the content of the form configuration code field to us - possibly cleaned up,
|
||||
// saved to database etc. if the form has been submitted in the meantime.
|
||||
$tableCfgArray = $this->getConfiguration($row, $request);
|
||||
|
||||
if ($tableCfgArray instanceof ResponseInterface) {
|
||||
return $tableCfgArray;
|
||||
}
|
||||
|
||||
// Generation of the Table Wizards HTML code:
|
||||
$content = $this->getTableWizard($tableCfgArray);
|
||||
// Return content:
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will get and return the configuration code string
|
||||
* Will also save (and possibly redirect/exit) the content if a save button has been pressed
|
||||
*
|
||||
* @param array $row Current parent record row
|
||||
* @param ServerRequestInterface $request
|
||||
* @return array|ResponseInterface Table config code in an array
|
||||
*/
|
||||
protected function getConfiguration(array $row, ServerRequestInterface $request)
|
||||
{
|
||||
// Get delimiter settings
|
||||
$this->tableParsing_quote = $row['table_enclosure'] ? chr((int)$row['table_enclosure']) : '';
|
||||
$this->tableParsing_delimiter = $row['table_delimiter'] ? chr((int)$row['table_delimiter']) : '|';
|
||||
// If some data has been submitted, then construct
|
||||
if (isset($this->TABLECFG['c'])) {
|
||||
// Process incoming:
|
||||
$this->manipulateTable();
|
||||
// Convert to string (either line based or XML):
|
||||
if ($this->xmlStorage) {
|
||||
// Convert the input array to XML:
|
||||
$bodyText = '<?xml version="1.0" encoding="utf-8" standalone="yes" ?>' . LF . GeneralUtility::array2xml($this->TABLECFG['c'], '', 0, 'T3TableWizard');
|
||||
// Setting cfgArr directly from the input:
|
||||
$configuration = $this->TABLECFG['c'];
|
||||
} else {
|
||||
// Convert the input array to a string of configuration code:
|
||||
$bodyText = $this->configurationArrayToString($this->TABLECFG['c']);
|
||||
// Create cfgArr from the string based configuration - that way it is cleaned up
|
||||
// and any incompatibilities will be removed!
|
||||
$configuration = $this->configurationStringToArray($bodyText, (int)$row[$this->colsFieldName]);
|
||||
}
|
||||
// If a save button has been pressed, then save the new field content:
|
||||
if ($_POST['_savedok'] || $_POST['_saveandclosedok']) {
|
||||
// Get DataHandler object:
|
||||
/** @var DataHandler $dataHandler */
|
||||
$dataHandler = GeneralUtility::makeInstance(DataHandler::class);
|
||||
// Put content into the data array:
|
||||
$data = [];
|
||||
if ($this->P['flexFormPath']) {
|
||||
// Current value of flexForm path:
|
||||
$currentFlexFormData = GeneralUtility::xml2array($row[$this->P['field']]);
|
||||
/** @var FlexFormTools $flexFormTools */
|
||||
$flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
|
||||
$flexFormTools->setArrayValueByPath($this->P['flexFormPath'], $currentFlexFormData, $bodyText);
|
||||
$data[$this->P['table']][$this->P['uid']][$this->P['field']] = $currentFlexFormData;
|
||||
} else {
|
||||
$data[$this->P['table']][$this->P['uid']][$this->P['field']] = $bodyText;
|
||||
}
|
||||
// Perform the update:
|
||||
$dataHandler->start($data, []);
|
||||
$dataHandler->process_datamap();
|
||||
// If the save/close button was pressed, then redirect the screen:
|
||||
if ($_POST['_saveandclosedok']) {
|
||||
return new RedirectResponse(GeneralUtility::sanitizeLocalUrl($this->P['returnUrl']));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If nothing has been submitted, load the $bodyText variable from the selected database row:
|
||||
if ($this->xmlStorage) {
|
||||
$configuration = GeneralUtility::xml2array($row[$this->P['field']]);
|
||||
} else {
|
||||
if ($this->P['flexFormPath']) {
|
||||
// Current value of flexForm path:
|
||||
$currentFlexFormData = GeneralUtility::xml2array($row[$this->P['field']]);
|
||||
/** @var FlexFormTools $flexFormTools */
|
||||
$flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
|
||||
$configuration = $flexFormTools->getArrayValueByPath(
|
||||
$this->P['flexFormPath'],
|
||||
$currentFlexFormData
|
||||
);
|
||||
$configuration = $this->configurationStringToArray($configuration, 0);
|
||||
} else {
|
||||
// Regular line based table configuration:
|
||||
$columns = $row[$this->colsFieldName] ?? 0;
|
||||
$configuration = $this->configurationStringToArray($row[$this->P['field']] ?? '', (int)$columns);
|
||||
}
|
||||
}
|
||||
$configuration = is_array($configuration) ? $configuration : [];
|
||||
}
|
||||
return $configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the HTML for the Table Wizard:
|
||||
*
|
||||
* @param array $configuration Table config array
|
||||
* @return string HTML for the table wizard
|
||||
*/
|
||||
protected function getTableWizard(array $configuration): string
|
||||
{
|
||||
// Traverse the rows:
|
||||
$tRows = [];
|
||||
$k = 0;
|
||||
$countLines = count($configuration);
|
||||
foreach ($configuration as $cellArr) {
|
||||
if (is_array($cellArr)) {
|
||||
// Initialize:
|
||||
$cells = [];
|
||||
$a = 0;
|
||||
// Traverse the columns:
|
||||
foreach ($cellArr as $cellContent) {
|
||||
if ($this->inputStyle) {
|
||||
$cells[] = '<input class="form-control" type="text" name="TABLE[c][' . ($k + 1) * 2 . '][' . ($a + 1) * 2 . ']" value="' . htmlspecialchars($cellContent) . '" />';
|
||||
} else {
|
||||
$cellContent = preg_replace('/<br[ ]?[\\/]?>/i', LF, $cellContent);
|
||||
$cells[] = '<textarea class="form-control" rows="6" name="TABLE[c][' . ($k + 1) * 2 . '][' . ($a + 1) * 2 . ']">' . htmlspecialchars($cellContent) . '</textarea>';
|
||||
}
|
||||
// Increment counter:
|
||||
$a++;
|
||||
}
|
||||
// CTRL panel for a table row (move up/down/around):
|
||||
$onClick = 'document.wizardForm.action+=' . GeneralUtility::quoteJSvalue('#ANC_' . (($k + 1) * 2 - 2)) . ';';
|
||||
$onClick = ' onclick="' . htmlspecialchars($onClick) . '"';
|
||||
$ctrl = '';
|
||||
if ($k !== 0) {
|
||||
$ctrl .= '<button class="btn btn-default" name="TABLE[row_up][' . ($k + 1) * 2 . ']" title="' . htmlspecialchars($this->getLanguageService()->getLL('table_up')) . '"' . $onClick . '><span class="t3-icon fa fa-fw fa-angle-up"></span></button>';
|
||||
} else {
|
||||
$ctrl .= '<button class="btn btn-default" name="TABLE[row_bottom][' . ($k + 1) * 2 . ']" title="' . htmlspecialchars($this->getLanguageService()->getLL('table_bottom')) . '"' . $onClick . '><span class="t3-icon fa fa-fw fa-angle-double-down"></span></button>';
|
||||
}
|
||||
if ($k + 1 !== $countLines) {
|
||||
$ctrl .= '<button class="btn btn-default" name="TABLE[row_down][' . ($k + 1) * 2 . ']" title="' . htmlspecialchars($this->getLanguageService()->getLL('table_down')) . '"' . $onClick . '><span class="t3-icon fa fa-fw fa-angle-down"></span></button>';
|
||||
} else {
|
||||
$ctrl .= '<button class="btn btn-default" name="TABLE[row_top][' . ($k + 1) * 2 . ']" title="' . htmlspecialchars($this->getLanguageService()->getLL('table_top')) . '"' . $onClick . '><span class="t3-icon fa fa-fw fa-angle-double-up"></span></button>';
|
||||
}
|
||||
$ctrl .= '<button class="btn btn-default" name="TABLE[row_remove][' . ($k + 1) * 2 . ']" title="' . htmlspecialchars($this->getLanguageService()->getLL('table_removeRow')) . '"' . $onClick . '><span class="t3-icon fa fa-fw fa-trash"></span></button>';
|
||||
$ctrl .= '<button class="btn btn-default" name="TABLE[row_add][' . ($k + 1) * 2 . ']" title="' . htmlspecialchars($this->getLanguageService()->getLL('table_addRow')) . '"' . $onClick . '><span class="t3-icon fa fa-fw fa-plus"></span></button>';
|
||||
$tRows[] = '
|
||||
<tr>
|
||||
<td>
|
||||
<a name="ANC_' . ($k + 1) * 2 . '"></a>
|
||||
<span class="btn-group' . ($this->inputStyle ? '' : '-vertical') . '">' . $ctrl . '</span>
|
||||
</td>
|
||||
<td>' . implode('</td>
|
||||
<td>', $cells) . '</td>
|
||||
</tr>';
|
||||
// Increment counter:
|
||||
$k++;
|
||||
}
|
||||
}
|
||||
// CTRL panel for a table column (move left/right/around/delete)
|
||||
$cells = [];
|
||||
$cells[] = '';
|
||||
// Finding first row:
|
||||
$firstRow = reset($configuration);
|
||||
if (is_array($firstRow)) {
|
||||
$cols = count($firstRow);
|
||||
for ($a = 1; $a <= $cols; $a++) {
|
||||
$b = $a * 2;
|
||||
$ctrl = '';
|
||||
if ($a !== 1) {
|
||||
$ctrl .= '<button class="btn btn-default" name="TABLE[col_left][' . $b . ']" title="' . htmlspecialchars($this->getLanguageService()->getLL('table_left')) . '"><span class="t3-icon fa fa-fw fa-angle-left"></span></button>';
|
||||
} else {
|
||||
$ctrl .= '<button class="btn btn-default" name="TABLE[col_end][' . $b . ']" title="' . htmlspecialchars($this->getLanguageService()->getLL('table_end')) . '"><span class="t3-icon fa fa-fw fa-angle-double-right"></span></button>';
|
||||
}
|
||||
if ($a != $cols) {
|
||||
$ctrl .= '<button class="btn btn-default" name="TABLE[col_right][' . $b . ']" title="' . htmlspecialchars($this->getLanguageService()->getLL('table_right')) . '"><span class="t3-icon fa fa-fw fa-angle-right"></span></button>';
|
||||
} else {
|
||||
$ctrl .= '<button class="btn btn-default" name="TABLE[col_start][' . $b . ']" title="' . htmlspecialchars($this->getLanguageService()->getLL('table_start')) . '"><span class="t3-icon fa fa-fw fa-angle-double-left"></span></button>';
|
||||
}
|
||||
$ctrl .= '<button class="btn btn-default" name="TABLE[col_remove][' . $b . ']" title="' . htmlspecialchars($this->getLanguageService()->getLL('table_removeColumn')) . '"><span class="t3-icon fa fa-fw fa-trash"></span></button>';
|
||||
$ctrl .= '<button class="btn btn-default" name="TABLE[col_add][' . $b . ']" title="' . htmlspecialchars($this->getLanguageService()->getLL('table_addColumn')) . '"><span class="t3-icon fa fa-fw fa-plus"></span></button>';
|
||||
$cells[] = '<span class="btn-group">' . $ctrl . '</span>';
|
||||
}
|
||||
$tRows[] = '
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td>' . implode('</td>
|
||||
<td>', $cells) . '</td>
|
||||
</tr>
|
||||
</tfoot>';
|
||||
}
|
||||
$content = '';
|
||||
$addSubmitOnClick = 'onclick="document.getElementById(\'TableController\').submit();"';
|
||||
// Implode all table rows into a string, wrapped in table tags.
|
||||
$content .= '
|
||||
|
||||
<!-- Table wizard -->
|
||||
<div class="table-fit table-fit-inline-block">
|
||||
<table id="typo3-tablewizard" class="table table-center">
|
||||
' . implode('', $tRows) . '
|
||||
</table>
|
||||
</div>';
|
||||
// Input type checkbox:
|
||||
$content .= '
|
||||
|
||||
<!-- Input mode check box: -->
|
||||
<div class="checkbox">
|
||||
<input type="hidden" name="TABLE[textFields]" value="0" />
|
||||
<label for="textFields">
|
||||
<input type="checkbox" ' . $addSubmitOnClick . ' name="TABLE[textFields]" id="textFields" value="1"' . ($this->inputStyle ? ' checked="checked"' : '') . ' />
|
||||
' . $this->getLanguageService()->getLL('table_smallFields') . '
|
||||
</label>
|
||||
</div>';
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects if a control button (up/down/around/delete) has been pressed for an item and accordingly it will
|
||||
* manipulate the internal TABLECFG array
|
||||
*/
|
||||
protected function manipulateTable(): void
|
||||
{
|
||||
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 (strpos($cmd, 'row_') === 0) {
|
||||
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] = [];
|
||||
} 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 (strpos($cmd, 'col_') === 0) {
|
||||
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(
|
||||
[CR, LF],
|
||||
['', '<br />'],
|
||||
$this->TABLECFG['c'][$a][$b]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the input array to a configuration code string
|
||||
*
|
||||
* @param array $cfgArr Array of table configuration (follows the input structure from the table wizard POST form)
|
||||
* @return string The array converted into a string with line-based configuration.
|
||||
* @see configurationStringToArray()
|
||||
*/
|
||||
protected function configurationArrayToString(array $cfgArr): string
|
||||
{
|
||||
$inLines = [];
|
||||
// Traverse the elements of the table wizard and transform the settings into configuration code.
|
||||
foreach ($cfgArr as $valueA) {
|
||||
$thisLine = [];
|
||||
foreach ($valueA as $valueB) {
|
||||
$thisLine[] = $this->tableParsing_quote
|
||||
. str_replace($this->tableParsing_delimiter, '', $valueB) . $this->tableParsing_quote;
|
||||
}
|
||||
$inLines[] = implode($this->tableParsing_delimiter, $thisLine);
|
||||
}
|
||||
// Finally, implode the lines into a string:
|
||||
return implode(LF, $inLines);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the input configuration code string into an array
|
||||
*
|
||||
* @param string $configurationCode Configuration code
|
||||
* @param int $columns Default number of columns
|
||||
* @return array Configuration array
|
||||
* @see configurationArrayToString()
|
||||
*/
|
||||
protected function configurationStringToArray(string $configurationCode, int $columns): array
|
||||
{
|
||||
// Explode lines in the configuration code - each line is a table row.
|
||||
$tableLines = explode(LF, $configurationCode);
|
||||
// Setting number of columns
|
||||
// auto...
|
||||
if (!$columns && trim($tableLines[0])) {
|
||||
$columns = count(explode($this->tableParsing_delimiter, $tableLines[0]));
|
||||
}
|
||||
$columns = $columns ?: 4;
|
||||
// Traverse the number of table elements:
|
||||
$configurationArray = [];
|
||||
foreach ($tableLines as $key => $value) {
|
||||
// Initialize:
|
||||
$valueParts = explode($this->tableParsing_delimiter, $value);
|
||||
// Traverse columns:
|
||||
for ($a = 0; $a < $columns; $a++) {
|
||||
if ($this->tableParsing_quote
|
||||
&& $valueParts[$a][0] === $this->tableParsing_quote
|
||||
&& substr($valueParts[$a], -1, 1) === $this->tableParsing_quote
|
||||
) {
|
||||
$valueParts[$a] = substr(trim($valueParts[$a]), 1, -1);
|
||||
}
|
||||
$configurationArray[$key][$a] = (string)$valueParts[$a];
|
||||
}
|
||||
}
|
||||
return $configurationArray;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user