first commit
This commit is contained in:
commit
1218dd1517
12716 files changed
+989233
No files matched your search
@@ -0,0 +1,541 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Backend\Controller\ContentElement;
|
||||
|
||||
/*
|
||||
* 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\History\RecordHistory;
|
||||
use TYPO3\CMS\Backend\Template\Components\ButtonBar;
|
||||
use TYPO3\CMS\Backend\Template\ModuleTemplate;
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
use TYPO3\CMS\Core\DataHandling\History\RecordHistoryStore;
|
||||
use TYPO3\CMS\Core\Http\HtmlResponse;
|
||||
use TYPO3\CMS\Core\Imaging\Icon;
|
||||
use TYPO3\CMS\Core\Type\Bitmask\Permission;
|
||||
use TYPO3\CMS\Core\Utility\DiffUtility;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Fluid\View\StandaloneView;
|
||||
|
||||
/**
|
||||
* Controller for showing the history module of TYPO3s backend
|
||||
* @see \TYPO3\CMS\Backend\History\RecordHistory
|
||||
* @internal This class is a specific Backend controller implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class ElementHistoryController
|
||||
{
|
||||
/**
|
||||
* @var StandaloneView
|
||||
*/
|
||||
protected $view;
|
||||
|
||||
/**
|
||||
* @var RecordHistory
|
||||
*/
|
||||
protected $historyObject;
|
||||
|
||||
/**
|
||||
* Display diff or not (0-no diff, 1-inline)
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $showDiff = 1;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $recordCache = [];
|
||||
|
||||
/**
|
||||
* ModuleTemplate object
|
||||
*
|
||||
* @var ModuleTemplate
|
||||
*/
|
||||
protected $moduleTemplate;
|
||||
|
||||
/**
|
||||
* Restrict editing by non-Admins (0-no, 1-yes)
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $editLock = false;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
|
||||
$this->view = $this->initializeView();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 the current request
|
||||
* @return ResponseInterface the response with the content
|
||||
*/
|
||||
public function mainAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$this->moduleTemplate->getDocHeaderComponent()->setMetaInformation([]);
|
||||
|
||||
$parsedBody = $request->getParsedBody();
|
||||
$queryParams = $request->getQueryParams();
|
||||
$lastHistoryEntry = (int)($parsedBody['historyEntry'] ?? $queryParams['historyEntry'] ?? 0);
|
||||
$rollbackFields = $parsedBody['rollbackFields'] ?? $queryParams['rollbackFields'] ?? null;
|
||||
$element = $parsedBody['element'] ?? $queryParams['element'] ?? null;
|
||||
$displaySettings = $this->prepareDisplaySettings($request);
|
||||
$this->view->assign('currentSelection', $displaySettings);
|
||||
|
||||
$this->showDiff = (int)$displaySettings['showDiff'];
|
||||
|
||||
// Start history object
|
||||
$this->historyObject = GeneralUtility::makeInstance(RecordHistory::class, $element, $rollbackFields);
|
||||
$this->historyObject->setShowSubElements((int)$displaySettings['showSubElements']);
|
||||
$this->historyObject->setLastHistoryEntry($lastHistoryEntry);
|
||||
if ($displaySettings['maxSteps']) {
|
||||
$this->historyObject->setMaxSteps((int)$displaySettings['maxSteps']);
|
||||
}
|
||||
|
||||
// Do the actual logic now (rollback, show a diff for certain changes,
|
||||
// or show the full history of a page or a specific record)
|
||||
$this->historyObject->createChangeLog();
|
||||
if (!empty($this->historyObject->changeLog)) {
|
||||
if ($this->historyObject->shouldPerformRollback()) {
|
||||
$this->historyObject->performRollback();
|
||||
} elseif ($lastHistoryEntry) {
|
||||
$completeDiff = $this->historyObject->createMultipleDiff();
|
||||
$this->displayMultipleDiff($completeDiff);
|
||||
$this->view->assign('showDifferences', true);
|
||||
$this->view->assign('fullViewUrl', $this->buildUrl(['historyEntry' => '']));
|
||||
}
|
||||
if ($this->historyObject->getElementData()) {
|
||||
$this->displayHistory($this->historyObject->changeLog);
|
||||
}
|
||||
}
|
||||
|
||||
/** @var \TYPO3\CMS\Core\Http\NormalizedParams $normalizedParams */
|
||||
$normalizedParams = $request->getAttribute('normalizedParams');
|
||||
$elementData = $this->historyObject->getElementData();
|
||||
if ($elementData) {
|
||||
$this->setPagePath($elementData[0], $elementData[1]);
|
||||
$this->editLock = $this->getEditLockFromElement($elementData[0], $elementData[1]);
|
||||
// Get link to page history if the element history is shown
|
||||
if ($elementData[0] !== 'pages') {
|
||||
$this->view->assign('singleElement', true);
|
||||
$parentPage = BackendUtility::getRecord($elementData[0], $elementData[1], '*', '', false);
|
||||
if ($parentPage['pid'] > 0 && BackendUtility::readPageAccess($parentPage['pid'], $this->getBackendUser()->getPagePermsClause(Permission::PAGE_SHOW))) {
|
||||
$this->view->assign('fullHistoryUrl', $this->buildUrl([
|
||||
'element' => 'pages:' . $parentPage['pid'],
|
||||
'historyEntry' => '',
|
||||
'returnUrl' => $normalizedParams->getRequestUri(),
|
||||
]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->view->assign('TYPO3_REQUEST_URI', $normalizedParams->getRequestUrl());
|
||||
$this->view->assign('editLock', $this->editLock);
|
||||
|
||||
// Setting up the buttons and markers for docheader
|
||||
$this->getButtons($request);
|
||||
// Build the <body> for the module
|
||||
$this->moduleTemplate->setContent($this->view->render());
|
||||
|
||||
return new HtmlResponse($this->moduleTemplate->renderContent());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the correct path to the current record
|
||||
*
|
||||
* @param string $table
|
||||
* @param int $uid
|
||||
*/
|
||||
protected function setPagePath($table, $uid)
|
||||
{
|
||||
$uid = (int)$uid;
|
||||
|
||||
if ($table === 'pages') {
|
||||
$pageId = $uid;
|
||||
} else {
|
||||
$record = BackendUtility::getRecord($table, $uid, '*', '', false);
|
||||
$pageId = $record['pid'];
|
||||
}
|
||||
|
||||
$pageAccess = BackendUtility::readPageAccess($pageId, $this->getBackendUser()->getPagePermsClause(Permission::PAGE_SHOW));
|
||||
if (is_array($pageAccess)) {
|
||||
$this->moduleTemplate->getDocHeaderComponent()->setMetaInformation($pageAccess);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the panel of buttons for submitting the form or otherwise perform operations.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
*/
|
||||
protected function getButtons(ServerRequestInterface $request)
|
||||
{
|
||||
$buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
|
||||
|
||||
$helpButton = $buttonBar->makeHelpButton()
|
||||
->setModuleName('xMOD_csh_corebe')
|
||||
->setFieldName('history_log');
|
||||
$buttonBar->addButton($helpButton);
|
||||
|
||||
// Get returnUrl parameter
|
||||
$parsedBody = $request->getParsedBody();
|
||||
$queryParams = $request->getQueryParams();
|
||||
$returnUrl = GeneralUtility::sanitizeLocalUrl($parsedBody['returnUrl'] ?? $queryParams['returnUrl'] ?? '');
|
||||
|
||||
if ($returnUrl) {
|
||||
$backButton = $buttonBar->makeLinkButton()
|
||||
->setHref($returnUrl)
|
||||
->setTitle($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:rm.closeDoc'))
|
||||
->setIcon($this->moduleTemplate->getIconFactory()->getIcon('actions-view-go-back', Icon::SIZE_SMALL));
|
||||
$buttonBar->addButton($backButton, ButtonBar::BUTTON_POSITION_LEFT, 10);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays settings evaluation
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return array
|
||||
*/
|
||||
protected function prepareDisplaySettings(ServerRequestInterface $request)
|
||||
{
|
||||
// Get current selection from UC, merge data, write it back to UC
|
||||
$currentSelection = is_array($this->getBackendUser()->uc['moduleData']['history'])
|
||||
? $this->getBackendUser()->uc['moduleData']['history']
|
||||
: ['maxSteps' => '', 'showDiff' => 1, 'showSubElements' => 1];
|
||||
$parsedBody = $request->getParsedBody();
|
||||
$queryParams = $request->getQueryParams();
|
||||
$currentSelectionOverride = $parsedBody['settings'] ?? $queryParams['settings'] ?? null;
|
||||
|
||||
if (is_array($currentSelectionOverride) && !empty($currentSelectionOverride)) {
|
||||
$currentSelection = array_merge($currentSelection, $currentSelectionOverride);
|
||||
$this->getBackendUser()->uc['moduleData']['history'] = $currentSelection;
|
||||
$this->getBackendUser()->writeUC($this->getBackendUser()->uc);
|
||||
}
|
||||
|
||||
// Display selector for number of history entries
|
||||
$selector['maxSteps'] = [
|
||||
10 => [
|
||||
'value' => 10
|
||||
],
|
||||
20 => [
|
||||
'value' => 20
|
||||
],
|
||||
50 => [
|
||||
'value' => 50
|
||||
],
|
||||
100 => [
|
||||
'value' => 100
|
||||
],
|
||||
999 => [
|
||||
'value' => 'maxSteps_all'
|
||||
]
|
||||
];
|
||||
$selector['showDiff'] = [
|
||||
0 => [
|
||||
'value' => 'showDiff_no'
|
||||
],
|
||||
1 => [
|
||||
'value' => 'showDiff_inline'
|
||||
]
|
||||
];
|
||||
$selector['showSubElements'] = [
|
||||
0 => [
|
||||
'value' => 'no'
|
||||
],
|
||||
1 => [
|
||||
'value' => 'yes'
|
||||
]
|
||||
];
|
||||
|
||||
$scriptUrl = GeneralUtility::linkThisScript();
|
||||
|
||||
foreach ($selector as $key => $values) {
|
||||
foreach ($values as $singleKey => $singleVal) {
|
||||
$selector[$key][$singleKey]['scriptUrl'] = htmlspecialchars(GeneralUtility::quoteJSvalue($scriptUrl . '&settings[' . $key . ']=' . $singleKey));
|
||||
}
|
||||
}
|
||||
$this->view->assign('settings', $selector);
|
||||
return $currentSelection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a diff over multiple fields including rollback links
|
||||
*
|
||||
* @param array $diff Difference array
|
||||
*/
|
||||
protected function displayMultipleDiff(array $diff)
|
||||
{
|
||||
// Get all array keys needed
|
||||
$arrayKeys = array_merge(array_keys($diff['newData']), array_keys($diff['insertsDeletes']), array_keys($diff['oldData']));
|
||||
$arrayKeys = array_unique($arrayKeys);
|
||||
if (!empty($arrayKeys)) {
|
||||
$lines = [];
|
||||
foreach ($arrayKeys as $key) {
|
||||
$singleLine = [];
|
||||
$elParts = explode(':', $key);
|
||||
// Turn around diff because it should be a "rollback preview"
|
||||
if ((int)$diff['insertsDeletes'][$key] === 1) {
|
||||
// insert
|
||||
$singleLine['insertDelete'] = 'delete';
|
||||
} elseif ((int)$diff['insertsDeletes'][$key] === -1) {
|
||||
$singleLine['insertDelete'] = 'insert';
|
||||
}
|
||||
// Build up temporary diff array
|
||||
// turn around diff because it should be a "rollback preview"
|
||||
if ($diff['newData'][$key]) {
|
||||
$tmpArr = [
|
||||
'newRecord' => $diff['oldData'][$key],
|
||||
'oldRecord' => $diff['newData'][$key]
|
||||
];
|
||||
$singleLine['differences'] = $this->renderDiff($tmpArr, $elParts[0], $elParts[1]);
|
||||
}
|
||||
$elParts = explode(':', $key);
|
||||
$singleLine['revertRecordUrl'] = $this->buildUrl(['rollbackFields' => $key]);
|
||||
$singleLine['title'] = $this->generateTitle($elParts[0], $elParts[1]);
|
||||
$lines[] = $singleLine;
|
||||
}
|
||||
$this->view->assign('revertAllUrl', $this->buildUrl(['rollbackFields' => 'ALL']));
|
||||
$this->view->assign('multipleDiff', $lines);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the full change log
|
||||
*
|
||||
* @param array $historyEntries
|
||||
*/
|
||||
protected function displayHistory(array $historyEntries)
|
||||
{
|
||||
if (empty($historyEntries)) {
|
||||
return;
|
||||
}
|
||||
$languageService = $this->getLanguageService();
|
||||
$lines = [];
|
||||
$beUserArray = BackendUtility::getUserNames();
|
||||
|
||||
// Traverse changeLog array:
|
||||
foreach ($historyEntries as $entry) {
|
||||
// Build up single line
|
||||
$singleLine = [];
|
||||
|
||||
// Get user names
|
||||
$singleLine['backendUserUid'] = $entry['userid'];
|
||||
$singleLine['backendUserName'] = $entry['userid'] ? $beUserArray[$entry['userid']]['username'] : '';
|
||||
// Executed by switch user
|
||||
if (!empty($entry['originaluserid'])) {
|
||||
$singleLine['originalBackendUserName'] = $beUserArray[$entry['originaluserid']]['username'];
|
||||
}
|
||||
|
||||
// Diff link
|
||||
$singleLine['diffUrl'] = $this->buildUrl(['historyEntry' => $entry['uid']]);
|
||||
// Add time
|
||||
$singleLine['time'] = BackendUtility::datetime($entry['tstamp']);
|
||||
// Add age
|
||||
$singleLine['age'] = BackendUtility::calcAge($GLOBALS['EXEC_TIME'] - $entry['tstamp'], $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.minutesHoursDaysYears'));
|
||||
|
||||
$singleLine['title'] = $this->generateTitle($entry['tablename'], $entry['recuid']);
|
||||
$singleLine['elementUrl'] = $this->buildUrl(['element' => $entry['tablename'] . ':' . $entry['recuid']]);
|
||||
$singleLine['actiontype'] = $entry['actiontype'];
|
||||
if ((int)$entry['actiontype'] === RecordHistoryStore::ACTION_MODIFY) {
|
||||
// show changes
|
||||
if (!$this->showDiff) {
|
||||
// Display field names instead of full diff
|
||||
// Re-write field names with labels
|
||||
$tmpFieldList = array_keys($entry['newRecord']);
|
||||
foreach ($tmpFieldList as $key => $value) {
|
||||
$tmp = str_replace(':', '', $languageService->sL(BackendUtility::getItemLabel($entry['tablename'], $value)));
|
||||
if ($tmp) {
|
||||
$tmpFieldList[$key] = $tmp;
|
||||
} else {
|
||||
// remove fields if no label available
|
||||
unset($tmpFieldList[$key]);
|
||||
}
|
||||
}
|
||||
$singleLine['fieldNames'] = implode(',', $tmpFieldList);
|
||||
} else {
|
||||
// Display diff
|
||||
$singleLine['differences'] = $this->renderDiff($entry, $entry['tablename']);
|
||||
}
|
||||
}
|
||||
// put line together
|
||||
$lines[] = $singleLine;
|
||||
}
|
||||
$this->view->assign('history', $lines);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders HTML table-rows with the comparison information of an sys_history entry record
|
||||
*
|
||||
* @param array $entry sys_history entry record.
|
||||
* @param string $table The table name
|
||||
* @param int $rollbackUid If set to UID of record, display rollback links
|
||||
* @return array array of records
|
||||
*/
|
||||
protected function renderDiff($entry, $table, $rollbackUid = 0): array
|
||||
{
|
||||
$lines = [];
|
||||
if (is_array($entry['newRecord'])) {
|
||||
/* @var DiffUtility $diffUtility */
|
||||
$diffUtility = GeneralUtility::makeInstance(DiffUtility::class);
|
||||
$diffUtility->stripTags = false;
|
||||
$fieldsToDisplay = array_keys($entry['newRecord']);
|
||||
$languageService = $this->getLanguageService();
|
||||
foreach ($fieldsToDisplay as $fN) {
|
||||
if (is_array($GLOBALS['TCA'][$table]['columns'][$fN]) && $GLOBALS['TCA'][$table]['columns'][$fN]['config']['type'] !== 'passthrough') {
|
||||
// Create diff-result:
|
||||
$diffres = $diffUtility->makeDiffDisplay(
|
||||
BackendUtility::getProcessedValue($table, $fN, $entry['oldRecord'][$fN], 0, true),
|
||||
BackendUtility::getProcessedValue($table, $fN, $entry['newRecord'][$fN], 0, true)
|
||||
);
|
||||
$rollbackUrl = '';
|
||||
if ($rollbackUid) {
|
||||
$rollbackUrl = $this->buildUrl(['rollbackFields' => $table . ':' . $rollbackUid . ':' . $fN]);
|
||||
}
|
||||
$lines[] = [
|
||||
'title' => $languageService->sL(BackendUtility::getItemLabel($table, $fN)),
|
||||
'rollbackUrl' => $rollbackUrl,
|
||||
'result' => str_replace('\n', PHP_EOL, str_replace('\r\n', '\n', $diffres))
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the URL for a link to the current page
|
||||
*
|
||||
* @param array $overrideParameters
|
||||
* @return string
|
||||
*/
|
||||
protected function buildUrl($overrideParameters = []): string
|
||||
{
|
||||
$params = [];
|
||||
// Setting default values based on GET parameters:
|
||||
if ($this->historyObject->getElementData()) {
|
||||
$params['element'] = $this->historyObject->getElementString();
|
||||
}
|
||||
$params['historyEntry'] = $this->historyObject->lastHistoryEntry;
|
||||
// Merging overriding values:
|
||||
$params = array_merge($params, $overrideParameters);
|
||||
// Make the link:
|
||||
|
||||
/** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
|
||||
$uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
|
||||
return (string)$uriBuilder->buildUriFromRoute('record_history', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the title and puts the record title behind
|
||||
*
|
||||
* @param string $table
|
||||
* @param string $uid
|
||||
* @return string
|
||||
*/
|
||||
protected function generateTitle($table, $uid): string
|
||||
{
|
||||
$title = $table . ':' . $uid;
|
||||
if (!empty($GLOBALS['TCA'][$table]['ctrl']['label'])) {
|
||||
$record = $this->getRecord($table, $uid);
|
||||
$title .= ' (' . BackendUtility::getRecordTitle($table, $record, true) . ')';
|
||||
}
|
||||
return $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a database record (cached).
|
||||
*
|
||||
* @param string $table
|
||||
* @param int $uid
|
||||
* @return array|null
|
||||
*/
|
||||
protected function getRecord($table, $uid)
|
||||
{
|
||||
if (!isset($this->recordCache[$table][$uid])) {
|
||||
$this->recordCache[$table][$uid] = BackendUtility::getRecord($table, $uid, '*', '', false);
|
||||
}
|
||||
return $this->recordCache[$table][$uid];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new standalone view, shorthand function
|
||||
*
|
||||
* @return StandaloneView
|
||||
*/
|
||||
protected function initializeView()
|
||||
{
|
||||
/** @var StandaloneView $view */
|
||||
$view = GeneralUtility::makeInstance(StandaloneView::class);
|
||||
$view->setLayoutRootPaths([GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Layouts')]);
|
||||
$view->setPartialRootPaths([GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Partials')]);
|
||||
$view->setTemplateRootPaths([GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates')]);
|
||||
|
||||
$view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates/RecordHistory/Main.html'));
|
||||
|
||||
$view->getRequest()->setControllerExtensionName('Backend');
|
||||
return $view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns LanguageService
|
||||
*
|
||||
* @return \TYPO3\CMS\Core\Localization\LanguageService
|
||||
*/
|
||||
protected function getLanguageService()
|
||||
{
|
||||
return $GLOBALS['LANG'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current backend user.
|
||||
*
|
||||
* @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
|
||||
*/
|
||||
protected function getBackendUser()
|
||||
{
|
||||
return $GLOBALS['BE_USER'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the editlock value from page of a history element
|
||||
*
|
||||
* @param string $tableName
|
||||
* @param int $elementUid
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function getEditLockFromElement($tableName, $elementUid): bool
|
||||
{
|
||||
// If the user is admin, then he may always edit the page.
|
||||
if ($this->getBackendUser()->isAdmin()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$record = BackendUtility::getRecord($tableName, $elementUid, '*', '', false);
|
||||
// we need the parent page record for the editlock info if element isn't a page
|
||||
if ($tableName !== 'pages') {
|
||||
$pageId = $record['pid'];
|
||||
$record = BackendUtility::getRecord('pages', $pageId, '*', '', false);
|
||||
}
|
||||
|
||||
return (bool)$record['editlock'];
|
||||
}
|
||||
}
|
||||
+830
@@ -0,0 +1,830 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
namespace TYPO3\CMS\Backend\Controller\ContentElement;
|
||||
|
||||
/*
|
||||
* 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 Doctrine\DBAL\Connection;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Backend\Backend\Avatar\Avatar;
|
||||
use TYPO3\CMS\Backend\Template\ModuleTemplate;
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
use TYPO3\CMS\Core\Compatibility\PublicMethodDeprecationTrait;
|
||||
use TYPO3\CMS\Core\Compatibility\PublicPropertyDeprecationTrait;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Http\HtmlResponse;
|
||||
use TYPO3\CMS\Core\Imaging\Icon;
|
||||
use TYPO3\CMS\Core\Imaging\IconFactory;
|
||||
use TYPO3\CMS\Core\Resource\AbstractFile;
|
||||
use TYPO3\CMS\Core\Resource\File;
|
||||
use TYPO3\CMS\Core\Resource\Folder;
|
||||
use TYPO3\CMS\Core\Resource\ResourceFactory;
|
||||
use TYPO3\CMS\Core\Type\Bitmask\Permission;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Fluid\View\StandaloneView;
|
||||
|
||||
/**
|
||||
* Script Class for showing information about an item.
|
||||
* @internal This class is a specific Backend controller implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class ElementInformationController
|
||||
{
|
||||
use PublicMethodDeprecationTrait;
|
||||
use PublicPropertyDeprecationTrait;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $deprecatedPublicMethods = [
|
||||
'getLabelForTableColumn' => 'Using ElementInformationController::getLabelForTableColumn() is deprecated and will not be possible anymore in TYPO3 v10.0.',
|
||||
];
|
||||
|
||||
/**
|
||||
* Properties which have been moved to protected status from public
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $deprecatedPublicProperties = [
|
||||
'table' => 'Using $table of class ElementInformationController from the outside is discouraged, as this variable is only used for internal storage.',
|
||||
'uid' => 'Using $uid of class ElementInformationController from the outside is discouraged, as this variable is only used for internal storage.',
|
||||
'access' => 'Using $access of class ElementInformationController from the outside is discouraged, as this variable is only used for internal storage.',
|
||||
'type' => 'Using $type of class ElementInformationController from the outside is discouraged, as this variable is only used for internal storage.',
|
||||
'pageInfo' => 'Using $pageInfo of class ElementInformationController from the outside is discouraged, as this variable is only used for internal storage.',
|
||||
];
|
||||
|
||||
/**
|
||||
* Record table name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table;
|
||||
|
||||
/**
|
||||
* Record uid
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $uid;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $permsClause;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $access = false;
|
||||
|
||||
/**
|
||||
* Which type of element:
|
||||
* - "file"
|
||||
* - "db"
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = '';
|
||||
|
||||
/**
|
||||
* @var ModuleTemplate
|
||||
*/
|
||||
protected $moduleTemplate;
|
||||
|
||||
/**
|
||||
* For type "db": Set to page record of the parent page of the item set
|
||||
* (if type="db")
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $pageInfo;
|
||||
|
||||
/**
|
||||
* Database records identified by table/uid
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $row;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Core\Resource\File
|
||||
*/
|
||||
protected $fileObject;
|
||||
|
||||
/**
|
||||
* @var Folder
|
||||
*/
|
||||
protected $folderObject;
|
||||
|
||||
/**
|
||||
* @var IconFactory
|
||||
*/
|
||||
protected $iconFactory;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
|
||||
$GLOBALS['SOBE'] = $this;
|
||||
|
||||
// @deprecated since TYPO3 v9, will be obsolete in TYPO3 v10.0 with removal of init()
|
||||
$request = $GLOBALS['TYPO3_REQUEST'];
|
||||
// @deprecated since TYPO3 v9, will be moved out of __construct() in TYPO3 v10.0
|
||||
$this->init($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 the current request
|
||||
* @return ResponseInterface the response with the content
|
||||
*/
|
||||
public function mainAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$this->main($request);
|
||||
return new HtmlResponse($this->moduleTemplate->renderContent());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if table/uid point to database record or file and
|
||||
* if user has access to view information
|
||||
*
|
||||
* @param ServerRequestInterface|null $request
|
||||
*/
|
||||
public function init(ServerRequestInterface $request = null): void
|
||||
{
|
||||
if ($request === null) {
|
||||
// Missing argument? This method must have been called from outside.
|
||||
// Method will be protected and $request mandatory in TYPO3 v10.0, giving core freedom to move stuff around
|
||||
// New v10 signature: "protected function init(ServerRequestInterface $request): void"
|
||||
// @deprecated since TYPO3 v9, method argument $request will be set to mandatory
|
||||
trigger_error('ElementInformationController->init() will be set to protected in TYPO3 v10.0. Do not call from other extension.', E_USER_DEPRECATED);
|
||||
$request = $GLOBALS['TYPO3_REQUEST'];
|
||||
}
|
||||
|
||||
$queryParams = $request->getQueryParams();
|
||||
|
||||
$this->table = $queryParams['table'] ?? null;
|
||||
$this->uid = $queryParams['uid'] ?? null;
|
||||
|
||||
$this->permsClause = $this->getBackendUser()->getPagePermsClause(Permission::PAGE_SHOW);
|
||||
$this->moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
|
||||
$this->moduleTemplate->getDocHeaderComponent()->disable();
|
||||
|
||||
if (isset($GLOBALS['TCA'][$this->table])) {
|
||||
$this->initDatabaseRecord();
|
||||
} elseif ($this->table === '_FILE' || $this->table === '_FOLDER' || $this->table === 'sys_file') {
|
||||
$this->initFileOrFolderRecord();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init database records (table)
|
||||
*/
|
||||
protected function initDatabaseRecord()
|
||||
{
|
||||
$this->type = 'db';
|
||||
$this->uid = (int)$this->uid;
|
||||
|
||||
// Check permissions and uid value:
|
||||
if ($this->uid && $this->getBackendUser()->check('tables_select', $this->table)) {
|
||||
if ((string)$this->table === 'pages') {
|
||||
$this->pageInfo = BackendUtility::readPageAccess($this->uid, $this->permsClause);
|
||||
$this->access = is_array($this->pageInfo);
|
||||
$this->row = $this->pageInfo;
|
||||
} else {
|
||||
$this->row = BackendUtility::getRecordWSOL($this->table, $this->uid);
|
||||
if ($this->row) {
|
||||
// Find the correct "pid" when a versionized record is given, otherwise "pid = -1" always fails
|
||||
if (!empty($this->row['t3ver_oid'])) {
|
||||
$t3OrigRow = BackendUtility::getRecord($this->table, (int)$this->row['t3ver_oid']);
|
||||
$this->pageInfo = BackendUtility::readPageAccess((int)$t3OrigRow['pid'], $this->permsClause);
|
||||
} else {
|
||||
$this->pageInfo = BackendUtility::readPageAccess($this->row['pid'], $this->permsClause);
|
||||
}
|
||||
$this->access = is_array($this->pageInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init file/folder parameters
|
||||
*/
|
||||
protected function initFileOrFolderRecord()
|
||||
{
|
||||
$fileOrFolderObject = ResourceFactory::getInstance()->retrieveFileOrFolderObject($this->uid);
|
||||
|
||||
if ($fileOrFolderObject instanceof Folder) {
|
||||
$this->folderObject = $fileOrFolderObject;
|
||||
$this->access = $this->folderObject->checkActionPermission('read');
|
||||
$this->type = 'folder';
|
||||
} elseif ($fileOrFolderObject instanceof File) {
|
||||
$this->fileObject = $fileOrFolderObject;
|
||||
$this->access = $this->fileObject->checkActionPermission('read');
|
||||
$this->type = 'file';
|
||||
$this->table = 'sys_file';
|
||||
|
||||
try {
|
||||
$this->row = BackendUtility::getRecordWSOL($this->table, $fileOrFolderObject->getUid());
|
||||
} catch (\Exception $e) {
|
||||
$this->row = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the whole content to be outputted, which is then set as content to the moduleTemplate
|
||||
* There is a hook to do a custom rendering of a record.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
*/
|
||||
public function main(ServerRequestInterface $request = null): void
|
||||
{
|
||||
if ($request === null) {
|
||||
// Missing argument? This method must have been called from outside.
|
||||
// @deprecated since TYPO3 v9, method argument $request will be set to mandatory
|
||||
trigger_error('ElementInformationController->main() will be set to protected in TYPO3 v10.0. Do not call from other extension.', E_USER_DEPRECATED);
|
||||
$request = $GLOBALS['TYPO3_REQUEST'];
|
||||
}
|
||||
|
||||
$content = '';
|
||||
|
||||
// Rendering of the output via fluid
|
||||
$view = GeneralUtility::makeInstance(StandaloneView::class);
|
||||
$view->setTemplateRootPaths([GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates')]);
|
||||
$view->setPartialRootPaths([GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Partials')]);
|
||||
$view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName(
|
||||
'EXT:backend/Resources/Private/Templates/ContentElement/ElementInformation.html'
|
||||
));
|
||||
|
||||
if ($this->access) {
|
||||
// render type by user func
|
||||
$typeRendered = false;
|
||||
foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/show_item.php']['typeRendering'] ?? [] as $className) {
|
||||
$typeRenderObj = GeneralUtility::makeInstance($className);
|
||||
if (is_object($typeRenderObj) && method_exists($typeRenderObj, 'isValid') && method_exists($typeRenderObj, 'render')) {
|
||||
if ($typeRenderObj->isValid($this->type, $this)) {
|
||||
$content .= $typeRenderObj->render($this->type, $this);
|
||||
$typeRendered = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$typeRendered) {
|
||||
$view->assign('accessAllowed', true);
|
||||
$view->assignMultiple($this->getPageTitle());
|
||||
$view->assignMultiple($this->getPreview());
|
||||
$view->assignMultiple($this->getPropertiesForTable());
|
||||
$view->assignMultiple($this->getReferences($request));
|
||||
$view->assign('returnUrl', GeneralUtility::sanitizeLocalUrl($request->getQueryParams()['returnUrl']));
|
||||
$view->assign('maxTitleLength', $this->getBackendUser()->uc['titleLen'] ?? 20);
|
||||
$content .= $view->render();
|
||||
}
|
||||
} else {
|
||||
$content .= $view->render();
|
||||
}
|
||||
|
||||
$this->moduleTemplate->setContent($content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get page title with icon, table title and record title
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getPageTitle(): array
|
||||
{
|
||||
$pageTitle = [
|
||||
'title' => BackendUtility::getRecordTitle($this->table, $this->row, false)
|
||||
];
|
||||
if ($this->type === 'folder') {
|
||||
$pageTitle['table'] = $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_common.xlf:folder');
|
||||
$pageTitle['icon'] = $this->iconFactory->getIconForResource($this->folderObject, Icon::SIZE_SMALL)->render();
|
||||
} elseif ($this->type === 'file') {
|
||||
$pageTitle['table'] = $this->getLanguageService()->sL($GLOBALS['TCA'][$this->table]['ctrl']['title']);
|
||||
$pageTitle['icon'] = $this->iconFactory->getIconForResource($this->fileObject, Icon::SIZE_SMALL)->render();
|
||||
} else {
|
||||
$pageTitle['table'] = $this->getLanguageService()->sL($GLOBALS['TCA'][$this->table]['ctrl']['title']);
|
||||
$pageTitle['icon'] = $this->iconFactory->getIconForRecord($this->table, $this->row, Icon::SIZE_SMALL);
|
||||
}
|
||||
$this->moduleTemplate->setTitle($pageTitle['table'] . ': ' . $pageTitle['title']);
|
||||
return $pageTitle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get preview for current record
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getPreview(): array
|
||||
{
|
||||
$preview = [];
|
||||
// Perhaps @todo in future: Also display preview for records - without fileObject
|
||||
if (!$this->fileObject) {
|
||||
return $preview;
|
||||
}
|
||||
|
||||
// check if file is marked as missing
|
||||
if ($this->fileObject->isMissing()) {
|
||||
$preview['missingFile'] = $this->fileObject->getName();
|
||||
} else {
|
||||
/** @var \TYPO3\CMS\Core\Resource\Rendering\RendererRegistry $rendererRegistry */
|
||||
$rendererRegistry = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Resource\Rendering\RendererRegistry::class);
|
||||
$fileRenderer = $rendererRegistry->getRenderer($this->fileObject);
|
||||
$fileExtension = $this->fileObject->getExtension();
|
||||
$preview['url'] = $this->fileObject->getPublicUrl(true);
|
||||
|
||||
$width = '590m';
|
||||
$heigth = '400m';
|
||||
|
||||
// Check if there is a FileRenderer
|
||||
if ($fileRenderer !== null) {
|
||||
$preview['fileRenderer'] = $fileRenderer->render(
|
||||
$this->fileObject,
|
||||
$width,
|
||||
$heigth,
|
||||
[],
|
||||
true
|
||||
);
|
||||
|
||||
// else check if we can create an Image preview
|
||||
} elseif (GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], $fileExtension)) {
|
||||
$preview['fileObject'] = $this->fileObject;
|
||||
$preview['width'] = $width;
|
||||
$preview['heigth'] = $heigth;
|
||||
}
|
||||
}
|
||||
return $preview;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get property array for html table
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getPropertiesForTable(): array
|
||||
{
|
||||
$propertiesForTable = [];
|
||||
$lang = $this->getLanguageService();
|
||||
|
||||
$extraFields = [
|
||||
'uid' => htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:show_item.php.uid'))
|
||||
];
|
||||
|
||||
if (in_array($this->type, ['folder', 'file'], true)) {
|
||||
if ($this->type === 'file') {
|
||||
$extraFields['creation_date'] = htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.creationDate'));
|
||||
$extraFields['modification_date'] = htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.timestamp'));
|
||||
if ($this->fileObject->getType() === AbstractFile::FILETYPE_IMAGE) {
|
||||
$extraFields['width'] = htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.width'));
|
||||
$extraFields['height'] = htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.height'));
|
||||
}
|
||||
}
|
||||
$extraFields['storage'] = htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file.storage'));
|
||||
$extraFields['folder'] = htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_common.xlf:folder'));
|
||||
} else {
|
||||
$extraFields['crdate'] = htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.creationDate'));
|
||||
$extraFields['cruser_id'] = htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.creationUserId'));
|
||||
$extraFields['tstamp'] = htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.timestamp'));
|
||||
|
||||
// check if the special fields are defined in the TCA ctrl section of the table
|
||||
foreach ($extraFields as $fieldName => $fieldLabel) {
|
||||
if (isset($GLOBALS['TCA'][$this->table]['ctrl'][$fieldName])) {
|
||||
$extraFields[$GLOBALS['TCA'][$this->table]['ctrl'][$fieldName]] = $fieldLabel;
|
||||
} elseif ($fieldName !== 'uid') {
|
||||
unset($extraFields[$fieldName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($extraFields as $name => $fieldLabel) {
|
||||
$rowValue = '';
|
||||
$thisRow = [];
|
||||
if (!isset($this->row[$name])) {
|
||||
$resourceObject = $this->fileObject ?: $this->folderObject;
|
||||
if ($name === 'storage') {
|
||||
$rowValue = $resourceObject->getStorage()->getName();
|
||||
} elseif ($name === 'folder') {
|
||||
$rowValue = $resourceObject->getParentFolder()->getReadablePath();
|
||||
} elseif ($name === 'width') {
|
||||
$rowValue = $this->fileObject->getProperty('width') . 'px';
|
||||
} elseif ($name === 'height') {
|
||||
$rowValue = $this->fileObject->getProperty('height') . 'px';
|
||||
}
|
||||
} elseif ($name === 'creation_date' || $name === 'modification_date' || $name === 'tstamp' || $name === 'crdate') {
|
||||
$rowValue = BackendUtility::datetime($this->row[$name]);
|
||||
} else {
|
||||
$rowValue = BackendUtility::getProcessedValueExtra($this->table, $name, $this->row[$name]);
|
||||
}
|
||||
$thisRow['value'] = $rowValue;
|
||||
$thisRow['fieldLabel'] = rtrim($fieldLabel, ':');
|
||||
// show the backend username who created the issue
|
||||
if ($name === 'cruser_id' && $rowValue) {
|
||||
$creatorRecord = BackendUtility::getRecord('be_users', $rowValue);
|
||||
if ($creatorRecord) {
|
||||
/** @var Avatar $avatar */
|
||||
$avatar = GeneralUtility::makeInstance(Avatar::class);
|
||||
$creatorRecord['icon'] = $avatar->render($creatorRecord);
|
||||
$thisRow['creatorRecord'] = $creatorRecord;
|
||||
$thisRow['value'] = '';
|
||||
}
|
||||
}
|
||||
$propertiesForTable['extraFields'][] = $thisRow;
|
||||
}
|
||||
|
||||
// Traverse the list of fields to display for the record:
|
||||
$fieldList = GeneralUtility::trimExplode(',', $GLOBALS['TCA'][$this->table]['interface']['showRecordFieldList'], true);
|
||||
foreach ($fieldList as $name) {
|
||||
$thisRow = [];
|
||||
$name = trim($name);
|
||||
$uid = $this->row['uid'];
|
||||
|
||||
if (!isset($GLOBALS['TCA'][$this->table]['columns'][$name])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Storage is already handled above
|
||||
if ($this->type === 'file' && $name === 'storage') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// format file size as bytes/kilobytes/megabytes
|
||||
if ($this->type === 'file' && $name === 'size') {
|
||||
$this->row[$name] = GeneralUtility::formatSize($this->row[$name], htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_common.xlf:byteSizeUnits')));
|
||||
}
|
||||
|
||||
$isExcluded = !(!$GLOBALS['TCA'][$this->table]['columns'][$name]['exclude'] || $this->getBackendUser()->check('non_exclude_fields', $this->table . ':' . $name));
|
||||
if ($isExcluded) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$thisRow['fieldValue'] = BackendUtility::getProcessedValue($this->table, $name, $this->row[$name], 0, 0, false, $uid);
|
||||
$thisRow['fieldLabel'] = htmlspecialchars($lang->sL(BackendUtility::getItemLabel($this->table, $name)));
|
||||
$propertiesForTable['fields'][] = $thisRow;
|
||||
}
|
||||
return $propertiesForTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get references section (references from and references to current record)
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return array
|
||||
*/
|
||||
protected function getReferences(ServerRequestInterface $request): array
|
||||
{
|
||||
$references = [];
|
||||
switch ($this->type) {
|
||||
case 'db': {
|
||||
$references['refLines'] = $this->makeRef($this->table, $this->row['uid'], $request);
|
||||
$references['refFromLines'] = $this->makeRefFrom($this->table, $this->row['uid'], $request);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'file': {
|
||||
if ($this->fileObject && $this->fileObject->isIndexed()) {
|
||||
$references['refLines'] = $this->makeRef('_FILE', $this->fileObject, $request);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $references;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get field name for specified table/column name
|
||||
*
|
||||
* @param string $tableName Table name
|
||||
* @param string $fieldName Column name
|
||||
* @return string label
|
||||
*/
|
||||
protected function getLabelForTableColumn($tableName, $fieldName)
|
||||
{
|
||||
if ($GLOBALS['TCA'][$tableName]['columns'][$fieldName]['label'] !== null) {
|
||||
$field = $this->getLanguageService()->sL($GLOBALS['TCA'][$tableName]['columns'][$fieldName]['label']);
|
||||
if (trim($field) === '') {
|
||||
$field = $fieldName;
|
||||
}
|
||||
} else {
|
||||
$field = $fieldName;
|
||||
}
|
||||
return $field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the record actions
|
||||
*
|
||||
* @param string $table
|
||||
* @param int $uid
|
||||
* @param ServerRequestInterface $request
|
||||
* @return array
|
||||
*/
|
||||
protected function getRecordActions($table, $uid, ServerRequestInterface $request): array
|
||||
{
|
||||
if ($table === '' || $uid < 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$actions = [];
|
||||
// Edit button
|
||||
$urlParameters = [
|
||||
'edit' => [
|
||||
$table => [
|
||||
$uid => 'edit'
|
||||
]
|
||||
],
|
||||
'returnUrl' => $request->getAttribute('normalizedParams')->getRequestUri()
|
||||
];
|
||||
/** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
|
||||
$uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
|
||||
$actions['recordEditUrl'] = (string)$uriBuilder->buildUriFromRoute('record_edit', $urlParameters);
|
||||
|
||||
// History button
|
||||
$urlParameters = [
|
||||
'element' => $table . ':' . $uid,
|
||||
'returnUrl' => $request->getAttribute('normalizedParams')->getRequestUri()
|
||||
];
|
||||
$actions['recordHistoryUrl'] = (string)$uriBuilder->buildUriFromRoute('record_history', $urlParameters);
|
||||
|
||||
if ($table === 'pages') {
|
||||
// Recordlist button
|
||||
$actions['webListUrl'] = (string)$uriBuilder->buildUriFromRoute('web_list', ['id' => $uid, 'returnUrl' => $request->getAttribute('normalizedParams')->getRequestUri()]);
|
||||
|
||||
// View page button
|
||||
$actions['viewOnClick'] = BackendUtility::viewOnClick($uid, '', BackendUtility::BEgetRootLine($uid));
|
||||
}
|
||||
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make reference display
|
||||
*
|
||||
* @param string $table Table name
|
||||
* @param string|\TYPO3\CMS\Core\Resource\File $ref Filename or uid
|
||||
* @param ServerRequestInterface $request
|
||||
* @return array
|
||||
*/
|
||||
protected function makeRef($table, $ref, ServerRequestInterface $request)
|
||||
{
|
||||
$refLines = [];
|
||||
$lang = $this->getLanguageService();
|
||||
// Files reside in sys_file table
|
||||
if ($table === '_FILE') {
|
||||
$selectTable = 'sys_file';
|
||||
$selectUid = $ref->getUid();
|
||||
} else {
|
||||
$selectTable = $table;
|
||||
$selectUid = $ref;
|
||||
}
|
||||
/** @var \TYPO3\CMS\Core\Database\Query\QueryBuilder $queryBuilder */
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable('sys_refindex');
|
||||
|
||||
$predicates = [
|
||||
$queryBuilder->expr()->eq(
|
||||
'ref_table',
|
||||
$queryBuilder->createNamedParameter($selectTable, \PDO::PARAM_STR)
|
||||
),
|
||||
$queryBuilder->expr()->eq(
|
||||
'ref_uid',
|
||||
$queryBuilder->createNamedParameter($selectUid, \PDO::PARAM_INT)
|
||||
),
|
||||
$queryBuilder->expr()->eq(
|
||||
'deleted',
|
||||
$queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
|
||||
)
|
||||
];
|
||||
|
||||
$backendUser = $this->getBackendUser();
|
||||
if (!$backendUser->isAdmin()) {
|
||||
$allowedSelectTables = GeneralUtility::trimExplode(',', $backendUser->groupData['tables_select']);
|
||||
$predicates[] = $queryBuilder->expr()->in(
|
||||
'tablename',
|
||||
$queryBuilder->createNamedParameter($allowedSelectTables, Connection::PARAM_STR_ARRAY)
|
||||
);
|
||||
}
|
||||
|
||||
$rows = $queryBuilder
|
||||
->select('*')
|
||||
->from('sys_refindex')
|
||||
->where(...$predicates)
|
||||
->execute()
|
||||
->fetchAll();
|
||||
|
||||
// Compile information for title tag:
|
||||
foreach ($rows as $row) {
|
||||
if ($row['tablename'] === 'sys_file_reference') {
|
||||
$row = $this->transformFileReferenceToRecordReference($row);
|
||||
if ($row['tablename'] === null || $row['recuid'] === null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$line = [];
|
||||
$record = BackendUtility::getRecord($row['tablename'], $row['recuid']);
|
||||
if ($record) {
|
||||
BackendUtility::fixVersioningPid($row['tablename'], $record);
|
||||
if (!$this->canAccessPage($row['tablename'], $record)) {
|
||||
continue;
|
||||
}
|
||||
$parentRecord = BackendUtility::getRecord('pages', $record['pid']);
|
||||
$parentRecordTitle = is_array($parentRecord)
|
||||
? BackendUtility::getRecordTitle('pages', $parentRecord)
|
||||
: '';
|
||||
$urlParameters = [
|
||||
'edit' => [
|
||||
$row['tablename'] => [
|
||||
$row['recuid'] => 'edit'
|
||||
]
|
||||
],
|
||||
'returnUrl' => $request->getAttribute('normalizedParams')->getRequestUri()
|
||||
];
|
||||
/** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
|
||||
$uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
|
||||
$url = (string)$uriBuilder->buildUriFromRoute('record_edit', $urlParameters);
|
||||
$line['url'] = $url;
|
||||
$line['icon'] = $this->iconFactory->getIconForRecord($row['tablename'], $record, Icon::SIZE_SMALL)->render();
|
||||
$line['row'] = $row;
|
||||
$line['record'] = $record;
|
||||
$line['recordTitle'] = BackendUtility::getRecordTitle($row['tablename'], $record, false, true);
|
||||
$line['parentRecordTitle'] = $parentRecordTitle;
|
||||
$line['title'] = $lang->sL($GLOBALS['TCA'][$row['tablename']]['ctrl']['title']);
|
||||
$line['labelForTableColumn'] = $this->getLabelForTableColumn($row['tablename'], $row['field']);
|
||||
$line['path'] = BackendUtility::getRecordPath($record['pid'], '', 0, 0);
|
||||
$line['actions'] = $this->getRecordActions($row['tablename'], $row['recuid'], $request);
|
||||
} else {
|
||||
$line['row'] = $row;
|
||||
$line['title'] = $lang->sL($GLOBALS['TCA'][$row['tablename']]['ctrl']['title']) ?: $row['tablename'];
|
||||
$line['labelForTableColumn'] = $this->getLabelForTableColumn($row['tablename'], $row['field']);
|
||||
}
|
||||
$refLines[] = $line;
|
||||
}
|
||||
return $refLines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make reference display (what this elements points to)
|
||||
*
|
||||
* @param string $table Table name
|
||||
* @param string $ref Filename or uid
|
||||
* @param ServerRequestInterface $request
|
||||
* @return array
|
||||
*/
|
||||
protected function makeRefFrom($table, $ref, ServerRequestInterface $request): array
|
||||
{
|
||||
$refFromLines = [];
|
||||
$lang = $this->getLanguageService();
|
||||
|
||||
/** @var \TYPO3\CMS\Core\Database\Query\QueryBuilder $queryBuilder */
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable('sys_refindex');
|
||||
|
||||
$predicates = [
|
||||
$queryBuilder->expr()->eq(
|
||||
'tablename',
|
||||
$queryBuilder->createNamedParameter($table, \PDO::PARAM_STR)
|
||||
),
|
||||
$queryBuilder->expr()->eq(
|
||||
'recuid',
|
||||
$queryBuilder->createNamedParameter($ref, \PDO::PARAM_INT)
|
||||
)
|
||||
];
|
||||
|
||||
$backendUser = $this->getBackendUser();
|
||||
if (!$backendUser->isAdmin()) {
|
||||
$allowedSelectTables = GeneralUtility::trimExplode(',', $backendUser->groupData['tables_select']);
|
||||
$predicates[] = $queryBuilder->expr()->in(
|
||||
'ref_table',
|
||||
$queryBuilder->createNamedParameter($allowedSelectTables, Connection::PARAM_STR_ARRAY)
|
||||
);
|
||||
}
|
||||
|
||||
$rows = $queryBuilder
|
||||
->select('*')
|
||||
->from('sys_refindex')
|
||||
->where(...$predicates)
|
||||
->execute()
|
||||
->fetchAll();
|
||||
|
||||
// Compile information for title tag:
|
||||
foreach ($rows as $row) {
|
||||
$line = [];
|
||||
$record = BackendUtility::getRecord($row['ref_table'], $row['ref_uid']);
|
||||
if ($record) {
|
||||
BackendUtility::fixVersioningPid($row['ref_table'], $record);
|
||||
if (!$this->canAccessPage($row['ref_table'], $record)) {
|
||||
continue;
|
||||
}
|
||||
$urlParameters = [
|
||||
'edit' => [
|
||||
$row['ref_table'] => [
|
||||
$row['ref_uid'] => 'edit'
|
||||
]
|
||||
],
|
||||
'returnUrl' => $request->getAttribute('normalizedParams')->getRequestUri()
|
||||
];
|
||||
/** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
|
||||
$uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
|
||||
$url = (string)$uriBuilder->buildUriFromRoute('record_edit', $urlParameters);
|
||||
$line['url'] = $url;
|
||||
$line['icon'] = $this->iconFactory->getIconForRecord($row['tablename'], $record, Icon::SIZE_SMALL)->render();
|
||||
$line['row'] = $row;
|
||||
$line['record'] = $record;
|
||||
$line['recordTitle'] = BackendUtility::getRecordTitle($row['ref_table'], $record, false, true);
|
||||
$line['title'] = $lang->sL($GLOBALS['TCA'][$row['ref_table']]['ctrl']['title']);
|
||||
$line['labelForTableColumn'] = $this->getLabelForTableColumn($table, $row['field']);
|
||||
$line['path'] = BackendUtility::getRecordPath($record['pid'], '', 0, 0);
|
||||
$line['actions'] = $this->getRecordActions($row['ref_table'], $row['ref_uid'], $request);
|
||||
} else {
|
||||
$line['row'] = $row;
|
||||
$line['title'] = $lang->sL($GLOBALS['TCA'][$row['ref_table']]['ctrl']['title']);
|
||||
$line['labelForTableColumn'] = $this->getLabelForTableColumn($table, $row['field']);
|
||||
}
|
||||
$refFromLines[] = $line;
|
||||
}
|
||||
return $refFromLines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert FAL file reference (sys_file_reference) to reference index (sys_refindex) table format
|
||||
*
|
||||
* @param array $referenceRecord
|
||||
* @return array
|
||||
*/
|
||||
protected function transformFileReferenceToRecordReference(array $referenceRecord)
|
||||
{
|
||||
/** @var \TYPO3\CMS\Core\Database\Query\QueryBuilder $queryBuilder */
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable('sys_file_reference');
|
||||
$queryBuilder->getRestrictions()->removeAll();
|
||||
$fileReference = $queryBuilder
|
||||
->select('*')
|
||||
->from('sys_file_reference')
|
||||
->where(
|
||||
$queryBuilder->expr()->eq(
|
||||
'uid',
|
||||
$queryBuilder->createNamedParameter($referenceRecord['recuid'], \PDO::PARAM_INT)
|
||||
)
|
||||
)
|
||||
->execute()
|
||||
->fetch();
|
||||
|
||||
return [
|
||||
'recuid' => $fileReference['uid_foreign'],
|
||||
'tablename' => $fileReference['tablenames'],
|
||||
'field' => $fileReference['fieldname'],
|
||||
'flexpointer' => '',
|
||||
'softref_key' => '',
|
||||
'sorting' => $fileReference['sorting_foreign']
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $tableName Name of the table
|
||||
* @param array $record Record to be checked (ensure pid is resolved for workspaces)
|
||||
* @return bool
|
||||
*/
|
||||
protected function canAccessPage(string $tableName, array $record): bool
|
||||
{
|
||||
$recordPid = (int)($tableName === 'pages' ? $record['uid'] : $record['pid']);
|
||||
return $this->getBackendUser()->isInWebMount($tableName === 'pages' ? $record : $record['pid'])
|
||||
|| $recordPid === 0 && !empty($GLOBALS['TCA'][$tableName]['ctrl']['security']['ignoreRootLevelRestriction']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns LanguageService
|
||||
*
|
||||
* @return \TYPO3\CMS\Core\Localization\LanguageService
|
||||
*/
|
||||
protected function getLanguageService()
|
||||
{
|
||||
return $GLOBALS['LANG'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current BE user.
|
||||
*
|
||||
* @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
|
||||
*/
|
||||
protected function getBackendUser()
|
||||
{
|
||||
return $GLOBALS['BE_USER'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
namespace TYPO3\CMS\Backend\Controller\ContentElement;
|
||||
|
||||
/*
|
||||
* 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\ModuleTemplate;
|
||||
use TYPO3\CMS\Backend\Tree\View\ContentMovingPagePositionMap;
|
||||
use TYPO3\CMS\Backend\Tree\View\PageMovingPagePositionMap;
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
|
||||
use TYPO3\CMS\Core\Compatibility\PublicPropertyDeprecationTrait;
|
||||
use TYPO3\CMS\Core\Http\HtmlResponse;
|
||||
use TYPO3\CMS\Core\Imaging\Icon;
|
||||
use TYPO3\CMS\Core\Localization\LanguageService;
|
||||
use TYPO3\CMS\Core\Type\Bitmask\Permission;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Fluid\View\StandaloneView;
|
||||
|
||||
/**
|
||||
* Script Class for rendering the move-element wizard display
|
||||
* @internal This class is a specific Backend controller implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class MoveElementController
|
||||
{
|
||||
use PublicPropertyDeprecationTrait;
|
||||
|
||||
/**
|
||||
* Properties which have been moved to protected status from public
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $deprecatedPublicProperties = [
|
||||
'sys_language' => 'Using $sys_language of MoveElementController from the outside is discouraged, as this variable is used for internal storage.',
|
||||
'page_id' => 'Using $page_id of MoveElementController from the outside is discouraged, as this variable is used for internal storage.',
|
||||
'table' => 'Using $table of MoveElementController from the outside is discouraged, as this variable is used for internal storage.',
|
||||
'R_URI' => 'Using $R_URI of MoveElementController from the outside is discouraged, as this variable is used for internal storage.',
|
||||
'input_moveUid' => 'Using $input_moveUid of MoveElementController from the outside is discouraged, as this variable is used for internal storage.',
|
||||
'moveUid' => 'Using $moveUid of MoveElementController from the outside is discouraged, as this variable is used for internal storage.',
|
||||
'makeCopy' => 'Using $makeCopy of MoveElementController from the outside is discouraged, as this variable is used for internal storage.',
|
||||
'perms_clause' => 'Using $perms_clause of MoveElementController from the outside is discouraged, as this variable is used for internal storage.',
|
||||
'content' => 'Using $content of MoveElementController from the outside is discouraged, as this variable is used for internal storage.',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $sys_language = 0;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $page_id;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $table;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $R_URI;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $input_moveUid;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $moveUid;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $makeCopy;
|
||||
|
||||
/**
|
||||
* Pages-select clause
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $perms_clause;
|
||||
|
||||
/**
|
||||
* Content for module accumulated here.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $content;
|
||||
|
||||
/**
|
||||
* 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_misc.xlf');
|
||||
|
||||
// @deprecated since TYPO3 v9, will be obsolete in TYPO3 v10.0 with removal of init()
|
||||
$request = $GLOBALS['TYPO3_REQUEST'];
|
||||
// @deprecated since TYPO3 v9, will be moved out of __construct() in TYPO3 v10.0
|
||||
$this->init($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 the current request
|
||||
* @return ResponseInterface the response with the content
|
||||
*/
|
||||
public function mainAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$this->renderContent();
|
||||
|
||||
return new HtmlResponse($this->content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor, initializing internal variables.
|
||||
*
|
||||
* @param ServerRequestInterface|null $request
|
||||
*/
|
||||
public function init(ServerRequestInterface $request = null)
|
||||
{
|
||||
if ($request === null) {
|
||||
// Method signature in TYPO3 v10.0: protected function init(ServerRequestInterface $request)
|
||||
trigger_error('MoveElementController->init() will be set to protected in TYPO3 v10.0. Do not call from other extension.', E_USER_DEPRECATED);
|
||||
$request = $GLOBALS['TYPO3_REQUEST'];
|
||||
}
|
||||
|
||||
$parsedBody = $request->getParsedBody();
|
||||
$queryParams = $request->getQueryParams();
|
||||
|
||||
// Setting internal vars:
|
||||
$this->sys_language = (int)($parsedBody['sys_language'] ?? $queryParams['sys_language'] ?? 0);
|
||||
$this->page_id = (int)($parsedBody['uid'] ?? $queryParams['uid'] ?? 0);
|
||||
$this->table = $parsedBody['table'] ?? $queryParams['table'] ?? null;
|
||||
$this->R_URI = GeneralUtility::sanitizeLocalUrl($parsedBody['returnUrl'] ?? $queryParams['returnUrl'] ?? '');
|
||||
$this->input_moveUid = $parsedBody['moveUid'] ?? $queryParams['moveUid'] ?? null;
|
||||
$this->moveUid = $this->input_moveUid ? $this->input_moveUid : $this->page_id;
|
||||
$this->makeCopy = $parsedBody['makeCopy'] ?? $queryParams['makeCopy'] ?? 0;
|
||||
// Select-pages where clause for read-access:
|
||||
$this->perms_clause = $this->getBackendUser()->getPagePermsClause(Permission::PAGE_SHOW);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creating the module output.
|
||||
*/
|
||||
protected function renderContent(): void
|
||||
{
|
||||
$lang = $this->getLanguageService();
|
||||
|
||||
if ($this->page_id) {
|
||||
$assigns = [];
|
||||
$backendUser = $this->getBackendUser();
|
||||
$this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/Tooltip');
|
||||
// Get record for element:
|
||||
$elRow = BackendUtility::getRecordWSOL($this->table, $this->moveUid);
|
||||
// Headerline: Icon, record title:
|
||||
$assigns['table'] = $this->table;
|
||||
$assigns['elRow'] = $elRow;
|
||||
$assigns['recordTooltip'] = BackendUtility::getRecordToolTip($elRow, $this->table);
|
||||
$assigns['recordTitle'] = BackendUtility::getRecordTitle($this->table, $elRow, true);
|
||||
// Make-copy checkbox (clicking this will reload the page with the GET var makeCopy set differently):
|
||||
$onClick = 'window.location.href=' . GeneralUtility::quoteJSvalue(GeneralUtility::linkThisScript(['makeCopy' => !$this->makeCopy])) . ';';
|
||||
$assigns['makeCopyChecked'] = $this->makeCopy ? ' checked="checked"' : '';
|
||||
$assigns['makeCopyOnClick'] = $onClick;
|
||||
// IF the table is "pages":
|
||||
if ((string)$this->table === 'pages') {
|
||||
// Get page record (if accessible):
|
||||
$pageInfo = BackendUtility::readPageAccess($this->page_id, $this->perms_clause);
|
||||
if (is_array($pageInfo) && $backendUser->isInWebMount($pageInfo['pid'], $this->perms_clause)) {
|
||||
// Initialize the position map:
|
||||
$posMap = GeneralUtility::makeInstance(PageMovingPagePositionMap::class);
|
||||
$posMap->moveOrCopy = $this->makeCopy ? 'copy' : 'move';
|
||||
$posMap->moveUid = $this->moveUid;
|
||||
// Print a "go-up" link IF there is a real parent page (and if the user has read-access to that page).
|
||||
if ($pageInfo['pid']) {
|
||||
$pidPageInfo = BackendUtility::readPageAccess($pageInfo['pid'], $this->perms_clause);
|
||||
if (is_array($pidPageInfo)) {
|
||||
if ($backendUser->isInWebMount($pidPageInfo['pid'], $this->perms_clause)) {
|
||||
$assigns['pages']['goUpUrl'] = GeneralUtility::linkThisScript([
|
||||
'uid' => (int)$pageInfo['pid'],
|
||||
'moveUid' => $this->moveUid
|
||||
]);
|
||||
} else {
|
||||
$assigns['pages']['pidPageInfo'] = $pidPageInfo;
|
||||
}
|
||||
$assigns['pages']['pidRecordTitle'] = BackendUtility::getRecordTitle('pages', $pidPageInfo, true);
|
||||
}
|
||||
}
|
||||
// Create the position tree:
|
||||
$assigns['pages']['positionTree'] = $posMap->positionTree($this->page_id, $pageInfo, $this->perms_clause, $this->R_URI);
|
||||
}
|
||||
}
|
||||
// IF the table is "tt_content":
|
||||
if ((string)$this->table === 'tt_content') {
|
||||
// First, get the record:
|
||||
$tt_content_rec = BackendUtility::getRecord('tt_content', $this->moveUid);
|
||||
// ?
|
||||
if (!$this->input_moveUid) {
|
||||
$this->page_id = $tt_content_rec['pid'];
|
||||
}
|
||||
// Checking if the parent page is readable:
|
||||
$pageInfo = BackendUtility::readPageAccess($this->page_id, $this->perms_clause);
|
||||
if (is_array($pageInfo) && $backendUser->isInWebMount($pageInfo['pid'], $this->perms_clause)) {
|
||||
// Initialize the position map:
|
||||
$posMap = GeneralUtility::makeInstance(ContentMovingPagePositionMap::class);
|
||||
$posMap->moveOrCopy = $this->makeCopy ? 'copy' : 'move';
|
||||
$posMap->moveUid = $this->moveUid;
|
||||
$posMap->cur_sys_language = $this->sys_language;
|
||||
// Headerline for the parent page: Icon, record title:
|
||||
$assigns['ttContent']['pageInfo'] = $pageInfo;
|
||||
$assigns['ttContent']['recordTooltip'] = BackendUtility::getRecordToolTip($pageInfo, 'pages');
|
||||
$assigns['ttContent']['recordTitle'] = BackendUtility::getRecordTitle('pages', $pageInfo, true);
|
||||
$colPosArray = GeneralUtility::callUserFunction(\TYPO3\CMS\Backend\View\BackendLayoutView::class . '->getColPosListItemsParsed', $this->page_id, $this);
|
||||
$colPosIds = [];
|
||||
foreach ($colPosArray as $colPos) {
|
||||
$colPosIds[] = $colPos[1];
|
||||
}
|
||||
// Removing duplicates, if any
|
||||
$colPosList = implode(',', array_unique($colPosIds));
|
||||
// Adding parent page-header and the content element columns from position-map:
|
||||
$assigns['ttContent']['contentElementColumns'] = $posMap->printContentElementColumns($this->page_id, $this->moveUid, $colPosList, 1, $this->R_URI);
|
||||
// Print a "go-up" link IF there is a real parent page (and if the user has read-access to that page).
|
||||
if ($pageInfo['pid']) {
|
||||
$pidPageInfo = BackendUtility::readPageAccess($pageInfo['pid'], $this->perms_clause);
|
||||
if (is_array($pidPageInfo)) {
|
||||
if ($backendUser->isInWebMount($pidPageInfo['pid'], $this->perms_clause)) {
|
||||
$assigns['ttContent']['goUpUrl'] = GeneralUtility::linkThisScript([
|
||||
'uid' => (int)$pageInfo['pid'],
|
||||
'moveUid' => $this->moveUid
|
||||
]);
|
||||
} else {
|
||||
$assigns['ttContent']['pidPageInfo'] = $pidPageInfo;
|
||||
}
|
||||
$assigns['ttContent']['pidRecordTitle'] = BackendUtility::getRecordTitle('pages', $pidPageInfo, true);
|
||||
}
|
||||
}
|
||||
// Create the position tree (for pages):
|
||||
$assigns['ttContent']['positionTree'] = $posMap->positionTree($this->page_id, $pageInfo, $this->perms_clause, $this->R_URI);
|
||||
}
|
||||
}
|
||||
// Rendering of the output via fluid
|
||||
$view = GeneralUtility::makeInstance(StandaloneView::class);
|
||||
$view->setTemplateRootPaths([GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates')]);
|
||||
$view->setPartialRootPaths([GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Partials')]);
|
||||
$view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName(
|
||||
'EXT:backend/Resources/Private/Templates/ContentElement/MoveElement.html'
|
||||
));
|
||||
$view->assignMultiple($assigns);
|
||||
$this->content .= $view->render();
|
||||
}
|
||||
|
||||
// Setting up the buttons and markers for docheader
|
||||
$this->getButtons();
|
||||
// Build the <body> for the module
|
||||
$this->moduleTemplate->setTitle($lang->getLL('movingElement'));
|
||||
$this->moduleTemplate->setContent($this->content);
|
||||
|
||||
$this->content = $this->moduleTemplate->renderContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creating the module output.
|
||||
*
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
|
||||
*/
|
||||
public function main()
|
||||
{
|
||||
trigger_error('MoveElementController->main() will be replaced by protected method renderContent() in TYPO3 v10.0. Do not call from other extension.', E_USER_DEPRECATED);
|
||||
$this->renderContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the panel of buttons for submitting the form or otherwise perform operations.
|
||||
*/
|
||||
protected function getButtons()
|
||||
{
|
||||
$buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
|
||||
if ($this->page_id) {
|
||||
if ((string)$this->table === 'pages') {
|
||||
$cshButton = $buttonBar->makeHelpButton()
|
||||
->setModuleName('xMOD_csh_corebe')
|
||||
->setFieldName('move_el_pages');
|
||||
$buttonBar->addButton($cshButton);
|
||||
} elseif ((string)$this->table === 'tt_content') {
|
||||
$cshButton = $buttonBar->makeHelpButton()
|
||||
->setModuleName('xMOD_csh_corebe')
|
||||
->setFieldName('move_el_cs');
|
||||
$buttonBar->addButton($cshButton);
|
||||
}
|
||||
|
||||
if ($this->R_URI) {
|
||||
$backButton = $buttonBar->makeLinkButton()
|
||||
->setHref($this->R_URI)
|
||||
->setTitle($this->getLanguageService()->getLL('goBack'))
|
||||
->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
|
||||
'actions-view-go-back',
|
||||
Icon::SIZE_SMALL
|
||||
));
|
||||
$buttonBar->addButton($backButton);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LanguageService
|
||||
*/
|
||||
protected function getLanguageService(): LanguageService
|
||||
{
|
||||
return $GLOBALS['LANG'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BackendUserAuthentication
|
||||
*/
|
||||
protected function getBackendUser(): BackendUserAuthentication
|
||||
{
|
||||
return $GLOBALS['BE_USER'];
|
||||
}
|
||||
}
|
||||
+744
@@ -0,0 +1,744 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
namespace TYPO3\CMS\Backend\Controller\ContentElement;
|
||||
|
||||
/*
|
||||
* 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\DocumentTemplate;
|
||||
use TYPO3\CMS\Backend\Template\ModuleTemplate;
|
||||
use TYPO3\CMS\Backend\Tree\View\ContentCreationPagePositionMap;
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
use TYPO3\CMS\Backend\View\BackendLayoutView;
|
||||
use TYPO3\CMS\Backend\Wizard\NewContentElementWizardHookInterface;
|
||||
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
|
||||
use TYPO3\CMS\Core\Compatibility\PublicPropertyDeprecationTrait;
|
||||
use TYPO3\CMS\Core\Http\HtmlResponse;
|
||||
use TYPO3\CMS\Core\Imaging\Icon;
|
||||
use TYPO3\CMS\Core\Localization\LanguageService;
|
||||
use TYPO3\CMS\Core\Service\DependencyOrderingService;
|
||||
use TYPO3\CMS\Core\Type\Bitmask\Permission;
|
||||
use TYPO3\CMS\Core\Utility\ArrayUtility;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\HttpUtility;
|
||||
use TYPO3\CMS\Fluid\View\StandaloneView;
|
||||
|
||||
/**
|
||||
* Script Class for the New Content element wizard
|
||||
* @internal This class is a specific Backend controller implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class NewContentElementController
|
||||
{
|
||||
use PublicPropertyDeprecationTrait;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $deprecatedPublicProperties = [
|
||||
'id' => 'Using $id of NewContentElementController from the outside is discouraged as this variable is used for internal storage.',
|
||||
'sys_language' => 'Using $sys_language of NewContentElementController from the outside is discouraged as this variable is used for internal storage.',
|
||||
'R_URI' => 'Using $R_URI of NewContentElementController from the outside is discouraged as this variable is used for internal storage.',
|
||||
'colPos' => 'Using $colPos of NewContentElementController from the outside is discouraged as this variable is used for internal storage.',
|
||||
'uid_pid' => 'Using $uid_pid of NewContentElementController from the outside is discouraged as this variable is used for internal storage.',
|
||||
'modTSconfig' => 'Using $modTSconfig of NewContentElementController from the outside is discouraged as this variable is used for internal storage.',
|
||||
'doc' => 'Using $doc of NewContentElementController from the outside is discouraged as this variable is used for internal storage.',
|
||||
'content' => 'Using $content of NewContentElementController from the outside is discouraged as this variable is used for internal storage.',
|
||||
'access' => 'Using $access of NewContentElementController from the outside is discouraged as this variable is used for internal storage.',
|
||||
'config' => 'Using $config of NewContentElementController from the outside is discouraged as this variable is used for internal storage.',
|
||||
];
|
||||
|
||||
/**
|
||||
* Page id
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* Sys language
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $sys_language = 0;
|
||||
|
||||
/**
|
||||
* Return URL.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $R_URI = '';
|
||||
|
||||
/**
|
||||
* If set, the content is destined for a specific column.
|
||||
*
|
||||
* @var int|null
|
||||
*/
|
||||
protected $colPos;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $uid_pid;
|
||||
|
||||
/**
|
||||
* Module TSconfig.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $modTSconfig = [];
|
||||
|
||||
/**
|
||||
* Internal backend template object
|
||||
*
|
||||
* @var DocumentTemplate
|
||||
*/
|
||||
protected $doc;
|
||||
|
||||
/**
|
||||
* Used to accumulate the content of the module.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $content;
|
||||
|
||||
/**
|
||||
* Access boolean.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $access;
|
||||
|
||||
/**
|
||||
* config of the wizard
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $pageInfo;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $onClickEvent;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $MCONF;
|
||||
|
||||
/**
|
||||
* @var StandaloneView
|
||||
*/
|
||||
protected $view;
|
||||
|
||||
/**
|
||||
* @var StandaloneView
|
||||
*/
|
||||
protected $menuItemView;
|
||||
|
||||
/**
|
||||
* ModuleTemplate object
|
||||
*
|
||||
* @var ModuleTemplate
|
||||
*/
|
||||
protected $moduleTemplate;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
|
||||
$GLOBALS['SOBE'] = $this;
|
||||
$this->view = $this->getFluidTemplateObject();
|
||||
$this->menuItemView = $this->getFluidTemplateObject('MenuItem.html');
|
||||
|
||||
// @deprecated since TYPO3 v9, will be obsolete in TYPO3 v10.0 with removal of init()
|
||||
$request = $GLOBALS['TYPO3_REQUEST'];
|
||||
// @deprecated since TYPO3 v9, will be moved out of __construct() in TYPO3 v10.0
|
||||
$this->init($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor, initializing internal variables.
|
||||
*
|
||||
* @param ServerRequestInterface|null $request
|
||||
*/
|
||||
public function init(ServerRequestInterface $request = null)
|
||||
{
|
||||
if ($request === null) {
|
||||
// Method signature in TYPO3 v10.0: protected function init(ServerRequestInterface $request)
|
||||
trigger_error('NewContentElementController->init() will be set to protected in TYPO3 v10.0. Do not call from other extension.', E_USER_DEPRECATED);
|
||||
$request = $GLOBALS['TYPO3_REQUEST'];
|
||||
}
|
||||
|
||||
$lang = $this->getLanguageService();
|
||||
$lang->includeLLFile('EXT:core/Resources/Private/Language/locallang_misc.xlf');
|
||||
$LOCAL_LANG_orig = $GLOBALS['LOCAL_LANG'];
|
||||
$lang->includeLLFile('EXT:backend/Resources/Private/Language/locallang_db_new_content_el.xlf');
|
||||
ArrayUtility::mergeRecursiveWithOverrule($LOCAL_LANG_orig, $GLOBALS['LOCAL_LANG']);
|
||||
$GLOBALS['LOCAL_LANG'] = $LOCAL_LANG_orig;
|
||||
|
||||
$parsedBody = $request->getParsedBody();
|
||||
$queryParams = $request->getQueryParams();
|
||||
|
||||
// Setting internal vars:
|
||||
$this->id = (int)($parsedBody['id'] ?? $queryParams['id'] ?? 0);
|
||||
$this->sys_language = (int)($parsedBody['sys_language_uid'] ?? $queryParams['sys_language_uid'] ?? 0);
|
||||
$this->R_URI = GeneralUtility::sanitizeLocalUrl($parsedBody['returnUrl'] ?? $queryParams['returnUrl'] ?? '');
|
||||
$colPos = $parsedBody['colPos'] ?? $queryParams['colPos'] ?? null;
|
||||
$this->colPos = $colPos === null ? null : (int)$colPos;
|
||||
$this->uid_pid = (int)($parsedBody['uid_pid'] ?? $queryParams['uid_pid'] ?? 0);
|
||||
$this->MCONF['name'] = 'xMOD_db_new_content_el';
|
||||
$this->modTSconfig['properties'] = BackendUtility::getPagesTSconfig($this->id)['mod.']['wizards.']['newContentElement.'] ?? [];
|
||||
$config = BackendUtility::getPagesTSconfig($this->id);
|
||||
$this->config = $config['mod.']['wizards.']['newContentElement.'];
|
||||
// Starting the document template object:
|
||||
// We keep this here in case somebody relies on it in a hook or alike
|
||||
$this->doc = GeneralUtility::makeInstance(DocumentTemplate::class);
|
||||
// Setting up the context sensitive menu:
|
||||
$this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ContextMenu');
|
||||
// Getting the current page and receiving access information (used in main())
|
||||
$perms_clause = $this->getBackendUser()->getPagePermsClause(Permission::PAGE_SHOW);
|
||||
$this->pageInfo = BackendUtility::readPageAccess($this->id, $perms_clause);
|
||||
$this->access = is_array($this->pageInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 the current request
|
||||
* @return ResponseInterface the response with the content
|
||||
*/
|
||||
public function mainAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$this->prepareContent('window');
|
||||
$this->moduleTemplate->setContent($this->content);
|
||||
return new HtmlResponse($this->moduleTemplate->renderContent());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 the current request
|
||||
* @return ResponseInterface the response with the content
|
||||
*/
|
||||
public function wizardAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$this->prepareContent('list_frame');
|
||||
return new HtmlResponse($this->content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creating the module output.
|
||||
*
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3v10 without substitute
|
||||
*/
|
||||
public function main()
|
||||
{
|
||||
trigger_error('NewContentElementController->main() will be replaced by protected method prepareContent() in TYPO3 v10.0. Do not call from other extension.', E_USER_DEPRECATED);
|
||||
$this->prepareContent('window');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the array of elements in the wizard display.
|
||||
* For the plugin section there is support for adding elements there from a global variable.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function wizardArray()
|
||||
{
|
||||
trigger_error('NewContentElementController->wizardArray() will be replaced by protected method getWizards() in TYPO3 v10.0. Do not call from other extension.', E_USER_DEPRECATED);
|
||||
return $this->getWizards();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $wizardElements
|
||||
* @return array
|
||||
*/
|
||||
public function wizard_appendWizards($wizardElements)
|
||||
{
|
||||
trigger_error('NewContentElementController->wizard_appendWizards() will be replaced by protected method getAppendWizards() in TYPO3 v10.0. Do not call from other extension.', E_USER_DEPRECATED);
|
||||
return $this->getAppendWizards($wizardElements);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $groupKey Not used
|
||||
* @param string $itemKey Not used
|
||||
* @param array $itemConf
|
||||
* @return array
|
||||
*/
|
||||
public function wizard_getItem($groupKey, $itemKey, $itemConf)
|
||||
{
|
||||
trigger_error('NewContentElementController->wizard_getItem() will be replaced by protected method getWizardItem() in TYPO3 v10.0. Do not call from other extension.', E_USER_DEPRECATED);
|
||||
return $this->getWizardItem($groupKey, $itemKey, $itemConf);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $groupKey Not used
|
||||
* @param array $wizardGroup
|
||||
* @return array
|
||||
*/
|
||||
public function wizard_getGroupHeader($groupKey, $wizardGroup)
|
||||
{
|
||||
trigger_error('NewContentElementController->wizard_getGroupHeader() will be replaced by protected method getWizardGroupHeader() in TYPO3 v10.0. Do not call from other extension.', E_USER_DEPRECATED);
|
||||
return $this->getWizardGroupHeader($wizardGroup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the array for elements which might contain unallowed default values and will unset them!
|
||||
* Looks for the "tt_content_defValues" key in each element and if found it will traverse that array as fieldname /
|
||||
* value pairs and check.
|
||||
* The values will be added to the "params" key of the array (which should probably be unset or empty by default).
|
||||
*
|
||||
* @param array $wizardItems Wizard items, passed by reference
|
||||
*/
|
||||
public function removeInvalidElements(&$wizardItems)
|
||||
{
|
||||
trigger_error('NewContentElementController->removeInvalidElements() will be replaced by protected method removeInvalidWizardItems() in TYPO3 v10.0. Do not call from other extension.', E_USER_DEPRECATED);
|
||||
$this->removeInvalidWizardItems($wizardItems);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creating the module output.
|
||||
*
|
||||
* @param string $clientContext JavaScript client context to be used
|
||||
* + 'window', legacy if rendered in current document
|
||||
* + 'list_frame', in case rendered in global modal
|
||||
*
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
protected function prepareContent(string $clientContext): void
|
||||
{
|
||||
$hasAccess = true;
|
||||
if ($this->id && $this->access) {
|
||||
|
||||
// Init position map object:
|
||||
$posMap = GeneralUtility::makeInstance(
|
||||
ContentCreationPagePositionMap::class,
|
||||
null,
|
||||
$clientContext
|
||||
);
|
||||
$posMap->cur_sys_language = $this->sys_language;
|
||||
// If a column is pre-set:
|
||||
if (isset($this->colPos)) {
|
||||
if ($this->uid_pid < 0) {
|
||||
$row = [];
|
||||
$row['uid'] = abs($this->uid_pid);
|
||||
} else {
|
||||
$row = '';
|
||||
}
|
||||
$this->onClickEvent = $posMap->onClickInsertRecord(
|
||||
$row,
|
||||
$this->colPos,
|
||||
'',
|
||||
$this->uid_pid,
|
||||
$this->sys_language
|
||||
);
|
||||
} else {
|
||||
$this->onClickEvent = '';
|
||||
}
|
||||
// ***************************
|
||||
// Creating content
|
||||
// ***************************
|
||||
// Wizard
|
||||
$wizardItems = $this->getWizards();
|
||||
// Wrapper for wizards
|
||||
// Hook for manipulating wizardItems, wrapper, onClickEvent etc.
|
||||
foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms']['db_new_content_el']['wizardItemsHook'] ?? [] as $className) {
|
||||
$hookObject = GeneralUtility::makeInstance($className);
|
||||
if (!$hookObject instanceof NewContentElementWizardHookInterface) {
|
||||
throw new \UnexpectedValueException(
|
||||
$className . ' must implement interface ' . NewContentElementWizardHookInterface::class,
|
||||
1227834741
|
||||
);
|
||||
}
|
||||
$hookObject->manipulateWizardItems($wizardItems, $this);
|
||||
}
|
||||
|
||||
// Traverse items for the wizard.
|
||||
// An item is either a header or an item rendered with a radio button and title/description and icon:
|
||||
$cc = ($key = 0);
|
||||
$menuItems = [];
|
||||
|
||||
$this->view->assignMultiple([
|
||||
'hasClickEvent' => $this->onClickEvent !== '',
|
||||
'onClickEvent' => 'function goToalt_doc() { ' . $this->onClickEvent . '}',
|
||||
]);
|
||||
|
||||
foreach ($wizardItems as $wizardKey => $wInfo) {
|
||||
$wizardOnClick = '';
|
||||
if (isset($wInfo['header'])) {
|
||||
$menuItems[] = [
|
||||
'label' => $wInfo['header'] ?: '-',
|
||||
'content' => ''
|
||||
];
|
||||
$key = count($menuItems) - 1;
|
||||
} else {
|
||||
if (!$this->onClickEvent) {
|
||||
// Radio button:
|
||||
$wizardOnClick = 'document.editForm.defValues.value=unescape(' . GeneralUtility::quoteJSvalue(rawurlencode($wInfo['params'])) . '); window.location.hash=\'#sel2\';';
|
||||
// Onclick action for icon/title:
|
||||
$aOnClick = 'document.getElementsByName(\'tempB\')[' . $cc . '].checked=1;' . $wizardOnClick . 'return false;';
|
||||
} else {
|
||||
$aOnClick = "document.editForm.defValues.value=unescape('" . rawurlencode($wInfo['params']) . "');goToalt_doc();";
|
||||
}
|
||||
|
||||
$icon = $this->moduleTemplate->getIconFactory()->getIcon($wInfo['iconIdentifier'])->render();
|
||||
|
||||
$this->menuItemView->assignMultiple([
|
||||
'onClickEvent' => $this->onClickEvent,
|
||||
'aOnClick' => $aOnClick,
|
||||
'wizardInformation' => $wInfo,
|
||||
'icon' => $icon,
|
||||
'wizardOnClick' => $wizardOnClick,
|
||||
'wizardKey' => $wizardKey
|
||||
]);
|
||||
$menuItems[$key]['content'] .= $this->menuItemView->render();
|
||||
$cc++;
|
||||
}
|
||||
}
|
||||
|
||||
$this->view->assign('renderedTabs', $this->moduleTemplate->getDynamicTabMenu(
|
||||
$menuItems,
|
||||
'new-content-element-wizard'
|
||||
));
|
||||
|
||||
// If the user must also select a column:
|
||||
if (!$this->onClickEvent) {
|
||||
|
||||
// Load SHARED page-TSconfig settings and retrieve column list from there, if applicable:
|
||||
$colPosArray = GeneralUtility::callUserFunction(
|
||||
BackendLayoutView::class . '->getColPosListItemsParsed',
|
||||
$this->id,
|
||||
$this
|
||||
);
|
||||
$colPosIds = array_column($colPosArray, 1);
|
||||
// Removing duplicates, if any
|
||||
$colPosList = implode(',', array_unique(array_map('intval', $colPosIds)));
|
||||
// Finally, add the content of the column selector to the content:
|
||||
$this->view->assign('posMap', $posMap->printContentElementColumns($this->id, 0, $colPosList, 1, $this->R_URI));
|
||||
}
|
||||
} else {
|
||||
// In case of no access:
|
||||
$hasAccess = false;
|
||||
}
|
||||
$this->view->assign('hasAccess', $hasAccess);
|
||||
|
||||
$this->content = $this->view->render();
|
||||
// Setting up the buttons and markers for docheader
|
||||
$this->getButtons();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the panel of buttons for submitting the form or otherwise perform operations.
|
||||
*/
|
||||
protected function getButtons()
|
||||
{
|
||||
$buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
|
||||
if ($this->R_URI) {
|
||||
$backButton = $buttonBar->makeLinkButton()
|
||||
->setHref($this->R_URI)
|
||||
->setTitle($this->getLanguageService()->getLL('goBack'))
|
||||
->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
|
||||
'actions-view-go-back',
|
||||
Icon::SIZE_SMALL
|
||||
));
|
||||
$buttonBar->addButton($backButton);
|
||||
}
|
||||
$cshButton = $buttonBar->makeHelpButton()->setModuleName('xMOD_csh_corebe')->setFieldName('new_ce');
|
||||
$buttonBar->addButton($cshButton);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the array of elements in the wizard display.
|
||||
* For the plugin section there is support for adding elements there from a global variable.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getWizards(): array
|
||||
{
|
||||
$wizardItems = [];
|
||||
if (is_array($this->config)) {
|
||||
$wizards = $this->config['wizardItems.'] ?? [];
|
||||
$appendWizards = $this->getAppendWizards($wizards['elements.'] ?? []);
|
||||
if (is_array($wizards)) {
|
||||
foreach ($wizards as $groupKey => $wizardGroup) {
|
||||
$this->prepareDependencyOrdering($wizards[$groupKey], 'before');
|
||||
$this->prepareDependencyOrdering($wizards[$groupKey], 'after');
|
||||
}
|
||||
$wizards = GeneralUtility::makeInstance(DependencyOrderingService::class)->orderByDependencies($wizards);
|
||||
|
||||
foreach ($wizards as $groupKey => $wizardGroup) {
|
||||
$groupKey = rtrim($groupKey, '.');
|
||||
$showItems = GeneralUtility::trimExplode(',', $wizardGroup['show'], true);
|
||||
$showAll = in_array('*', $showItems, true);
|
||||
$groupItems = [];
|
||||
if (is_array($appendWizards[$groupKey . '.']['elements.'])) {
|
||||
$wizardElements = array_merge((array)$wizardGroup['elements.'], $appendWizards[$groupKey . '.']['elements.']);
|
||||
} else {
|
||||
$wizardElements = $wizardGroup['elements.'];
|
||||
}
|
||||
if (is_array($wizardElements)) {
|
||||
foreach ($wizardElements as $itemKey => $itemConf) {
|
||||
$itemKey = rtrim($itemKey, '.');
|
||||
if ($showAll || in_array($itemKey, $showItems)) {
|
||||
$tmpItem = $this->getWizardItem($groupKey, $itemKey, $itemConf);
|
||||
if ($tmpItem) {
|
||||
$groupItems[$groupKey . '_' . $itemKey] = $tmpItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($groupItems)) {
|
||||
$wizardItems[$groupKey] = $this->getWizardGroupHeader($wizardGroup);
|
||||
$wizardItems = array_merge($wizardItems, $groupItems);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Remove elements where preset values are not allowed:
|
||||
$this->removeInvalidWizardItems($wizardItems);
|
||||
return $wizardItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $wizardElements
|
||||
* @return array
|
||||
*/
|
||||
protected function getAppendWizards(array $wizardElements): array
|
||||
{
|
||||
if (!is_array($wizardElements)) {
|
||||
$wizardElements = [];
|
||||
}
|
||||
if (is_array($GLOBALS['TBE_MODULES_EXT']['xMOD_db_new_content_el']['addElClasses'])) {
|
||||
foreach ($GLOBALS['TBE_MODULES_EXT']['xMOD_db_new_content_el']['addElClasses'] as $class => $path) {
|
||||
if (!class_exists($class) && file_exists($path)) {
|
||||
require_once $path;
|
||||
}
|
||||
$modObj = GeneralUtility::makeInstance($class);
|
||||
if (method_exists($modObj, 'proc')) {
|
||||
$wizardElements = $modObj->proc($wizardElements);
|
||||
}
|
||||
}
|
||||
}
|
||||
$returnElements = [];
|
||||
foreach ($wizardElements as $key => $wizardItem) {
|
||||
preg_match('/^[a-zA-Z0-9]+_/', $key, $group);
|
||||
$wizardGroup = $group[0] ? substr($group[0], 0, -1) . '.' : $key;
|
||||
$returnElements[$wizardGroup]['elements.'][substr($key, strlen($wizardGroup)) . '.'] = $wizardItem;
|
||||
}
|
||||
return $returnElements;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $groupKey Not used
|
||||
* @param string $itemKey Not used
|
||||
* @param array $itemConf
|
||||
* @return array
|
||||
*/
|
||||
protected function getWizardItem(string $groupKey, string $itemKey, array $itemConf): array
|
||||
{
|
||||
$itemConf['title'] = $this->getLanguageService()->sL($itemConf['title']);
|
||||
$itemConf['description'] = $this->getLanguageService()->sL($itemConf['description']);
|
||||
$itemConf['tt_content_defValues'] = $itemConf['tt_content_defValues.'];
|
||||
unset($itemConf['tt_content_defValues.']);
|
||||
return $itemConf;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $wizardGroup
|
||||
* @return array
|
||||
*/
|
||||
protected function getWizardGroupHeader(array $wizardGroup): array
|
||||
{
|
||||
return [
|
||||
'header' => $this->getLanguageService()->sL($wizardGroup['header'])
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the array for elements which might contain unallowed default values and will unset them!
|
||||
* Looks for the "tt_content_defValues" key in each element and if found it will traverse that array as fieldname /
|
||||
* value pairs and check.
|
||||
* The values will be added to the "params" key of the array (which should probably be unset or empty by default).
|
||||
*
|
||||
* @param array $wizardItems Wizard items, passed by reference
|
||||
*/
|
||||
protected function removeInvalidWizardItems(array &$wizardItems): void
|
||||
{
|
||||
// Get TCEFORM from TSconfig of current page
|
||||
$row = ['pid' => $this->id];
|
||||
$TCEFORM_TSconfig = BackendUtility::getTCEFORM_TSconfig('tt_content', $row);
|
||||
$headersUsed = [];
|
||||
// Traverse wizard items:
|
||||
foreach ($wizardItems as $key => $cfg) {
|
||||
// Exploding parameter string, if any (old style)
|
||||
if ($wizardItems[$key]['params']) {
|
||||
// Explode GET vars recursively
|
||||
$tempGetVars = [];
|
||||
parse_str($wizardItems[$key]['params'], $tempGetVars);
|
||||
// If tt_content values are set, merge them into the tt_content_defValues array,
|
||||
// unset them from $tempGetVars and re-implode $tempGetVars into the param string
|
||||
// (in case remaining parameters are around).
|
||||
if (is_array($tempGetVars['defVals']['tt_content'])) {
|
||||
$wizardItems[$key]['tt_content_defValues'] = array_merge(
|
||||
is_array($wizardItems[$key]['tt_content_defValues']) ? $wizardItems[$key]['tt_content_defValues'] : [],
|
||||
$tempGetVars['defVals']['tt_content']
|
||||
);
|
||||
unset($tempGetVars['defVals']['tt_content']);
|
||||
$wizardItems[$key]['params'] = HttpUtility::buildQueryString($tempGetVars, '&');
|
||||
}
|
||||
}
|
||||
// If tt_content_defValues are defined...:
|
||||
if (is_array($wizardItems[$key]['tt_content_defValues'])) {
|
||||
$backendUser = $this->getBackendUser();
|
||||
// Traverse field values:
|
||||
foreach ($wizardItems[$key]['tt_content_defValues'] as $fN => $fV) {
|
||||
if (is_array($GLOBALS['TCA']['tt_content']['columns'][$fN])) {
|
||||
// Get information about if the field value is OK:
|
||||
$config = &$GLOBALS['TCA']['tt_content']['columns'][$fN]['config'];
|
||||
$authModeDeny = $config['type'] === 'select' && $config['authMode']
|
||||
&& !$backendUser->checkAuthMode('tt_content', $fN, $fV, $config['authMode']);
|
||||
// explode TSconfig keys only as needed
|
||||
if (!isset($removeItems[$fN]) && isset($TCEFORM_TSconfig[$fN]['removeItems']) && $TCEFORM_TSconfig[$fN]['removeItems'] !== '') {
|
||||
$removeItems[$fN] = array_flip(GeneralUtility::trimExplode(
|
||||
',',
|
||||
$TCEFORM_TSconfig[$fN]['removeItems'],
|
||||
true
|
||||
));
|
||||
}
|
||||
if (!isset($keepItems[$fN]) && isset($TCEFORM_TSconfig[$fN]['keepItems']) && $TCEFORM_TSconfig[$fN]['keepItems'] !== '') {
|
||||
$keepItems[$fN] = array_flip(GeneralUtility::trimExplode(
|
||||
',',
|
||||
$TCEFORM_TSconfig[$fN]['keepItems'],
|
||||
true
|
||||
));
|
||||
}
|
||||
$isNotInKeepItems = !empty($keepItems[$fN]) && !isset($keepItems[$fN][$fV]);
|
||||
if ($authModeDeny || ($fN === 'CType' && (isset($removeItems[$fN][$fV]) || $isNotInKeepItems))) {
|
||||
// Remove element all together:
|
||||
unset($wizardItems[$key]);
|
||||
break;
|
||||
}
|
||||
// Add the parameter:
|
||||
$wizardItems[$key]['params'] .= '&defVals[tt_content][' . $fN . ']=' . rawurlencode($this->getLanguageService()->sL($fV));
|
||||
$tmp = explode('_', $key);
|
||||
$headersUsed[$tmp[0]] = $tmp[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// remove headers without elements
|
||||
foreach ($wizardItems as $key => $cfg) {
|
||||
$tmp = explode('_', $key);
|
||||
if ($tmp[0] && !$tmp[1] && !in_array($tmp[0], $headersUsed)) {
|
||||
unset($wizardItems[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a wizard tab configuration for sorting.
|
||||
*
|
||||
* @param array $wizardGroup TypoScript wizard tab configuration
|
||||
* @param string $key Which array key should be prepared
|
||||
*/
|
||||
protected function prepareDependencyOrdering(&$wizardGroup, $key)
|
||||
{
|
||||
if (isset($wizardGroup[$key])) {
|
||||
$wizardGroup[$key] = GeneralUtility::trimExplode(',', $wizardGroup[$key]);
|
||||
$wizardGroup[$key] = array_map(function ($s) {
|
||||
return $s . '.';
|
||||
}, $wizardGroup[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LanguageService
|
||||
*/
|
||||
protected function getLanguageService(): LanguageService
|
||||
{
|
||||
return $GLOBALS['LANG'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BackendUserAuthentication
|
||||
*/
|
||||
protected function getBackendUser(): BackendUserAuthentication
|
||||
{
|
||||
return $GLOBALS['BE_USER'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
* @return StandaloneView
|
||||
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\InvalidExtensionNameException
|
||||
*/
|
||||
protected function getFluidTemplateObject(string $filename = 'Main.html'): StandaloneView
|
||||
{
|
||||
/** @var StandaloneView $view */
|
||||
$view = GeneralUtility::makeInstance(StandaloneView::class);
|
||||
$view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates/NewContentElement/' . $filename));
|
||||
$view->getRequest()->setControllerExtensionName('Backend');
|
||||
return $view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide information about the current page making use of the wizard
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPageInfo(): array
|
||||
{
|
||||
return $this->pageInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide information about the column position of the button that triggered the wizard
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getColPos(): ?int
|
||||
{
|
||||
return $this->colPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide information about the language used while triggering the wizard
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getSysLanguage(): int
|
||||
{
|
||||
return $this->sys_language;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide information about the element to position the new element after (uid) or into (pid)
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getUidPid(): int
|
||||
{
|
||||
return $this->uid_pid;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user