diff --git a/application/Espo/Core/HookManager.php b/application/Espo/Core/HookManager.php index 28c63e9d91..a77b0bb06a 100644 --- a/application/Espo/Core/HookManager.php +++ b/application/Espo/Core/HookManager.php @@ -2,7 +2,8 @@ namespace Espo\Core; -use \Espo\Core\Exceptions\Error; +use \Espo\Core\Exceptions\Error, + \Espo\Core\Utils\Util; class HookManager { @@ -14,33 +15,51 @@ class HookManager protected $cacheFile = 'data/cache/application/hooks.php'; + /** + * List of defined hooks + * + * @var array + */ + protected $hookList = array( + 'beforeSave', + 'afterSave', + ); + + public function __construct(Container $container) { $this->container = $container; $this->loadHooks(); } - protected getConfig() + protected function getConfig() { return $this->container->get('config'); } - + + protected function getFileManager() + { + return $this->container->get('fileManager'); + } + + protected function loadHooks() - { + { if ($this->getConfig()->get('useCache') && file_exists($this->cacheFile)) { - $this->hooks = include($this->cacheFile); + $this->hooks = $this->getFileManager()->getContent($this->cacheFile); + //$this->hooks = include($this->cacheFile); return; } $metadata = $this->container->get('metadata'); - - // TODO scan Espo/Hooks/{ScopeName}/{HookName}.php + + $this->hooks = $this->getHookData( array('Espo/Hooks', 'Espo/Custom/Hooks') ); foreach ($metadata->getModuleList() as $moduleName) { - // TODO scan Espo/Modules/{$moduleName}/ScopeName/HookName.php + $this->hooks = array_merge($this->hooks, $this->getHookData( array('Espo/Modules/'.$moduleName.'/Hooks', 'Espo/Custom/Modules/'.$moduleName.'/Hooks') )); } - + if ($this->getConfig()->get('useCache')) { - // TODO write $this->hooks into cache file + $this->getFileManager()->setContentPHP($this->hooks, $this->cacheFile); } } @@ -71,5 +90,58 @@ class HookManager } throw new Error("Class '$className' does not exist"); } + + /** + * Get and merge hook data by checking the files exist in $hookDirs + * + * @param array $hookDirs - it can be an array('Espo/Hooks', 'Espo/Custom/Hooks', 'Espo/Custom/Modules/Crm/Hooks') + * + * @return array + */ + protected function getHookData(array $hookDirs) + { + $hooks = array(); + + foreach($hookDirs as $hookDir) { + + $fullHookDir = 'application/'.$hookDir; + if (file_exists($fullHookDir)) { + $fileList = $this->getFileManager()->getFileList($fullHookDir, 1, '\.php$', 'file'); + + foreach($fileList as $scopeName => $hookFiles) { + + $hookScopeDirPath = Util::concatPath($hookDir, $scopeName); + + $scopeHooks = array(); + foreach($hookFiles as $hookFile) { + $hookFilePath = Util::concatPath($hookScopeDirPath, $hookFile); + $className = '\\'.Util::toFormat(preg_replace('/\.php$/i', '', $hookFilePath), '\\'); + + foreach($this->hookList as $hookName) { + if (method_exists($className, $hookName)) { + $scopeHooks[$hookName][$className::$order][] = $className; + } + } + } + + //sort hooks by order + foreach($scopeHooks as $hookName => $hookList) { + ksort($hookList); + + $sortedHookList = array(); + foreach($hookList as $hookDetails) { + $sortedHookList = array_merge($sortedHookList, $hookDetails); + } + + $hooks[$scopeName][$hookName] = isset($hooks[$scopeName][$hookName]) ? array_merge($hooks[$scopeName][$hookName], $sortedHookList) : $sortedHookList; + } + } + } + + } + + return $hooks; + } + } diff --git a/application/Espo/Core/Hooks/Base.php b/application/Espo/Core/Hooks/Base.php index ed8fbcf7d5..5545a0e925 100644 --- a/application/Espo/Core/Hooks/Base.php +++ b/application/Espo/Core/Hooks/Base.php @@ -1,6 +1,6 @@ params = $params; } @@ -36,13 +36,14 @@ class Manager * Get a list of files in specified directory * * @param string $path string - Folder path, Ex. myfolder - * @param bool $recursively - Find files in subfolders + * @param bool | int $recursively - Find files in subfolders * @param string $filter - Filter for files. Use regular expression, Ex. \.json$ * @param string $fileType [all, file, dir] - Filter for type of files/directories. + * @param bool $isReturnSingleArray - if need to return a single array of file list * * @return array */ - function getFileList($path, $recursively=false, $filter='', $fileType='all') + function getFileList($path, $recursively=false, $filter='', $fileType='all', $isReturnSingleArray = false) { if (!file_exists($path)) { return false; @@ -57,8 +58,9 @@ class Manager { $add= false; if (is_dir($path . Utils\Util::getSeparator() . $value)) { - if ($recursively) { - $result[$value] = $this->getFileList($path.Utils\Util::getSeparator().$value, $recursively, $filter, $fileType); + if ($recursively || (is_int($recursively) && $recursively!=0) ) { + $nextRecursively = is_int($recursively) ? ($recursively-1) : $recursively; + $result[$value] = $this->getFileList($path.Utils\Util::getSeparator().$value, $nextRecursively, $filter, $fileType); } else if (in_array($fileType, array('all', 'dir'))){ $add= true; @@ -82,9 +84,37 @@ class Manager } } + if ($isReturnSingleArray) { + return $this->getSingeFileList($result); + } + return $result; } + /** + * Convert file list to a single array + * + * @param aray $fileList + * @param string $parentDirName + * + * @return aray + */ + protected function getSingeFileList(array $fileList, $parentDirName = '') + { + $singleFileList = array(); + foreach($fileList as $dirName => $fileName) { + + if (is_array($fileName)) { + $currentDir = Utils\Util::concatPath($parentDirName, $dirName); + $singleFileList = array_merge($singleFileList, $this->getSingeFileList($fileName, $currentDir)); + } else { + $singleFileList[] = Utils\Util::concatPath($parentDirName, $fileName); + } + } + + return $singleFileList; + } + /** * Get content from file * @@ -419,7 +449,7 @@ class Manager * * @return string | false */ - function getPHPFormat($content) + public function getPHPFormat($content) { if (empty($content)) { return false; @@ -759,4 +789,4 @@ return '.var_export($content, true).'; } -?> \ No newline at end of file +?> diff --git a/application/Espo/Core/Utils/Metadata.php b/application/Espo/Core/Utils/Metadata.php index 206ae8ea19..ec8e0ed1c9 100644 --- a/application/Espo/Core/Utils/Metadata.php +++ b/application/Espo/Core/Utils/Metadata.php @@ -295,7 +295,7 @@ class Metadata //NEED TO CHANGE public function getScopes() { - if (!$reload && !empty($this->scopes)) { + if (!empty($this->scopes)) { return $this->scopes; } @@ -314,16 +314,16 @@ class Metadata if (is_null($this->moduleList)) { $this->moduleList = array(); $scopes = $this->getScopes(); + // TODO order - foreach ($this->scopes as $defs) { - if (!empty($defs['module'])) { - $module = $defs['module']; - if (!in_array($module, $this->moduleList)) { - $this->moduleList[] = $module; - } + foreach ($scopes as $moduleName) { + if (!empty($moduleName)) { + if (!in_array($moduleName, $this->moduleList)) { + $this->moduleList[] = $moduleName; + } } } - } + } return $this->moduleList; } diff --git a/application/Espo/Core/Utils/Util.php b/application/Espo/Core/Utils/Util.php index b064fbebe9..7c87d3dae4 100644 --- a/application/Espo/Core/Utils/Util.php +++ b/application/Espo/Core/Utils/Util.php @@ -161,6 +161,10 @@ class Util if (empty($filePath)) { return $folderPath; } + if (empty($folderPath)) { + return $filePath; + } + else { if (substr($folderPath, -1) == static::getSeparator()) { return $folderPath . $filePath; diff --git a/application/Espo/Modules/Crm/Hooks/Prospect/Test.php b/application/Espo/Modules/Crm/Hooks/Prospect/Test.php index 6a52e6f9e8..eb9cc3a623 100644 --- a/application/Espo/Modules/Crm/Hooks/Prospect/Test.php +++ b/application/Espo/Modules/Crm/Hooks/Prospect/Test.php @@ -1,6 +1,6 @@ object = new \Espo\Core\Utils\File\Manager( + (object) array( + 'defaultPermissions' => (object) array ( + 'dir' => '0775', + 'file' => '0664', + 'user' => '', + 'group' => '', + ), + ) + ); + } + + protected function tearDown() + { + $this->object = NULL; + } + + public function invokeMethod($methodName, array $parameters = array()) + { + $reflection = new \ReflectionClass(get_class($this->object)); + $method = $reflection->getMethod($methodName); + $method->setAccessible(true); + + return $method->invokeArgs($this->object, $parameters); + } + + + + function testGetFileList() + { + $result= array('Dir1', 'file1.json','file1.php',); //no recursive + $this->assertEquals($result, $this->object->getFileList($this->filesPath.'/getFileList', false)); + + $result= array('Dir1'); //no recursive + $this->assertEquals($result, $this->object->getFileList($this->filesPath.'/getFileList', false, '', 'dir')); + + $result= array('file1.json','file1.php',); //no recursive + $this->assertEquals($result, $this->object->getFileList($this->filesPath.'/getFileList', false, '', 'file')); + + + $result= array( + 'Dir1' => array( + 'file2.json', + 'Dir2' => array ( + 'file3.json' + ), + ), + 'file1.json', + 'file1.php', + ); + $this->assertEquals($result, $this->object->getFileList($this->filesPath.'/getFileList', true)); + } + + function testGetFileListWithFilter() + { + $result= array('file1.json'); //no recursive + $this->assertEquals($result, $this->object->getFileList($this->filesPath.'/getFileList', false, '.*\.json$')); + + + $result= array( + 'Dir1' => array( + 'file2.json', + 'Dir2' => array ( + 'file3.json' + ), + ), + 'file1.json', + ); + $this->assertEquals($result, $this->object->getFileList($this->filesPath.'/getFileList', true, '.*\.json$')); + + $result= array( + 'file1.php', + 'Dir1' => array( + 'Dir2' => array ( + ), + ), + ); + $this->assertEquals($result, $this->object->getFileList($this->filesPath.'/getFileList', true, '.*\.php$')); + } + + function testGetFileListRecursively() + { + $result= array( + 'Dir1', + 'file1.json', + 'file1.php', + ); + $this->assertEquals($result, $this->object->getFileList($this->filesPath.'/getFileList', false)); + + + $result= array( + 'Dir1' => array( + 'file2.json', + 'Dir2' => array ( + 'file3.json' + ), + ), + 'file1.json', + 'file1.php', + ); + $this->assertEquals($result, $this->object->getFileList($this->filesPath.'/getFileList', true)); + + + $result= array( + 'Dir1' => array( + 'Dir2', + 'file2.json', + ), + 'file1.json', + 'file1.php', + ); + $this->assertEquals($result, $this->object->getFileList($this->filesPath.'/getFileList', 1)); + + + $result= array( + 'Dir1' => array( + 'file2.json', + 'Dir2' => array ( + 'file3.json' + ), + ), + 'file1.json', + 'file1.php', + ); + $this->assertEquals($result, $this->object->getFileList($this->filesPath.'/getFileList', 2)); + + + $result= array( + 'Dir1' => array( + 'file2.json', + 'Dir2' => array ( + 'file3.json' + ), + ), + 'file1.json', + 'file1.php', + ); + $this->assertEquals($result, $this->object->getFileList($this->filesPath.'/getFileList', 3)); + } + + function testGetContent() + { + $testPath= $this->filesPath.'/getContent'; + + $result= '{"testData":"Test"}'; + $this->assertEquals($result, $this->object->getContent($testPath, 'test.json')); + + $result= '{"testData":"Test"}'; + $this->assertEquals($result, $this->object->getContent($testPath.'/test.json')); + } + + /*function testSetContent() + { + $testPath= $this->filesPath.'/setContent'; + + $result= 'next value'; + $this->assertTrue($this->object->setContent($result, $testPath, 'test.json')); + //$this->assertEquals($result, $this->object->getContent($testPath, 'test.json')); + + //$this->assertTrue($this->object->setContent('initial value', $testPath.'/test.json')); + } */ + + /*function testMergeContent() + { + $testPath= $this->filesPath.'/setContent'; + + $initialVal= $this->object->getContent($testPath, 'test2.json'); + + $result= '{"var1":"val1","var2":"val2"}'; + $this->assertTrue($this->object->setContent($result, $testPath, 'test2.json')); + + $result= '{"var2":"new val"}'; + $this->assertTrue($this->object->mergeContent($result, $testPath, 'test2.json', true)); + + $result= '{"var1":"val1","var2":"new val"}'; + $this->assertEquals($result, $this->object->getContent($testPath, 'test2.json')); + } */ + + function testGetCurrentPermission() + { + $this->assertEquals(4, strlen($this->object->getCurrentPermission($this->filesPath.'/setContent/test.json'))); + $this->assertStringMatchesFormat('%d', $this->object->getCurrentPermission($this->filesPath.'/setContent/test.json')); + } + + /*function testCheckCreateFile() + { + $filePath= $this->filesPath.'/setContent/test.json'; + + $this->assertTrue($this->object->checkCreateFile($filePath)); + + $perm= $this->object->getDefaultPermissions(); + $this->assertTrue( in_array($this->object->getCurrentPermission($filePath), array($perm->file, $perm->dir)) ); + }*/ + + + function testGetDefaultPermissions() + { + $this->assertObjectHasAttribute('dir', $this->object->getDefaultPermissions()); + $this->assertObjectHasAttribute('file', $this->object->getDefaultPermissions()); + $this->assertObjectHasAttribute('user', $this->object->getDefaultPermissions()); + $this->assertObjectHasAttribute('group', $this->object->getDefaultPermissions()); + } + + + function testGetFileName() + { + $result= 'file1'; + $this->assertEquals($result, $this->object->getFileName('file1.json')); + + $result= 'file1'; + $this->assertEquals($result, $this->object->getFileName('file1.json', 'json')); + + $result= 'file1'; + $this->assertEquals($result, $this->object->getFileName('file1.json', '.json')); + } + + function testGetDirName() + { + $result= 'dirname'; + $this->assertEquals('dirname', $this->object->getDirName('test/dirname/Test.json')); + + $result= 'dirname'; + $this->assertEquals('dirname', $this->object->getDirName('test/dirname/')); + + $result= 'dirname'; + $this->assertEquals('dirname', $this->object->getDirName('test/dirname')); + } + + function testGetSingeFileList() + { + $input = array( + 'Dir1' => array( + 'file2.json', + 'Dir2' => array ( + 'file3.json' + ), + ), + 'file1.json', + 'file1.php', + ); + + $result = array( + 'Dir1/file2.json', + 'Dir1/Dir2/file3.json', + 'file1.json', + 'file1.php', + ); + + $this->assertEquals($result, $this->invokeMethod('getSingeFileList', array($input))); + } + + + +} + +?> diff --git a/tests/testData/FileManager/getFileList/Dir1/Dir2/file3.json b/tests/testData/FileManager/getFileList/Dir1/Dir2/file3.json new file mode 100644 index 0000000000..e69de29bb2