[TASK] Install Tool: Optimize permission checking and fixing 16/25416/10
authorErnesto Baschny <ernst@cron-it.de>
Thu, 14 Nov 2013 12:44:02 +0000 (13:44 +0100)
committerErnesto Baschny <ernst@cron-it.de>
Sat, 15 Mar 2014 21:06:30 +0000 (22:06 +0100)
* Adjust default permissions back to "world readable" (775/664)
* Do not attempt to fix permissions on an existing folder structure
* Streamline the separation of Notices, Warnings and Errors
* Cosmetical optimizations in the "Folder Structure" item
* Also report if the default permissions are too insecure (i.e.
  world writeable / readable).

Note that the new defaults (775/664) are set to make the system work
by default on almost most scenarios, but the recommended secure setup
is still 770/660. This is on purpose to make the administrator aware
of it but will require manual intervention (i.e. "thinking") if this
setup will work or not (we cannot detect that automatically).

Resolves: #52668
Releases: 6.2
Change-Id: I77c72617fa58fd808ebc5fd818e94b48c2644102
Reviewed-on: https://review.typo3.org/25416
Reviewed-by: Christian Kuhn
Reviewed-by: Wouter Wolters
Reviewed-by: Stefan Neufeind
Reviewed-by: Philipp Gampe
Reviewed-by: Anja Leichsenring
Tested-by: Anja Leichsenring
Reviewed-by: Ernesto Baschny
Tested-by: Ernesto Baschny
18 files changed:
typo3/sysext/core/Configuration/DefaultConfiguration.php
typo3/sysext/install/Classes/Controller/Action/AbstractAction.php
typo3/sysext/install/Classes/Controller/Action/Tool/FolderStructure.php
typo3/sysext/install/Classes/FolderStructure/AbstractNode.php
typo3/sysext/install/Classes/FolderStructure/DefaultFactory.php
typo3/sysext/install/Classes/FolderStructure/DefaultPermissionsCheck.php [new file with mode: 0644]
typo3/sysext/install/Classes/FolderStructure/DirectoryNode.php
typo3/sysext/install/Classes/FolderStructure/FileNode.php
typo3/sysext/install/Classes/FolderStructure/LinkNode.php
typo3/sysext/install/Classes/FolderStructure/NodeInterface.php
typo3/sysext/install/Classes/FolderStructure/RootNode.php
typo3/sysext/install/Resources/Private/Templates/Action/Tool/FolderStructure.html
typo3/sysext/install/Resources/Public/Stylesheets/Action/Common/Install.css
typo3/sysext/install/Tests/Unit/FolderStructure/AbstractNodeTest.php
typo3/sysext/install/Tests/Unit/FolderStructure/DirectoryNodeTest.php
typo3/sysext/install/Tests/Unit/FolderStructure/FileNodeTest.php
typo3/sysext/install/Tests/Unit/FolderStructure/LinkNodeTest.php
typo3/sysext/install/Tests/Unit/FolderStructure/RootNodeTest.php

index 51af9c8..46faef1 100644 (file)
@@ -295,8 +295,8 @@ return array(
                'userHomePath' => '',                                                   // Path to the directory where TYPO3 backend-users have their home-dirs.  Eg. '/home/typo3/users/'. A home for backend user 2 would be: '/home/typo3/users/2/'. Ending slash required!
                'groupHomePath' => '',                                                  // Path to the directory where TYPO3 backend-groups have their home-dirs. Remember that the first part of this path must be 'lockRootPath'. Eg. '/home/typo3/groups/'. A home for backend group 1 would be: '/home/typo3/groups/1/'. Ending slash required!
                'userUploadDir' => '',                                                  // Suffix to the user home dir which is what gets mounted in TYPO3. Eg. if the user dir is "../123_user/" and this value is "/upload" then "../123_user/upload" gets mounted.
-               'fileCreateMask' => '0660',                                             // File mode mask for Unix file systems (when files are uploaded/created).
-               'folderCreateMask' => '2770',                                   // As above, but for folders.
+               'fileCreateMask' => '0665',                                             // File mode mask for Unix file systems (when files are uploaded/created).
+               'folderCreateMask' => '2775',                                   // As above, but for folders.
                'createGroup' => '',                                                    // Group for newly created files and folders (Unix only). Group ownership can be changed on Unix file systems (see above). Set this if you want to change the group ownership of created files/folders to a specific group. This makes sense in all cases where the webserver is running with a different user/group as you do. Create a new group on your system and add you and the webserver user to the group. Now you can safely set the last bit in fileCreateMask/folderCreateMask to 0 (e.g. 770). Important: The user who is running your webserver needs to be a member of the group you specify here! Otherwise you might get some error messages.
                'warning_email_addr' => '',                                             // Email address that will receive notification whenever an attempt to login to the Install Tool is made and that will also receive warnings whenever more than 3 failed backend login attempts (regardless of user) are detected within an hour.
                'warning_mode' => '',                                                   // Bit 1: If set, warning_email_addr will be notified every time a backend user logs in. Bit 2: If set, warning_email_addr will be notified every time an ADMIN backend user logs in. Other bits are reserved for future options.
index 50ec9f0..7f9fdb5 100644 (file)
@@ -115,7 +115,14 @@ abstract class AbstractAction implements ActionInterface {
                // Count of folder structure errors are displayed in left navigation menu
                /** @var $folderStructureFacade \TYPO3\CMS\Install\FolderStructure\StructureFacade */
                $folderStructureFacade = $this->objectManager->get('TYPO3\\CMS\\Install\\FolderStructure\\DefaultFactory')->getStructure();
-               $folderStructureErrors = $statusUtility->filterBySeverity($folderStructureFacade->getStatus(), 'error');
+               $folderStatus = $folderStructureFacade->getStatus();
+
+               /** @var $permissionCheck \TYPO3\CMS\Install\FolderStructure\DefaultPermissionsCheck */
+               $permissionCheck = $this->objectManager->get('TYPO3\\CMS\\Install\\FolderStructure\\DefaultPermissionsCheck');
+               $folderStatus[] = $permissionCheck->getMaskStatus('fileCreateMask');
+               $folderStatus[] = $permissionCheck->getMaskStatus('folderCreateMask');
+
+               $folderStructureErrors = $statusUtility->filterBySeverity($folderStatus, 'error');
 
                // Context service distinguishes between standalone and backend context
                $contextService = $this->objectManager->get('TYPO3\\CMS\\Install\\Service\\ContextService');
index dc0c0c0..cbb8cd4 100644 (file)
@@ -53,15 +53,25 @@ class FolderStructure extends Action\AbstractAction {
 
                $errorStatus = array_merge(
                        $statusUtility->filterBySeverity($statusObjects, 'error'),
-                       $statusUtility->filterBySeverity($statusObjects, 'warning'),
+                       $statusUtility->filterBySeverity($statusObjects, 'warning')
+               );
+               $okStatus = array_merge(
                        $statusUtility->filterBySeverity($statusObjects, 'notice'),
-                       $statusUtility->filterBySeverity($statusObjects, 'information')
+                       $statusUtility->filterBySeverity($statusObjects, 'information'),
+                       $statusUtility->filterBySeverity($statusObjects, 'ok')
                );
 
+               /** @var \TYPO3\CMS\Install\FolderStructure\DefaultPermissionsCheck $permissionCheck */
+               $permissionCheck = $this->objectManager->get('TYPO3\\CMS\\Install\\FolderStructure\\DefaultPermissionsCheck');
+               $filePermissionStatus = $permissionCheck->getMaskStatus('fileCreateMask');
+               $directoryPermissionStatus = $permissionCheck->getMaskStatus('folderCreateMask');
+
                $this->view
+                       ->assign('filePermissionStatus', $filePermissionStatus)
+                       ->assign('directoryPermissionStatus', $directoryPermissionStatus)
                        ->assign('fixedStatus', $fixedStatusObjects)
                        ->assign('errorStatus', $errorStatus)
-                       ->assign('okStatus', $statusUtility->filterBySeverity($statusObjects, 'ok'));
+                       ->assign('okStatus', $okStatus);
 
                return $this->view->render();
        }
index 3fe2ec6..2c072e8 100644 (file)
@@ -37,16 +37,11 @@ abstract class AbstractNode {
        protected $name = '';
 
        /**
-        * @var NULL|string Target permissions for unix, eg. 2770
+        * @var NULL|string Target permissions for unix, eg. '2775' or '0664' (4 characters string)
         */
        protected $targetPermission = NULL;
 
        /**
-        * @var boolean If TRUE, permission check and fix do not throw error level status if wrong
-        */
-       protected $targetPermissionRelaxed = FALSE;
-
-       /**
         * @var NULL|NodeInterface Parent object of this structure node
         */
        protected $parent = NULL;
@@ -68,19 +63,26 @@ abstract class AbstractNode {
        /**
         * Get target permission
         *
-        * @return string Permission, eg. 2770
+        * 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;
        }
 
        /**
-        * Get target permission relaxed flag
+        * Set target permission
         *
-        * @return boolean TRUE if relaxed permission check should be done
+        * @param string $permission Permissions as a 4 character octal string, i.e. 2775 or 0644
+        *
+        * @return void
         */
-       protected function getTargetPermissionRelaxed() {
-               return $this->targetPermissionRelaxed;
+       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;
        }
 
        /**
@@ -152,21 +154,12 @@ abstract class AbstractNode {
                        $status = new Status\OkStatus();
                        $status->setTitle('Fixed permission on ' . $this->getRelativePathBelowSiteRoot() . '.');
                } else {
-                       if ($this->getTargetPermissionRelaxed() === TRUE) {
-                               $status = new Status\NoticeStatus();
-                               $status->setTitle('Permission change on ' . $this->getRelativePathBelowSiteRoot() . ' not successful');
-                               $status->setMessage(
-                                       'Permissions could not be changed to ' . $this->targetPermission . '. This is not a problem as' .
-                                               ' long as files and folders within this node can be written.'
-                               );
-                       } else {
-                               $status = new Status\ErrorStatus();
-                               $status->setTitle('Permission change on ' . $this->getRelativePathBelowSiteRoot() . ' not successful!');
-                               $status->setMessage(
-                                       'Permissions could not be changed to ' . $this->targetPermission . '. There is probably a' .
-                                               ' group or owner permission problem on the parent directory.'
-                               );
-                       }
+                       $status = new Status\NoticeStatus();
+                       $status->setTitle('Permission change on ' . $this->getRelativePathBelowSiteRoot() . ' not successful');
+                       $status->setMessage(
+                               'Permissions could not be changed to ' . $this->getTargetPermission() .
+                                       '. This only is a problem if files and folders within this node cannot be written.'
+                       );
                }
                return $status;
        }
@@ -190,17 +183,11 @@ abstract class AbstractNode {
        /**
         * Get current permission of node
         *
-        * @return string, eg. 2770 for dirs, 0660 for files
+        * @return string, eg. 2775 for dirs, 0664 for files
         */
        protected function getCurrentPermission() {
-               $absolutePath = $this->getAbsolutePath();
                $permissions = decoct(fileperms($this->getAbsolutePath()));
-               if (is_dir($absolutePath)) {
-                       $result = substr($permissions, 1);
-               } else {
-                       $result = substr($permissions, 2);
-               }
-               return $result;
+               return substr($permissions, -4);
        }
 
        /**
index 129a5f6..a576888 100644 (file)
@@ -68,7 +68,6 @@ class DefaultFactory {
                        // Cut off trailing forward / from PATH_site, so root node has no trailing slash like all others
                        'name' => substr(PATH_site, 0, -1),
                        'targetPermission' => $directoryPermission,
-                       'targetPermissionRelaxed' => TRUE,
                        'children' => array(
                                array(
                                        'name' => 'index.php',
diff --git a/typo3/sysext/install/Classes/FolderStructure/DefaultPermissionsCheck.php b/typo3/sysext/install/Classes/FolderStructure/DefaultPermissionsCheck.php
new file mode 100644 (file)
index 0000000..3a94238
--- /dev/null
@@ -0,0 +1,103 @@
+<?php
+namespace TYPO3\CMS\Install\FolderStructure;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2014 Ernesto Baschny <ernst@cron-it.de>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+/**
+ * Service class to check the default folder permissions
+ *
+ */
+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 = array(
+               'fileCreateMask' => '0660',
+               'folderCreateMask' => '2770',
+       );
+
+       /**
+        * @var array Verbose names of the settings
+        */
+       protected $names = array(
+               '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
+        * @return \TYPO3\CMS\Install\Status\StatusInterface
+        */
+       public function getMaskStatus($which) {
+               $octal = '0' . $GLOBALS['TYPO3_CONF_VARS']['BE'][$which];
+               $dec = octdec($octal);
+               $perms = array(
+                       '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 = '.';
+               if ($perms['ow']) {
+                       $permissionStatus = new \TYPO3\CMS\Install\Status\ErrorStatus();
+                       $extraMessage = ' (writable by anyone on the server)';
+               } elseif ($perms['or']) {
+                       $permissionStatus = new \TYPO3\CMS\Install\Status\WarningStatus();
+                       $extraMessage = ' (readable by anyone on the server)';
+               } elseif ($perms['gw']) {
+                       $permissionStatus = new \TYPO3\CMS\Install\Status\NoticeStatus();
+               } elseif ($perms['gr']) {
+                       $permissionStatus = new \TYPO3\CMS\Install\Status\NoticeStatus();
+               } else {
+                       $permissionStatus = new \TYPO3\CMS\Install\Status\OkStatus();
+               }
+               $permissionStatus->setTitle($this->names[$which] . ' (BE/' . $which . ')');
+               $permissionStatus->setMessage('Currently configured as ' . $GLOBALS['TYPO3_CONF_VARS']['BE'][$which] .
+                       $extraMessage . '.' .
+                       ' Recommended: ' . $this->recommended[$which] . '. Make sure that your' .
+                       ' server environment supports this before changing it.'
+               );
+               return $permissionStatus;
+       }
+
+}
\ No newline at end of file
index 0bf1b98..4ff5cc1 100644 (file)
@@ -32,9 +32,9 @@ use TYPO3\CMS\Install\Status;
 class DirectoryNode extends AbstractNode implements NodeInterface {
 
        /**
-        * @var NULL|string Default for directories is 2770
+        * @var NULL|integer Default for directories is octal 02775 == decimal 1533
         */
-       protected $targetPermission = '2770';
+       protected $targetPermission = '2775';
 
        /**
         * Implement constructor
@@ -62,7 +62,7 @@ class DirectoryNode extends AbstractNode implements NodeInterface {
                $this->name = $structure['name'];
 
                if (isset($structure['targetPermission'])) {
-                       $this->targetPermission = $structure['targetPermission'];
+                       $this->setTargetPermission($structure['targetPermission']);
                }
 
                if (array_key_exists('children', $structure)) {
@@ -78,8 +78,9 @@ class DirectoryNode extends AbstractNode implements NodeInterface {
        public function getStatus() {
                $result = array();
                if (!$this->exists()) {
-                       $status = new Status\WarningStatus();
-                       $status->setTitle($this->getRelativePathBelowSiteRoot() . ' does not exist');
+                       $status = new Status\ErrorStatus();
+                       $status->setTitle('Directory ' . $this->getRelativePathBelowSiteRoot() . ' does not exist');
+                       $status->setMessage('The Install Tool can try to create it');
                        $result[] = $status;
                } else {
                        $result = $this->getSelfStatus();
@@ -106,6 +107,8 @@ class DirectoryNode extends AbstractNode implements NodeInterface {
        /**
         * Fix structure
         *
+        * If there is nothing to fix, returns an empty array
+        *
         * @return array<\TYPO3\CMS\Install\Status\StatusInterface>
         */
        public function fix() {
@@ -118,23 +121,36 @@ class DirectoryNode extends AbstractNode implements NodeInterface {
        }
 
        /**
-        * Fix this node: create if not there, fix permissions
+        * 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 array<\TYPO3\CMS\Install\Status\StatusInterface>
         */
        protected function fixSelf() {
                $result = array();
                if (!$this->exists()) {
-                       $result[] = $this->createDirectory();
-               }
-               if (!$this->isDirectory()) {
+                       $resultCreateDirectory = $this->createDirectory();
+                       $result[] = $resultCreateDirectory;
+                       if ($resultCreateDirectory instanceof \TYPO3\CMS\Install\Status\OkStatus &&
+                               !$this->isPermissionCorrect()
+                       ) {
+                               $result[] = $this->fixPermission();
+                       }
+               } elseif (!$this->isWritable()) {
+                       // If directory is not writeable, we might have permissions to fix that
+                       // Try it:
+                       $result[] = $this->fixPermission();
+               } elseif (!$this->isDirectory()) {
                        $status = new Status\ErrorStatus();
                        $status->setTitle('Path ' . $this->getRelativePathBelowSiteRoot() . ' is not a directory');
                        $fileType = @filetype($this->getAbsolutePath());
                        if ($fileType) {
                                $status->setMessage(
                                        'The target ' . $this->getRelativePathBelowSiteRoot() . ' should be a directory,' .
-                                       ' but is of type ' . $fileType . '. I cannot fix this. Please investigate.'
+                                       ' but is of type ' . $fileType . '. This cannot be fixed automatically. Please investigate.'
                                );
                        } else {
                                $status->setMessage(
@@ -143,8 +159,6 @@ class DirectoryNode extends AbstractNode implements NodeInterface {
                                );
                        }
                        $result[] = $status;
-               } elseif (!$this->isPermissionCorrect()) {
-                       $result[] = $this->fixPermission();
                }
                return $result;
        }
@@ -188,39 +202,32 @@ class DirectoryNode extends AbstractNode implements NodeInterface {
                        $status = new Status\ErrorStatus();
                        $status->setTitle($this->getRelativePathBelowSiteRoot() . ' is not a directory');
                        $status->setMessage(
-                               'Path ' . $this->getAbsolutePath() . ' should be a directory,' .
+                               'Directory ' . $this->getRelativePathBelowSiteRoot() . ' should be a directory,' .
                                ' but is of type ' . filetype($this->getAbsolutePath())
                        );
                        $result[] = $status;
                } elseif (!$this->isWritable()) {
                        $status = new Status\ErrorStatus();
-                       $status->setTitle($this->getRelativePathBelowSiteRoot() . ' is not writable');
+                       $status->setTitle('Directory ' . $this->getRelativePathBelowSiteRoot() . ' is not writable');
                        $status->setMessage(
                                'Path ' . $this->getAbsolutePath() . ' exists, but no file underneath it' .
                                ' can be created.'
                        );
                        $result[] = $status;
                } elseif (!$this->isPermissionCorrect()) {
-                       if ($this->getTargetPermissionRelaxed() === TRUE) {
-                               $status = new Status\NoticeStatus();
-                               $status->setTitle($this->getRelativePathBelowSiteRoot() . ' has wrong permissions');
-                               $status->setMessage(
-                                       'Target permissions are ' . $this->targetPermission .
-                                       ' but current permissions are ' . $this->getCurrentPermission()
-                               );
-                               $result[] = $status;
-                       } else {
-                               $status = new Status\WarningStatus();
-                               $status->setTitle($this->getRelativePathBelowSiteRoot() . ' has wrong permissions');
-                               $status->setMessage(
-                                       'Target permissions are ' . $this->targetPermission .
-                                       ' but current permissions are ' . $this->getCurrentPermission()
-                               );
-                               $result[] = $status;
-                       }
+                       $status = new Status\NoticeStatus();
+                       $status->setTitle('Directory ' . $this->getRelativePathBelowSiteRoot() . ' permissions mismatch');
+                       $status->setMessage(
+                               'Default configured permissions are ' . $this->getTargetPermission() .
+                               ' but current permissions are ' . $this->getCurrentPermission()
+                       );
+                       $result[] = $status;
                } else {
                        $status = new Status\OkStatus();
-                       $status->setTitle($this->getRelativePathBelowSiteRoot());
+                       $status->setTitle('Directory ' . $this->getRelativePathBelowSiteRoot());
+                       $status->setMessage(
+                               'Is a directory with the configured permissions of ' . $this->getTargetPermission()
+                       );
                        $result[] = $status;
                }
                return $result;
index 32f7f35..5ad7e26 100644 (file)
@@ -32,9 +32,9 @@ use TYPO3\CMS\Install\Status;
 class FileNode extends AbstractNode implements NodeInterface {
 
        /**
-        * @var NULL|string Default for files is 0660
+        * @var NULL|integer Default for files is octal 0664 == decimal 436
         */
-       protected $targetPermission = '0660';
+       protected $targetPermission = '0664';
 
        /**
         * @var string|NULL Target content of file. If NULL, target content is ignored
@@ -67,7 +67,7 @@ class FileNode extends AbstractNode implements NodeInterface {
                $this->name = $structure['name'];
 
                if (isset($structure['targetPermission'])) {
-                       $this->targetPermission = $structure['targetPermission'];
+                       $this->setTargetPermission($structure['targetPermission']);
                }
 
                if (isset($structure['targetContent']) && isset($structure['targetContentFile'])) {
@@ -102,7 +102,8 @@ class FileNode extends AbstractNode implements NodeInterface {
                $result = array();
                if (!$this->exists()) {
                        $status = new Status\WarningStatus();
-                       $status->setTitle($this->getRelativePathBelowSiteRoot() . ' does not exist');
+                       $status->setTitle('File ' . $this->getRelativePathBelowSiteRoot() . ' does not exist');
+                       $status->setMessage('By using "Try to fix errors" we can try to create it');
                        $result[] = $status;
                } else {
                        $result = $this->getSelfStatus();
@@ -113,6 +114,8 @@ class FileNode extends AbstractNode implements NodeInterface {
        /**
         * Fix structure
         *
+        * If there is nothing to fix, returns an empty array
+        *
         * @return array<\TYPO3\CMS\Install\Status\StatusInterface>
         */
        public function fix() {
@@ -134,16 +137,18 @@ class FileNode extends AbstractNode implements NodeInterface {
                                && !is_null($this->targetContent)
                        ) {
                                $result[] = $this->setContent();
+                               if (!$this->isPermissionCorrect()) {
+                                       $result[] = $this->fixPermission();
+                               }
                        }
-               }
-               if (!$this->isFile()) {
+               } elseif (!$this->isFile()) {
                        $status = new Status\ErrorStatus();
                        $status->setTitle('Path ' . $this->getRelativePathBelowSiteRoot() . ' is not a file');
                        $fileType = @filetype($this->getAbsolutePath());
                        if ($fileType) {
                                $status->setMessage(
                                        'The target ' . $this->getRelativePathBelowSiteRoot() . ' should be a file,' .
-                                       ' but is of type ' . $fileType . '. I cannot fix this. Please investigate.'
+                                       ' but is of type ' . $fileType . '. This cannot be fixed automatically. Please investigate.'
                                );
                        } else {
                                $status->setMessage(
@@ -167,7 +172,7 @@ class FileNode extends AbstractNode implements NodeInterface {
        protected function createFile() {
                if ($this->exists()) {
                        throw new Exception(
-                               'File ' . $this->getAbsolutePath() . ' already exists',
+                               'File ' . $this->getRelativePathBelowSiteRoot() . ' already exists',
                                1367048077
                        );
                }
@@ -202,25 +207,24 @@ class FileNode extends AbstractNode implements NodeInterface {
                        );
                        $result[] = $status;
                } elseif (!$this->isWritable()) {
-                       $status = new Status\WarningStatus();
-                       $status->setTitle($this->getRelativePathBelowSiteRoot() . ' is not writable');
+                       $status = new Status\NoticeStatus();
+                       $status->setTitle('File ' . $this->getRelativePathBelowSiteRoot() . ' is not writable');
                        $status->setMessage(
-                               'Path ' . $this->getAbsolutePath() . ' exists, but no file beneath it' .
-                               ' can be created.'
+                               'File ' . $this->getRelativePathBelowSiteRoot() . ' exists, but is not writeable.'
                        );
                        $result[] = $status;
                } elseif (!$this->isPermissionCorrect()) {
-                       $status = new Status\WarningStatus();
-                       $status->setTitle($this->getRelativePathBelowSiteRoot() . ' has wrong permissions');
+                       $status = new Status\NoticeStatus();
+                       $status->setTitle('File ' . $this->getRelativePathBelowSiteRoot() . ' permissions mismatch');
                        $status->setMessage(
-                               'Target permissions are ' . $this->targetPermission .
-                               ' but current permissions are ' . $this->getCurrentPermission()
+                               'Default configured permissions are ' . $this->getTargetPermission() .
+                               ' but file permissions are ' . $this->getCurrentPermission()
                        );
                        $result[] = $status;
                }
                if ($this->isFile() && !$this->isContentCorrect()) {
-                       $status = new Status\ErrorStatus();
-                       $status->setTitle($this->getRelativePathBelowSiteRoot() . ' content differs');
+                       $status = new Status\NoticeStatus();
+                       $status->setTitle('File ' . $this->getRelativePathBelowSiteRoot() . ' content differs');
                        $status->setMessage(
                                'File content is not identical to default content. This file may have been changed manually.' .
                                ' The Install Tool will not overwrite the current version!'
@@ -228,7 +232,10 @@ class FileNode extends AbstractNode implements NodeInterface {
                        $result[] = $status;
                } else {
                        $status = new Status\OkStatus();
-                       $status->setTitle($this->getRelativePathBelowSiteRoot());
+                       $status->setTitle('File ' . $this->getRelativePathBelowSiteRoot());
+                       $status->setMessage(
+                               'Is a file with the default content and configured permissions of ' . $this->getTargetPermission()
+                       );
                        $result[] = $status;
                }
                return $result;
index a3894c7..f7da1b8 100644 (file)
@@ -92,13 +92,13 @@ class LinkNode extends AbstractNode implements NodeInterface {
                }
 
                if (!$this->isLink()) {
-                       $status = new Status\ErrorStatus();
+                       $status = new Status\WarningStatus();
                        $status->setTitle('Path ' . $this->getRelativePathBelowSiteRoot() . ' is not a link');
                        $type = @filetype($this->getAbsolutePath());
                        if ($type) {
                                $status->setMessage(
                                        'The target ' . $this->getRelativePathBelowSiteRoot() . ' should be a link,' .
-                                       ' but is of type ' . $type . '. This cannot be fixed. Please investigate.'
+                                       ' but is of type ' . $type . '. This cannot be fixed automatically. Please investigate.'
                                );
                        } else {
                                $status->setMessage(
@@ -119,19 +119,24 @@ class LinkNode extends AbstractNode implements NodeInterface {
                }
 
                $status = new Status\OkStatus();
+               $message = 'Is a link';
+               if ($this->getTarget() !== '') {
+                       $message .= ' and correctly points to target ' . $this->getTarget();
+               }
                $status->setTitle($this->getRelativePathBelowSiteRoot());
+               $status->setMessage($message);
                return array($status);
        }
 
        /**
         * Fix structure
         *
+        * If there is nothing to fix, returns an empty array
+        *
         * @return array<\TYPO3\CMS\Install\Status\StatusInterface>
         */
        public function fix() {
-               $status = new Status\NoticeStatus();
-               $status->setTitle('Fixing link ' . $this->getRelativePathBelowSiteRoot() . ' is not implemented');
-               return array($status);
+               return array();
        }
 
        /**
index c3e8f20..d36af82 100644 (file)
@@ -68,6 +68,8 @@ interface NodeInterface {
        /**
         * Fix structure
         *
+        * If there is nothing to fix, returns an empty array
+        *
         * @return array<\TYPO3\CMS\Install\Status\StatusInterface>
         */
        public function fix();
index 024344d..57cc975 100644 (file)
@@ -59,11 +59,7 @@ class RootNode extends DirectoryNode implements RootNodeInterface {
                $this->name = $structure['name'];
 
                if (isset($structure['targetPermission'])) {
-                       $this->targetPermission = $structure['targetPermission'];
-               }
-
-               if (isset($structure['targetPermissionRelaxed']) && $structure['targetPermissionRelaxed'] === TRUE) {
-                       $this->targetPermissionRelaxed = TRUE;
+                       $this->setTargetPermission($structure['targetPermission']);
                }
 
                if (array_key_exists('children', $structure)) {
index d767eb6..a84336d 100644 (file)
@@ -3,33 +3,37 @@
 <f:layout name="ToolAuthenticated" />
 
 <f:section name="Content">
-       <h3>File and folder status below <i:constant name="PATH_site" /></h3>
+       <h3>Folder Structure</h3>
+       <p>Root Directory: <strong><i:constant name="PATH_site" /></strong></p>
 
        <f:if condition="{fixedStatus}">
+               <hr />
                <h4>Fix action results:</h4>
                <f:for each="{fixedStatus}" as="statusMessage">
                        <f:render partial="Action/Common/StatusMessage" arguments="{message: statusMessage}" />
                </f:for>
-               <hr />
        </f:if>
 
        <f:if condition="{errorStatus}">
-               <form method="post">
-                       <f:render partial="Action/Common/HiddenFormFields" arguments="{_all}" />
-                       <f:render partial="Action/Common/SubmitButton" arguments="{name:'fix', text:'Fix file and folder permissions'}"/>
-               </form>
                <hr />
-       </f:if>
-
-       <f:if condition="{errorStatus}">
-               <h4>These files or folders have errors and may be automatically fixable:</h4>
+               <h4>These files or folders have errors and may be fixed automatically:</h4>
                <f:for each="{errorStatus}" as="statusMessage">
                        <f:render partial="Action/Common/StatusMessage" arguments="{message: statusMessage}" />
                </f:for>
+               <form method="post">
+                       <f:render partial="Action/Common/HiddenFormFields" arguments="{_all}" />
+                       <f:render partial="Action/Common/SubmitButton" arguments="{name:'fix', text:'Try to fix file and folder permissions'}"/>
+               </form>
        </f:if>
 
+       <hr />
+       <h4>Default File and Directory permissions</h4>
+       <f:render partial="Action/Common/StatusMessage" arguments="{message: filePermissionStatus}" />
+       <f:render partial="Action/Common/StatusMessage" arguments="{message: directoryPermissionStatus}" />
+
        <f:if condition="{okStatus}">
-               <h4>These structures are ok:</h4>
+               <hr />
+               <h4>These structures don't need to be fixed:</h4>
                <f:for each="{okStatus}" as="statusMessage">
                        <f:render partial="Action/Common/StatusMessage" arguments="{message: statusMessage}" />
                </f:for>
index 56a77cf..0047718 100644 (file)
@@ -107,13 +107,13 @@ h2 {
 }
 
 h3 {
-       font-size: 120%;
+       font-size: 140%;
        margin: 0.7em 0 1.5em
 }
 
 h4 {
        font-size: 110%;
-       margin: 0;
+       margin: 1.5em 0 1.2em;
 }
 
 .clearfix:after {
@@ -450,7 +450,7 @@ body.standalone #t3-install-menu-logout a {
 }
 
 #t3-install-box-body h3 {
-       margin: 0.75em 0 2em;
+       margin: 0.75em 0 1.5em;
 }
 
 .t3-install-login #t3-install-box-body h3 {
@@ -477,6 +477,7 @@ body.standalone #t3-install-menu-logout a {
 
 #t3-install-box-body .toggleButton h4 {
        border: 1px solid transparent;
+       margin: 0 0;
 }
 
 #t3-install-box-body .expanded .toggleButton h4 {
index 7d52417..ff704de 100644 (file)
@@ -74,25 +74,6 @@ class AbstractNodeTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
        /**
         * @test
         */
-       public function getTargetPermissionRelaxedReturnsFalseByDefault() {
-               /** @var $node \TYPO3\CMS\Install\FolderStructure\AbstractNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
-               $node = $this->getAccessibleMock('TYPO3\\CMS\\Install\\FolderStructure\\AbstractNode', array('dummy'), array(), '', FALSE);
-               $this->assertFalse($node->_call('getTargetPermissionRelaxed'));
-       }
-
-       /**
-        * @test
-        */
-       public function getTargetPermissionRelaxedReturnsTrueIfPermissionCheckIsRelaxed() {
-               /** @var $node \TYPO3\CMS\Install\FolderStructure\AbstractNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
-               $node = $this->getAccessibleMock('TYPO3\\CMS\\Install\\FolderStructure\\AbstractNode', array('dummy'), array(), '', FALSE);
-               $node->_set('targetPermissionRelaxed', TRUE);
-               $this->assertTrue($node->_call('getTargetPermissionRelaxed'));
-       }
-
-       /**
-        * @test
-        */
        public function getChildrenReturnsSetChildren() {
                /** @var $node \TYPO3\CMS\Install\FolderStructure\AbstractNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
                $node = $this->getAccessibleMock('TYPO3\\CMS\\Install\\FolderStructure\\AbstractNode', array('dummy'), array(), '', FALSE);
@@ -213,7 +194,7 @@ class AbstractNodeTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
        /**
         * @test
         */
-       public function fixPermissionReturnsErrorStatusIfPermissionCanNotBeChanged() {
+       public function fixPermissionReturnsNoticeStatusIfPermissionCanNotBeChanged() {
                if (TYPO3_OS === 'WIN') {
                        $this->markTestSkipped('Test not available on Windows OS.');
                }
@@ -234,18 +215,18 @@ class AbstractNodeTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                mkdir($path);
                $subPath = $path . '/' . uniqid('dir_');
                mkdir($subPath);
-               chmod($path, octdec(2000));
+               chmod($path, 02000);
                $this->testNodesToDelete[] = $path;
                $node->expects($this->any())->method('getAbsolutePath')->will($this->returnValue($subPath));
                $node->_set('targetPermission', '2770');
-               $this->assertInstanceOf('TYPO3\\CMS\\Install\\Status\\ErrorStatus', $node->_call('fixPermission'));
-               chmod($path, octdec(2770));
+               $this->assertInstanceOf('TYPO3\\CMS\\Install\\Status\\NoticeStatus', $node->_call('fixPermission'));
+               chmod($path, 02770);
        }
 
        /**
         * @test
         */
-       public function fixPermissionReturnsNoticeStatusIfPermissionsCanNotBeChangedAndRelaxedPermissionCheckIsEnabled() {
+       public function fixPermissionReturnsNoticeStatusIfPermissionsCanNotBeChanged() {
                if (TYPO3_OS === 'WIN') {
                        $this->markTestSkipped('Test not available on Windows OS.');
                }
@@ -266,13 +247,12 @@ class AbstractNodeTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                mkdir($path);
                $subPath = $path . '/' . uniqid('dir_');
                mkdir($subPath);
-               chmod($path, octdec(2000));
+               chmod($path, 02000);
                $this->testNodesToDelete[] = $path;
                $node->expects($this->any())->method('getAbsolutePath')->will($this->returnValue($subPath));
                $node->_set('targetPermission', '2770');
-               $node->_set('targetPermissionRelaxed', TRUE);
                $this->assertInstanceOf('TYPO3\\CMS\\Install\\Status\\NoticeStatus', $node->_call('fixPermission'));
-               chmod($path, octdec(2770));
+               chmod($path, 02770);
        }
 
        /**
@@ -296,13 +276,13 @@ class AbstractNodeTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                mkdir($path);
                $subPath = $path . '/' . uniqid('dir_');
                mkdir($subPath);
-               chmod($path, octdec(2770));
+               chmod($path, 02770);
                $this->testNodesToDelete[] = $path;
-               $node->_set('targetPermission', '2775');
+               $node->_set('targetPermission', '2770');
                $node->expects($this->any())->method('getAbsolutePath')->will($this->returnValue($subPath));
                $this->assertInstanceOf('TYPO3\\CMS\\Install\\Status\\OkStatus', $node->_call('fixPermission'));
                $resultDirectoryPermissions = substr(decoct(fileperms($subPath)), 1);
-               $this->assertSame('2775', $resultDirectoryPermissions);
+               $this->assertSame('2770', $resultDirectoryPermissions);
        }
 
        /**
@@ -318,18 +298,6 @@ class AbstractNodeTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
        /**
         * @test
         */
-       public function isPermissionCorrectReturnsTrueIfTargetPermissionAndCurrentPermissionAreIdentical() {
-               /** @var $node \TYPO3\CMS\Install\FolderStructure\AbstractNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
-               $node = $this->getAccessibleMock('TYPO3\\CMS\\Install\\FolderStructure\\AbstractNode', array('isWindowsOs', 'getCurrentPermission'), array(), '', FALSE);
-               $node->expects($this->any())->method('isWindowsOs')->will($this->returnValue(FALSE));
-               $node->expects($this->any())->method('getCurrentPermission')->will($this->returnValue('foo'));
-               $node->_set('targetPermission', 'foo');
-               $this->assertTrue($node->_call('isPermissionCorrect'));
-       }
-
-       /**
-        * @test
-        */
        public function isPermissionCorrectReturnsFalseIfTargetPermissionAndCurrentPermissionAreNotIdentical() {
                /** @var $node \TYPO3\CMS\Install\FolderStructure\AbstractNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
                $node = $this->getAccessibleMock('TYPO3\\CMS\\Install\\FolderStructure\\AbstractNode', array('isWindowsOs', 'getCurrentPermission'), array(), '', FALSE);
@@ -351,7 +319,7 @@ class AbstractNodeTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                $path = PATH_site . 'typo3temp/' . uniqid('dir_');
                \TYPO3\CMS\Core\Utility\GeneralUtility::mkdir_deep($path);
                $this->testNodesToDelete[] = $path;
-               chmod($path, octdec(2775));
+               chmod($path, 02775);
                clearstatcache();
                $node->expects($this->any())->method('getAbsolutePath')->will($this->returnValue($path));
                $this->assertSame('2775', $node->_call('getCurrentPermission'));
@@ -369,7 +337,7 @@ class AbstractNodeTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                $file = PATH_site . 'typo3temp/' . uniqid('file_');
                touch($file);
                $this->testNodesToDelete[] = $file;
-               chmod($file, octdec(770));
+               chmod($file, 0770);
                clearstatcache();
                $node->expects($this->any())->method('getAbsolutePath')->will($this->returnValue($file));
                $this->assertSame('0770', $node->_call('getCurrentPermission'));
@@ -420,4 +388,5 @@ class AbstractNodeTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                $result = $node->_call('getRelativePathBelowSiteRoot', PATH_site . 'foo/bar');
                $this->assertSame('/foo/bar', $result);
        }
+
 }
index 63d18c9..2321292 100644 (file)
@@ -160,7 +160,7 @@ class DirectoryNodeTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
        /**
         * @test
         */
-       public function getStatusReturnsArrayWithWarningStatusIfDirectoryNotExists() {
+       public function getStatusReturnsArrayWithErrorStatusIfDirectoryNotExists() {
                /** @var $node \TYPO3\CMS\Install\FolderStructure\DirectoryNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
                $node = $this->getAccessibleMock(
                        'TYPO3\\CMS\\Install\\FolderStructure\\DirectoryNode',
@@ -172,13 +172,13 @@ class DirectoryNodeTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                $path = PATH_site . 'typo3temp/' . uniqid('dir_');
                $node->expects($this->any())->method('getAbsolutePath')->will($this->returnValue($path));
                $node->expects($this->any())->method('exists')->will($this->returnValue(FALSE));
-               $node->expects($this->any())->method('isDirectory')->will($this->returnValue(TRUE));
-               $node->expects($this->any())->method('isPermissionCorrect')->will($this->returnValue(TRUE));
-               $node->expects($this->any())->method('isWritable')->will($this->returnValue(TRUE));
+               $node->expects($this->any())->method('isDirectory')->will($this->returnValue(FALSE));
+               $node->expects($this->any())->method('isPermissionCorrect')->will($this->returnValue(FALSE));
+               $node->expects($this->any())->method('isWritable')->will($this->returnValue(FALSE));
                $statusArray = $node->getStatus();
                /** @var $status \TYPO3\CMS\Install\Status\StatusInterface */
                $status = $statusArray[0];
-               $this->assertInstanceOf('\TYPO3\CMS\Install\Status\WarningStatus', $status);
+               $this->assertInstanceOf('\TYPO3\CMS\Install\Status\ErrorStatus', $status);
        }
 
        /**
@@ -236,7 +236,7 @@ class DirectoryNodeTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
        /**
         * @test
         */
-       public function getStatusReturnsArrayWithWarningStatusIfDirectoryExistsButPermissionAreNotCorrect() {
+       public function getStatusReturnsArrayWithNoticeStatusIfDirectoryExistsButPermissionAreNotCorrect() {
                /** @var $node \TYPO3\CMS\Install\FolderStructure\DirectoryNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
                $node = $this->getAccessibleMock(
                        'TYPO3\\CMS\\Install\\FolderStructure\\DirectoryNode',
@@ -256,33 +256,6 @@ class DirectoryNodeTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                $statusArray = $node->getStatus();
                /** @var $status \TYPO3\CMS\Install\Status\StatusInterface */
                $status = $statusArray[0];
-               $this->assertInstanceOf('\TYPO3\CMS\Install\Status\WarningStatus', $status);
-       }
-
-       /**
-        * @test
-        */
-       public function getStatusReturnsArrayWithNoticeStatusIfDirectoryExistsButPermissionAreNotCorrectAndPermissionCheckIsRelaxed() {
-               /** @var $node \TYPO3\CMS\Install\FolderStructure\DirectoryNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
-               $node = $this->getAccessibleMock(
-                       'TYPO3\\CMS\\Install\\FolderStructure\\DirectoryNode',
-                       array('getAbsolutePath', 'exists', 'isDirectory', 'isWritable', 'isPermissionCorrect', 'getTargetPermissionRelaxed'),
-                       array(),
-                       '',
-                       FALSE
-               );
-               $path = PATH_site . 'typo3temp/' . uniqid('dir_');
-               touch ($path);
-               $this->testNodesToDelete[] = $path;
-               $node->expects($this->any())->method('getAbsolutePath')->will($this->returnValue($path));
-               $node->expects($this->any())->method('exists')->will($this->returnValue(TRUE));
-               $node->expects($this->any())->method('isDirectory')->will($this->returnValue(TRUE));
-               $node->expects($this->any())->method('isPermissionCorrect')->will($this->returnValue(FALSE));
-               $node->expects($this->any())->method('isWritable')->will($this->returnValue(TRUE));
-               $node->expects($this->once())->method('getTargetPermissionRelaxed')->will($this->returnValue(TRUE));
-               $statusArray = $node->getStatus();
-               /** @var $status \TYPO3\CMS\Install\Status\StatusInterface */
-               $status = $statusArray[0];
                $this->assertInstanceOf('\TYPO3\CMS\Install\Status\NoticeStatus', $status);
        }
 
@@ -445,18 +418,38 @@ class DirectoryNodeTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
        /**
         * @test
         */
-       public function fixSelfCallsFixPermissionIfDirectoryExistsButPermissionAreWrong() {
+       public function fixSelfOnExistingDirectoryDoesNotFixPermissions() {
                /** @var $node \TYPO3\CMS\Install\FolderStructure\DirectoryNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
                $node = $this->getAccessibleMock(
                        'TYPO3\\CMS\\Install\\FolderStructure\\DirectoryNode',
-                       array('exists', 'createDirectory', 'getAbsolutePath', 'isDirectory', 'isPermissionCorrect', 'getRelativePathBelowSiteRoot'),
+                       array('exists', 'createDirectory', 'getAbsolutePath', 'isDirectory', 'isPermissionCorrect', 'fixPermission'),
                        array(),
                        '',
                        FALSE
                );
                $node->expects($this->any())->method('exists')->will($this->returnValue(TRUE));
-               $node->expects($this->once())->method('isDirectory')->will($this->returnValue(FALSE));
+               $node->expects($this->any())->method('isDirectory')->will($this->returnValue(TRUE));
+               $node->expects($this->any())->method('isPermissionCorrect')->will($this->returnValue(FALSE));
+               $node->expects($this->once())->method('fixPermission');
+               $node->_call('fixSelf');
+       }
+
+       /**
+        * @test
+        */
+       public function fixSelfCallsFixPermissionIfDirectoryExistsButPermissionAreWrong() {
+               /** @var $node \TYPO3\CMS\Install\FolderStructure\DirectoryNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $node = $this->getAccessibleMock(
+                       'TYPO3\\CMS\\Install\\FolderStructure\\DirectoryNode',
+                       array('exists', 'createDirectory', 'getAbsolutePath', 'isDirectory', 'isPermissionCorrect', 'getRelativePathBelowSiteRoot', 'fixPermission'),
+                       array(),
+                       '',
+                       FALSE
+               );
+               $node->expects($this->any())->method('exists')->will($this->returnValue(FALSE));
+               $node->expects($this->once())->method('createDirectory')->will($this->returnValue(new \TYPO3\CMS\Install\Status\OkStatus()));
                $node->expects($this->any())->method('isPermissionCorrect')->will($this->returnValue(TRUE));
+               $node->expects($this->never())->method('fixPermission');
                $resultArray = $node->_call('fixSelf');
                $this->assertInstanceOf('TYPO3\\CMS\Install\\Status\\StatusInterface', $resultArray[0]);
        }
@@ -467,15 +460,17 @@ class DirectoryNodeTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
        public function fixSelfReturnsArrayOfStatusMessages() {
                $node = $this->getAccessibleMock(
                        'TYPO3\\CMS\\Install\\FolderStructure\\DirectoryNode',
-                       array('exists', 'isDirectory', 'isPermissionCorrect'),
+                       array('exists', 'isDirectory', 'isWritable', 'isPermissionCorrect'),
                        array(),
                        '',
                        FALSE
                );
                $node->expects($this->any())->method('exists')->will($this->returnValue(TRUE));
+               $node->expects($this->any())->method('isWritable')->will($this->returnValue(TRUE));
                $node->expects($this->any())->method('isDirectory')->will($this->returnValue(TRUE));
                $node->expects($this->any())->method('isPermissionCorrect')->will($this->returnValue(TRUE));
-               $this->assertInternalType('array', $node->_call('fixSelf'));
+               $return = $node->_call('fixSelf');
+               $this->assertInternalType('array', $return);
        }
 
        /**
@@ -528,7 +523,7 @@ class DirectoryNodeTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                $node = $this->getAccessibleMock('TYPO3\\CMS\\Install\\FolderStructure\\DirectoryNode', array('exists', 'getAbsolutePath'), array(), '', FALSE);
                $path = PATH_site . 'typo3temp/' . uniqid('root_');
                mkdir($path);
-               chmod($path, octdec(2550));
+               chmod($path, 02550);
                $subPath = $path . '/' . uniqid('dir_');
                $this->testNodesToDelete[] = $path;
                $node->expects($this->once())->method('exists')->will($this->returnValue(FALSE));
@@ -651,7 +646,7 @@ class DirectoryNodeTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                $path = PATH_site . 'typo3temp/' . uniqid('root_');
                \TYPO3\CMS\Core\Utility\GeneralUtility::mkdir_deep($path);
                $this->testNodesToDelete[] = $path;
-               chmod($path, octdec(2550));
+               chmod($path, 02550);
                $node->expects($this->any())->method('getAbsolutePath')->will($this->returnValue($path));
                $this->assertFalse($node->isWritable());
        }
index 7d0ccac..1a74b9e 100644 (file)
@@ -91,7 +91,7 @@ class FileNodeTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                $parent = $this->getMock('TYPO3\CMS\Install\FolderStructure\NodeInterface', array(), array(), '', FALSE);
                /** @var $node \TYPO3\CMS\Install\FolderStructure\FileNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
                $node = $this->getAccessibleMock('TYPO3\\CMS\\Install\\FolderStructure\\FileNode', array('dummy'), array(), '', FALSE);
-               $targetPermission = '660';
+               $targetPermission = '0660';
                $structure = array(
                        'name' => 'foo',
                        'targetPermission' => $targetPermission,
@@ -270,7 +270,7 @@ class FileNodeTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
        /**
         * @test
         */
-       public function getStatusReturnsArrayWithWarningStatusIfFileExistsButIsNotWritable() {
+       public function getStatusReturnsArrayNoticeStatusIfFileExistsButIsNotWritable() {
                /** @var $node \TYPO3\CMS\Install\FolderStructure\FileNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
                $node = $this->getAccessibleMock(
                        'TYPO3\\CMS\\Install\\FolderStructure\\FileNode',
@@ -291,13 +291,13 @@ class FileNodeTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                $statusArray = $node->getStatus();
                /** @var $status \TYPO3\CMS\Install\Status\StatusInterface */
                $status = $statusArray[0];
-               $this->assertInstanceOf('\TYPO3\CMS\Install\Status\WarningStatus', $status);
+               $this->assertInstanceOf('\TYPO3\CMS\Install\Status\NoticeStatus', $status);
        }
 
        /**
         * @test
         */
-       public function getStatusReturnsArrayWithWarningStatusIfFileExistsButPermissionAreNotCorrect() {
+       public function getStatusReturnsArrayWithNoticeStatusIfFileExistsButPermissionAreNotCorrect() {
                /** @var $node \TYPO3\CMS\Install\FolderStructure\FileNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
                $node = $this->getAccessibleMock(
                        'TYPO3\\CMS\\Install\\FolderStructure\\FileNode',
@@ -318,13 +318,13 @@ class FileNodeTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                $statusArray = $node->getStatus();
                /** @var $status \TYPO3\CMS\Install\Status\StatusInterface */
                $status = $statusArray[0];
-               $this->assertInstanceOf('\TYPO3\CMS\Install\Status\WarningStatus', $status);
+               $this->assertInstanceOf('\TYPO3\CMS\Install\Status\NoticeStatus', $status);
        }
 
        /**
         * @test
         */
-       public function getStatusReturnsArrayWithErrorStatusIfFileExistsButContentIsNotCorrect() {
+       public function getStatusReturnsArrayWithNoticeStatusIfFileExistsButContentIsNotCorrect() {
                /** @var $node \TYPO3\CMS\Install\FolderStructure\FileNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
                $node = $this->getAccessibleMock(
                        'TYPO3\\CMS\\Install\\FolderStructure\\FileNode',
@@ -345,7 +345,7 @@ class FileNodeTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                $statusArray = $node->getStatus();
                /** @var $status \TYPO3\CMS\Install\Status\StatusInterface */
                $status = $statusArray[0];
-               $this->assertInstanceOf('\TYPO3\CMS\Install\Status\ErrorStatus', $status);
+               $this->assertInstanceOf('\TYPO3\CMS\Install\Status\NoticeStatus', $status);
        }
 
        /**
@@ -589,7 +589,7 @@ class FileNodeTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                $node = $this->getAccessibleMock('TYPO3\\CMS\\Install\\FolderStructure\\FileNode', array('exists', 'getAbsolutePath'), array(), '', FALSE);
                $path = PATH_site . 'typo3temp/' . uniqid('root_');
                mkdir($path);
-               chmod($path, octdec(2550));
+               chmod($path, 02550);
                $subPath = $path . '/' . uniqid('file_');
                $this->testNodesToDelete[] = $path;
                $node->expects($this->once())->method('exists')->will($this->returnValue(FALSE));
@@ -658,6 +658,24 @@ class FileNodeTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
 
        /**
         * @test
+        */
+       public function isPermissionCorrectReturnsTrueIfTargetPermissionAndCurrentPermissionAreIdentical() {
+               $parent = $this->getMock('TYPO3\CMS\Install\FolderStructure\NodeInterface', array(), array(), '', FALSE);
+               /** @var $node \TYPO3\CMS\Install\FolderStructure\FileNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $node = $this->getAccessibleMock('TYPO3\\CMS\\Install\\FolderStructure\\FileNode', array('getCurrentPermission', 'isWindowsOs'), array(), '', FALSE);
+               $node->expects($this->any())->method('isWindowsOs')->will($this->returnValue(FALSE));
+               $node->expects($this->any())->method('getCurrentPermission')->will($this->returnValue('0664'));
+               $targetPermission = '0664';
+               $structure = array(
+                       'name' => 'foo',
+                       'targetPermission' => $targetPermission,
+               );
+               $node->__construct($structure, $parent);
+               $this->assertTrue($node->_call('isPermissionCorrect'));
+       }
+
+       /**
+        * @test
         * @expectedException \TYPO3\CMS\Install\FolderStructure\Exception
         */
        public function setContentThrowsExceptionIfTargetIsNotAFile() {
@@ -734,7 +752,7 @@ class FileNodeTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                mkdir($dir);
                $file = $dir . '/' . uniqid('file_');
                touch($file);
-               chmod($file, octdec(0440));
+               chmod($file, 0440);
                $this->testNodesToDelete[] = $dir;
                $node->expects($this->any())->method('getAbsolutePath')->will($this->returnValue($file));
                $targetContent = uniqid('content_');
index 5b6fdc2..20fda24 100644 (file)
@@ -171,7 +171,7 @@ class LinkNodeTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
        /**
         * @test
         */
-       public function getStatusReturnsArrayWithErrorStatusIfNodeIsNotALink() {
+       public function getStatusReturnsArrayWithWarningStatusIfNodeIsNotALink() {
                /** @var $node \TYPO3\CMS\Install\FolderStructure\LinkNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
                $node = $this->getAccessibleMock(
                        'TYPO3\\CMS\\Install\\FolderStructure\\LinkNode',
@@ -186,7 +186,7 @@ class LinkNodeTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                $statusArray = $node->getStatus();
                /** @var $status \TYPO3\CMS\Install\Status\StatusInterface */
                $status = $statusArray[0];
-               $this->assertInstanceOf('\TYPO3\CMS\Install\Status\ErrorStatus', $status);
+               $this->assertInstanceOf('\TYPO3\CMS\Install\Status\WarningStatus', $status);
        }
 
        /**
@@ -237,7 +237,7 @@ class LinkNodeTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
        /**
         * @test
         */
-       public function fixReturnsNoticeStatus() {
+       public function fixReturnsEmptyArray() {
                /** @var $node \TYPO3\CMS\Install\FolderStructure\LinkNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
                $node = $this->getAccessibleMock(
                        'TYPO3\\CMS\\Install\\FolderStructure\\LinkNode',
@@ -247,9 +247,7 @@ class LinkNodeTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                        FALSE
                );
                $statusArray = $node->fix();
-               /** @var $status \TYPO3\CMS\Install\Status\StatusInterface */
-               $status = $statusArray[0];
-               $this->assertInstanceOf('\TYPO3\CMS\Install\Status\NoticeStatus', $status);
+               $this->assertEmpty($statusArray);
        }
 
        /**
index bcf6297..dec4260 100644 (file)
@@ -177,21 +177,6 @@ class RootNodeTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
        /**
         * @test
         */
-       public function constructorSetsRelaxedPermissionCheckIfGiven() {
-               /** @var $node \TYPO3\CMS\Install\FolderStructure\RootNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
-               $node = $this->getAccessibleMock('TYPO3\\CMS\\Install\\FolderStructure\\RootNode', array('isWindowsOs'), array(), '', FALSE);
-               $node->expects($this->any())->method('isWindowsOs')->will($this->returnValue(FALSE));
-               $structure = array(
-                       'name' => '/foo',
-                       'targetPermissionRelaxed' => TRUE,
-               );
-               $node->__construct($structure, NULL);
-               $this->assertTrue($node->_call('getTargetPermissionRelaxed'));
-       }
-
-       /**
-        * @test
-        */
        public function constructorSetsName() {
                /** @var $node \TYPO3\CMS\Install\FolderStructure\RootNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
                $node = $this->getAccessibleMock('TYPO3\\CMS\\Install\\FolderStructure\\RootNode', array('isWindowsOs'), array(), '', FALSE);