From 620e695d7ee5d17a8b6001c7555354a9cf3910e0 Mon Sep 17 00:00:00 2001 From: Taras Machyshyn Date: Tue, 30 Sep 2014 17:59:17 +0300 Subject: [PATCH] Extension Manager improvements and fixes --- application/Espo/Controllers/Extension.php | 21 ++---- .../Espo/Core/Upgrades/Actions/Base.php | 71 +++++++++++-------- .../Core/Upgrades/Actions/Base/Delete.php | 2 +- .../Core/Upgrades/Actions/Base/Install.php | 39 ++++++++-- .../Core/Upgrades/Actions/Base/Uninstall.php | 53 ++++++++++---- .../Core/Upgrades/Actions/Base/Upload.php | 4 +- .../Upgrades/Actions/Extension/Delete.php | 6 -- .../Upgrades/Actions/Extension/Install.php | 44 ++++++++++-- .../Upgrades/Actions/Extension/Uninstall.php | 7 -- 9 files changed, 166 insertions(+), 81 deletions(-) diff --git a/application/Espo/Controllers/Extension.php b/application/Espo/Controllers/Extension.php index e943bd3012..0cab3b960a 100644 --- a/application/Espo/Controllers/Extension.php +++ b/application/Espo/Controllers/Extension.php @@ -79,19 +79,6 @@ class Extension extends \Espo\Core\Controllers\Record return true; } - public function actionDeletePackage($params, $data, $request) - { - if (!$request->isPost()) { - throw new Forbidden(); - } - - $manager = new \Espo\Core\ExtensionManager($this->getContainer()); - - $manager->delete($data['id']); - - return true; - } - public function actionCreate() { throw new Forbidden(); @@ -112,9 +99,13 @@ class Extension extends \Espo\Core\Controllers\Record throw new Forbidden(); } - public function actionDelete() + public function actionDelete($params, $data, $request) { - throw new Forbidden(); + $manager = new \Espo\Core\ExtensionManager($this->getContainer()); + + $manager->delete($params['id']); + + return true; } public function actionMassUpdate() diff --git a/application/Espo/Core/Upgrades/Actions/Base.php b/application/Espo/Core/Upgrades/Actions/Base.php index 4b0bd525ea..471bfcb8ee 100644 --- a/application/Espo/Core/Upgrades/Actions/Base.php +++ b/application/Espo/Core/Upgrades/Actions/Base.php @@ -58,16 +58,6 @@ abstract class Base */ const SCRIPTS = 'scripts'; - /** - * Statuses of Extension Entity - */ - const DISABLED = 'Disabled'; - - /** - * Statuses of Extension Entity - */ - const ENABLED = 'Enabled'; - /** * Package types */ @@ -139,7 +129,7 @@ abstract class Base return $this->entityManager; } - protected function throwErrorWithDetelePackage($errorMessage = '') + protected function throwErrorAndRemovePackage($errorMessage = '') { $this->deletePackageFiles(); $this->deletePackageArchive(); @@ -220,7 +210,7 @@ abstract class Base } } - $this->throwErrorWithDetelePackage('Your EspoCRM version doesn\'t match for this installation package.'); + $this->throwErrorAndRemovePackage('Your EspoCRM version doesn\'t match for this installation package.'); } protected function checkPackageType() @@ -232,11 +222,11 @@ abstract class Base $manifestType = isset($manifest['type']) ? strtolower($manifest['type']) : $this->defaultPackageType; if (!in_array($manifestType, $this->packageTypes)) { - $this->throwErrorWithDetelePackage('Unknown package type.'); + $this->throwErrorAndRemovePackage('Unknown package type.'); } if ($type != $manifestType) { - $this->throwErrorWithDetelePackage('Wrong package type. You cannot install '.$manifestType.' package via '.ucfirst($type).' Manager.'); + $this->throwErrorAndRemovePackage('Wrong package type. You cannot install '.$manifestType.' package via '.ucfirst($type).' Manager.'); } return true; @@ -249,7 +239,7 @@ abstract class Base */ protected function runScript($type) { - $packagePath = $this->getPath('packagePath'); + $packagePath = $this->getPackagePath(); $scriptNames = $this->getParams('scriptNames'); $scriptName = $scriptNames[$type]; @@ -262,7 +252,12 @@ abstract class Base if (file_exists($beforeInstallScript)) { require_once($beforeInstallScript); $script = new $scriptName(); - $script->run($this->getContainer()); + + try { + $script->run($this->getContainer()); + } catch (\Exception $e) { + $this->throwErrorAndRemovePackage($e->getMessage()); + } } } @@ -282,6 +277,11 @@ abstract class Base return $path . $postfix; } + protected function getPackagePath($isPackage = false) + { + return $this->getPath('packagePath', $isPackage); + } + /** * Get a list of files defined in manifest.json * @@ -317,7 +317,7 @@ abstract class Base protected function getCopyFileList() { if (!isset($this->data['fileList'])) { - $packagePath = $this->getPath('packagePath'); + $packagePath = $this->getPackagePath(); $filesPath = Util::concatPath($packagePath, self::FILES); $this->data['fileList'] = $this->getFileManager()->getFileList($filesPath, true, '', 'all', true); @@ -326,6 +326,17 @@ abstract class Base return $this->data['fileList']; } + protected function copy($sourcePath, $destPath, $recursively = false, array $fileList = null, $copyOnlyFiles = false) + { + try { + $res = $this->getFileManager()->copy($sourcePath, $destPath, $recursively, $fileList, $copyOnlyFiles); + } catch (\Exception $e) { + $this->throwErrorAndRemovePackage($e->getMessage()); + } + + return $res; + } + /** * Copy files from upgrade/extension package * @@ -334,31 +345,31 @@ abstract class Base */ protected function copyFiles() { - $packagePath = $this->getPath('packagePath'); + $packagePath = $this->getPackagePath(); $filesPath = Util::concatPath($packagePath, self::FILES); - return $this->getFileManager()->copy($filesPath, '', true); + return $this->copy($filesPath, '', true); } public function getManifest() { if (!isset($this->data['manifest'])) { - $packagePath = $this->getPath('packagePath'); + $packagePath = $this->getPackagePath(); $manifestPath = Util::concatPath($packagePath, $this->manifestName); if (!file_exists($manifestPath)) { - throw new Error('It\'s not an Installation package.'); + $this->throwErrorAndRemovePackage('It\'s not an Installation package.'); } $manifestJson = $this->getFileManager()->getContents($manifestPath); $this->data['manifest'] = Json::decode($manifestJson, true); if (!$this->data['manifest']) { - throw new Error('Syntax error in manifest.json.'); + $this->throwErrorAndRemovePackage('Syntax error in manifest.json.'); } if (!$this->checkManifest($this->data['manifest'])) { - throw new Error('Unsupported package.'); + $this->throwErrorAndRemovePackage('Unsupported package.'); } } @@ -392,10 +403,14 @@ abstract class Base * * @return void */ - protected function unzipArchive() + protected function unzipArchive($packagePath = null) { - $packagePath = $this->getPath('packagePath'); - $packageArchivePath = $this->getPath('packagePath', true); + $packagePath = isset($packagePath) ? $packagePath : $this->getPackagePath(); + $packageArchivePath = $this->getPackagePath(true); + + if (!file_exists($packageArchivePath)) { + throw new Error('Package Archive doesn\'t exist.'); + } $res = $this->getZipUtil()->unzip($packageArchivePath, $packagePath); if ($res === false) { @@ -410,7 +425,7 @@ abstract class Base */ protected function deletePackageFiles() { - $packagePath = $this->getPath('packagePath'); + $packagePath = $this->getPackagePath(); $res = $this->getFileManager()->removeInDir($packagePath, true); return $res; @@ -423,7 +438,7 @@ abstract class Base */ protected function deletePackageArchive() { - $packageArchive = $this->getPath('packagePath', true); + $packageArchive = $this->getPackagePath(true); $res = $this->getFileManager()->removeFile($packageArchive); return $res; diff --git a/application/Espo/Core/Upgrades/Actions/Base/Delete.php b/application/Espo/Core/Upgrades/Actions/Base/Delete.php index 60a95b0fe5..210dceb73d 100644 --- a/application/Espo/Core/Upgrades/Actions/Base/Delete.php +++ b/application/Espo/Core/Upgrades/Actions/Base/Delete.php @@ -46,7 +46,7 @@ class Delete extends \Espo\Core\Upgrades\Actions\Base protected function deletePackage() { - $packageArchivePath = $this->getPath('packagePath', true); + $packageArchivePath = $this->getPackagePath(true); $res = $this->getFileManager()->removeFile($packageArchivePath); return $res; diff --git a/application/Espo/Core/Upgrades/Actions/Base/Install.php b/application/Espo/Core/Upgrades/Actions/Base/Install.php index c21d3f67e5..4eb235165d 100644 --- a/application/Espo/Core/Upgrades/Actions/Base/Install.php +++ b/application/Espo/Core/Upgrades/Actions/Base/Install.php @@ -26,6 +26,13 @@ use Espo\Core\Exceptions\Error; class Install extends \Espo\Core\Upgrades\Actions\Base { + /** + * Is copied extension files to Espo + * + * @var [type] + */ + protected $isCopied = null; + /** * Main installation process * @@ -42,8 +49,10 @@ class Install extends \Espo\Core\Upgrades\Actions\Base $this->setProcessId($processId); + $this->isCopied = false; + /** check if an archive is unzipped, if no then unzip */ - $packagePath = $this->getPath('packagePath'); + $packagePath = $this->getPackagePath(); if (!file_exists($packagePath)) { $this->unzipArchive(); $this->isAcceptable(); @@ -56,16 +65,17 @@ class Install extends \Espo\Core\Upgrades\Actions\Base /* remove files defined in a manifest */ if (!$this->deleteFiles()) { - throw new Error('Permission denied to delete files.'); + $this->throwErrorAndRemovePackage('Permission denied to delete files.'); } /* copy files from directory "Files" to EspoCRM files */ if (!$this->copyFiles()) { - throw new Error('Cannot copy files.'); + $this->throwErrorAndRemovePackage('Cannot copy files.'); } + $this->isCopied = true; if (!$this->systemRebuild()) { - throw new Error('Error occurred while EspoCRM rebuild.'); + $this->throwErrorAndRemovePackage('Error occurred while EspoCRM rebuild.'); } /* run before install script */ @@ -78,4 +88,25 @@ class Install extends \Espo\Core\Upgrades\Actions\Base $GLOBALS['log']->debug('Installation process ['.$processId.']: end run.'); } + + 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'); + } + + $res &= $this->getFileManager()->removeInDir($backupPath, true); + + return $res; + } + + protected function throwErrorAndRemovePackage($errorMessage = '') + { + $this->restoreFiles(); + parent::throwErrorAndRemovePackage($errorMessage); + } } diff --git a/application/Espo/Core/Upgrades/Actions/Base/Uninstall.php b/application/Espo/Core/Upgrades/Actions/Base/Uninstall.php index 8d0730ed4a..68528b50bd 100644 --- a/application/Espo/Core/Upgrades/Actions/Base/Uninstall.php +++ b/application/Espo/Core/Upgrades/Actions/Base/Uninstall.php @@ -22,7 +22,8 @@ namespace Espo\Core\Upgrades\Actions\Base; -use Espo\Core\Exceptions\Error; +use Espo\Core\Exceptions\Error, + Espo\Core\Utils\Util; class Uninstall extends \Espo\Core\Upgrades\Actions\Base { @@ -41,14 +42,18 @@ class Uninstall extends \Espo\Core\Upgrades\Actions\Base /* run before install script */ $this->runScript('beforeUninstall'); - /* remove extension files, saved in fileList */ - if (!$this->deleteFiles()) { - throw new Error('Permission denied to delete files.'); - } + $backupPath = $this->getPath('backupPath'); + if (file_exists($backupPath)) { - /* copy core files */ - if (!$this->copyFiles()) { - throw new Error('Cannot copy files.'); + /* remove extension files, saved in fileList */ + if (!$this->deleteFiles()) { + throw new Error('Permission denied to delete files.'); + } + + /* copy core files */ + if (!$this->copyFiles()) { + throw new Error('Cannot copy files.'); + } } if (!$this->systemRebuild()) { @@ -72,10 +77,25 @@ class Uninstall extends \Espo\Core\Upgrades\Actions\Base return $extensionEntity->get('fileList'); } + protected function restoreFiles() + { + $packagePath = $this->getPath('packagePath'); + $filesPath = Util::concatPath($packagePath, self::FILES); + + if (!file_exists($filesPath)) { + $this->unzipArchive($packagePath); + } + + $res = $this->copy($filesPath, '', true); + $res &= $this->getFileManager()->removeInDir($packagePath, true); + + return $res; + } + protected function copyFiles() { $backupPath = $this->getPath('backupPath'); - $res = $this->getFileManager()->copy(array($backupPath, self::FILES), '', true); + $res = $this->copy(array($backupPath, self::FILES), '', true); return $res; } @@ -86,10 +106,13 @@ class Uninstall extends \Espo\Core\Upgrades\Actions\Base * @param string $processId * @return string */ - protected function getPath($name = 'packagePath', $isPackage = false) + protected function getPackagePath($isPackage = false) { - $name = ($name == 'packagePath') ? 'backupPath' : $name; - return parent::getPath($name, $isPackage); + if ($isPackage) { + return $this->getPath('packagePath', $isPackage); + } + + return $this->getPath('backupPath'); } protected function deletePackageFiles() @@ -100,4 +123,10 @@ class Uninstall extends \Espo\Core\Upgrades\Actions\Base return $res; } + protected function throwErrorAndRemovePackage($errorMessage = '') + { + $this->restoreFiles(); + throw new Error($errorMessage); + } + } diff --git a/application/Espo/Core/Upgrades/Actions/Base/Upload.php b/application/Espo/Core/Upgrades/Actions/Base/Upload.php index ee679cd13a..aff8d61a84 100644 --- a/application/Espo/Core/Upgrades/Actions/Base/Upload.php +++ b/application/Espo/Core/Upgrades/Actions/Base/Upload.php @@ -38,8 +38,8 @@ class Upload extends \Espo\Core\Upgrades\Actions\Base $GLOBALS['log']->debug('Installation process ['.$processId.']: start upload the package.'); - $packagePath = $this->getPath('packagePath'); - $packageArchivePath = $this->getPath('packagePath', true); + $packagePath = $this->getPackagePath(); + $packageArchivePath = $this->getPackagePath(true); if (!empty($data)) { list($prefix, $contents) = explode(',', $data); diff --git a/application/Espo/Core/Upgrades/Actions/Extension/Delete.php b/application/Espo/Core/Upgrades/Actions/Extension/Delete.php index daadecef66..4edc165261 100644 --- a/application/Espo/Core/Upgrades/Actions/Extension/Delete.php +++ b/application/Espo/Core/Upgrades/Actions/Extension/Delete.php @@ -58,12 +58,6 @@ class Delete extends \Espo\Core\Upgrades\Actions\Base\Delete throw new Error('Extension Entity not found.'); } $this->setExtensionEntity($extensionEntity); - - /** check if extension archive exsists */ - $packageArchivePath = $this->getPath('packagePath', true); - if (!file_exists($packageArchivePath)) { - throw new Error('Extension Archive doesn\'t exist.'); - } } protected function afterRunAction() diff --git a/application/Espo/Core/Upgrades/Actions/Extension/Install.php b/application/Espo/Core/Upgrades/Actions/Extension/Install.php index 9d46603707..f60471855e 100644 --- a/application/Espo/Core/Upgrades/Actions/Extension/Install.php +++ b/application/Espo/Core/Upgrades/Actions/Extension/Install.php @@ -51,11 +51,24 @@ class Install extends \Espo\Core\Upgrades\Actions\Base\Install $fileList = $this->getCopyFileList(); $backupPath = $this->getPath('backupPath'); - $res = $this->getFileManager()->copy('', array($backupPath, self::FILES), false, $fileList); + $res = $this->copy('', array($backupPath, self::FILES), false, $fileList); /** copy scripts files */ - $packagePath = $this->getPath('packagePath'); - $res &= $this->getFileManager()->copy(array($packagePath, self::SCRIPTS), array($backupPath, self::SCRIPTS), true); + $packagePath = $this->getPackagePath(); + $res &= $this->copy(array($packagePath, self::SCRIPTS), array($backupPath, self::SCRIPTS), true); + + return $res; + } + + protected function restoreFiles() + { + $res = true; + if ($this->isCopied) { + $extensionFileList = $this->getCopyFileList(); + $res &= $this->getFileManager()->remove($extensionFileList); + } + + $res &= parent::restoreFiles(); return $res; } @@ -125,7 +138,11 @@ class Install extends \Espo\Core\Upgrades\Actions\Base\Install protected function storeExtension() { $entityManager = $this->getEntityManager(); - $extensionEntity = $entityManager->getEntity('Extension'); + + $extensionEntity = $entityManager->getEntity('Extension', $this->getProcessId()); + if (!isset($extensionEntity)) { + $extensionEntity = $entityManager->getEntity('Extension'); + } $manifest = $this->getManifest(); $fileList = $this->getCopyFileList(); @@ -133,7 +150,6 @@ class Install extends \Espo\Core\Upgrades\Actions\Base\Install $data = array( 'id' => $this->getProcessId(), 'name' => $manifest['name'], - 'status' => self::ENABLED, 'isInstalled' => true, 'version' => $manifest['version'], 'fileList' => $fileList, @@ -157,10 +173,26 @@ class Install extends \Espo\Core\Upgrades\Actions\Base\Install if (isset($extensionEntity)) { $comparedVersion = version_compare($manifest['version'], $extensionEntity->get('version')); if ($comparedVersion <= 0) { - $this->throwErrorWithDetelePackage('You cannot install an older version of this extension.'); + $this->throwErrorAndRemovePackage('You cannot install an older version of this extension.'); } } } + /** + * Throw an exception and remove package files. + * Redeclared to prevent of deleting a package of installed extension. + * + * @param string $errorMessage [description] + * @return [type] [description] + */ + protected function throwErrorAndRemovePackage($errorMessage = '') + { + if (!$this->isNew()) { + throw new Error($errorMessage); + } + + return parent::throwErrorAndRemovePackage($errorMessage); + } + } diff --git a/application/Espo/Core/Upgrades/Actions/Extension/Uninstall.php b/application/Espo/Core/Upgrades/Actions/Extension/Uninstall.php index acc277b914..a405e6beea 100644 --- a/application/Espo/Core/Upgrades/Actions/Extension/Uninstall.php +++ b/application/Espo/Core/Upgrades/Actions/Extension/Uninstall.php @@ -58,12 +58,6 @@ class Uninstall extends \Espo\Core\Upgrades\Actions\Base\Uninstall throw new Error('Extension Entity not found.'); } $this->setExtensionEntity($extensionEntity); - - /** check if backup of the extension exsists */ - $backupPath = $this->getPath('backupPath'); - if (!file_exists($backupPath)) { - throw new Error('Backup files don\'t exist.'); - } } protected function afterRunAction() @@ -71,7 +65,6 @@ class Uninstall extends \Espo\Core\Upgrades\Actions\Base\Uninstall /** Set extension entity, isInstalled = false */ $extensionEntity = $this->getExtensionEntity(); - $extensionEntity->set('status', self::DISABLED); $extensionEntity->set('isInstalled', false); $this->getEntityManager()->saveEntity($extensionEntity); }