43097a15658242172c303845911498974e3845d6
[Packages/TYPO3.CMS.git] / typo3 / sysext / install / Classes / FolderStructure / DirectoryNode.php
1 <?php
2 namespace TYPO3\CMS\Install\FolderStructure;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2013 Christian Kuhn <lolli@schwarzbu.ch>
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 *
19 * This script is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * This copyright notice MUST APPEAR in all copies of the script!
25 ***************************************************************/
26
27 use TYPO3\CMS\Install\Status;
28
29 /**
30 * A directory
31 */
32 class DirectoryNode extends AbstractNode implements NodeInterface {
33
34 /**
35 * @var NULL|integer Default for directories is octal 02775 == decimal 1533
36 */
37 protected $targetPermission = '2775';
38
39 /**
40 * Implement constructor
41 *
42 * @param array $structure Structure array
43 * @param NodeInterface $parent Parent object
44 * @throws Exception\InvalidArgumentException
45 */
46 public function __construct(array $structure, NodeInterface $parent = NULL) {
47 if (is_null($parent)) {
48 throw new \TYPO3\CMS\Install\FolderStructure\Exception\InvalidArgumentException(
49 'Node must have parent',
50 1366222203
51 );
52 }
53 $this->parent = $parent;
54
55 // Ensure name is a single segment, but not a path like foo/bar or an absolute path /foo
56 if (strstr($structure['name'], '/') !== FALSE) {
57 throw new \TYPO3\CMS\Install\FolderStructure\Exception\InvalidArgumentException(
58 'Directory name must not contain forward slash',
59 1366226639
60 );
61 }
62 $this->name = $structure['name'];
63
64 if (isset($structure['targetPermission'])) {
65 $this->setTargetPermission($structure['targetPermission']);
66 }
67
68 if (array_key_exists('children', $structure)) {
69 $this->createChildren($structure['children']);
70 }
71 }
72
73 /**
74 * Get own status and status of child objects
75 *
76 * @return array<\TYPO3\CMS\Install\Status\StatusInterface>
77 */
78 public function getStatus() {
79 $result = array();
80 if (!$this->exists()) {
81 $status = new Status\WarningStatus();
82 $status->setTitle('Directory ' . $this->getRelativePathBelowSiteRoot() . ' does not exist');
83 $status->setMessage('The Install Tool can try to create it');
84 $result[] = $status;
85 } else {
86 $result = $this->getSelfStatus();
87 }
88 $result = array_merge($result, $this->getChildrenStatus());
89 return $result;
90 }
91
92 /**
93 * Create a test file and delete again if directory exists
94 *
95 * @return boolean TRUE if test file creation was successful
96 */
97 public function isWritable() {
98 $result = TRUE;
99 if (!$this->exists()) {
100 $result = FALSE;
101 } elseif (!$this->canFileBeCreated()) {
102 $result = FALSE;
103 }
104 return $result;
105 }
106
107 /**
108 * Fix structure
109 *
110 * If there is nothing to fix, returns an empty array
111 *
112 * @return array<\TYPO3\CMS\Install\Status\StatusInterface>
113 */
114 public function fix() {
115 $result = $this->fixSelf();
116 foreach ($this->children as $child) {
117 /** @var $child NodeInterface */
118 $result = array_merge($result, $child->fix());
119 }
120 return $result;
121 }
122
123 /**
124 * Fix this directory:
125 *
126 * - create with correct permissions if it was not existing
127 * - if there is no "write" permissions, try to fix it
128 * - leave it alone otherwise
129 *
130 * @return array<\TYPO3\CMS\Install\Status\StatusInterface>
131 */
132 protected function fixSelf() {
133 $result = array();
134 if (!$this->exists()) {
135 $resultCreateDirectory = $this->createDirectory();
136 $result[] = $resultCreateDirectory;
137 if ($resultCreateDirectory instanceof \TYPO3\CMS\Install\Status\OkStatus &&
138 !$this->isPermissionCorrect()
139 ) {
140 $result[] = $this->fixPermission();
141 }
142 } elseif (!$this->isWritable()) {
143 // If directory is not writeable, we might have permissions to fix that
144 // Try it:
145 $result[] = $this->fixPermission();
146 } elseif (!$this->isDirectory()) {
147 $status = new Status\ErrorStatus();
148 $status->setTitle('Path ' . $this->getRelativePathBelowSiteRoot() . ' is not a directory');
149 $fileType = @filetype($this->getAbsolutePath());
150 if ($fileType) {
151 $status->setMessage(
152 'The target ' . $this->getRelativePathBelowSiteRoot() . ' should be a directory,' .
153 ' but is of type ' . $fileType . '. This cannot be fixed automatically. Please investigate.'
154 );
155 } else {
156 $status->setMessage(
157 'The target ' . $this->getRelativePathBelowSiteRoot() . ' should be a directory,' .
158 ' but is of unknown type, probably because an upper level directory does not exist. Please investigate.'
159 );
160 }
161 $result[] = $status;
162 }
163 return $result;
164 }
165
166 /**
167 * Create directory if not exists
168 *
169 * @throws Exception
170 * @return \TYPO3\CMS\Install\Status\StatusInterface
171 */
172 protected function createDirectory() {
173 if ($this->exists()) {
174 throw new Exception(
175 'Directory ' . $this->getAbsolutePath() . ' already exists',
176 1366740091
177 );
178 }
179 $result = @mkdir($this->getAbsolutePath());
180 if ($result === TRUE) {
181 $status = new Status\OkStatus();
182 $status->setTitle('Directory ' . $this->getRelativePathBelowSiteRoot() . ' successfully created.');
183 } else {
184 $status = new Status\ErrorStatus();
185 $status->setTitle('Directory ' . $this->getRelativePathBelowSiteRoot() . ' not created!');
186 $status->setMessage(
187 'The target directory could not be created. There is probably a' .
188 ' group or owner permission problem on the parent directory.'
189 );
190 }
191 return $status;
192 }
193
194 /**
195 * Get status of directory - used in root and directory node
196 *
197 * @return array<\TYPO3\CMS\Install\Status\StatusInterface>
198 */
199 protected function getSelfStatus() {
200 $result = array();
201 if (!$this->isDirectory()) {
202 $status = new Status\ErrorStatus();
203 $status->setTitle($this->getRelativePathBelowSiteRoot() . ' is not a directory');
204 $status->setMessage(
205 'Directory ' . $this->getRelativePathBelowSiteRoot() . ' should be a directory,' .
206 ' but is of type ' . filetype($this->getAbsolutePath())
207 );
208 $result[] = $status;
209 } elseif (!$this->isWritable()) {
210 $status = new Status\ErrorStatus();
211 $status->setTitle('Directory ' . $this->getRelativePathBelowSiteRoot() . ' is not writable');
212 $status->setMessage(
213 'Path ' . $this->getAbsolutePath() . ' exists, but no file underneath it' .
214 ' can be created.'
215 );
216 $result[] = $status;
217 } elseif (!$this->isPermissionCorrect()) {
218 $status = new Status\NoticeStatus();
219 $status->setTitle('Directory ' . $this->getRelativePathBelowSiteRoot() . ' permissions mismatch');
220 $status->setMessage(
221 'Default configured permissions are ' . $this->getTargetPermission() .
222 ' but current permissions are ' . $this->getCurrentPermission()
223 );
224 $result[] = $status;
225 } else {
226 $status = new Status\OkStatus();
227 $status->setTitle('Directory ' . $this->getRelativePathBelowSiteRoot());
228 $status->setMessage(
229 'Is a directory with the configured permissions of ' . $this->getTargetPermission()
230 );
231 $result[] = $status;
232 }
233 return $result;
234 }
235
236 /**
237 * Get status of children
238 *
239 * @return array<\TYPO3\CMS\Install\Status\StatusInterface>
240 */
241 protected function getChildrenStatus() {
242 $result = array();
243 foreach ($this->children as $child) {
244 /** @var $child NodeInterface */
245 $result = array_merge($result, $child->getStatus());
246 }
247 return $result;
248 }
249
250 /**
251 * Create a test file and delete again - helper for isWritable
252 *
253 * @return boolean TRUE if test file creation was successful
254 */
255 protected function canFileBeCreated() {
256 $testFileName = uniqid('installToolTest_');
257 $result = @touch($this->getAbsolutePath() . '/' . $testFileName);
258 if ($result === TRUE) {
259 unlink($this->getAbsolutePath() . '/' . $testFileName);
260 }
261 return $result;
262 }
263
264 /**
265 * Checks if not is a directory
266 *
267 * @return boolean True if node is a directory
268 */
269 protected function isDirectory() {
270 $path = $this->getAbsolutePath();
271 return (!@is_link($path) && @is_dir($path));
272 }
273
274 /**
275 * Create children nodes - done in directory and root node
276 *
277 * @param array $structure Array of childs
278 * @throws Exception\InvalidArgumentException
279 */
280 protected function createChildren(array $structure) {
281 foreach ($structure as $child) {
282 if (!array_key_exists('type', $child)) {
283 throw new \TYPO3\CMS\Install\FolderStructure\Exception\InvalidArgumentException(
284 'Child must have type',
285 1366222204
286 );
287 }
288 if (!array_key_exists('name', $child)) {
289 throw new \TYPO3\CMS\Install\FolderStructure\Exception\InvalidArgumentException(
290 'Child must have name',
291 1366222205
292 );
293 }
294 $name = $child['name'];
295 foreach ($this->children as $existingChild) {
296 /** @var $existingChild NodeInterface */
297 if ($existingChild->getName() === $name) {
298 throw new \TYPO3\CMS\Install\FolderStructure\Exception\InvalidArgumentException(
299 'Child name must be unique',
300 1366222206
301 );
302 }
303 }
304 $this->children[] = new $child['type']($child, $this);
305 }
306 }
307 }