From ebdb649bd7728649ebf2bfcc60a3da9838d59537 Mon Sep 17 00:00:00 2001 From: Taras Machyshyn Date: Tue, 17 Feb 2015 11:02:01 +0200 Subject: [PATCH] Improved upgrade/install extenions feature --- application/Espo/Core/ExtensionManager.php | 1 - application/Espo/Core/UpgradeManager.php | 1 + .../Espo/Core/Upgrades/Actions/Base.php | 33 ++++++++- .../Core/Upgrades/Actions/Base/Install.php | 42 +++++++---- .../Core/Upgrades/Actions/Base/Uninstall.php | 65 ++++++++++++----- .../Upgrades/Actions/Extension/Install.php | 22 +----- .../Upgrades/Actions/Extension/Uninstall.php | 10 +++ .../Core/Upgrades/Actions/Upgrade/Install.php | 11 +-- application/Espo/Core/Utils/File/Manager.php | 70 +++++++++++++++++++ 9 files changed, 196 insertions(+), 59 deletions(-) diff --git a/application/Espo/Core/ExtensionManager.php b/application/Espo/Core/ExtensionManager.php index f3c35baf7a..8613b3a958 100644 --- a/application/Espo/Core/ExtensionManager.php +++ b/application/Espo/Core/ExtensionManager.php @@ -28,7 +28,6 @@ class ExtensionManager extends Upgrades\Base protected $params = array( 'packagePath' => 'data/upload/extensions', - 'backupPath' => 'data/.backup/extensions', 'scriptNames' => array( diff --git a/application/Espo/Core/UpgradeManager.php b/application/Espo/Core/UpgradeManager.php index adae1d87f3..3e805b5b52 100644 --- a/application/Espo/Core/UpgradeManager.php +++ b/application/Espo/Core/UpgradeManager.php @@ -30,6 +30,7 @@ class UpgradeManager extends Upgrades\Base protected $params = array( 'packagePath' => 'data/upload/upgrades', + 'backupPath' => 'data/.backup/upgrades', 'scriptNames' => array( 'before' => 'BeforeUpgrade', diff --git a/application/Espo/Core/Upgrades/Actions/Base.php b/application/Espo/Core/Upgrades/Actions/Base.php index 52e7f7ad0a..2b0f530910 100644 --- a/application/Espo/Core/Upgrades/Actions/Base.php +++ b/application/Espo/Core/Upgrades/Actions/Base.php @@ -304,7 +304,7 @@ abstract class Base /** * Get a list of files defined in manifest.json * - * @return [type] [description] + * @return array */ protected function getDeleteFileList() { @@ -352,6 +352,18 @@ abstract class Base return $this->data['fileList']; } + protected function getRestoreFileList() + { + if (!isset($this->data['restoreFileList'])) { + $backupPath = $this->getPath('backupPath'); + $backupFilePath = Util::concatPath($backupPath, self::FILES); + + $this->data['restoreFileList'] = $this->getFileManager()->getFileList($backupFilePath, true, '', true, true); + } + + return $this->data['restoreFileList']; + } + protected function copy($sourcePath, $destPath, $recursively = false, array $fileList = null, $copyOnlyFiles = false) { try { @@ -506,4 +518,23 @@ abstract class Base { return $this->getContainer()->get('dataManager')->clearCache(); } + + protected function checkIsWritable() + { + $fullFileList = array_merge($this->getDeleteFileList(), $this->getCopyFileList()); + + $result = $this->getFileManager()->isWritableList($fullFileList); + if (!$result) { + $permissionDeniedList = $this->getFileManager()->getLastPermissionDeniedList(); + throw new Error("Permission denied in
". implode(",
", $permissionDeniedList)); + } + } + + protected function backupExistingFiles() + { + $fullFileList = array_merge($this->getDeleteFileList(), $this->getCopyFileList()); + + $backupPath = $this->getPath('backupPath'); + return $this->copy('', array($backupPath, self::FILES), false, $fullFileList); + } } \ No newline at end of file diff --git a/application/Espo/Core/Upgrades/Actions/Base/Install.php b/application/Espo/Core/Upgrades/Actions/Base/Install.php index 7a0f9dd93a..4d27ba9b87 100644 --- a/application/Espo/Core/Upgrades/Actions/Base/Install.php +++ b/application/Espo/Core/Upgrades/Actions/Base/Install.php @@ -23,6 +23,7 @@ namespace Espo\Core\Upgrades\Actions\Base; use Espo\Core\Exceptions\Error; +use Espo\Core\Utils\Util; class Install extends \Espo\Core\Upgrades\Actions\Base { @@ -58,15 +59,15 @@ class Install extends \Espo\Core\Upgrades\Actions\Base $this->isAcceptable(); } - $this->beforeRunAction(); + //check permissions copied and deleted files + $this->checkIsWritable(); + + $this->backupExistingFiles(); /* run before install script */ $this->runScript('before'); - /* remove files defined in a manifest */ - if (!$this->deleteFiles()) { - $this->throwErrorAndRemovePackage('Permission denied to delete files.'); - } + $this->beforeRunAction(); /* copy files from directory "Files" to EspoCRM files */ if (!$this->copyFiles()) { @@ -74,15 +75,18 @@ class Install extends \Espo\Core\Upgrades\Actions\Base } $this->isCopied = true; + /* remove files defined in a manifest */ + $this->deleteFiles(); + if (!$this->systemRebuild()) { $this->throwErrorAndRemovePackage('Error occurred while EspoCRM rebuild.'); } + $this->afterRunAction(); + /* run before install script */ $this->runScript('after'); - $this->afterRunAction(); - $this->clearCache(); /* delete unziped files */ @@ -93,15 +97,25 @@ class Install extends \Espo\Core\Upgrades\Actions\Base protected function restoreFiles() { - $backupPath = $this->getPath('backupPath'); - - $res = true; - if ($this->isCopied) { - $res &= $this->copy(array($backupPath, self::FILES), '', true); - $GLOBALS['log']->info('Restore: copy back'); + if (!$this->isCopied) { + return; } - $res &= $this->getFileManager()->removeInDir($backupPath, true); + $GLOBALS['log']->info('Installer: Restore previous files.'); + + $backupPath = $this->getPath('backupPath'); + $backupFilePath = Util::concatPath($backupPath, self::FILES); + + $backupFileList = $this->getRestoreFileList(); + $copyFileList = $this->getCopyFileList(); + $deleteFileList = array_diff($copyFileList, $backupFileList); + + $res = $this->copy($backupFilePath, '', true); + $res &= $this->getFileManager()->remove($deleteFileList, null, true); + + if ($res) { + $this->getFileManager()->removeInDir($backupPath, true); + } return $res; } diff --git a/application/Espo/Core/Upgrades/Actions/Base/Uninstall.php b/application/Espo/Core/Upgrades/Actions/Base/Uninstall.php index 5c84377eb8..722e0e9287 100644 --- a/application/Espo/Core/Upgrades/Actions/Base/Uninstall.php +++ b/application/Espo/Core/Upgrades/Actions/Base/Uninstall.php @@ -37,34 +37,36 @@ class Uninstall extends \Espo\Core\Upgrades\Actions\Base $this->setProcessId($processId); - $this->beforeRunAction(); + $this->checkIsWritable(); /* run before install script */ $this->runScript('beforeUninstall'); + $this->beforeRunAction(); + $backupPath = $this->getPath('backupPath'); if (file_exists($backupPath)) { - /* remove extension files, saved in fileList */ - if (!$this->deleteFiles(true)) { - throw new Error('Permission denied to delete files.'); - } - /* copy core files */ if (!$this->copyFiles()) { - throw new Error('Cannot copy files.'); + throw new $this->throwErrorAndRemovePackage('Cannot copy files.'); + } + + /* remove extension files, saved in fileList */ + if (!$this->deleteFiles(true)) { + throw new $this->throwErrorAndRemovePackage('Permission denied to delete files.'); } } if (!$this->systemRebuild()) { - throw new Error('Error occurred while EspoCRM rebuild.'); + throw new $this->throwErrorAndRemovePackage('Error occurred while EspoCRM rebuild.'); } + $this->afterRunAction(); + /* run before install script */ $this->runScript('afterUninstall'); - $this->afterRunAction(); - $this->clearCache(); /* delete backup files */ @@ -73,12 +75,6 @@ class Uninstall extends \Espo\Core\Upgrades\Actions\Base $GLOBALS['log']->debug('Uninstallation process ['.$processId.']: end run.'); } - protected function getDeleteFileList() - { - $extensionEntity = $this->getExtensionEntity(); - return $extensionEntity->get('fileList'); - } - protected function restoreFiles() { $packagePath = $this->getPath('packagePath'); @@ -131,4 +127,41 @@ class Uninstall extends \Espo\Core\Upgrades\Actions\Base throw new Error($errorMessage); } + protected function getCopyFileList() + { + if (!isset($this->data['fileList'])) { + $backupPath = $this->getPath('backupPath'); + $filesPath = Util::concatPath($backupPath, self::FILES); + + $this->data['fileList'] = $this->getFileManager()->getFileList($filesPath, true, '', true, true); + } + + return $this->data['fileList']; + } + + protected function getRestoreFileList() + { + if (!isset($this->data['restoreFileList'])) { + $packagePath = $this->getPackagePath(); + $filesPath = Util::concatPath($packagePath, self::FILES); + + if (!file_exists($filesPath)) { + $this->unzipArchive($packagePath); + } + + $this->data['restoreFileList'] = $this->getFileManager()->getFileList($filesPath, true, '', true, true); + } + + return $this->data['restoreFileList']; + } + + protected function getDeleteFileList() + { + $packageFileList = $this->getRestoreFileList(); + $backupFileList = $this->getCopyFileList(); + + $deleteFileList = array_diff($packageFileList, $backupFileList); + + return $deleteFileList; + } } diff --git a/application/Espo/Core/Upgrades/Actions/Extension/Install.php b/application/Espo/Core/Upgrades/Actions/Extension/Install.php index dff99f54a0..1fb605cd8a 100644 --- a/application/Espo/Core/Upgrades/Actions/Extension/Install.php +++ b/application/Espo/Core/Upgrades/Actions/Extension/Install.php @@ -37,8 +37,6 @@ class Install extends \Espo\Core\Upgrades\Actions\Base\Install $this->uninstallExtension(); $this->deleteExtension(); } - - $this->copyExistingFiles(); } protected function afterRunAction() @@ -51,12 +49,11 @@ class Install extends \Espo\Core\Upgrades\Actions\Base\Install * * @return bool */ - protected function copyExistingFiles() + protected function backupExistingFiles() { - $fileList = $this->getCopyFileList(); - $backupPath = $this->getPath('backupPath'); + parent::backupExistingFiles(); - $res = $this->copy('', array($backupPath, self::FILES), false, $fileList); + $backupPath = $this->getPath('backupPath'); /** copy scripts files */ $packagePath = $this->getPackagePath(); @@ -65,19 +62,6 @@ class Install extends \Espo\Core\Upgrades\Actions\Base\Install return $res; } - protected function restoreFiles() - { - $res = true; - if ($this->isCopied) { - $extensionFileList = $this->getCopyFileList(); - $res &= $this->getFileManager()->remove($extensionFileList); - } - - $res &= parent::restoreFiles(); - - return $res; - } - protected function isNew() { $extensionEntity = $this->getExtensionEntity(); diff --git a/application/Espo/Core/Upgrades/Actions/Extension/Uninstall.php b/application/Espo/Core/Upgrades/Actions/Extension/Uninstall.php index 41745c0979..d68fe183d2 100644 --- a/application/Espo/Core/Upgrades/Actions/Extension/Uninstall.php +++ b/application/Espo/Core/Upgrades/Actions/Extension/Uninstall.php @@ -68,4 +68,14 @@ class Uninstall extends \Espo\Core\Upgrades\Actions\Base\Uninstall $extensionEntity->set('isInstalled', false); $this->getEntityManager()->saveEntity($extensionEntity); } + + protected function getRestoreFileList() + { + if (!isset($this->data['restoreFileList'])) { + $extensionEntity = $this->getExtensionEntity(); + $this->data['restoreFileList'] = $extensionEntity->get('fileList'); + } + + return $this->data['restoreFileList']; + } } \ No newline at end of file diff --git a/application/Espo/Core/Upgrades/Actions/Upgrade/Install.php b/application/Espo/Core/Upgrades/Actions/Upgrade/Install.php index 5d3b69a8c5..407ff85733 100644 --- a/application/Espo/Core/Upgrades/Actions/Upgrade/Install.php +++ b/application/Espo/Core/Upgrades/Actions/Upgrade/Install.php @@ -24,17 +24,12 @@ namespace Espo\Core\Upgrades\Actions\Upgrade; class Install extends \Espo\Core\Upgrades\Actions\Base\Install { - protected function systemRebuild() + protected function afterRunAction() { $manifest = $this->getManifest(); - $res = $this->getConfig()->set('version', $manifest['version']); - if (method_exists($this->getConfig(), 'save')) { - $res = $this->getConfig()->save(); - } - $res &= parent::systemRebuild(); - - return $res; + $this->getConfig()->set('version', $manifest['version']); + $this->getConfig()->save(); } /** diff --git a/application/Espo/Core/Utils/File/Manager.php b/application/Espo/Core/Utils/File/Manager.php index e0cb8f4576..391ffabef6 100644 --- a/application/Espo/Core/Utils/File/Manager.php +++ b/application/Espo/Core/Utils/File/Manager.php @@ -29,6 +29,8 @@ class Manager { private $permission; + private $permissionDeniedList = array(); + public function __construct(\Espo\Core\Utils\Config $config = null) { $params = null; @@ -774,5 +776,73 @@ return '.var_export($content, true).'; ?>'; } + /** + * Check if $paths are writable. Permission denied list are defined in getLastPermissionDeniedList() + * + * @param array $paths + * + * @return boolean + */ + public function isWritableList(array $paths) + { + $permissionDeniedList = array(); + + $result = true; + foreach ($paths as $path) { + $rowResult = $this->isWritable($path); + if (!$rowResult) { + $permissionDeniedList[] = $path; + } + $result &= $rowResult; + } + + if (!empty($permissionDeniedList)) { + $this->permissionDeniedList = $this->getPermissionUtils()->arrangePermissionList($permissionDeniedList); + } + + return (bool) $result; + } + + /** + * Get last permission denied list + * + * @return array + */ + public function getLastPermissionDeniedList() + { + return $this->permissionDeniedList; + } + + /** + * Check if $path is writable + * + * @param string | array $path + * + * @return boolean + */ + public function isWritable($path) + { + $existFile = $this->getExistsPath($path); + + return is_writable($existFile); + } + + /** + * Get exists path. Ex. if check /var/www/espocrm/custom/someFile.php and this file doesn't extist, result will be /var/www/espocrm/custom + * + * @param string | array $path + * + * @return string + */ + protected function getExistsPath($path) + { + $fullPath = $this->concatPaths($path); + + if (!file_exists($fullPath)) { + $fullPath = $this->getExistsPath($fullPath, PATHINFO_DIRNAME); + } + + return $fullPath; + } }