2 namespace TYPO3\CMS\Install\FolderStructure
;
5 * This file is part of the TYPO3 CMS project.
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
14 * The TYPO3 project - inspiring people to share!
17 use TYPO3\CMS\Install\Status
;
22 class DirectoryNode
extends AbstractNode
implements NodeInterface
{
25 * @var NULL|integer Default for directories is octal 02775 == decimal 1533
27 protected $targetPermission = '2775';
30 * Implement constructor
32 * @param array $structure Structure array
33 * @param NodeInterface $parent Parent object
34 * @throws Exception\InvalidArgumentException
36 public function __construct(array $structure, NodeInterface
$parent = NULL) {
37 if (is_null($parent)) {
38 throw new Exception\
InvalidArgumentException(
39 'Node must have parent',
43 $this->parent
= $parent;
45 // Ensure name is a single segment, but not a path like foo/bar or an absolute path /foo
46 if (strstr($structure['name'], '/') !== FALSE) {
47 throw new Exception\
InvalidArgumentException(
48 'Directory name must not contain forward slash',
52 $this->name
= $structure['name'];
54 if (isset($structure['targetPermission'])) {
55 $this->setTargetPermission($structure['targetPermission']);
58 if (array_key_exists('children', $structure)) {
59 $this->createChildren($structure['children']);
64 * Get own status and status of child objects
66 * @return array<\TYPO3\CMS\Install\Status\StatusInterface>
68 public function getStatus() {
70 if (!$this->exists()) {
71 $status = new Status\
WarningStatus();
72 $status->setTitle('Directory ' . $this->getRelativePathBelowSiteRoot() . ' does not exist');
73 $status->setMessage('The Install Tool can try to create it');
76 $result = $this->getSelfStatus();
78 $result = array_merge($result, $this->getChildrenStatus());
83 * Create a test file and delete again if directory exists
85 * @return bool TRUE if test file creation was successful
87 public function isWritable() {
89 if (!$this->exists()) {
91 } elseif (!$this->canFileBeCreated()) {
100 * If there is nothing to fix, returns an empty array
102 * @return array<\TYPO3\CMS\Install\Status\StatusInterface>
104 public function fix() {
105 $result = $this->fixSelf();
106 foreach ($this->children
as $child) {
107 /** @var $child NodeInterface */
108 $result = array_merge($result, $child->fix());
114 * Fix this directory:
116 * - create with correct permissions if it was not existing
117 * - if there is no "write" permissions, try to fix it
118 * - leave it alone otherwise
120 * @return array<\TYPO3\CMS\Install\Status\StatusInterface>
122 protected function fixSelf() {
124 if (!$this->exists()) {
125 $resultCreateDirectory = $this->createDirectory();
126 $result[] = $resultCreateDirectory;
127 if ($resultCreateDirectory instanceof Status\OkStatus
&&
128 !$this->isPermissionCorrect()
130 $result[] = $this->fixPermission();
132 } elseif (!$this->isWritable()) {
133 // If directory is not writable, we might have permissions to fix that
135 $result[] = $this->fixPermission();
136 } elseif (!$this->isDirectory()) {
137 $status = new Status\
ErrorStatus();
138 $status->setTitle('Path ' . $this->getRelativePathBelowSiteRoot() . ' is not a directory');
139 $fileType = @filetype
($this->getAbsolutePath());
142 'The target ' . $this->getRelativePathBelowSiteRoot() . ' should be a directory,' .
143 ' but is of type ' . $fileType . '. This cannot be fixed automatically. Please investigate.'
147 'The target ' . $this->getRelativePathBelowSiteRoot() . ' should be a directory,' .
148 ' but is of unknown type, probably because an upper level directory does not exist. Please investigate.'
157 * Create directory if not exists
160 * @return \TYPO3\CMS\Install\Status\StatusInterface
162 protected function createDirectory() {
163 if ($this->exists()) {
165 'Directory ' . $this->getAbsolutePath() . ' already exists',
169 $result = @mkdir
($this->getAbsolutePath());
170 if ($result === TRUE) {
171 $status = new Status\
OkStatus();
172 $status->setTitle('Directory ' . $this->getRelativePathBelowSiteRoot() . ' successfully created.');
174 $status = new Status\
ErrorStatus();
175 $status->setTitle('Directory ' . $this->getRelativePathBelowSiteRoot() . ' not created!');
177 'The target directory could not be created. There is probably a' .
178 ' group or owner permission problem on the parent directory.'
185 * Get status of directory - used in root and directory node
187 * @return array<\TYPO3\CMS\Install\Status\StatusInterface>
189 protected function getSelfStatus() {
191 if (!$this->isDirectory()) {
192 $status = new Status\
ErrorStatus();
193 $status->setTitle($this->getRelativePathBelowSiteRoot() . ' is not a directory');
195 'Directory ' . $this->getRelativePathBelowSiteRoot() . ' should be a directory,' .
196 ' but is of type ' . filetype($this->getAbsolutePath())
199 } elseif (!$this->isWritable()) {
200 $status = new Status\
ErrorStatus();
201 $status->setTitle('Directory ' . $this->getRelativePathBelowSiteRoot() . ' is not writable');
203 'Path ' . $this->getAbsolutePath() . ' exists, but no file underneath it' .
207 } elseif (!$this->isPermissionCorrect()) {
208 $status = new Status\
NoticeStatus();
209 $status->setTitle('Directory ' . $this->getRelativePathBelowSiteRoot() . ' permissions mismatch');
211 'Default configured permissions are ' . $this->getTargetPermission() .
212 ' but current permissions are ' . $this->getCurrentPermission()
216 $status = new Status\
OkStatus();
217 $status->setTitle('Directory ' . $this->getRelativePathBelowSiteRoot());
219 'Is a directory with the configured permissions of ' . $this->getTargetPermission()
227 * Get status of children
229 * @return array<\TYPO3\CMS\Install\Status\StatusInterface>
231 protected function getChildrenStatus() {
233 foreach ($this->children
as $child) {
234 /** @var $child NodeInterface */
235 $result = array_merge($result, $child->getStatus());
241 * Create a test file and delete again - helper for isWritable
243 * @return bool TRUE if test file creation was successful
245 protected function canFileBeCreated() {
246 $testFileName = uniqid('installToolTest_', TRUE);
247 $result = @touch
($this->getAbsolutePath() . '/' . $testFileName);
248 if ($result === TRUE) {
249 unlink($this->getAbsolutePath() . '/' . $testFileName);
255 * Checks if not is a directory
257 * @return bool True if node is a directory
259 protected function isDirectory() {
260 $path = $this->getAbsolutePath();
261 return (@is_dir
($path) ||
(is_link($path) && @is_dir
(@readlink
($path))));
265 * Create children nodes - done in directory and root node
267 * @param array $structure Array of children
268 * @throws Exception\InvalidArgumentException
270 protected function createChildren(array $structure) {
271 foreach ($structure as $child) {
272 if (!array_key_exists('type', $child)) {
273 throw new Exception\
InvalidArgumentException(
274 'Child must have type',
278 if (!array_key_exists('name', $child)) {
279 throw new Exception\
InvalidArgumentException(
280 'Child must have name',
284 $name = $child['name'];
285 foreach ($this->children
as $existingChild) {
286 /** @var $existingChild NodeInterface */
287 if ($existingChild->getName() === $name) {
288 throw new Exception\
InvalidArgumentException(
289 'Child name must be unique',
294 $this->children
[] = new $child['type']($child, $this);