[BUGFIX] ext:install Better error handling in first folder step
[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|string Default for directories is 2770
36 */
37 protected $targetPermission = '2770';
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->targetPermission = $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($this->getRelativePathBelowSiteRoot() . ' does not exist');
83 $result[] = $status;
84 } else {
85 $result[] = $this->getSelfStatus();
86 }
87 $result = array_merge($result, $this->getChildrenStatus());
88 return $result;
89 }
90
91 /**
92 * Create a test file and delete again if directory exists
93 *
94 * @return boolean TRUE if test file creation was successful
95 */
96 public function isWritable() {
97 $result = TRUE;
98 if (!$this->exists()) {
99 $result = FALSE;
100 } elseif (!$this->canFileBeCreated()) {
101 $result = FALSE;
102 }
103 return $result;
104 }
105
106 /**
107 * Fix structure
108 *
109 * @return array<\TYPO3\CMS\Install\Status\StatusInterface>
110 */
111 public function fix() {
112 $result = $this->fixSelf();
113 foreach ($this->children as $child) {
114 /** @var $child NodeInterface */
115 $result = array_merge($result, $child->fix());
116 }
117 return $result;
118 }
119
120 /**
121 * Fix this node: create if not there, fix permissions
122 *
123 * @return array<\TYPO3\CMS\Install\Status\StatusInterface>
124 */
125 protected function fixSelf() {
126 $result = array();
127 if (!$this->exists()) {
128 $result[] = $this->createDirectory();
129 }
130 if (!$this->isDirectory()) {
131 $status = new Status\ErrorStatus();
132 $status->setTitle('Path ' . $this->getRelativePathBelowSiteRoot() . ' is not a directory');
133 $fileType = @filetype($this->getAbsolutePath());
134 if ($fileType) {
135 $status->setMessage(
136 'The target ' . $this->getRelativePathBelowSiteRoot() . ' should be a directory,' .
137 ' but is of type ' . $fileType . '. I can not fix this. Please investigate.'
138 );
139 } else {
140 $status->setMessage(
141 'The target ' . $this->getRelativePathBelowSiteRoot() . ' should be a directory,' .
142 ' but is of unknown type, probably because some upper level directory does not exist. Please investigate.'
143 );
144 }
145 $result[] = $status;
146 } elseif (!$this->isPermissionCorrect()) {
147 $result[] = $this->fixPermission();
148 }
149 return $result;
150 }
151
152 /**
153 * Create directory if not exists
154 *
155 * @throws Exception
156 * @return \TYPO3\CMS\Install\Status\StatusInterface
157 */
158 protected function createDirectory() {
159 if ($this->exists()) {
160 throw new Exception(
161 'Directory ' . $this->getAbsolutePath() . ' already exists',
162 1366740091
163 );
164 }
165 $result = @mkdir($this->getAbsolutePath());
166 if ($result === TRUE) {
167 $status = new Status\OkStatus();
168 $status->setTitle('Directory ' . $this->getRelativePathBelowSiteRoot() . ' successfully created.');
169 } else {
170 $status = new Status\ErrorStatus();
171 $status->setTitle('Directory ' . $this->getRelativePathBelowSiteRoot() . ' not created!');
172 $status->setMessage(
173 'The target directory could not be created. There is probably some' .
174 ' group or owner permission problem on the parent directory.'
175 );
176 }
177 return $status;
178 }
179
180 /**
181 * Get status of directory - used in root and directory node
182 *
183 * @return \TYPO3\CMS\Install\Status\StatusInterface
184 */
185 protected function getSelfStatus() {
186 $result = NULL;
187 if (!$this->isDirectory()) {
188 $status = new Status\ErrorStatus();
189 $status->setTitle($this->getRelativePathBelowSiteRoot() . ' is not a directory');
190 $status->setMessage(
191 'Path ' . $this->getAbsolutePath() . ' should be a directory,' .
192 ' but is of type ' . filetype($this->getAbsolutePath())
193 );
194 $result = $status;
195 } elseif (!$this->isWritable()) {
196 $status = new Status\WarningStatus();
197 $status->setTitle($this->getRelativePathBelowSiteRoot() . ' is not writable');
198 $status->setMessage(
199 'Path ' . $this->getAbsolutePath() . ' exists, but no file below' .
200 ' can be created.'
201 );
202 $result = $status;
203 } elseif (!$this->isPermissionCorrect()) {
204 if ($this->getTargetPermissionRelaxed() === TRUE) {
205 $status = new Status\NoticeStatus();
206 $status->setTitle($this->getRelativePathBelowSiteRoot() . ' has wrong permission');
207 $status->setMessage(
208 'Target permission are ' . $this->targetPermission .
209 ' but current permission are ' . $this->getCurrentPermission()
210 );
211 $result = $status;
212 } else {
213 $status = new Status\WarningStatus();
214 $status->setTitle($this->getRelativePathBelowSiteRoot() . ' has wrong permission');
215 $status->setMessage(
216 'Target permission are ' . $this->targetPermission .
217 ' but current permission are ' . $this->getCurrentPermission()
218 );
219 $result = $status;
220 }
221 } else {
222 $status = new Status\OkStatus();
223 $status->setTitle($this->getRelativePathBelowSiteRoot());
224 $result = $status;
225 }
226 return $result;
227 }
228
229 /**
230 * Get status of children
231 *
232 * @return array<\TYPO3\CMS\Install\Status\StatusInterface>
233 */
234 protected function getChildrenStatus() {
235 $result = array();
236 foreach ($this->children as $child) {
237 /** @var $child NodeInterface */
238 $result = array_merge($result, $child->getStatus());
239 }
240 return $result;
241 }
242
243 /**
244 * Create a test file and delete again - helper for isWritable
245 *
246 * @return boolean TRUE if test file creation was successful
247 */
248 protected function canFileBeCreated() {
249 $testFileName = uniqid('installToolTest_');
250 $result = @touch($this->getAbsolutePath() . '/' . $testFileName);
251 if ($result === TRUE) {
252 unlink($this->getAbsolutePath() . '/' . $testFileName);
253 }
254 return $result;
255 }
256
257 /**
258 * Checks if not is a directory
259 *
260 * @return boolean True if node is a directory
261 */
262 protected function isDirectory() {
263 $path = $this->getAbsolutePath();
264 return (!@is_link($path) && @is_dir($path));
265 }
266
267 /**
268 * Create children nodes - done in directory and root node
269 *
270 * @param array $structure Array of childs
271 * @throws Exception\InvalidArgumentException
272 */
273 protected function createChildren(array $structure) {
274 foreach ($structure as $child) {
275 if (!array_key_exists('type', $child)) {
276 throw new \TYPO3\CMS\Install\FolderStructure\Exception\InvalidArgumentException(
277 'Child must have type',
278 1366222204
279 );
280 }
281 if (!array_key_exists('name', $child)) {
282 throw new \TYPO3\CMS\Install\FolderStructure\Exception\InvalidArgumentException(
283 'Child must have name',
284 1366222205
285 );
286 }
287 $name = $child['name'];
288 foreach ($this->children as $existingChild) {
289 /** @var $existingChild NodeInterface */
290 if ($existingChild->getName() === $name) {
291 throw new \TYPO3\CMS\Install\FolderStructure\Exception\InvalidArgumentException(
292 'Child name must be unique',
293 1366222206
294 );
295 }
296 }
297 $this->children[] = new $child['type']($child, $this);
298 }
299 }
300 }
301 ?>