first commit

This commit is contained in:
salam committed 2026-10-19 08:18:39 +02:00
commit 1218dd1517
12716 files changed
+989233

No files matched your search

@@ -0,0 +1,315 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Backend\Controller\File;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Routing\UriBuilder;
use TYPO3\CMS\Backend\Template\ModuleTemplate;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Compatibility\PublicMethodDeprecationTrait;
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\Resource\Exception\InsufficientFolderAccessPermissionsException;
use TYPO3\CMS\Core\Resource\OnlineMedia\Helpers\OnlineMediaHelperRegistry;
use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\MathUtility;
use TYPO3\CMS\Fluid\View\StandaloneView;
/**
* Script class for the create-new script
*
* Displays forms for creating folders (1 to 10), a media asset or a new file.
* @internal This class is a specific Backend controller implementation and is not considered part of the Public TYPO3 API.
*/
class CreateFolderController
{
use PublicMethodDeprecationTrait;
use PublicPropertyDeprecationTrait;
/**
* @var array
*/
private $deprecatedPublicMethods = [
'main' => 'Using CreateFolderController::main() is deprecated and will not be possible anymore in TYPO3 v10.0.',
];
/**
* @var array
*/
private $deprecatedPublicProperties = [
'number' => 'Using $number of class CreateFolderController from outside is discouraged, as this variable is only used for internal storage.',
'folderNumber' => 'Using $folderNumber of class CreateFolderController from outside is discouraged, as this variable is only used for internal storage.',
'target' => 'Using $target of class CreateFolderController from outside is discouraged, as this variable is only used for internal storage.',
'content' => 'Using $content of class CreateFolderController from outside is discouraged, as this variable is only used for internal storage.',
'returnUrl' => 'Using $content of class CreateFolderController from outside is discouraged, as this variable is only used for internal storage.',
'title' => 'Using $content of class CreateFolderController from outside is discouraged, as this variable is only used for internal storage.',
];
/**
* @var int
*/
protected $folderNumber = 10;
/**
* Name of the filemount
*
* @var string
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0, unused
*/
protected $title;
/**
* @var int
*/
protected $number;
/**
* Set with the target path inputted in &target
*
* @var string
*/
protected $target;
/**
* The folder object which is the target directory
*
* @var \TYPO3\CMS\Core\Resource\Folder $folderObject
*/
protected $folderObject;
/**
* Return URL of list module.
*
* @var string
*/
protected $returnUrl;
/**
* @var array
*/
protected $pathInfo;
/**
* Accumulating content
*
* @var string
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0, unused
*/
protected $content;
/**
* ModuleTemplate object
*
* @var ModuleTemplate
*/
protected $moduleTemplate;
/**
* Constructor
*/
public function __construct()
{
$this->moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
// @deprecated since TYPO3 v9, will be moved out of __construct() in TYPO3 v10.0
$this->init($GLOBALS['TYPO3_REQUEST']);
}
/**
* Processes the request, currently everything is handled and put together via "main()"
*
* @param ServerRequestInterface $request the current request
* @return ResponseInterface the response with the content
*/
public function mainAction(ServerRequestInterface $request): ResponseInterface
{
$this->main();
return new HtmlResponse($this->moduleTemplate->renderContent());
}
/**
* @param ServerRequestInterface|null $request
*
* @throws InsufficientFolderAccessPermissionsException
* @throws \RuntimeException
*/
protected function init(ServerRequestInterface $request): void
{
$parsedBody = $request->getParsedBody();
$queryParams = $request->getQueryParams();
$this->number = $parsedBody['number'] ?? $queryParams['number'] ?? 0;
$this->target = ($combinedIdentifier = $parsedBody['target'] ?? $queryParams['target'] ?? '');
$this->returnUrl = GeneralUtility::sanitizeLocalUrl($parsedBody['returnUrl'] ?? $queryParams['returnUrl'] ?? '');
// create the folder object
if ($combinedIdentifier) {
$this->folderObject = ResourceFactory::getInstance()
->getFolderObjectFromCombinedIdentifier($combinedIdentifier);
}
// Cleaning and checking target directory
if (!$this->folderObject) {
$title = $this->getLanguageService()->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:paramError');
$message = $this->getLanguageService()->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:targetNoDir');
throw new \RuntimeException($title . ': ' . $message, 1294586845);
}
if ($this->folderObject->getStorage()->getUid() === 0) {
throw new InsufficientFolderAccessPermissionsException(
'You are not allowed to access folders outside your storages',
1375889838
);
}
$pathInfo = [
'combined_identifier' => $this->folderObject->getCombinedIdentifier(),
];
$uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
$this->moduleTemplate->getDocHeaderComponent()->setMetaInformation($pathInfo);
$this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ContextMenu');
$this->moduleTemplate->addJavaScriptCode(
'CreateFolderInlineJavaScript',
'var path = "' . $this->target . '";
var confirmTitle = '
. GeneralUtility::quoteJSvalue(
$this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_common.xlf:pleaseConfirm')
)
. ';
var confirmText = '
. GeneralUtility::quoteJSvalue(
$this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:mess.redraw')
)
. ';
function reload(a) {
var params = "&target="+encodeURIComponent(path)+"&number="+a+"&returnUrl=' . rawurlencode($this->returnUrl) . '";
var url = \'' . (string)$uriBuilder->buildUriFromRoute('file_newfolder') . '\';
if (!changed) {
window.location.href = url + params;
} else {
var modal = top.TYPO3.Modal.confirm(confirmTitle, confirmText);
modal.on(\'confirm.button.cancel\', function(e) {
top.TYPO3.Modal.currentModal.trigger(\'modal-dismiss\');
});
modal.on(\'confirm.button.ok\', function(e) {
top.TYPO3.Modal.currentModal.trigger(\'modal-dismiss\');
window.location.href = url + params;
});
}
}
function backToList() {
top.goToModule("file_FilelistList");
}
var changed = 0;'
);
}
/**
* Main function, rendering the main module content
*/
protected function main()
{
$lang = $this->getLanguageService();
$assigns = [];
$assigns['target'] = $this->target;
if ($this->folderObject->checkActionPermission('add')) {
$uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
$assigns['moduleUrlTceFile'] = (string)$uriBuilder->buildUriFromRoute('tce_file');
$assigns['cshFileNewFolder'] = BackendUtility::cshItem('xMOD_csh_corebe', 'file_newfolder');
// Making the selector box for the number of concurrent folder-creations
$this->number = MathUtility::forceIntegerInRange($this->number, 1, 10);
for ($a = 1; $a <= $this->folderNumber; $a++) {
$options = [];
$options['value'] = $a;
$options['selected'] = ($this->number == $a ? ' selected="selected"' : '');
$assigns['options'][] = $options;
}
// Making the number of new-folder boxes needed:
for ($a = 0; $a < $this->number; $a++) {
$folder = [];
$folder['this'] = $a;
$folder['next'] = $a + 1;
$assigns['folders'][] = $folder;
}
// Making submit button for folder creation:
$assigns['returnUrl'] = $this->returnUrl;
}
if ($this->folderObject->getStorage()->checkUserActionPermission('add', 'File')) {
$uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
$assigns['moduleUrlOnlineMedia'] = (string)$uriBuilder->buildUriFromRoute('online_media');
$assigns['cshFileNewMedia'] = BackendUtility::cshItem('xMOD_csh_corebe', 'file_newMedia');
// Create a list of allowed file extensions with the readable format "youtube, vimeo" etc.
$fileExtList = [];
$onlineMediaFileExt = OnlineMediaHelperRegistry::getInstance()->getSupportedFileExtensions();
foreach ($onlineMediaFileExt as $fileExt) {
if (GeneralUtility::verifyFilenameAgainstDenyPattern('.' . $fileExt)) {
$fileExtList[] = strtoupper(htmlspecialchars($fileExt));
}
}
$assigns['fileExtList'] = $fileExtList;
$assigns['moduleUrlTceFile'] = (string)$uriBuilder->buildUriFromRoute('tce_file');
$assigns['cshFileNewFile'] = BackendUtility::cshItem('xMOD_csh_corebe', 'file_newfile');
// Create a list of allowed file extensions with a text format "*.txt, *.css" etc.
$fileExtList = [];
$textFileExt = GeneralUtility::trimExplode(',', $GLOBALS['TYPO3_CONF_VARS']['SYS']['textfile_ext'], true);
foreach ($textFileExt as $fileExt) {
if (GeneralUtility::verifyFilenameAgainstDenyPattern('.' . $fileExt)) {
$fileExtList[] = strtoupper(htmlspecialchars($fileExt));
}
}
$assigns['txtFileExtList'] = $fileExtList;
}
$buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
// CSH button
$helpButton = $buttonBar->makeHelpButton()
->setFieldName('file_new')
->setModuleName('xMOD_csh_corebe');
$buttonBar->addButton($helpButton);
// Back
if ($this->returnUrl) {
$backButton = $buttonBar->makeLinkButton()
->setHref($this->returnUrl)
->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.goBack'))
->setIcon($this->moduleTemplate->getIconFactory()->getIcon('actions-view-go-back', Icon::SIZE_SMALL));
$buttonBar->addButton($backButton);
}
// 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/File/CreateFolder.html'
));
$view->assignMultiple($assigns);
$this->moduleTemplate->setContent($view->render());
}
/**
* Returns LanguageService
*
* @return LanguageService
*/
protected function getLanguageService(): LanguageService
{
return $GLOBALS['LANG'];
}
}
@@ -0,0 +1,425 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Backend\Controller\File;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Form\FormResultCompiler;
use TYPO3\CMS\Backend\Form\NodeFactory;
use TYPO3\CMS\Backend\Routing\UriBuilder;
use TYPO3\CMS\Backend\Template\Components\ButtonBar;
use TYPO3\CMS\Backend\Template\DocumentTemplate;
use TYPO3\CMS\Backend\Template\ModuleTemplate;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Compatibility\PublicMethodDeprecationTrait;
use TYPO3\CMS\Core\Compatibility\PublicPropertyDeprecationTrait;
use TYPO3\CMS\Core\Http\HtmlResponse;
use TYPO3\CMS\Core\Http\RedirectResponse;
use TYPO3\CMS\Core\Imaging\Icon;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Messaging\FlashMessage;
use TYPO3\CMS\Core\Messaging\FlashMessageService;
use TYPO3\CMS\Core\Resource\Exception\InsufficientFileAccessPermissionsException;
use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\HttpUtility;
use TYPO3\CMS\Fluid\View\StandaloneView;
/**
* Script Class for rendering the file editing screen
* @internal This class is a specific Backend controller implementation and is not considered part of the Public TYPO3 API.
*/
class EditFileController
{
use PublicMethodDeprecationTrait;
use PublicPropertyDeprecationTrait;
/**
* @var array
*/
private $deprecatedPublicMethods = [
'main' => 'Using EditFileController::main() is deprecated and will not be possible anymore in TYPO3 v10.0.',
];
/**
* @var array
*/
private $deprecatedPublicProperties = [
'origTarget' => 'Using $origTarget of class EditFileController from outside is discouraged, as this variable is only used for internal storage.',
'target' => 'Using $target of class EditFileController from outside is discouraged, as this variable is only used for internal storage.',
'returnUrl' => 'Using $returnUrl of class EditFileController from outside is discouraged, as this variable is only used for internal storage.',
'content' => 'Using $content of class EditFileController from outside is discouraged, as this variable is only used for internal storage.',
'title' => 'Using $title of class EditFileController from outside is discouraged, as this variable is only used for internal storage.',
'doc' => 'Using $doc of class EditFileController from outside is discouraged, as this variable is only used for internal storage.',
];
/**
* Module content accumulated.
*
* @var string
*/
protected $content;
/**
* @var string
*/
protected $title;
/**
* Document template object
*
* @var DocumentTemplate
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0, unused
*/
protected $doc;
/**
* Original input target
*
* @var string
*/
protected $origTarget;
/**
* The original target, but validated.
*
* @var string
*/
protected $target;
/**
* Return URL of list module.
*
* @var string
*/
protected $returnUrl;
/**
* the file that is being edited on
*
* @var \TYPO3\CMS\Core\Resource\AbstractFile
*/
protected $fileObject;
/**
* ModuleTemplate object
*
* @var ModuleTemplate
*/
protected $moduleTemplate;
/**
* Constructor
*/
public function __construct()
{
$this->moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
// @deprecated since TYPO3 v9, will be moved out of __construct() in TYPO3 v10.0
$this->init($GLOBALS['TYPO3_REQUEST']);
}
/**
* Processes the request, currently everything is handled and put together via "main()"
*
* @param ServerRequestInterface $request the current request
* @return ResponseInterface the response with the content
*/
public function mainAction(ServerRequestInterface $request): ResponseInterface
{
if ($response = $this->process()) {
return $response;
}
return new HtmlResponse($this->moduleTemplate->renderContent());
}
/**
* Main function, rendering the actual content of the editing page
*/
protected function main()
{
$response = $this->process();
if ($response instanceof RedirectResponse) {
HttpUtility::redirect($response->getHeaderLine('location'), $response->getStatusCode());
}
}
/**
* Initialize script class
*
* @param ServerRequestInterface $request
*
* @throws InsufficientFileAccessPermissionsException
*/
protected function init(ServerRequestInterface $request): void
{
$parsedBody = $request->getParsedBody();
$queryParams = $request->getQueryParams();
// Setting target, which must be a file reference to a file within the mounts.
$this->target = $this->origTarget = $parsedBody['target'] ?? $queryParams['target'] ?? '';
$this->returnUrl = GeneralUtility::sanitizeLocalUrl($parsedBody['returnUrl'] ?? $queryParams['returnUrl'] ?? '');
// create the file object
if ($this->target) {
$this->fileObject = ResourceFactory::getInstance()
->retrieveFileOrFolderObject($this->target);
}
// Cleaning and checking target directory
if (!$this->fileObject) {
$title = $this->getLanguageService()->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:paramError');
$message = $this->getLanguageService()->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:targetNoDir');
throw new \RuntimeException($title . ': ' . $message, 1294586841);
}
if ($this->fileObject->getStorage()->getUid() === 0) {
throw new InsufficientFileAccessPermissionsException(
'You are not allowed to access files outside your storages',
1375889832
);
}
// Setting the title and the icon
$icon = $this->moduleTemplate->getIconFactory()->getIcon('apps-filetree-root', Icon::SIZE_SMALL)->render();
$this->title = $icon
. htmlspecialchars(
$this->fileObject->getStorage()->getName()
) . ': ' . htmlspecialchars(
$this->fileObject->getIdentifier()
);
// Setting template object
$this->moduleTemplate->addJavaScriptCode(
'FileEditBackToList',
'function backToList() {
top.goToModule("file_FilelistList");
}'
);
}
/**
* Main function, rendering the actual content of the editing page
*
* @return ResponseInterface|null Possible redirect response
*/
protected function process(): ?ResponseInterface
{
$dataColumnDefinition = [
'label' => htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_common.xlf:file'))
. ' ' . htmlspecialchars($this->target),
'config' => [
'type' => 'text',
'cols' => 48,
'wrap' => 'OFF',
],
'defaultExtras' => 'fixed-font: enable-tab'
];
$this->getButtonsInternal();
// Hook: before compiling the output
foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/file_edit.php']['preOutputProcessingHook'] ?? [] as $hookFunction) {
$hookParameters = [
'content' => &$this->content,
'target' => &$this->target,
'dataColumnDefinition' => &$dataColumnDefinition,
];
GeneralUtility::callUserFunction($hookFunction, $hookParameters, $this);
}
$assigns = [];
$uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
$assigns['moduleUrlTceFile'] = (string)$uriBuilder->buildUriFromRoute('tce_file');
$assigns['fileName'] = $this->fileObject->getName();
$extList = $GLOBALS['TYPO3_CONF_VARS']['SYS']['textfile_ext'];
try {
if (!$extList || !GeneralUtility::inList($extList, $this->fileObject->getExtension())) {
// @todo throw a minor exception here, not the global one
throw new \Exception('Files with that extension are not editable. Allowed extensions are: ' . $extList, 1476050135);
}
// Making the formfields
$hValue = (string)$uriBuilder->buildUriFromRoute('file_edit', [
'target' => $this->origTarget,
'returnUrl' => $this->returnUrl
]);
$formData = [
'databaseRow' => [
'uid' => 0,
'data' => $this->fileObject->getContents(),
'target' => $this->fileObject->getUid(),
'redirect' => $hValue,
],
'tableName' => 'editfile',
'processedTca' => [
'columns' => [
'data' => $dataColumnDefinition,
'target' => [
'config' => [
'type' => 'input',
'renderType' => 'hidden',
],
],
'redirect' => [
'config' => [
'type' => 'input',
'renderType' => 'hidden',
],
],
],
'types' => [
1 => [
'showitem' => 'data,target,redirect',
],
],
],
'recordTypeValue' => 1,
'inlineStructure' => [],
'renderType' => 'fullRecordContainer',
];
$resultArray = GeneralUtility::makeInstance(NodeFactory::class)->create($formData)->render();
$formResultCompiler = GeneralUtility::makeInstance(FormResultCompiler::class);
$formResultCompiler->mergeResult($resultArray);
$form = $formResultCompiler->addCssFiles()
. $resultArray['html']
. $formResultCompiler->printNeededJSFunctions();
$assigns['form'] = $form;
} catch (\Exception $e) {
// @todo catch dedicated exceptions, not the global one, if possible
$flashMessage = GeneralUtility::makeInstance(FlashMessage::class, $e->getMessage(), '', FlashMessage::ERROR, true);
$flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
$defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
$defaultFlashMessageQueue->enqueue($flashMessage);
return new RedirectResponse($this->returnUrl, 500);
}
// 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/File/EditFile.html'
));
$view->assignMultiple($assigns);
$pageContent = $view->render();
// Hook: after compiling the output
foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/file_edit.php']['postOutputProcessingHook'] ?? [] as $hookFunction) {
$hookParameters = [
'pageContent' => &$pageContent,
'target' => &$this->target
];
GeneralUtility::callUserFunction($hookFunction, $hookParameters, $this);
}
$this->content .= $pageContent;
$this->moduleTemplate->setContent($this->content);
return null;
}
/**
* Builds the buttons for the docheader and returns them as an
*
* @deprecated since TYPO3 v9, will be set protected in TYPO3 v10.0
*/
public function getButtons()
{
trigger_error('EditFileController->getButtons() will be replaced by protected method getButtonsInternal() in TYPO3 v10.0. Do not call from other extension.', E_USER_DEPRECATED);
$this->getButtonsInternal();
}
/**
* Builds the buttons for the docheader
*/
protected function getButtonsInternal(): void
{
$buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
$lang = $this->getLanguageService();
// CSH button
$helpButton = $buttonBar->makeHelpButton()
->setFieldName('file_edit')
->setModuleName('xMOD_csh_corebe');
$buttonBar->addButton($helpButton);
// Save button
$saveButton = $buttonBar->makeInputButton()
->setName('_save')
->setValue('1')
->setForm('EditFileController')
->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:file_edit.php.submit'))
->setIcon($this->moduleTemplate->getIconFactory()->getIcon('actions-document-save', Icon::SIZE_SMALL));
// Save and Close button
$saveAndCloseButton = $buttonBar->makeInputButton()
->setName('_saveandclosedok')
->setValue('1')
->setForm('EditFileController')
->setOnClick(
'document.editform.elements.namedItem("data[editfile][0][redirect]").value='
. GeneralUtility::quoteJSvalue($this->returnUrl)
. ';'
)
->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:file_edit.php.saveAndClose'))
->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
'actions-document-save-close',
Icon::SIZE_SMALL
));
$splitButton = $buttonBar->makeSplitButton()
->addItem($saveButton)
->addItem($saveAndCloseButton);
$buttonBar->addButton($splitButton, ButtonBar::BUTTON_POSITION_LEFT, 20);
// Cancel button
$closeButton = $buttonBar->makeLinkButton()
->setHref('#')
->setOnClick('backToList(); return false;')
->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.cancel'))
->setIcon($this->moduleTemplate->getIconFactory()->getIcon('actions-close', Icon::SIZE_SMALL));
$buttonBar->addButton($closeButton, ButtonBar::BUTTON_POSITION_LEFT, 10);
// Make shortcut:
$shortButton = $buttonBar->makeShortcutButton()
->setModuleName('file_edit')
->setGetVariables(['target']);
$buttonBar->addButton($shortButton);
}
/**
* Returns LanguageService
*
* @return LanguageService
*/
protected function getLanguageService(): LanguageService
{
return $GLOBALS['LANG'];
}
/**
* Returns the current BE user.
*
* @return BackendUserAuthentication
*/
protected function getBackendUser(): BackendUserAuthentication
{
return $GLOBALS['BE_USER'];
}
}
@@ -0,0 +1,341 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Backend\Controller\File;
/*
* 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\Clipboard\Clipboard;
use TYPO3\CMS\Backend\Routing\UriBuilder;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Compatibility\PublicMethodDeprecationTrait;
use TYPO3\CMS\Core\Http\HtmlResponse;
use TYPO3\CMS\Core\Http\JsonResponse;
use TYPO3\CMS\Core\Http\RedirectResponse;
use TYPO3\CMS\Core\Imaging\Icon;
use TYPO3\CMS\Core\Imaging\IconFactory;
use TYPO3\CMS\Core\Resource\DuplicationBehavior;
use TYPO3\CMS\Core\Resource\File;
use TYPO3\CMS\Core\Resource\Folder;
use TYPO3\CMS\Core\Resource\ProcessedFile;
use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Utility\File\ExtendedFileUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\HttpUtility;
/**
* Gateway for TCE (TYPO3 Core Engine) file-handling through POST forms.
* This script serves as the file administration part of the TYPO3 Core Engine.
* Basically it includes two libraries which are used to manipulate files on the server.
* @internal This class is a specific Backend controller implementation and is not considered part of the Public TYPO3 API.
*/
class FileController
{
use PublicMethodDeprecationTrait;
/**
* @var array
*/
private $deprecatedPublicMethods = [
'initClipboard' => 'Using FileController::initClipboard() is deprecated and will not be possible anymore in TYPO3 v10.0.',
'main' => 'Using FileController::main() is deprecated and will not be possible anymore in TYPO3 v10.0.',
];
/**
* Array of file-operations.
*
* @var array
*/
protected $file;
/**
* Clipboard operations array
*
* @var array
*/
protected $CB;
/**
* Defines behaviour when uploading files with names that already exist; possible values are
* the values of the \TYPO3\CMS\Core\Resource\DuplicationBehavior enumeration
*
* @var \TYPO3\CMS\Core\Resource\DuplicationBehavior
*/
protected $overwriteExistingFiles;
/**
* The page where the user should be redirected after everything is done
*
* @var string
*/
protected $redirect;
/**
* Internal, dynamic:
* File processor object
*
* @var ExtendedFileUtility
*/
protected $fileProcessor;
/**
* The result array from the file processor
*
* @var array
*/
protected $fileData;
/**
* Constructor
*/
public function __construct()
{
// @deprecated since TYPO3 v9, will be moved out of __construct() in TYPO3 v10.0
$this->init($GLOBALS['TYPO3_REQUEST']);
}
/**
* Injects the request object for the current request or subrequest
* As this controller goes only through the main() method, it just redirects to the given URL afterwards.
*
* @param ServerRequestInterface $request the current request
* @return ResponseInterface the response with the content
*/
public function mainAction(ServerRequestInterface $request): ResponseInterface
{
$this->main();
BackendUtility::setUpdateSignal('updateFolderTree');
// go and edit the new created file
if ($request->getParsedBody()['edit'] ?? '') {
/** @var File $file */
$file = $this->fileData['newfile'][0];
if ($file !== null) {
$this->redirect = $this->getFileEditRedirect($file) ?? $this->redirect;
}
}
if ($this->redirect) {
return new RedirectResponse(
GeneralUtility::locationHeaderUrl($this->redirect),
303
);
}
// empty response
return new HtmlResponse('');
}
/**
* Handles the actual process from within the ajaxExec function
* therefore, it does exactly the same as the real typo3/tce_file.php
* but without calling the "finish" method, thus makes it simpler to deal with the
* actual return value
*
* @param ServerRequestInterface $request
* @return ResponseInterface
*/
public function processAjaxRequest(ServerRequestInterface $request): ResponseInterface
{
$this->main();
$errors = $this->fileProcessor->getErrorMessages();
if (!empty($errors)) {
return (new HtmlResponse('<t3err>' . implode(',', $errors) . '</t3err>'))->withStatus(500, '(AJAX)');
}
$flatResult = [];
foreach ($this->fileData as $action => $results) {
foreach ($results as $result) {
if (is_array($result)) {
foreach ($result as $subResult) {
$flatResult[$action][] = $this->flattenResultDataValue($subResult);
}
} else {
$flatResult[$action][] = $this->flattenResultDataValue($result);
}
}
}
return (new JsonResponse())->setPayload($flatResult);
}
/**
* Ajax entry point to check if a file exists in a folder
*
* @param ServerRequestInterface $request
* @return ResponseInterface
*/
public function fileExistsInFolderAction(ServerRequestInterface $request): ResponseInterface
{
$fileName = $request->getParsedBody()['fileName'] ?? $request->getQueryParams()['fileName'] ?? null;
$fileTarget = $request->getParsedBody()['fileTarget'] ?? $request->getQueryParams()['fileTarget'] ?? null;
$fileFactory = GeneralUtility::makeInstance(ResourceFactory::class);
$fileTargetObject = $fileFactory->retrieveFileOrFolderObject($fileTarget);
$processedFileName = $fileTargetObject->getStorage()->sanitizeFileName($fileName, $fileTargetObject);
$result = [];
if ($fileTargetObject->hasFile($processedFileName)) {
$result = $this->flattenResultDataValue($fileTargetObject->getStorage()->getFileInFolder($processedFileName, $fileTargetObject));
}
return (new JsonResponse())->setPayload($result);
}
/**
* Registering incoming data
*
* @param ServerRequestInterface $request
*/
protected function init(ServerRequestInterface $request): void
{
// Set the GPvars from outside
$parsedBody = $request->getParsedBody();
$queryParams = $request->getQueryParams();
$this->file = $parsedBody['data'] ?? $queryParams['data'] ?? null;
if ($this->file === null) {
// This happens in clipboard mode only
$this->redirect = GeneralUtility::sanitizeLocalUrl($parsedBody['redirect'] ?? $queryParams['redirect'] ?? '');
} else {
$mode = key($this->file);
$elementKey = key($this->file[$mode]);
$this->redirect = GeneralUtility::sanitizeLocalUrl($this->file[$mode][$elementKey]['redirect']);
}
$this->CB = $parsedBody['CB'] ?? $queryParams['CB'] ?? null;
if (isset($this->file['rename'][0]['conflictMode'])) {
$conflictMode = $this->file['rename'][0]['conflictMode'];
unset($this->file['rename'][0]['conflictMode']);
$this->overwriteExistingFiles = DuplicationBehavior::cast($conflictMode);
} else {
$this->overwriteExistingFiles = DuplicationBehavior::cast($parsedBody['overwriteExistingFiles'] ?? $queryParams['overwriteExistingFiles'] ?? null);
}
$this->initClipboard();
$this->fileProcessor = GeneralUtility::makeInstance(ExtendedFileUtility::class);
}
/**
* Initialize the Clipboard. This will fetch the data about files to paste/delete if such an action has been sent.
*/
protected function initClipboard(): void
{
if (is_array($this->CB)) {
$clipObj = GeneralUtility::makeInstance(Clipboard::class);
$clipObj->initializeClipboard();
if ($this->CB['paste']) {
$clipObj->setCurrentPad($this->CB['pad']);
$this->file = $clipObj->makePasteCmdArray_file($this->CB['paste'], $this->file);
}
if ($this->CB['delete']) {
$clipObj->setCurrentPad($this->CB['pad']);
$this->file = $clipObj->makeDeleteCmdArray_file($this->file);
}
}
}
/**
* Performing the file admin action:
* Initializes the objects, setting permissions, sending data to object.
*/
protected function main(): void
{
// Initializing:
$this->fileProcessor->setActionPermissions();
$this->fileProcessor->setExistingFilesConflictMode($this->overwriteExistingFiles);
$this->fileProcessor->start($this->file);
$this->fileData = $this->fileProcessor->processData();
}
/**
* Gets URI to be used for editing given file (if file extension is defined in textfile_ext)
*
* @param File $file to be edited
* @return string|null URI to be redirected to
* @throws \TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException
*/
protected function getFileEditRedirect(File $file): ?string
{
$textFileExtensionList = $GLOBALS['TYPO3_CONF_VARS']['SYS']['textfile_ext'] ?? '';
if (!GeneralUtility::inList($textFileExtensionList, $file->getExtension())) {
return null;
}
$properties = $file->getProperties();
$urlParameters = [
'target' => $properties['storage'] . ':' . $properties['identifier']
];
if ($this->redirect) {
$urlParameters['returnUrl'] = $this->redirect;
}
$uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
return (string)$uriBuilder->buildUriFromRoute('file_edit', $urlParameters);
}
/**
* Redirecting the user after the processing has been done.
* Might also display error messages directly, if any.
*
* @deprecated since TYPO3 v9. Will be removed in TYPO3 v10.0.
*/
public function finish()
{
trigger_error('FileController->finish() will be removed in TYPO3 v10.0. Do not call from other extension.', E_USER_DEPRECATED);
BackendUtility::setUpdateSignal('updateFolderTree');
if ($this->redirect) {
HttpUtility::redirect($this->redirect);
}
}
/**
* Flatten result value from FileProcessor
*
* The value can be a File, Folder or boolean
*
* @param bool|File|Folder $result
*
* @return bool|string|array
*/
protected function flattenResultDataValue($result)
{
if ($result instanceof File) {
$thumbUrl = '';
if (GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], $result->getExtension())) {
$processedFile = $result->process(ProcessedFile::CONTEXT_IMAGEPREVIEW, []);
if ($processedFile) {
$thumbUrl = $processedFile->getPublicUrl(true);
}
}
$iconFactory = GeneralUtility::makeInstance(IconFactory::class);
$result = array_merge(
$result->toArray(),
[
'date' => BackendUtility::date($result->getModificationTime()),
'icon' => $iconFactory->getIconForFileExtension($result->getExtension(), Icon::SIZE_SMALL)->render(),
'thumbUrl' => $thumbUrl
]
);
} elseif ($result instanceof Folder) {
$result = $result->getIdentifier();
}
return $result;
}
/**
* Returns the current BE user.
*
* @return BackendUserAuthentication
*/
protected function getBackendUser(): BackendUserAuthentication
{
return $GLOBALS['BE_USER'];
}
}
@@ -0,0 +1,264 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Backend\Controller\File;
/*
* 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\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\Resource\Exception\InsufficientFolderAccessPermissionsException;
use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Script Class for display up to 10 upload fields
* @internal This class is a specific Backend controller implementation and is not considered part of the Public TYPO3 API.
*/
class FileUploadController
{
use PublicPropertyDeprecationTrait;
/**
* @var array
*/
protected $deprecatedPublicProperties = [
'title' => 'Using $title of class FileUploadController from outside is discouraged as this variable is only used for internal storage.',
'target' => 'Using $target of class FileUploadController from outside is discouraged as this variable is only used for internal storage.',
'returnUrl' => 'Using $returnUrl of class FileUploadController from outside is discouraged as this variable is only used for internal storage.',
'content' => 'Using $content of class FileUploadController from outside is discouraged as this variable is only used for internal storage.',
];
/**
* Name of the filemount
*
* @var string
*/
protected $title;
/**
* Set with the target path inputted in &target
*
* @var string
*/
protected $target;
/**
* Return URL of list module.
*
* @var string
*/
protected $returnUrl;
/**
* Accumulating content
*
* @var string
*/
protected $content;
/**
* The folder object which is the target directory for the upload
*
* @var \TYPO3\CMS\Core\Resource\Folder $folderObject
*/
protected $folderObject;
/**
* 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 moved out of __construct() in TYPO3 v10.0
$this->init($GLOBALS['TYPO3_REQUEST']);
}
/**
* Processes the request, currently everything is handled and put together via "renderContent()"
*
* @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->moduleTemplate->renderContent());
}
/**
* Main function, rendering the upload file form fields
*
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
*/
public function main()
{
trigger_error('FileUploadController->main() will be replaced by protected method renderContent() in TYPO3 v10.0. Do not call from other extension.', E_USER_DEPRECATED);
$this->renderContent();
}
/**
* This function renders the upload form
*
* @return string The HTML form as a string, ready for outputting
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
*/
public function renderUploadForm()
{
trigger_error('FileUploadController->renderUploadForm() will be replaced by protected method renderUploadFormInternal() in TYPO3 v10.0. Do not call from other extension.', E_USER_DEPRECATED);
return $this->renderUploadFormInternal();
}
/**
* Initialize
*
* @param ServerRequestInterface $request
* @throws InsufficientFolderAccessPermissionsException
*/
protected function init(ServerRequestInterface $request): void
{
$parsedBody = $request->getParsedBody();
$queryParams = $request->getQueryParams();
/** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
$uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
// Initialize GPvars:
$this->target = $parsedBody['target'] ?? $queryParams['target'] ?? null;
$this->returnUrl = GeneralUtility::sanitizeLocalUrl($parsedBody['returnUrl'] ?? $queryParams['returnUrl'] ?? '');
if (!$this->returnUrl) {
$this->returnUrl = (string)$uriBuilder->buildUriFromRoute('file_list', [
'id' => rawurlencode($this->target)
]);
}
// Create the folder object
if ($this->target) {
$this->folderObject = ResourceFactory::getInstance()
->retrieveFileOrFolderObject($this->target);
}
if ($this->folderObject->getStorage()->getUid() === 0) {
throw new InsufficientFolderAccessPermissionsException(
'You are not allowed to access folders outside your storages',
1375889834
);
}
// Cleaning and checking target directory
if (!$this->folderObject) {
$title = $this->getLanguageService()->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:paramError');
$message = $this->getLanguageService()->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:targetNoDir');
throw new \RuntimeException($title . ': ' . $message, 1294586843);
}
// Setting up the context sensitive menu
$this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ContextMenu');
// building pathInfo for metaInformation
$pathInfo = [
'combined_identifier' => $this->folderObject->getCombinedIdentifier(),
];
$this->moduleTemplate->getDocHeaderComponent()->setMetaInformation($pathInfo);
}
/**
* Render module content
*/
protected function renderContent(): void
{
$lang = $this->getLanguageService();
/** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
$uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
// set page title
$this->moduleTemplate->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:file_upload.php.pagetitle'));
$pageContent = '<form action="'
. htmlspecialchars((string)$uriBuilder->buildUriFromRoute('tce_file'))
. '" method="post" id="FileUploadController" name="editform" enctype="multipart/form-data">';
// Make page header:
$pageContent .= '<h1>' . $lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:file_upload.php.pagetitle') . '</h1>';
$pageContent .= $this->renderUploadFormInternal();
// Header Buttons
$buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
// csh button
$cshButton = $buttonBar->makeHelpButton()
->setModuleName('xMOD_csh_corebe')
->setFieldName('file_upload');
$buttonBar->addButton($cshButton);
// back button
if ($this->returnUrl) {
$backButton = $buttonBar->makeLinkButton()
->setHref($this->returnUrl)
->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.goBack'))
->setIcon($this->moduleTemplate->getIconFactory()->getIcon('actions-view-go-back', Icon::SIZE_SMALL));
$buttonBar->addButton($backButton);
}
$pageContent .= '</form>';
$this->content .= '<div>' . $pageContent . '</div>';
$this->moduleTemplate->setContent($this->content);
}
/**
* This function renders the upload form
*
* @return string The HTML form as a string, ready for outputting
*/
protected function renderUploadFormInternal(): string
{
$content = '
<div class="form-group">
<input type="file" multiple="multiple" class="form-control" name="upload_1[]" />
<input type="hidden" name="data[upload][1][target]" value="' . htmlspecialchars($this->folderObject->getCombinedIdentifier()) . '" />
<input type="hidden" name="data[upload][1][data]" value="1" />
</div>
<div class="checkbox">
<label for="overwriteExistingFiles">
<input type="checkbox" name="overwriteExistingFiles" id="overwriteExistingFiles" value="replace" /> ' . htmlspecialchars($this->getLanguageService()->getLL('overwriteExistingFiles')) . '</label>
</div>
<div>
<input type="hidden" name="data[upload][1][redirect]" value="' . $this->returnUrl . '" />
<input class="btn btn-primary" type="submit" value="' . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:file_upload.php.submit')) . '" />
</div>
<div class="callout callout-warning">
' . htmlspecialchars($this->getLanguageService()->getLL('uploadMultipleFilesInfo')) . '
</div>
';
return $content;
}
/**
* @return LanguageService
*/
protected function getLanguageService(): LanguageService
{
return $GLOBALS['LANG'];
}
}
@@ -0,0 +1,281 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Backend\Controller\File;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Template\Components\ButtonBar;
use TYPO3\CMS\Backend\Template\DocumentTemplate;
use TYPO3\CMS\Backend\Template\ModuleTemplate;
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\Resource\DuplicationBehavior;
use TYPO3\CMS\Core\Resource\Exception\InsufficientFileAccessPermissionsException;
use TYPO3\CMS\Core\Resource\File;
use TYPO3\CMS\Core\Resource\Folder;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\View\StandaloneView;
/**
* Script Class for the rename-file form.
* @internal This class is a specific Backend controller implementation and is not considered part of the Public TYPO3 API.
*/
class RenameFileController
{
use PublicPropertyDeprecationTrait;
/**
* @var array
*/
protected $deprecatedPublicProperties = [
'title' => 'Using $title of class RenameFileController from outside is discouraged as this variable is only used for internal storage.',
'target' => 'Using $target of class RenameFileController from outside is discouraged as this variable is only used for internal storage.',
'returnUrl' => 'Using $returnUrl of class RenameFileController from outside is discouraged as this variable is only used for internal storage.',
'content' => 'Using $content of class RenameFileController from outside is discouraged as this variable is only used for internal storage.',
];
/**
* Name of the filemount
*
* @var string
*/
protected $title;
/**
* Target path
*
* @var string
* @internal
*/
protected $target;
/**
* The file or folder object that should be renamed
*
* @var File|Folder $fileOrFolderObject
*/
protected $fileOrFolderObject;
/**
* Return URL of list module.
*
* @var string
*/
protected $returnUrl;
/**
* Accumulating content
*
* @var string
* @internal
*/
protected $content;
/**
* ModuleTemplate object
*
* @var ModuleTemplate
*/
protected $moduleTemplate;
/**
* Constructor
*/
public function __construct()
{
$this->moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
// @deprecated since TYPO3 v9, will be moved out of __construct() in TYPO3 v10.0
$this->init($GLOBALS['TYPO3_REQUEST']);
}
/**
* Processes the request, currently everything is handled and put together via "renderContent()"
*
* @param ServerRequestInterface $request
* @return ResponseInterface the response with the content
*/
public function mainAction(ServerRequestInterface $request): ResponseInterface
{
$this->renderContent();
return new HtmlResponse($this->moduleTemplate->renderContent());
}
/**
* Main function, rendering the content of the rename form
*
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
*/
public function main()
{
trigger_error('RenameFileController->main() will be replaced by protected method renderContent() in TYPO3 v10.0. Do not call from other extension.', E_USER_DEPRECATED);
$this->renderContent();
}
/**
* Initialize
*
* @param ServerRequestInterface $request
* @throws InsufficientFileAccessPermissionsException
*/
protected function init(ServerRequestInterface $request): void
{
$parsedBody = $request->getParsedBody();
$queryParams = $request->getQueryParams();
// Initialize GPvars:
$this->target = $parsedBody['target'] ?? $queryParams['target'] ?? null;
$this->returnUrl = GeneralUtility::sanitizeLocalUrl($parsedBody['returnUrl'] ?? $queryParams['returnUrl'] ?? '');
// Cleaning and checking target
if ($this->target) {
$this->fileOrFolderObject = \TYPO3\CMS\Core\Resource\ResourceFactory::getInstance()->retrieveFileOrFolderObject($this->target);
}
if (!$this->fileOrFolderObject) {
$title = $this->getLanguageService()->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:paramError');
$message = $this->getLanguageService()->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:targetNoDir');
throw new \RuntimeException($title . ': ' . $message, 1294586844);
}
if ($this->fileOrFolderObject->getStorage()->getUid() === 0) {
throw new InsufficientFileAccessPermissionsException('You are not allowed to access files outside your storages', 1375889840);
}
// If a folder should be renamed, AND the returnURL should go to the old directory name, the redirect is forced
// so the redirect will NOT end in an error message
// this case only happens if you select the folder itself in the foldertree and then use the clickmenu to
// rename the folder
if ($this->fileOrFolderObject instanceof Folder) {
$parsedUrl = parse_url($this->returnUrl);
$queryParts = GeneralUtility::explodeUrl2Array(urldecode($parsedUrl['query']));
if ($queryParts['id'] === $this->fileOrFolderObject->getCombinedIdentifier()) {
$this->returnUrl = str_replace(
urlencode($queryParts['id']),
urlencode($this->fileOrFolderObject->getStorage()->getRootLevelFolder()->getCombinedIdentifier()),
$this->returnUrl
);
}
}
// building pathInfo for metaInformation
$pathInfo = [
'combined_identifier' => $this->fileOrFolderObject->getCombinedIdentifier(),
];
$this->moduleTemplate->getDocHeaderComponent()->setMetaInformation($pathInfo);
// Setting up the context sensitive menu
$this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ContextMenu');
$this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/RenameFile');
// Add javaScript
$this->moduleTemplate->addJavaScriptCode(
'RenameFileInlineJavaScript',
'function backToList() {top.goToModule("file_FilelistList");}'
);
}
/**
* Render module content
*/
protected function renderContent(): void
{
$assigns = [];
/** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
$uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
$assigns['moduleUrlTceFile'] = (string)$uriBuilder->buildUriFromRoute('tce_file');
$assigns['returnUrl'] = $this->returnUrl;
if ($this->fileOrFolderObject instanceof Folder) {
$fileIdentifier = $this->fileOrFolderObject->getCombinedIdentifier();
$targetLabel = 'file_rename.php.label.target.folder';
} else {
$fileIdentifier = $this->fileOrFolderObject->getUid();
$targetLabel = 'file_rename.php.label.target.file';
$assigns['conflictMode'] = DuplicationBehavior::cast(DuplicationBehavior::RENAME);
$assigns['destination'] = $this->fileOrFolderObject->getParentFolder()->getCombinedIdentifier();
}
$assigns['fileName'] = $this->fileOrFolderObject->getName();
$assigns['fileIdentifier'] = $fileIdentifier;
$assigns['fieldLabel'] = $targetLabel;
// Create buttons
$buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
// csh button
$cshButton = $buttonBar->makeHelpButton()
->setModuleName('xMOD_csh_corebe')
->setFieldName('file_rename');
$buttonBar->addButton($cshButton);
// back button
if ($this->returnUrl) {
$backButton = $buttonBar->makeLinkButton()
->setHref($this->returnUrl)
->setTitle($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.goBack'))
->setIcon($this->moduleTemplate->getIconFactory()->getIcon('actions-close', Icon::SIZE_SMALL));
$buttonBar->addButton($backButton);
}
// Save and Close button
$saveAndCloseButton = $buttonBar->makeInputButton()
->setName('_saveandclose')
->setValue('1')
->setShowLabelText(true)
->setClasses('t3js-submit-file-rename')
->setForm('RenameFileController')
->setTitle($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:file_edit.php.saveAndClose'))
->setIcon($this->moduleTemplate->getIconFactory()->getIcon('actions-document-save-close', Icon::SIZE_SMALL));
$buttonBar->addButton($saveAndCloseButton, ButtonBar::BUTTON_POSITION_LEFT, 20);
$this->moduleTemplate->getPageRenderer()->addInlineLanguageLabelArray([
'file_rename.actions.cancel' => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:file_rename.actions.cancel'),
'file_rename.actions.rename' => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:file_rename.actions.rename'),
'file_rename.actions.override' => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:file_rename.actions.override'),
'file_rename.exists.title' => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:file_rename.exists.title'),
'file_rename.exists.description' => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:file_rename.exists.description'),
]);
// 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/File/RenameFile.html'
));
$view->assignMultiple($assigns);
$this->content = $view->render();
$this->moduleTemplate->setContent($this->content);
}
/**
* @return LanguageService
*/
protected function getLanguageService(): LanguageService
{
return $GLOBALS['LANG'];
}
/**
* @return DocumentTemplate
*/
protected function getDocumentTemplate(): DocumentTemplate
{
return $GLOBALS['TBE_TEMPLATE'];
}
}
@@ -0,0 +1,242 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Backend\Controller\File;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Routing\UriBuilder;
use TYPO3\CMS\Backend\Template\ModuleTemplate;
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\Resource\Exception\InsufficientFileAccessPermissionsException;
use TYPO3\CMS\Core\Resource\Folder;
use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\View\StandaloneView;
/**
* Script Class for the replace-file form
* @internal This class is a specific Backend controller implementation and is not considered part of the Public TYPO3 API.
*/
class ReplaceFileController
{
use PublicPropertyDeprecationTrait;
/**
* @var array
*/
protected $deprecatedPublicProperties = [
'doc' => 'Using $doc of class ReplaceFileController from outside is discouraged as this variable is only used for internal storage.',
'title' => 'Using $title of class ReplaceFileController from outside is discouraged as this variable is only used for internal storage.',
'uid' => 'Using $uid of class ReplaceFileController from outside is discouraged as this variable is only used for internal storage.',
'returnUrl' => 'Using $returnUrl of class ReplaceFileController from outside is discouraged as this variable is only used for internal storage.',
'content' => 'Using $content of class ReplaceFileController from outside is discouraged as this variable is only used for internal storage.',
];
/**
* Document template object
*
* @var \TYPO3\CMS\Backend\Template\DocumentTemplate
*/
protected $doc;
/**
* Name of the filemount
*
* @var string
*/
protected $title;
/**
* sys_file uid
*
* @var int
*/
protected $uid;
/**
* The file or folder object that should be renamed
*
* @var \TYPO3\CMS\Core\Resource\ResourceInterface $fileOrFolderObject
*/
protected $fileOrFolderObject;
/**
* Return URL of list module.
*
* @var string
*/
protected $returnUrl;
/**
* Accumulating content
*
* @var string
*/
protected $content;
/**
* ModuleTemplate object
*
* @var ModuleTemplate
*/
protected $moduleTemplate;
/**
* Constructor
*/
public function __construct()
{
$this->moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
// @deprecated since TYPO3 v9, will be moved out of __construct() in TYPO3 v10.0
$this->init($GLOBALS['TYPO3_REQUEST']);
}
/**
* Processes the request, currently everything is handled and put together via "main()"
*
* @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->moduleTemplate->renderContent());
}
/**
* Main function, rendering the content of the replace form
*/
public function main()
{
trigger_error('ReplaceFileController->main() will be replaced by protected method renderContent() in TYPO3 v10.0. Do not call from other extension.', E_USER_DEPRECATED);
$this->renderContent();
}
/**
* Init
*
* @param ServerRequestInterface $request
* @throws \RuntimeException
* @throws InsufficientFileAccessPermissionsException
*/
protected function init(ServerRequestInterface $request): void
{
$parsedBody = $request->getParsedBody();
$queryParams = $request->getQueryParams();
$lang = $this->getLanguageService();
// Initialize GPvars:
$this->uid = (int)($parsedBody['uid'] ?? $queryParams['uid'] ?? 0);
$this->returnUrl = GeneralUtility::sanitizeLocalUrl($parsedBody['returnUrl'] ?? $queryParams['returnUrl'] ?? '');
// Cleaning and checking uid
if ($this->uid > 0) {
$this->fileOrFolderObject = ResourceFactory::getInstance()
->retrieveFileOrFolderObject('file:' . $this->uid);
}
if (!$this->fileOrFolderObject) {
$title = $lang->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:paramError');
$message = $lang->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:targetNoDir');
throw new \RuntimeException($title . ': ' . $message, 1436895930);
}
if ($this->fileOrFolderObject->getStorage()->getUid() === 0) {
throw new InsufficientFileAccessPermissionsException(
'You are not allowed to access files outside your storages',
1436895931
);
}
// If a folder should be renamed, AND the returnURL should go to the old directory name, the redirect is forced
// so the redirect will NOT end in an error message
// this case only happens if you select the folder itself in the foldertree and then use the clickmenu to
// rename the folder
if ($this->fileOrFolderObject instanceof Folder) {
$parsedUrl = parse_url($this->returnUrl);
$queryParts = GeneralUtility::explodeUrl2Array(urldecode($parsedUrl['query']));
if ($queryParts['id'] === $this->fileOrFolderObject->getCombinedIdentifier()) {
$this->returnUrl = str_replace(
urlencode($queryParts['id']),
urlencode($this->fileOrFolderObject->getStorage()->getRootLevelFolder()->getCombinedIdentifier()),
$this->returnUrl
);
}
}
$pathInfo = [
'combined_identifier' => $this->fileOrFolderObject->getCombinedIdentifier(),
];
$this->moduleTemplate->getDocHeaderComponent()->setMetaInformation($pathInfo);
$this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ContextMenu');
$this->moduleTemplate->addJavaScriptCode(
'ReplaceFileOnlineJavaScript',
'function backToList() {top.goToModule("file_FilelistList");}'
);
}
/**
* Render module content
*/
protected function renderContent(): void
{
// Assign variables used by the fluid template
$assigns = [];
/** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
$uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
$assigns['moduleUrlTceFile'] = (string)$uriBuilder->buildUriFromRoute('tce_file');
$assigns['uid'] = $this->uid;
$assigns['returnUrl'] = $this->returnUrl;
$buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
// csh button
$cshButton = $buttonBar->makeHelpButton()
->setModuleName('xMOD_csh_corebe')
->setFieldName('file_rename');
$buttonBar->addButton($cshButton);
// Back button
if ($this->returnUrl) {
$returnButton = $buttonBar->makeLinkButton()
->setHref($this->returnUrl)
->setTitle($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.goBack'))
->setIcon($this->moduleTemplate->getIconFactory()->getIcon('actions-view-go-back', Icon::SIZE_SMALL));
$buttonBar->addButton($returnButton);
}
// 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/File/ReplaceFile.html'
));
$view->assignMultiple($assigns);
$this->content = $view->render();
$this->moduleTemplate->setContent($this->content);
}
/**
* @return LanguageService
*/
protected function getLanguageService(): LanguageService
{
return $GLOBALS['LANG'];
}
}
@@ -0,0 +1,116 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Backend\Controller\File;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Core\Http\Response;
use TYPO3\CMS\Core\Resource\ProcessedFile;
use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* @internal This class is a specific Backend controller implementation and is not considered part of the Public TYPO3 API.
*/
class ThumbnailController
{
/**
* @var array
*/
protected $defaultConfiguration = [
'width' => 64,
'height' => 64,
'crop' => null,
];
/**
* @param ServerRequestInterface $request
* @return ResponseInterface
*/
public function render(ServerRequestInterface $request): ResponseInterface
{
try {
$parameters = $this->extractParameters($request->getQueryParams());
$response = $this->generateThumbnail(
$parameters['fileId'] ?? null,
$parameters['configuration'] ?? []
);
} catch (\TYPO3\CMS\Core\Resource\Exception $exception) {
// catch and handle only resource related exceptions
$response = $this->generateNotFoundResponse();
}
return $response;
}
/**
* @param array $queryParameters
* @return array|null
*/
protected function extractParameters(array $queryParameters)
{
$expectedHash = GeneralUtility::hmac(
$queryParameters['parameters'] ?? '',
ThumbnailController::class
);
if (!hash_equals($expectedHash, $queryParameters['hmac'] ?? '')) {
throw new \InvalidArgumentException(
'HMAC could not be verified',
1534484203
);
}
return json_decode($queryParameters['parameters'] ?? null, true);
}
/**
* @param mixed|int $fileId
* @param array $configuration
* @return Response
* @throws \TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException
*/
protected function generateThumbnail($fileId, array $configuration): ResponseInterface
{
$file = ResourceFactory::getInstance()->getFileObject($fileId);
if (empty($file) || $file->isMissing()) {
return $this->generateNotFoundResponse();
}
$processingConfiguration = $this->defaultConfiguration;
ArrayUtility::mergeRecursiveWithOverrule(
$processingConfiguration,
$configuration
);
$processedImage = $file->process(
ProcessedFile::CONTEXT_IMAGECROPSCALEMASK,
$processingConfiguration
);
$filePath = $processedImage->getForLocalProcessing(false);
return new Response($filePath, 200, [
'Content-Type' => $processedImage->getMimeType()
]);
}
/**
* @return ResponseInterface
*/
protected function generateNotFoundResponse(): ResponseInterface
{
return new Response('', 404);
}
}