TYPO3 v15 dev-main snapshot ()
This commit is contained in:
@@ -0,0 +1,230 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Install\FolderStructure;
|
||||
|
||||
use TYPO3\CMS\Core\Core\Environment;
|
||||
use TYPO3\CMS\Core\Messaging\FlashMessage;
|
||||
use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity;
|
||||
use TYPO3\CMS\Install\FolderStructure\Exception\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Abstract node implements common methods
|
||||
* @internal This class is only meant to be used within EXT:install and is not part of the TYPO3 Core API.
|
||||
*/
|
||||
abstract class AbstractNode
|
||||
{
|
||||
/**
|
||||
* @var string Name
|
||||
*/
|
||||
protected $name = '';
|
||||
|
||||
/**
|
||||
* @var string|null Target permissions for unix, eg. '2775' or '0664' (4 characters string)
|
||||
*/
|
||||
protected $targetPermission;
|
||||
|
||||
/**
|
||||
* @var NodeInterface|null Parent object of this structure node
|
||||
*/
|
||||
protected $parent;
|
||||
|
||||
/**
|
||||
* @var array Directories and root may have children, files and link always empty array
|
||||
*/
|
||||
protected $children = [];
|
||||
|
||||
/**
|
||||
* Get name
|
||||
*
|
||||
* @return string Name
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get target permission
|
||||
*
|
||||
* Make sure to call octdec on the value when passing this to chmod
|
||||
*
|
||||
* @return string Permissions as a 4 character octal string, i.e. 2775 or 0644
|
||||
*/
|
||||
protected function getTargetPermission()
|
||||
{
|
||||
return $this->targetPermission ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Set target permission
|
||||
*
|
||||
* @param string $permission Permissions as a 4 character octal string, i.e. 2775 or 0644
|
||||
*/
|
||||
protected function setTargetPermission($permission)
|
||||
{
|
||||
// Normalize the permission string to "4 characters", padding with leading "0" if necessary:
|
||||
$permission = substr($permission, 0, 4);
|
||||
$permission = str_pad($permission, 4, '0', STR_PAD_LEFT);
|
||||
$this->targetPermission = $permission;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get children
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getChildren()
|
||||
{
|
||||
return $this->children;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get parent
|
||||
*
|
||||
* @return NodeInterface|null
|
||||
*/
|
||||
protected function getParent()
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get absolute path of node
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAbsolutePath()
|
||||
{
|
||||
return $this->getParent()->getAbsolutePath() . '/' . $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Current node is writable if parent is writable
|
||||
*
|
||||
* @return bool TRUE if parent is writable
|
||||
*/
|
||||
public function isWritable()
|
||||
{
|
||||
return $this->getParent()->isWritable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if node exists.
|
||||
* Returns TRUE if it is there, even if it is only a link.
|
||||
* Does not check the type!
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function exists()
|
||||
{
|
||||
if (@is_link($this->getAbsolutePath())) {
|
||||
return true;
|
||||
}
|
||||
return @file_exists($this->getAbsolutePath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix permission if they are not equal to target permission
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function fixPermission(): FlashMessage
|
||||
{
|
||||
if ($this->isPermissionCorrect()) {
|
||||
return new FlashMessage(
|
||||
'',
|
||||
'Permission on ' . $this->getAbsolutePath() . ' is already ok.'
|
||||
);
|
||||
}
|
||||
$result = @chmod($this->getAbsolutePath(), (int)octdec($this->getTargetPermission()));
|
||||
if ($result === true) {
|
||||
return new FlashMessage(
|
||||
'',
|
||||
'Fixed permission on ' . $this->getRelativePathBelowSiteRoot() . '.'
|
||||
);
|
||||
}
|
||||
return new FlashMessage(
|
||||
'Permissions could not be changed to ' . $this->getTargetPermission()
|
||||
. '. This only is a problem if files and folders within this node cannot be written.',
|
||||
'Permission change on ' . $this->getRelativePathBelowSiteRoot() . ' not successful',
|
||||
ContextualFeedbackSeverity::NOTICE
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if current permission are identical to target permission
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isPermissionCorrect()
|
||||
{
|
||||
if ($this->isWindowsOs()) {
|
||||
return true;
|
||||
}
|
||||
if ($this->getCurrentPermission() === $this->getTargetPermission()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current permission of node
|
||||
*
|
||||
* @return string eg. 2775 for dirs, 0664 for files
|
||||
*/
|
||||
protected function getCurrentPermission()
|
||||
{
|
||||
$permissions = decoct((int)fileperms($this->getAbsolutePath()));
|
||||
return substr($permissions, -4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns TRUE if OS is windows
|
||||
*
|
||||
* @return bool TRUE on windows
|
||||
*/
|
||||
protected function isWindowsOs()
|
||||
{
|
||||
return Environment::isWindows();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cut off project path from given path
|
||||
*
|
||||
* @param string $path Given path
|
||||
* @return string Relative path, but beginning with /
|
||||
* @throws Exception\InvalidArgumentException
|
||||
*/
|
||||
protected function getRelativePathBelowSiteRoot($path = null)
|
||||
{
|
||||
if ($path === null) {
|
||||
$path = $this->getAbsolutePath();
|
||||
}
|
||||
$projectPath = Environment::getProjectPath();
|
||||
if (strpos($path, $projectPath, 0) !== 0) {
|
||||
throw new InvalidArgumentException(
|
||||
'Public path is not first part of given path',
|
||||
1366398198
|
||||
);
|
||||
}
|
||||
$relativePath = substr($path, strlen($projectPath), strlen($path));
|
||||
// Add a forward slash again, so we don't end up with an empty string
|
||||
if ($relativePath === '') {
|
||||
$relativePath = '/';
|
||||
}
|
||||
return $relativePath;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,406 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Install\FolderStructure;
|
||||
|
||||
use TYPO3\CMS\Core\Core\Environment;
|
||||
use TYPO3\CMS\Install\WebserverType;
|
||||
|
||||
/**
|
||||
* Factory returns default folder structure object hierarchy
|
||||
* @internal This class is only meant to be used within EXT:install and is not part of the TYPO3 Core API.
|
||||
*/
|
||||
final readonly class DefaultFactory
|
||||
{
|
||||
private const string TEMPLATE_PATH = __DIR__ . '/../../Resources/Private/FolderStructureTemplateFiles';
|
||||
|
||||
/**
|
||||
* Get default structure object hierarchy
|
||||
*/
|
||||
public function getStructure(WebserverType $webserverType = WebserverType::Other): StructureFacadeInterface
|
||||
{
|
||||
$rootNode = new RootNode($this->getDefaultStructureDefinition($webserverType), null);
|
||||
return new StructureFacade($rootNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default definition of folder and file structure with dynamic
|
||||
* permission settings
|
||||
*/
|
||||
private function getDefaultStructureDefinition(WebserverType $webserverType): array
|
||||
{
|
||||
$filePermission = $GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'];
|
||||
$directoryPermission = $GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'];
|
||||
if (Environment::getPublicPath() === Environment::getProjectPath()) {
|
||||
$structure = [
|
||||
// Note that root node has no trailing slash like all others
|
||||
'name' => Environment::getPublicPath(),
|
||||
'targetPermission' => $directoryPermission,
|
||||
'children' => [
|
||||
[
|
||||
'name' => 'typo3temp',
|
||||
'type' => LinkOrDirectoryNode::class,
|
||||
'targetPermission' => $directoryPermission,
|
||||
'children' => [
|
||||
[
|
||||
'name' => 'index.html',
|
||||
'type' => FileNode::class,
|
||||
'targetPermission' => $filePermission,
|
||||
'targetContent' => '',
|
||||
],
|
||||
$this->getTemporaryAssetsFolderStructure(),
|
||||
[
|
||||
'name' => 'var',
|
||||
'type' => LinkOrDirectoryNode::class,
|
||||
'targetPermission' => $directoryPermission,
|
||||
'children' => [
|
||||
[
|
||||
'name' => '.htaccess',
|
||||
'type' => FileNode::class,
|
||||
'targetPermission' => $filePermission,
|
||||
'targetContentFile' => self::TEMPLATE_PATH . '/typo3temp-var-htaccess',
|
||||
],
|
||||
[
|
||||
'name' => 'cache',
|
||||
'type' => DirectoryNode::class,
|
||||
'targetPermission' => $directoryPermission,
|
||||
],
|
||||
[
|
||||
'name' => 'build',
|
||||
'type' => DirectoryNode::class,
|
||||
'targetPermission' => $directoryPermission,
|
||||
],
|
||||
[
|
||||
'name' => 'lock',
|
||||
'type' => DirectoryNode::class,
|
||||
'targetPermission' => $directoryPermission,
|
||||
],
|
||||
[
|
||||
'name' => 'transient',
|
||||
'type' => DirectoryNode::class,
|
||||
'targetPermission' => $directoryPermission,
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
$this->getFileadminStructure(),
|
||||
],
|
||||
];
|
||||
|
||||
// Have a default .htaccess if running apache web server or a default web.config if running IIS
|
||||
if ($webserverType->isApacheServer()) {
|
||||
$structure['children'][] = [
|
||||
'name' => '.htaccess',
|
||||
'type' => FileNode::class,
|
||||
'targetPermission' => $filePermission,
|
||||
'targetContentFile' => self::TEMPLATE_PATH . '/root-htaccess',
|
||||
];
|
||||
} elseif ($webserverType->isMicrosoftInternetInformationServer()) {
|
||||
$structure['children'][] = [
|
||||
'name' => 'web.config',
|
||||
'type' => FileNode::class,
|
||||
'targetPermission' => $filePermission,
|
||||
'targetContentFile' => self::TEMPLATE_PATH . '/root-web-config',
|
||||
];
|
||||
}
|
||||
|
||||
if (!Environment::isComposerMode()) {
|
||||
$structure['children'][] = [
|
||||
'name' => 'typo3conf',
|
||||
'type' => DirectoryNode::class,
|
||||
'targetPermission' => $directoryPermission,
|
||||
'children' => [
|
||||
[
|
||||
'name' => 'ext',
|
||||
'type' => DirectoryNode::class,
|
||||
'targetPermission' => $directoryPermission,
|
||||
],
|
||||
[
|
||||
'name' => 'l10n',
|
||||
'type' => DirectoryNode::class,
|
||||
'targetPermission' => $directoryPermission,
|
||||
],
|
||||
[
|
||||
'name' => 'sites',
|
||||
'type' => DirectoryNode::class,
|
||||
'targetPermission' => $directoryPermission,
|
||||
],
|
||||
[
|
||||
'name' => 'system',
|
||||
'type' => DirectoryNode::class,
|
||||
'targetPermission' => $directoryPermission,
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
} else {
|
||||
// This is when the public path is a subfolder (e.g. public/ or web/)
|
||||
$publicPath = rtrim(Environment::getRelativePublicPath(), '/');
|
||||
|
||||
$publicPathSubStructure = [
|
||||
[
|
||||
'name' => 'typo3temp',
|
||||
'type' => DirectoryNode::class,
|
||||
'targetPermission' => $directoryPermission,
|
||||
'children' => [
|
||||
[
|
||||
'name' => 'index.html',
|
||||
'type' => FileNode::class,
|
||||
'targetPermission' => $filePermission,
|
||||
'targetContent' => '',
|
||||
],
|
||||
$this->getTemporaryAssetsFolderStructure(),
|
||||
],
|
||||
],
|
||||
$this->getFileadminStructure(),
|
||||
];
|
||||
|
||||
// Have a default .htaccess if running apache web server or a default web.config if running IIS
|
||||
if ($webserverType->isApacheServer()) {
|
||||
$publicPathSubStructure[] = [
|
||||
'name' => '.htaccess',
|
||||
'type' => FileNode::class,
|
||||
'targetPermission' => $filePermission,
|
||||
'targetContentFile' => self::TEMPLATE_PATH . '/root-htaccess',
|
||||
];
|
||||
} elseif ($webserverType->isMicrosoftInternetInformationServer()) {
|
||||
$publicPathSubStructure[] = [
|
||||
'name' => 'web.config',
|
||||
'type' => FileNode::class,
|
||||
'targetPermission' => $filePermission,
|
||||
'targetContentFile' => self::TEMPLATE_PATH . '/root-web-config',
|
||||
];
|
||||
}
|
||||
|
||||
if (!Environment::isComposerMode()) {
|
||||
$publicPathSubStructure[] = [
|
||||
'name' => 'typo3conf',
|
||||
'type' => DirectoryNode::class,
|
||||
'targetPermission' => $directoryPermission,
|
||||
];
|
||||
}
|
||||
|
||||
$structure = [
|
||||
// Note that root node has no trailing slash like all others
|
||||
'name' => Environment::getProjectPath(),
|
||||
'targetPermission' => $directoryPermission,
|
||||
'children' => [
|
||||
[
|
||||
'name' => 'config',
|
||||
'type' => DirectoryNode::class,
|
||||
'targetPermission' => $directoryPermission,
|
||||
'children' => [
|
||||
[
|
||||
'name' => 'sites',
|
||||
'type' => DirectoryNode::class,
|
||||
'targetPermission' => $directoryPermission,
|
||||
],
|
||||
[
|
||||
'name' => 'system',
|
||||
'type' => DirectoryNode::class,
|
||||
'targetPermission' => $directoryPermission,
|
||||
],
|
||||
],
|
||||
],
|
||||
$this->getPublicStructure($publicPath, $publicPathSubStructure),
|
||||
[
|
||||
'name' => 'var',
|
||||
'type' => DirectoryNode::class,
|
||||
'targetPermission' => $directoryPermission,
|
||||
'children' => [
|
||||
[
|
||||
'name' => '.htaccess',
|
||||
'type' => FileNode::class,
|
||||
'targetPermission' => $filePermission,
|
||||
'targetContentFile' => self::TEMPLATE_PATH . '/typo3temp-var-htaccess',
|
||||
],
|
||||
[
|
||||
'name' => 'charset',
|
||||
'type' => DirectoryNode::class,
|
||||
'targetPermission' => $directoryPermission,
|
||||
],
|
||||
[
|
||||
'name' => 'cache',
|
||||
'type' => DirectoryNode::class,
|
||||
'targetPermission' => $directoryPermission,
|
||||
],
|
||||
[
|
||||
'name' => 'labels',
|
||||
'type' => DirectoryNode::class,
|
||||
'targetPermission' => $directoryPermission,
|
||||
],
|
||||
[
|
||||
'name' => 'lock',
|
||||
'type' => DirectoryNode::class,
|
||||
'targetPermission' => $directoryPermission,
|
||||
],
|
||||
[
|
||||
'name' => 'transient',
|
||||
'type' => DirectoryNode::class,
|
||||
'targetPermission' => $directoryPermission,
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
return $structure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get public path structure while resolving nested paths
|
||||
*/
|
||||
private function getPublicStructure(string $publicPath, array $subStructure): array
|
||||
{
|
||||
$directoryPermission = $GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'];
|
||||
$publicPathParts = array_reverse(explode('/', $publicPath));
|
||||
|
||||
$lastNode = null;
|
||||
foreach ($publicPathParts as $publicPathPart) {
|
||||
$node = [
|
||||
'name' => $publicPathPart,
|
||||
'type' => DirectoryNode::class,
|
||||
'targetPermission' => $directoryPermission,
|
||||
];
|
||||
if ($lastNode !== null) {
|
||||
$node['children'][] = $lastNode;
|
||||
} else {
|
||||
$node['children'] = $subStructure;
|
||||
}
|
||||
$lastNode = $node;
|
||||
}
|
||||
|
||||
return $lastNode;
|
||||
}
|
||||
|
||||
private function getFileadminStructure(): array
|
||||
{
|
||||
$filePermission = $GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'];
|
||||
$directoryPermission = $GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'];
|
||||
return [
|
||||
'name' => !empty($GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir']) ? rtrim($GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'], '/') : 'fileadmin',
|
||||
'type' => LinkOrDirectoryNode::class,
|
||||
'targetPermission' => $directoryPermission,
|
||||
'children' => [
|
||||
[
|
||||
'name' => '.htaccess',
|
||||
'type' => FileNode::class,
|
||||
'targetPermission' => $filePermission,
|
||||
'targetContentFile' => self::TEMPLATE_PATH . '/resources-root-htaccess',
|
||||
],
|
||||
[
|
||||
'name' => '_temp_',
|
||||
'type' => DirectoryNode::class,
|
||||
'targetPermission' => $directoryPermission,
|
||||
'children' => [
|
||||
[
|
||||
'name' => '.htaccess',
|
||||
'type' => FileNode::class,
|
||||
'targetPermission' => $filePermission,
|
||||
'targetContentFile' => self::TEMPLATE_PATH . '/fileadmin-temp-htaccess',
|
||||
],
|
||||
[
|
||||
'name' => 'index.html',
|
||||
'type' => FileNode::class,
|
||||
'targetPermission' => $filePermission,
|
||||
'targetContentFile' => self::TEMPLATE_PATH . '/fileadmin-temp-index.html',
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'user_upload',
|
||||
'type' => LinkOrDirectoryNode::class,
|
||||
'targetPermission' => $directoryPermission,
|
||||
'children' => [
|
||||
[
|
||||
'name' => '_temp_',
|
||||
'type' => DirectoryNode::class,
|
||||
'targetPermission' => $directoryPermission,
|
||||
'children' => [
|
||||
[
|
||||
'name' => 'index.html',
|
||||
'type' => FileNode::class,
|
||||
'targetPermission' => $filePermission,
|
||||
'targetContent' => '',
|
||||
],
|
||||
[
|
||||
'name' => 'importexport',
|
||||
'type' => DirectoryNode::class,
|
||||
'targetPermission' => $directoryPermission,
|
||||
'children' => [
|
||||
[
|
||||
'name' => '.htaccess',
|
||||
'type' => FileNode::class,
|
||||
'targetPermission' => $filePermission,
|
||||
'targetContentFile' => self::TEMPLATE_PATH . '/fileadmin-user_upload-temp-importexport-htaccess',
|
||||
],
|
||||
[
|
||||
'name' => 'index.html',
|
||||
'type' => FileNode::class,
|
||||
'targetPermission' => $filePermission,
|
||||
'targetContentFile' => self::TEMPLATE_PATH . '/fileadmin-temp-index.html',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'index.html',
|
||||
'type' => FileNode::class,
|
||||
'targetPermission' => $filePermission,
|
||||
'targetContent' => '',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* This defines the structure for typo3temp/assets
|
||||
*/
|
||||
private function getTemporaryAssetsFolderStructure(): array
|
||||
{
|
||||
$directoryPermission = $GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'];
|
||||
return [
|
||||
'name' => 'assets',
|
||||
'type' => DirectoryNode::class,
|
||||
'targetPermission' => $directoryPermission,
|
||||
'children' => [
|
||||
[
|
||||
'name' => 'css',
|
||||
'type' => DirectoryNode::class,
|
||||
'targetPermission' => $directoryPermission,
|
||||
],
|
||||
[
|
||||
'name' => 'js',
|
||||
'type' => DirectoryNode::class,
|
||||
'targetPermission' => $directoryPermission,
|
||||
],
|
||||
[
|
||||
'name' => 'images',
|
||||
'type' => DirectoryNode::class,
|
||||
'targetPermission' => $directoryPermission,
|
||||
],
|
||||
[
|
||||
'name' => '_processed_',
|
||||
'type' => DirectoryNode::class,
|
||||
'targetPermission' => $directoryPermission,
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Install\FolderStructure;
|
||||
|
||||
use TYPO3\CMS\Core\Core\Environment;
|
||||
use TYPO3\CMS\Core\Messaging\FlashMessage;
|
||||
use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity;
|
||||
|
||||
/**
|
||||
* Service class to check the default folder permissions
|
||||
* @internal This class is only meant to be used within EXT:install and is not part of the TYPO3 Core API.
|
||||
*/
|
||||
class DefaultPermissionsCheck
|
||||
{
|
||||
/**
|
||||
* @var array Recommended values for a secure production site
|
||||
*
|
||||
* These are not the default settings (which are 0664/2775), because they might not work on every installation.
|
||||
* For security reasons these are the recommended values nevertheless (no world-readable files).
|
||||
* It's up to the admins to decide if these recommended secure values can be applied to their installation.
|
||||
*/
|
||||
protected $recommended = [
|
||||
'fileCreateMask' => '0660',
|
||||
'folderCreateMask' => '2770',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array Verbose names of the settings
|
||||
*/
|
||||
protected $names = [
|
||||
'fileCreateMask' => 'Default File permissions',
|
||||
'folderCreateMask' => 'Default Directory permissions',
|
||||
];
|
||||
|
||||
/**
|
||||
* Checks a BE/*mask setting for it's security
|
||||
*
|
||||
* If it permits world writing: Error
|
||||
* If it permits world reading: Warning
|
||||
* If it permits group writing: Notice
|
||||
* If it permits group reading: Notice
|
||||
* If it permits only user read/write: Ok
|
||||
*
|
||||
* @param string $which fileCreateMask or folderCreateMask
|
||||
*/
|
||||
public function getMaskStatus($which): FlashMessage
|
||||
{
|
||||
$octal = '0' . $GLOBALS['TYPO3_CONF_VARS']['SYS'][$which];
|
||||
$dec = octdec($octal);
|
||||
$perms = [
|
||||
'ox' => ($dec & 001) == 001,
|
||||
'ow' => ($dec & 002) == 002,
|
||||
'or' => ($dec & 004) == 004,
|
||||
'gx' => ($dec & 010) == 010,
|
||||
'gw' => ($dec & 020) == 020,
|
||||
'gr' => ($dec & 040) == 040,
|
||||
'ux' => ($dec & 0100) == 0100,
|
||||
'uw' => ($dec & 0200) == 0200,
|
||||
'ur' => ($dec & 0400) == 0400,
|
||||
'setgid' => ($dec & 02000) == 02000,
|
||||
];
|
||||
$extraMessage = '';
|
||||
$groupPermissions = false;
|
||||
if (!$perms['uw'] || !$perms['ur']) {
|
||||
$permissionStatus = ContextualFeedbackSeverity::ERROR;
|
||||
$extraMessage = ' (not read or writable by the user)';
|
||||
} elseif ($perms['ow']) {
|
||||
if (Environment::isWindows()) {
|
||||
$permissionStatus = ContextualFeedbackSeverity::INFO;
|
||||
$extraMessage = ' (writable by anyone on the server). This is the default behavior on a Windows system';
|
||||
} else {
|
||||
$permissionStatus = ContextualFeedbackSeverity::ERROR;
|
||||
$extraMessage = ' (writable by anyone on the server)';
|
||||
}
|
||||
} elseif ($perms['or']) {
|
||||
$permissionStatus = ContextualFeedbackSeverity::NOTICE;
|
||||
$extraMessage = ' (readable by anyone on the server). This is the default set by TYPO3 CMS to be as much compatible as possible but if your system allows, please consider to change rights';
|
||||
} elseif ($perms['gw']) {
|
||||
$permissionStatus = ContextualFeedbackSeverity::OK;
|
||||
$extraMessage = ' (group writable)';
|
||||
$groupPermissions = true;
|
||||
} elseif ($perms['gr']) {
|
||||
$permissionStatus = ContextualFeedbackSeverity::OK;
|
||||
$extraMessage = ' (group readable)';
|
||||
$groupPermissions = true;
|
||||
} else {
|
||||
$permissionStatus = ContextualFeedbackSeverity::OK;
|
||||
}
|
||||
$message = 'Recommended: ' . $this->recommended[$which] . '.';
|
||||
$message .= ' Currently configured as ';
|
||||
if ($GLOBALS['TYPO3_CONF_VARS']['SYS'][$which] === $this->recommended[$which]) {
|
||||
$message .= 'recommended';
|
||||
} else {
|
||||
$message .= $GLOBALS['TYPO3_CONF_VARS']['SYS'][$which];
|
||||
}
|
||||
$message .= $extraMessage . '.';
|
||||
if ($groupPermissions) {
|
||||
$message .= ' This is fine as long as the web server\'s group only comprises trusted users.';
|
||||
if (!empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['createGroup'])) {
|
||||
$message .= ' Your site is configured (SYS/createGroup) to write as group \'' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['createGroup'] . '\'.';
|
||||
}
|
||||
}
|
||||
return new FlashMessage(
|
||||
$message,
|
||||
$this->names[$which] . ' (SYS/' . $which . ')',
|
||||
$permissionStatus
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,310 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Install\FolderStructure;
|
||||
|
||||
use TYPO3\CMS\Core\Messaging\FlashMessage;
|
||||
use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity;
|
||||
use TYPO3\CMS\Core\Utility\StringUtility;
|
||||
use TYPO3\CMS\Install\FolderStructure\Exception\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* A directory
|
||||
* @internal This class is only meant to be used within EXT:install and is not part of the TYPO3 Core API.
|
||||
*/
|
||||
class DirectoryNode extends AbstractNode implements NodeInterface
|
||||
{
|
||||
/**
|
||||
* @var string Default for directories is octal 02775 == decimal 1533
|
||||
*/
|
||||
protected $targetPermission = '2775';
|
||||
|
||||
/**
|
||||
* Implement constructor
|
||||
*
|
||||
* @param array $structure Structure array
|
||||
* @param NodeInterface $parent Parent object
|
||||
* @throws Exception\InvalidArgumentException
|
||||
*/
|
||||
public function __construct(array $structure, ?NodeInterface $parent = null)
|
||||
{
|
||||
if ($parent === null) {
|
||||
throw new InvalidArgumentException(
|
||||
'Node must have parent',
|
||||
1366222203
|
||||
);
|
||||
}
|
||||
$this->parent = $parent;
|
||||
|
||||
// Ensure name is a single segment, but not a path like foo/bar or an absolute path /foo
|
||||
if (str_contains($structure['name'], '/')) {
|
||||
throw new InvalidArgumentException(
|
||||
'Directory name must not contain forward slash',
|
||||
1366226639
|
||||
);
|
||||
}
|
||||
$this->name = $structure['name'];
|
||||
|
||||
if (isset($structure['targetPermission'])) {
|
||||
$this->setTargetPermission($structure['targetPermission']);
|
||||
}
|
||||
|
||||
if (array_key_exists('children', $structure)) {
|
||||
$this->createChildren($structure['children']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get own status and status of child objects
|
||||
*
|
||||
* @return FlashMessage[]
|
||||
*/
|
||||
public function getStatus(): array
|
||||
{
|
||||
$result = [];
|
||||
if (!$this->exists()) {
|
||||
$status = new FlashMessage(
|
||||
'The Install Tool can try to create it',
|
||||
'Directory ' . $this->getRelativePathBelowSiteRoot() . ' does not exist',
|
||||
ContextualFeedbackSeverity::WARNING
|
||||
);
|
||||
$result[] = $status;
|
||||
} else {
|
||||
$result = $this->getSelfStatus();
|
||||
}
|
||||
return array_merge($result, $this->getChildrenStatus());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a test file and delete again if directory exists
|
||||
*
|
||||
* @return bool TRUE if test file creation was successful
|
||||
*/
|
||||
public function isWritable()
|
||||
{
|
||||
$result = true;
|
||||
if (!$this->exists()) {
|
||||
$result = false;
|
||||
} elseif (!$this->canFileBeCreated()) {
|
||||
$result = false;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix structure
|
||||
*
|
||||
* If there is nothing to fix, returns an empty array
|
||||
*
|
||||
* @return FlashMessage[]
|
||||
*/
|
||||
public function fix(): array
|
||||
{
|
||||
$result = $this->fixSelf();
|
||||
foreach ($this->children as $child) {
|
||||
/** @var NodeInterface $child */
|
||||
$result = array_merge($result, $child->fix());
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix this directory:
|
||||
*
|
||||
* - create with correct permissions if it was not existing
|
||||
* - if there is no "write" permissions, try to fix it
|
||||
* - leave it alone otherwise
|
||||
*
|
||||
* @return FlashMessage[]
|
||||
*/
|
||||
protected function fixSelf(): array
|
||||
{
|
||||
$result = [];
|
||||
if (!$this->exists()) {
|
||||
$resultCreateDirectory = $this->createDirectory();
|
||||
$result[] = $resultCreateDirectory;
|
||||
if ($resultCreateDirectory->getSeverity() === ContextualFeedbackSeverity::OK
|
||||
&& !$this->isPermissionCorrect()
|
||||
) {
|
||||
$result[] = $this->fixPermission();
|
||||
}
|
||||
} elseif (!$this->isWritable()) {
|
||||
// If directory is not writable, we might have permissions to fix that
|
||||
// Try it:
|
||||
$result[] = $this->fixPermission();
|
||||
} elseif (!$this->isDirectory()) {
|
||||
$fileType = @filetype($this->getAbsolutePath());
|
||||
if ($fileType) {
|
||||
$messageBody
|
||||
= 'The target ' . $this->getRelativePathBelowSiteRoot() . ' should be a directory,'
|
||||
. ' but is of type ' . $fileType . '. This cannot be fixed automatically. Please investigate.'
|
||||
;
|
||||
} else {
|
||||
$messageBody
|
||||
= 'The target ' . $this->getRelativePathBelowSiteRoot() . ' should be a directory,'
|
||||
. ' but is of unknown type, probably because an upper level directory does not exist. Please investigate.'
|
||||
;
|
||||
}
|
||||
$result[] = new FlashMessage(
|
||||
$messageBody,
|
||||
'Path ' . $this->getRelativePathBelowSiteRoot() . ' is not a directory',
|
||||
ContextualFeedbackSeverity::ERROR
|
||||
);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create directory if not exists
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function createDirectory(): FlashMessage
|
||||
{
|
||||
if ($this->exists()) {
|
||||
throw new Exception(
|
||||
'Directory ' . $this->getAbsolutePath() . ' already exists',
|
||||
1366740091
|
||||
);
|
||||
}
|
||||
$result = @mkdir($this->getAbsolutePath());
|
||||
if ($result === true) {
|
||||
return new FlashMessage(
|
||||
'',
|
||||
'Directory ' . $this->getRelativePathBelowSiteRoot() . ' successfully created.'
|
||||
);
|
||||
}
|
||||
return new FlashMessage(
|
||||
'The target directory could not be created. There is probably a'
|
||||
. ' group or owner permission problem on the parent directory.',
|
||||
'Directory ' . $this->getRelativePathBelowSiteRoot() . ' not created!',
|
||||
ContextualFeedbackSeverity::ERROR
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get status of directory - used in root and directory node
|
||||
*
|
||||
* @return FlashMessage[]
|
||||
*/
|
||||
protected function getSelfStatus(): array
|
||||
{
|
||||
$result = [];
|
||||
if (!$this->isDirectory()) {
|
||||
$result[] = new FlashMessage(
|
||||
'Directory ' . $this->getRelativePathBelowSiteRoot() . ' should be a directory,'
|
||||
. ' but is of type ' . filetype($this->getAbsolutePath()),
|
||||
$this->getRelativePathBelowSiteRoot() . ' is not a directory',
|
||||
ContextualFeedbackSeverity::ERROR
|
||||
);
|
||||
} elseif (!$this->isWritable()) {
|
||||
$result[] = new FlashMessage(
|
||||
'Path ' . $this->getAbsolutePath() . ' exists, but no file underneath it'
|
||||
. ' can be created.',
|
||||
'Directory ' . $this->getRelativePathBelowSiteRoot() . ' is not writable',
|
||||
ContextualFeedbackSeverity::ERROR
|
||||
);
|
||||
} elseif (!$this->isPermissionCorrect()) {
|
||||
$result[] = new FlashMessage(
|
||||
'Default configured permissions are ' . $this->getTargetPermission()
|
||||
. ' but current permissions are ' . $this->getCurrentPermission(),
|
||||
'Directory ' . $this->getRelativePathBelowSiteRoot() . ' permissions mismatch',
|
||||
ContextualFeedbackSeverity::NOTICE
|
||||
);
|
||||
} else {
|
||||
$result[] = new FlashMessage(
|
||||
'Is a directory with the configured permissions of ' . $this->getTargetPermission(),
|
||||
'Directory ' . $this->getRelativePathBelowSiteRoot()
|
||||
);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get status of children
|
||||
*
|
||||
* @return FlashMessage[]
|
||||
*/
|
||||
protected function getChildrenStatus(): array
|
||||
{
|
||||
$result = [];
|
||||
foreach ($this->children as $child) {
|
||||
/** @var NodeInterface $child */
|
||||
$result = array_merge($result, $child->getStatus());
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a test file and delete again - helper for isWritable
|
||||
*
|
||||
* @return bool TRUE if test file creation was successful
|
||||
*/
|
||||
protected function canFileBeCreated()
|
||||
{
|
||||
$testFileName = StringUtility::getUniqueId('installToolTest_');
|
||||
$result = @touch($this->getAbsolutePath() . '/' . $testFileName);
|
||||
if ($result === true) {
|
||||
unlink($this->getAbsolutePath() . '/' . $testFileName);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if not is a directory
|
||||
*
|
||||
* @return bool True if node is a directory
|
||||
*/
|
||||
protected function isDirectory()
|
||||
{
|
||||
$path = $this->getAbsolutePath();
|
||||
return !@is_link($path) && @is_dir($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create children nodes - done in directory and root node
|
||||
*
|
||||
* @param array $structure Array of children
|
||||
* @throws Exception\InvalidArgumentException
|
||||
*/
|
||||
protected function createChildren(array $structure)
|
||||
{
|
||||
foreach ($structure as $child) {
|
||||
if (!array_key_exists('type', $child)) {
|
||||
throw new InvalidArgumentException(
|
||||
'Child must have type',
|
||||
1366222204
|
||||
);
|
||||
}
|
||||
if (!array_key_exists('name', $child)) {
|
||||
throw new InvalidArgumentException(
|
||||
'Child must have name',
|
||||
1366222205
|
||||
);
|
||||
}
|
||||
$name = $child['name'];
|
||||
foreach ($this->children as $existingChild) {
|
||||
/** @var NodeInterface $existingChild */
|
||||
if ($existingChild->getName() === $name) {
|
||||
throw new InvalidArgumentException(
|
||||
'Child name must be unique',
|
||||
1366222206
|
||||
);
|
||||
}
|
||||
}
|
||||
$this->children[] = new $child['type']($child, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Install\FolderStructure;
|
||||
|
||||
/**
|
||||
* A folder structure exception
|
||||
*/
|
||||
class Exception extends \TYPO3\CMS\Install\Exception {}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Install\FolderStructure\Exception;
|
||||
|
||||
use TYPO3\CMS\Install\FolderStructure\Exception;
|
||||
|
||||
/**
|
||||
* An invalid argument exception
|
||||
*/
|
||||
class InvalidArgumentException extends Exception {}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Install\FolderStructure\Exception;
|
||||
|
||||
use TYPO3\CMS\Install\FolderStructure\Exception;
|
||||
|
||||
/**
|
||||
* A root node exception
|
||||
*/
|
||||
class RootNodeException extends Exception {}
|
||||
@@ -0,0 +1,319 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Install\FolderStructure;
|
||||
|
||||
use TYPO3\CMS\Core\Messaging\FlashMessage;
|
||||
use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity;
|
||||
use TYPO3\CMS\Install\FolderStructure\Exception\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* A file
|
||||
* @internal This class is only meant to be used within EXT:install and is not part of the TYPO3 Core API.
|
||||
*/
|
||||
class FileNode extends AbstractNode implements NodeInterface
|
||||
{
|
||||
/**
|
||||
* @var string Default for files is octal 0664 == decimal 436
|
||||
*/
|
||||
protected $targetPermission = '0664';
|
||||
|
||||
/**
|
||||
* @var string|null Target content of file. If NULL, target content is ignored
|
||||
*/
|
||||
protected $targetContent;
|
||||
|
||||
/**
|
||||
* Implement constructor
|
||||
*
|
||||
* @param array $structure Structure array
|
||||
* @param NodeInterface $parent Parent object
|
||||
* @throws Exception\InvalidArgumentException
|
||||
*/
|
||||
public function __construct(array $structure, ?NodeInterface $parent = null)
|
||||
{
|
||||
if ($parent === null) {
|
||||
throw new InvalidArgumentException(
|
||||
'File node must have parent',
|
||||
1366927513
|
||||
);
|
||||
}
|
||||
$this->parent = $parent;
|
||||
|
||||
// Ensure name is a single segment, but not a path like foo/bar or an absolute path /foo
|
||||
if (str_contains($structure['name'], '/')) {
|
||||
throw new InvalidArgumentException(
|
||||
'File name must not contain forward slash',
|
||||
1366222207
|
||||
);
|
||||
}
|
||||
$this->name = $structure['name'];
|
||||
|
||||
if (isset($structure['targetPermission'])) {
|
||||
$this->setTargetPermission($structure['targetPermission']);
|
||||
}
|
||||
|
||||
if (isset($structure['targetContent']) && isset($structure['targetContentFile'])) {
|
||||
throw new InvalidArgumentException(
|
||||
'Either targetContent or targetContentFile can be set, but not both',
|
||||
1380364361
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($structure['targetContent'])) {
|
||||
$this->targetContent = $structure['targetContent'];
|
||||
}
|
||||
if (isset($structure['targetContentFile'])) {
|
||||
if (!is_readable($structure['targetContentFile'])) {
|
||||
throw new InvalidArgumentException(
|
||||
'targetContentFile ' . $structure['targetContentFile'] . ' does not exist or is not readable',
|
||||
1380364362
|
||||
);
|
||||
}
|
||||
$fileContent = file_get_contents($structure['targetContentFile']);
|
||||
if ($fileContent === false) {
|
||||
throw new InvalidArgumentException(
|
||||
'Error while reading targetContentFile ' . $structure['targetContentFile'],
|
||||
1380364363
|
||||
);
|
||||
}
|
||||
$this->targetContent = $fileContent;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get own status
|
||||
* Returns warning if file not exists
|
||||
* Returns error if file exists but content is not as expected (can / shouldn't be fixed)
|
||||
*
|
||||
* @return FlashMessage[]
|
||||
*/
|
||||
public function getStatus(): array
|
||||
{
|
||||
$result = [];
|
||||
if (!$this->exists()) {
|
||||
$result[] = new FlashMessage(
|
||||
'By using "Try to fix errors" we can try to create it',
|
||||
'File ' . $this->getRelativePathBelowSiteRoot() . ' does not exist',
|
||||
ContextualFeedbackSeverity::WARNING
|
||||
);
|
||||
} else {
|
||||
$result = $this->getSelfStatus();
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix structure
|
||||
*
|
||||
* If there is nothing to fix, returns an empty array
|
||||
*
|
||||
* @return FlashMessage[]
|
||||
*/
|
||||
public function fix(): array
|
||||
{
|
||||
$result = $this->fixSelf();
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix this node: create if not there, fix permissions
|
||||
*
|
||||
* @return FlashMessage[]
|
||||
*/
|
||||
protected function fixSelf(): array
|
||||
{
|
||||
$result = [];
|
||||
if (!$this->exists()) {
|
||||
$resultCreateFile = $this->createFile();
|
||||
$result[] = $resultCreateFile;
|
||||
if ($resultCreateFile->getSeverity() === ContextualFeedbackSeverity::OK
|
||||
&& $this->targetContent !== null
|
||||
) {
|
||||
$result[] = $this->setContent();
|
||||
if (!$this->isPermissionCorrect()) {
|
||||
$result[] = $this->fixPermission();
|
||||
}
|
||||
}
|
||||
} elseif (!$this->isFile()) {
|
||||
$fileType = @filetype($this->getAbsolutePath());
|
||||
if ($fileType) {
|
||||
$messageBody
|
||||
= 'The target ' . $this->getRelativePathBelowSiteRoot() . ' should be a file,'
|
||||
. ' but is of type ' . $fileType . '. This cannot be fixed automatically. Please investigate.'
|
||||
;
|
||||
} else {
|
||||
$messageBody
|
||||
= 'The target ' . $this->getRelativePathBelowSiteRoot() . ' should be a file,'
|
||||
. ' but is of unknown type, probably because an upper level directory does not exist. Please investigate.'
|
||||
;
|
||||
}
|
||||
$result[] = new FlashMessage(
|
||||
$messageBody,
|
||||
'Path ' . $this->getRelativePathBelowSiteRoot() . ' is not a file',
|
||||
ContextualFeedbackSeverity::ERROR
|
||||
);
|
||||
} elseif (!$this->isPermissionCorrect()) {
|
||||
$result[] = $this->fixPermission();
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create file if not exists
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function createFile(): FlashMessage
|
||||
{
|
||||
if ($this->exists()) {
|
||||
throw new Exception(
|
||||
'File ' . $this->getRelativePathBelowSiteRoot() . ' already exists',
|
||||
1367048077
|
||||
);
|
||||
}
|
||||
$result = @touch($this->getAbsolutePath());
|
||||
if ($result === true) {
|
||||
return new FlashMessage(
|
||||
'',
|
||||
'File ' . $this->getRelativePathBelowSiteRoot() . ' successfully created.'
|
||||
);
|
||||
}
|
||||
return new FlashMessage(
|
||||
'The target file could not be created. There is probably a'
|
||||
. ' group or owner permission problem on the parent directory.',
|
||||
'File ' . $this->getRelativePathBelowSiteRoot() . ' not created!',
|
||||
ContextualFeedbackSeverity::ERROR
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get status of file
|
||||
*
|
||||
* @return FlashMessage[]
|
||||
*/
|
||||
protected function getSelfStatus(): array
|
||||
{
|
||||
$result = [];
|
||||
if (!$this->isFile()) {
|
||||
$result[] = new FlashMessage(
|
||||
'Path ' . $this->getAbsolutePath() . ' should be a file,'
|
||||
. ' but is of type ' . filetype($this->getAbsolutePath()),
|
||||
$this->getRelativePathBelowSiteRoot() . ' is not a file',
|
||||
ContextualFeedbackSeverity::ERROR
|
||||
);
|
||||
} elseif (!$this->isWritable()) {
|
||||
$result[] = new FlashMessage(
|
||||
'File ' . $this->getRelativePathBelowSiteRoot() . ' exists, but is not writable.',
|
||||
'File ' . $this->getRelativePathBelowSiteRoot() . ' is not writable',
|
||||
ContextualFeedbackSeverity::NOTICE
|
||||
);
|
||||
} elseif (!$this->isPermissionCorrect()) {
|
||||
$result[] = new FlashMessage(
|
||||
'Default configured permissions are ' . $this->getTargetPermission()
|
||||
. ' but file permissions are ' . $this->getCurrentPermission(),
|
||||
'File ' . $this->getRelativePathBelowSiteRoot() . ' permissions mismatch',
|
||||
ContextualFeedbackSeverity::NOTICE
|
||||
);
|
||||
}
|
||||
if ($this->isFile() && !$this->isContentCorrect()) {
|
||||
$result[] = new FlashMessage(
|
||||
'File content is not identical to default content. This file may have been changed manually.'
|
||||
. ' The Install Tool will not overwrite the current version!',
|
||||
'File ' . $this->getRelativePathBelowSiteRoot() . ' content differs',
|
||||
ContextualFeedbackSeverity::NOTICE
|
||||
);
|
||||
} else {
|
||||
$result[] = new FlashMessage(
|
||||
'Is a file with the default content and configured permissions of ' . $this->getTargetPermission(),
|
||||
'File ' . $this->getRelativePathBelowSiteRoot()
|
||||
);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare current file content with target file content
|
||||
*
|
||||
* @throws Exception If file does not exist
|
||||
* @return bool TRUE if current and target file content are identical
|
||||
*/
|
||||
protected function isContentCorrect()
|
||||
{
|
||||
$absolutePath = $this->getAbsolutePath();
|
||||
if (is_link($absolutePath) || !is_file($absolutePath)) {
|
||||
throw new Exception(
|
||||
'File ' . $absolutePath . ' must exist',
|
||||
1367056363
|
||||
);
|
||||
}
|
||||
$result = false;
|
||||
if ($this->targetContent === null) {
|
||||
$result = true;
|
||||
} else {
|
||||
$targetContentHash = md5($this->targetContent);
|
||||
$currentContentHash = md5((string)file_get_contents($absolutePath));
|
||||
if ($targetContentHash === $currentContentHash) {
|
||||
$result = true;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets content of file to target content
|
||||
*
|
||||
* @throws Exception If file does not exist
|
||||
*/
|
||||
protected function setContent(): FlashMessage
|
||||
{
|
||||
$absolutePath = $this->getAbsolutePath();
|
||||
if (is_link($absolutePath) || !is_file($absolutePath)) {
|
||||
throw new Exception(
|
||||
'File ' . $absolutePath . ' must exist',
|
||||
1367060201
|
||||
);
|
||||
}
|
||||
if ($this->targetContent === null) {
|
||||
throw new Exception(
|
||||
'Target content not defined for ' . $absolutePath,
|
||||
1367060202
|
||||
);
|
||||
}
|
||||
$result = @file_put_contents($absolutePath, $this->targetContent);
|
||||
if ($result !== false) {
|
||||
return new FlashMessage(
|
||||
'',
|
||||
'Set content to ' . $this->getRelativePathBelowSiteRoot()
|
||||
);
|
||||
}
|
||||
return new FlashMessage(
|
||||
'Setting content of the file failed for unknown reasons.',
|
||||
'Setting content to ' . $this->getRelativePathBelowSiteRoot() . ' failed',
|
||||
ContextualFeedbackSeverity::ERROR
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if not is a file
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isFile()
|
||||
{
|
||||
$path = $this->getAbsolutePath();
|
||||
return !is_link($path) && is_file($path);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Install\FolderStructure;
|
||||
|
||||
use TYPO3\CMS\Core\Messaging\FlashMessage;
|
||||
use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity;
|
||||
use TYPO3\CMS\Install\FolderStructure\Exception\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* A link
|
||||
* @internal This class is only meant to be used within EXT:install and is not part of the TYPO3 Core API.
|
||||
*/
|
||||
class LinkNode extends AbstractNode implements NodeInterface
|
||||
{
|
||||
/**
|
||||
* @var string Optional link target
|
||||
*/
|
||||
protected $target = '';
|
||||
|
||||
/**
|
||||
* Implement constructor
|
||||
*
|
||||
* @param array $structure Structure array
|
||||
* @param NodeInterface $parent Parent object
|
||||
* @throws Exception\InvalidArgumentException
|
||||
*/
|
||||
public function __construct(array $structure, ?NodeInterface $parent = null)
|
||||
{
|
||||
if ($parent === null) {
|
||||
throw new InvalidArgumentException(
|
||||
'Link node must have parent',
|
||||
1380485700
|
||||
);
|
||||
}
|
||||
$this->parent = $parent;
|
||||
|
||||
// Ensure name is a single segment, but not a path like foo/bar or an absolute path /foo
|
||||
if (str_contains($structure['name'], '/')) {
|
||||
throw new InvalidArgumentException(
|
||||
'File name must not contain forward slash',
|
||||
1380546061
|
||||
);
|
||||
}
|
||||
$this->name = $structure['name'];
|
||||
|
||||
if (isset($structure['target']) && $structure['target'] !== '') {
|
||||
$this->target = $structure['target'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get own status
|
||||
* Returns information status if running on Windows
|
||||
* Returns OK status if is link and possible target is correct
|
||||
* Else returns error (not fixable)
|
||||
*
|
||||
* @return FlashMessage[]
|
||||
*/
|
||||
public function getStatus(): array
|
||||
{
|
||||
if ($this->isWindowsOs()) {
|
||||
return [
|
||||
new FlashMessage(
|
||||
'This node is not handled for Windows OS and should be checked manually.',
|
||||
$this->getRelativePathBelowSiteRoot() . ' should be a link, but this support is incomplete for Windows.',
|
||||
ContextualFeedbackSeverity::INFO
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
if (!$this->exists()) {
|
||||
return [
|
||||
new FlashMessage(
|
||||
'Links cannot be fixed by this system',
|
||||
$this->getRelativePathBelowSiteRoot() . ' should be a link, but it does not exist',
|
||||
ContextualFeedbackSeverity::ERROR
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
if (!$this->isLink()) {
|
||||
$type = @filetype($this->getAbsolutePath());
|
||||
if ($type) {
|
||||
$messageBody
|
||||
= 'The target ' . $this->getRelativePathBelowSiteRoot() . ' should be a link,'
|
||||
. ' but is of type ' . $type . '. This cannot be fixed automatically. Please investigate.'
|
||||
;
|
||||
} else {
|
||||
$messageBody
|
||||
= 'The target ' . $this->getRelativePathBelowSiteRoot() . ' should be a file,'
|
||||
. ' but is of unknown type, probably because an upper level directory does not exist. Please investigate.'
|
||||
;
|
||||
}
|
||||
return [
|
||||
new FlashMessage(
|
||||
$messageBody,
|
||||
'Path ' . $this->getRelativePathBelowSiteRoot() . ' is not a link',
|
||||
ContextualFeedbackSeverity::WARNING
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
if (!$this->isTargetCorrect()) {
|
||||
return [
|
||||
new FlashMessage(
|
||||
'Link target should be ' . $this->getTarget() . ' but is ' . $this->getCurrentTarget(),
|
||||
$this->getRelativePathBelowSiteRoot() . ' is a link, but link target is not as specified',
|
||||
ContextualFeedbackSeverity::ERROR
|
||||
),
|
||||
];
|
||||
}
|
||||
$message = 'Is a link';
|
||||
if ($this->getTarget() !== '') {
|
||||
$message .= ' and correctly points to target ' . $this->getTarget();
|
||||
}
|
||||
return [
|
||||
new FlashMessage(
|
||||
$message,
|
||||
$this->getRelativePathBelowSiteRoot()
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix structure
|
||||
*
|
||||
* If there is nothing to fix, returns an empty array
|
||||
*
|
||||
* @return FlashMessage[]
|
||||
*/
|
||||
public function fix(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get link target
|
||||
*
|
||||
* @return string Link target
|
||||
*/
|
||||
protected function getTarget()
|
||||
{
|
||||
return $this->target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find out if node is a link
|
||||
*
|
||||
* @throws Exception\InvalidArgumentException
|
||||
* @return bool TRUE if node is a link
|
||||
*/
|
||||
protected function isLink()
|
||||
{
|
||||
if (!$this->exists()) {
|
||||
throw new InvalidArgumentException(
|
||||
'Link does not exist',
|
||||
1380556246
|
||||
);
|
||||
}
|
||||
return @is_link($this->getAbsolutePath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the real link target is identical to given target
|
||||
*
|
||||
* @throws Exception\InvalidArgumentException
|
||||
* @return bool TRUE if target is correct
|
||||
*/
|
||||
protected function isTargetCorrect()
|
||||
{
|
||||
if (!$this->exists()) {
|
||||
throw new InvalidArgumentException(
|
||||
'Link does not exist',
|
||||
1380556245
|
||||
);
|
||||
}
|
||||
if (!$this->isLink()) {
|
||||
throw new InvalidArgumentException(
|
||||
'Node is not a link',
|
||||
1380556247
|
||||
);
|
||||
}
|
||||
|
||||
$result = false;
|
||||
$expectedTarget = $this->getTarget();
|
||||
if (empty($expectedTarget)) {
|
||||
$result = true;
|
||||
} else {
|
||||
$actualTarget = $this->getCurrentTarget();
|
||||
if ($expectedTarget === rtrim((string)$actualTarget, '/')) {
|
||||
$result = true;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return current target of link
|
||||
*
|
||||
* @return false|string target
|
||||
*/
|
||||
protected function getCurrentTarget()
|
||||
{
|
||||
return readlink($this->getAbsolutePath());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Install\FolderStructure;
|
||||
|
||||
use TYPO3\CMS\Core\Messaging\FlashMessage;
|
||||
use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity;
|
||||
|
||||
/**
|
||||
* A directory but a link is ok as well
|
||||
* @internal This class is only meant to be used within EXT:install and is not part of the TYPO3 Core API.
|
||||
*/
|
||||
class LinkOrDirectoryNode extends DirectoryNode
|
||||
{
|
||||
/**
|
||||
* Get status of directory - used in root and directory node
|
||||
*
|
||||
* @return FlashMessage[]
|
||||
*/
|
||||
protected function getSelfStatus(): array
|
||||
{
|
||||
$result = [];
|
||||
if (!$this->isDirectory()) {
|
||||
$result[] = new FlashMessage(
|
||||
'Directory ' . $this->getRelativePathBelowSiteRoot() . ' should be a directory or link,'
|
||||
. ' but is of type ' . filetype($this->getAbsolutePath()),
|
||||
$this->getRelativePathBelowSiteRoot() . ' is not a directory',
|
||||
ContextualFeedbackSeverity::ERROR
|
||||
);
|
||||
} elseif (!$this->isWritable()) {
|
||||
$result[] = new FlashMessage(
|
||||
'Path ' . $this->getAbsolutePath() . ' exists, but no file underneath it'
|
||||
. ' can be created.',
|
||||
'Directory ' . $this->getRelativePathBelowSiteRoot() . ' is not writable',
|
||||
ContextualFeedbackSeverity::ERROR
|
||||
);
|
||||
} elseif (!$this->isPermissionCorrect()) {
|
||||
$result[] = new FlashMessage(
|
||||
'Default configured permissions are ' . $this->getTargetPermission()
|
||||
. ' but current permissions are ' . $this->getCurrentPermission(),
|
||||
'Directory ' . $this->getRelativePathBelowSiteRoot() . ' permissions mismatch',
|
||||
ContextualFeedbackSeverity::NOTICE
|
||||
);
|
||||
} else {
|
||||
if ($this->isLink()) {
|
||||
$result[] = new FlashMessage(
|
||||
'Is a link to a directory with the configured permissions of ' . $this->getTargetPermission(),
|
||||
'Link ' . $this->getRelativePathBelowSiteRoot()
|
||||
);
|
||||
} else {
|
||||
$result[] = new FlashMessage(
|
||||
'Is a directory with the configured permissions of ' . $this->getTargetPermission(),
|
||||
'Directory ' . $this->getRelativePathBelowSiteRoot()
|
||||
);
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if node is a directory or link
|
||||
*
|
||||
* @return bool True if node is a directory
|
||||
*/
|
||||
protected function isDirectory()
|
||||
{
|
||||
$path = $this->getAbsolutePath();
|
||||
return $this->isLink() || @is_dir($path);
|
||||
}
|
||||
|
||||
private function isLink(): bool
|
||||
{
|
||||
$path = $this->getAbsolutePath();
|
||||
return @is_link($path) && @is_dir(realpath($path));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Install\FolderStructure;
|
||||
|
||||
use TYPO3\CMS\Core\Messaging\FlashMessage;
|
||||
|
||||
/**
|
||||
* Interface for structure nodes root, link, file, ...
|
||||
*/
|
||||
interface NodeInterface
|
||||
{
|
||||
/**
|
||||
* Constructor gets structure and parent object defaulting to NULL
|
||||
*
|
||||
* @param array $structure Structure
|
||||
* @param NodeInterface $parent Parent
|
||||
*/
|
||||
public function __construct(array $structure, ?NodeInterface $parent = null);
|
||||
|
||||
/**
|
||||
* Get node name
|
||||
*
|
||||
* @return string Node name
|
||||
*/
|
||||
public function getName();
|
||||
|
||||
/**
|
||||
* Get absolute path of node
|
||||
*
|
||||
* @return string Absolute path
|
||||
*/
|
||||
public function getAbsolutePath();
|
||||
|
||||
/**
|
||||
* Get the status of the object tree, recursive for directory and root node
|
||||
*
|
||||
* @return FlashMessage[]
|
||||
*/
|
||||
public function getStatus(): array;
|
||||
|
||||
/**
|
||||
* Check if node is writable - can be created and permission can be fixed
|
||||
*
|
||||
* @return bool TRUE if node is writable
|
||||
*/
|
||||
public function isWritable();
|
||||
|
||||
/**
|
||||
* Fix structure
|
||||
*
|
||||
* If there is nothing to fix, returns an empty array
|
||||
*
|
||||
* @return FlashMessage[]
|
||||
*/
|
||||
public function fix(): array;
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Install\FolderStructure;
|
||||
|
||||
use TYPO3\CMS\Core\Messaging\FlashMessage;
|
||||
use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity;
|
||||
use TYPO3\CMS\Install\FolderStructure\Exception\InvalidArgumentException;
|
||||
use TYPO3\CMS\Install\FolderStructure\Exception\RootNodeException;
|
||||
|
||||
/**
|
||||
* Root node of structure
|
||||
* @internal This class is only meant to be used within EXT:install and is not part of the TYPO3 Core API.
|
||||
*/
|
||||
class RootNode extends DirectoryNode implements RootNodeInterface
|
||||
{
|
||||
/**
|
||||
* Implement constructor
|
||||
*
|
||||
* @param array $structure Given structure
|
||||
* @param NodeInterface $parent Must be NULL for RootNode
|
||||
* @throws Exception\RootNodeException
|
||||
* @throws Exception\InvalidArgumentException
|
||||
*/
|
||||
public function __construct(array $structure, ?NodeInterface $parent = null)
|
||||
{
|
||||
if ($parent !== null) {
|
||||
throw new RootNodeException(
|
||||
'Root node must not have parent',
|
||||
1366140117
|
||||
);
|
||||
}
|
||||
|
||||
if (!isset($structure['name'])
|
||||
|| ($this->isWindowsOs() && substr($structure['name'], 1, 2) !== ':/')
|
||||
|| (!$this->isWindowsOs() && $structure['name'][0] !== '/')
|
||||
) {
|
||||
throw new InvalidArgumentException(
|
||||
'Root node expects absolute path as name',
|
||||
1366141329
|
||||
);
|
||||
}
|
||||
$this->name = $structure['name'];
|
||||
|
||||
if (isset($structure['targetPermission'])) {
|
||||
$this->setTargetPermission($structure['targetPermission']);
|
||||
}
|
||||
|
||||
if (array_key_exists('children', $structure)) {
|
||||
$this->createChildren($structure['children']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get own status and status of child objects - Root node gives error status if not exists
|
||||
*
|
||||
* @return FlashMessage[]
|
||||
*/
|
||||
public function getStatus(): array
|
||||
{
|
||||
$result = [];
|
||||
if (!$this->exists()) {
|
||||
$result[] = new FlashMessage(
|
||||
'',
|
||||
$this->getAbsolutePath() . ' does not exist',
|
||||
ContextualFeedbackSeverity::ERROR
|
||||
);
|
||||
} else {
|
||||
$result = $this->getSelfStatus();
|
||||
}
|
||||
$result = array_merge($result, $this->getChildrenStatus());
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Root node does not call parent, but returns own name only
|
||||
*
|
||||
* @return string Absolute path
|
||||
*/
|
||||
public function getAbsolutePath()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Install\FolderStructure;
|
||||
|
||||
/**
|
||||
* Interface implemented by root node
|
||||
*/
|
||||
interface RootNodeInterface extends NodeInterface {}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Install\FolderStructure;
|
||||
|
||||
use TYPO3\CMS\Core\Messaging\FlashMessageQueue;
|
||||
|
||||
/**
|
||||
* Structure facade, a facade class in front of root node.
|
||||
* This is the main API interface to the node structure and should
|
||||
* be the only class used from outside.
|
||||
*/
|
||||
class StructureFacade implements StructureFacadeInterface
|
||||
{
|
||||
/**
|
||||
* @var RootNodeInterface The structure to work on
|
||||
*/
|
||||
protected $structure;
|
||||
|
||||
/**
|
||||
* Constructor sets structure to work on
|
||||
*/
|
||||
public function __construct(RootNodeInterface $structure)
|
||||
{
|
||||
$this->structure = $structure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get status of node tree
|
||||
*/
|
||||
public function getStatus(): FlashMessageQueue
|
||||
{
|
||||
$messageQueue = new FlashMessageQueue('install');
|
||||
foreach ($this->structure->getStatus() as $message) {
|
||||
$messageQueue->enqueue($message);
|
||||
}
|
||||
return $messageQueue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix structure
|
||||
*/
|
||||
public function fix(): FlashMessageQueue
|
||||
{
|
||||
$messageQueue = new FlashMessageQueue('install');
|
||||
foreach ($this->structure->fix() as $message) {
|
||||
$messageQueue->enqueue($message);
|
||||
}
|
||||
return $messageQueue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Install\FolderStructure;
|
||||
|
||||
use TYPO3\CMS\Core\Messaging\FlashMessageQueue;
|
||||
|
||||
/**
|
||||
* Interface of structure facade, a facade class in front of root node
|
||||
*/
|
||||
interface StructureFacadeInterface
|
||||
{
|
||||
/**
|
||||
* Constructor gets structure to work on
|
||||
*/
|
||||
public function __construct(RootNodeInterface $structure);
|
||||
|
||||
/**
|
||||
* Get status of node tree
|
||||
*/
|
||||
public function getStatus(): FlashMessageQueue;
|
||||
|
||||
/**
|
||||
* Fix structure
|
||||
*/
|
||||
public function fix(): FlashMessageQueue;
|
||||
}
|
||||
Reference in New Issue
Block a user