Merge branch 'hotfix/3.0.1'
This commit is contained in:
@@ -28,7 +28,6 @@ class ExtensionManager extends Upgrades\Base
|
||||
|
||||
protected $params = array(
|
||||
'packagePath' => 'data/upload/extensions',
|
||||
|
||||
'backupPath' => 'data/.backup/extensions',
|
||||
|
||||
'scriptNames' => array(
|
||||
|
||||
@@ -24,6 +24,15 @@ namespace Espo\Core\Mail\Storage;
|
||||
|
||||
class Message extends \Zend\Mail\Storage\Message
|
||||
{
|
||||
public function __isset($name)
|
||||
{
|
||||
$headers = $this->getHeaders();
|
||||
if (empty($headers) || !is_object($headers)) {
|
||||
return false;
|
||||
}
|
||||
return $this->getHeaders()->has($name);
|
||||
}
|
||||
|
||||
public function isMultipart()
|
||||
{
|
||||
if (!isset($this->contentType)) {
|
||||
|
||||
@@ -30,6 +30,7 @@ class UpgradeManager extends Upgrades\Base
|
||||
|
||||
protected $params = array(
|
||||
'packagePath' => 'data/upload/upgrades',
|
||||
'backupPath' => 'data/.backup/upgrades',
|
||||
|
||||
'scriptNames' => array(
|
||||
'before' => 'BeforeUpgrade',
|
||||
|
||||
@@ -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 {
|
||||
@@ -492,6 +504,16 @@ abstract class Base
|
||||
$this->getActionManager()->setAction($currentAction);
|
||||
}
|
||||
|
||||
protected function initialize()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected function finalize()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected function beforeRunAction()
|
||||
{
|
||||
|
||||
@@ -506,4 +528,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 <br>". implode(", <br>", $permissionDeniedList));
|
||||
}
|
||||
}
|
||||
|
||||
protected function backupExistingFiles()
|
||||
{
|
||||
$fullFileList = array_merge($this->getDeleteFileList(), $this->getCopyFileList());
|
||||
|
||||
$backupPath = $this->getPath('backupPath');
|
||||
return $this->copy('', array($backupPath, self::FILES), false, $fullFileList);
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,8 @@ class Delete extends \Espo\Core\Upgrades\Actions\Base
|
||||
throw new Error('Delete package package ID was not specified.');
|
||||
}
|
||||
|
||||
$this->initialize();
|
||||
|
||||
$this->setProcessId($processId);
|
||||
|
||||
$this->beforeRunAction();
|
||||
@@ -41,6 +43,8 @@ class Delete extends \Espo\Core\Upgrades\Actions\Base
|
||||
|
||||
$this->afterRunAction();
|
||||
|
||||
$this->finalize();
|
||||
|
||||
$GLOBALS['log']->debug('Delete package process ['.$processId.']: end run.');
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
@@ -49,6 +50,8 @@ class Install extends \Espo\Core\Upgrades\Actions\Base
|
||||
|
||||
$this->setProcessId($processId);
|
||||
|
||||
$this->initialize();
|
||||
|
||||
$this->isCopied = false;
|
||||
|
||||
/** check if an archive is unzipped, if no then unzip */
|
||||
@@ -58,22 +61,25 @@ class Install extends \Espo\Core\Upgrades\Actions\Base
|
||||
$this->isAcceptable();
|
||||
}
|
||||
|
||||
//check permissions copied and deleted files
|
||||
$this->checkIsWritable();
|
||||
|
||||
$this->backupExistingFiles();
|
||||
|
||||
$this->beforeRunAction();
|
||||
|
||||
/* run before install script */
|
||||
$this->runScript('before');
|
||||
|
||||
/* remove files defined in a manifest */
|
||||
if (!$this->deleteFiles()) {
|
||||
$this->throwErrorAndRemovePackage('Permission denied to delete files.');
|
||||
}
|
||||
|
||||
/* copy files from directory "Files" to EspoCRM files */
|
||||
if (!$this->copyFiles()) {
|
||||
$this->throwErrorAndRemovePackage('Cannot copy files.');
|
||||
}
|
||||
$this->isCopied = true;
|
||||
|
||||
/* remove files defined in a manifest */
|
||||
$this->deleteFiles(true);
|
||||
|
||||
if (!$this->systemRebuild()) {
|
||||
$this->throwErrorAndRemovePackage('Error occurred while EspoCRM rebuild.');
|
||||
}
|
||||
@@ -88,20 +94,32 @@ class Install extends \Espo\Core\Upgrades\Actions\Base
|
||||
/* delete unziped files */
|
||||
$this->deletePackageFiles();
|
||||
|
||||
$this->finalize();
|
||||
|
||||
$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');
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -22,8 +22,9 @@
|
||||
|
||||
namespace Espo\Core\Upgrades\Actions\Base;
|
||||
|
||||
use Espo\Core\Exceptions\Error,
|
||||
Espo\Core\Utils\Util;
|
||||
use Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Utils\Util;
|
||||
use Espo\Core\Utils\Json;
|
||||
|
||||
class Uninstall extends \Espo\Core\Upgrades\Actions\Base
|
||||
{
|
||||
@@ -37,6 +38,10 @@ class Uninstall extends \Espo\Core\Upgrades\Actions\Base
|
||||
|
||||
$this->setProcessId($processId);
|
||||
|
||||
$this->initialize();
|
||||
|
||||
$this->checkIsWritable();
|
||||
|
||||
$this->beforeRunAction();
|
||||
|
||||
/* run before install script */
|
||||
@@ -45,19 +50,19 @@ class Uninstall extends \Espo\Core\Upgrades\Actions\Base
|
||||
$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.');
|
||||
}
|
||||
|
||||
/* run before install script */
|
||||
@@ -70,13 +75,9 @@ class Uninstall extends \Espo\Core\Upgrades\Actions\Base
|
||||
/* delete backup files */
|
||||
$this->deletePackageFiles();
|
||||
|
||||
$GLOBALS['log']->debug('Uninstallation process ['.$processId.']: end run.');
|
||||
}
|
||||
$this->finalize();
|
||||
|
||||
protected function getDeleteFileList()
|
||||
{
|
||||
$extensionEntity = $this->getExtensionEntity();
|
||||
return $extensionEntity->get('fileList');
|
||||
$GLOBALS['log']->debug('Uninstallation process ['.$processId.']: end run.');
|
||||
}
|
||||
|
||||
protected function restoreFiles()
|
||||
@@ -89,6 +90,13 @@ class Uninstall extends \Espo\Core\Upgrades\Actions\Base
|
||||
}
|
||||
|
||||
$res = $this->copy($filesPath, '', true);
|
||||
|
||||
$manifestJson = $this->getFileManager()->getContents(array($packagePath, $this->manifestName));
|
||||
$manifest = Json::decode($manifestJson, true);
|
||||
if (!empty($manifest['delete'])) {
|
||||
$res &= $this->getFileManager()->remove($manifest['delete'], null, true);
|
||||
}
|
||||
|
||||
$res &= $this->getFileManager()->removeInDir($packagePath, true);
|
||||
|
||||
return $res;
|
||||
@@ -131,4 +139,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,10 @@ class Upload extends \Espo\Core\Upgrades\Actions\Base
|
||||
|
||||
$GLOBALS['log']->debug('Installation process ['.$processId.']: start upload the package.');
|
||||
|
||||
$this->initialize();
|
||||
|
||||
$this->beforeRunAction();
|
||||
|
||||
$packagePath = $this->getPackagePath();
|
||||
$packageArchivePath = $this->getPackagePath(true);
|
||||
|
||||
@@ -55,6 +59,10 @@ class Upload extends \Espo\Core\Upgrades\Actions\Base
|
||||
|
||||
$this->isAcceptable();
|
||||
|
||||
$this->afterRunAction();
|
||||
|
||||
$this->finalize();
|
||||
|
||||
$GLOBALS['log']->debug('Installation process ['.$processId.']: end upload the package.');
|
||||
|
||||
return $processId;
|
||||
|
||||
@@ -35,29 +35,15 @@ class Delete extends \Espo\Core\Upgrades\Actions\Base\Delete
|
||||
*/
|
||||
protected function getExtensionEntity()
|
||||
{
|
||||
return $this->extensionEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Extension Entity
|
||||
*
|
||||
* @param \Espo\Entities\Extension $extensionEntity
|
||||
*/
|
||||
protected function setExtensionEntity(\Espo\Entities\Extension $extensionEntity)
|
||||
{
|
||||
$this->extensionEntity = $extensionEntity;
|
||||
}
|
||||
|
||||
protected function beforeRunAction()
|
||||
{
|
||||
$processId = $this->getProcessId();
|
||||
|
||||
/** get extension entity */
|
||||
$extensionEntity = $this->getEntityManager()->getEntity('Extension', $processId);
|
||||
if (!isset($extensionEntity)) {
|
||||
throw new Error('Extension Entity not found.');
|
||||
if (!isset($this->extensionEntity)) {
|
||||
$processId = $this->getProcessId();
|
||||
$this->extensionEntity = $this->getEntityManager()->getEntity('Extension', $processId);
|
||||
if (!isset($this->extensionEntity)) {
|
||||
throw new Error('Extension Entity not found.');
|
||||
}
|
||||
}
|
||||
$this->setExtensionEntity($extensionEntity);
|
||||
|
||||
return $this->extensionEntity;
|
||||
}
|
||||
|
||||
protected function afterRunAction()
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -35,29 +35,15 @@ class Uninstall extends \Espo\Core\Upgrades\Actions\Base\Uninstall
|
||||
*/
|
||||
protected function getExtensionEntity()
|
||||
{
|
||||
return $this->extensionEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Extension Entity
|
||||
*
|
||||
* @param \Espo\Entities\Extension $extensionEntity [description]
|
||||
*/
|
||||
protected function setExtensionEntity(\Espo\Entities\Extension $extensionEntity)
|
||||
{
|
||||
$this->extensionEntity = $extensionEntity;
|
||||
}
|
||||
|
||||
protected function beforeRunAction()
|
||||
{
|
||||
$processId = $this->getProcessId();
|
||||
|
||||
/** get extension entity */
|
||||
$extensionEntity = $this->getEntityManager()->getEntity('Extension', $processId);
|
||||
if (!isset($extensionEntity)) {
|
||||
throw new Error('Extension Entity not found.');
|
||||
if (!isset($this->extensionEntity)) {
|
||||
$processId = $this->getProcessId();
|
||||
$this->extensionEntity = $this->getEntityManager()->getEntity('Extension', $processId);
|
||||
if (!isset($this->extensionEntity)) {
|
||||
throw new Error('Extension Entity not found.');
|
||||
}
|
||||
}
|
||||
$this->setExtensionEntity($extensionEntity);
|
||||
|
||||
return $this->extensionEntity;
|
||||
}
|
||||
|
||||
protected function afterRunAction()
|
||||
@@ -68,4 +54,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'];
|
||||
}
|
||||
}
|
||||
@@ -24,17 +24,12 @@ namespace Espo\Core\Upgrades\Actions\Upgrade;
|
||||
|
||||
class Install extends \Espo\Core\Upgrades\Actions\Base\Install
|
||||
{
|
||||
protected function systemRebuild()
|
||||
protected function finalize()
|
||||
{
|
||||
$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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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(pathinfo($fullPath, PATHINFO_DIRNAME));
|
||||
}
|
||||
|
||||
return $fullPath;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
@@ -389,7 +389,23 @@ class ManagerTest extends \PHPUnit_Framework_TestCase
|
||||
}
|
||||
}
|
||||
|
||||
public function existsPathSet()
|
||||
{
|
||||
return array(
|
||||
array( 'application/Espo/Core/Application.php', 'application/Espo/Core/Application.php', ),
|
||||
array( 'application/Espo/Core/NotRealApplication.php', 'application/Espo/Core'),
|
||||
array( array('application', 'Espo/Core', 'NotRealApplication.php'), 'application/Espo/Core'),
|
||||
array( 'application/NoEspo/Core/Application.php', 'application'),
|
||||
array( 'notRealPath/Espo/Core/Application.php', '.'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider existsPathSet
|
||||
*/
|
||||
public function testGetExistsPath($input, $result)
|
||||
{
|
||||
$this->assertEquals($result, $this->reflection->invokeMethod('getExistsPath', array($input)) );
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
Reference in New Issue
Block a user