diff --git a/.htaccess b/.htaccess old mode 100644 new mode 100755 index b1516e7580..35ec7436c1 --- a/.htaccess +++ b/.htaccess @@ -1,3 +1,5 @@ +DirectoryIndex index.php + #SetEnv HTTP_MOD_REWRITE On diff --git a/Web.Config b/Web.Config new file mode 100755 index 0000000000..c695b40eb2 --- /dev/null +++ b/Web.Config @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api/v1/.htaccess b/api/v1/.htaccess old mode 100644 new mode 100755 diff --git a/api/v1/Web.Config b/api/v1/Web.Config new file mode 100755 index 0000000000..be763f982b --- /dev/null +++ b/api/v1/Web.Config @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/application/Espo/Controllers/Admin.php b/application/Espo/Controllers/Admin.php index 216091604d..47e5d5b171 100644 --- a/application/Espo/Controllers/Admin.php +++ b/application/Espo/Controllers/Admin.php @@ -1,4 +1,4 @@ - $this->getUser()->toArray(), - 'acl' => $this->getAcl()->toArray(), - 'preferences' => $this->getPreferences()->toArray() - ); - } -} + $this->getUser()->toArray(), + 'acl' => $this->getAcl()->toArray(), + 'preferences' => $this->getPreferences()->toArray() + ); + } +} diff --git a/application/Espo/Controllers/Attachment.php b/application/Espo/Controllers/Attachment.php index 894d3408fe..2641dd1b15 100644 --- a/application/Espo/Controllers/Attachment.php +++ b/application/Espo/Controllers/Attachment.php @@ -1,4 +1,4 @@ - $data['headerRow'], - 'fieldDelimiter' => $data['fieldDelimiter'], - 'textQualifier' => $data['textQualifier'], - 'dateFormat' => $data['dateFormat'], - 'timeFormat' => $data['timeFormat'], - 'decimalMark' => $data['decimalMark'], - 'currency' => $data['currency'], - 'defaultValues' => $data['defaultValues'], - 'action' => $data['action'], - ); - - if (!$this->getAcl()->check($data['entityType'], 'edit')) { - throw new Forbidden(); - } - - return $this->getService('Import')->import($data['entityType'], $data['fields'], $data['fileContents'], $importParams); - } -} - + $data['headerRow'], + 'fieldDelimiter' => $data['fieldDelimiter'], + 'textQualifier' => $data['textQualifier'], + 'dateFormat' => $data['dateFormat'], + 'timeFormat' => $data['timeFormat'], + 'decimalMark' => $data['decimalMark'], + 'currency' => $data['currency'], + 'defaultValues' => $data['defaultValues'], + 'action' => $data['action'], + ); + + if (!$this->getAcl()->check($data['entityType'], 'edit')) { + throw new Forbidden(); + } + + return $this->getService('Import')->import($data['entityType'], $data['fields'], $data['fileContents'], $importParams); + } +} + diff --git a/application/Espo/Controllers/Layout.php b/application/Espo/Controllers/Layout.php index f4e4ef24f4..6d08a48061 100644 --- a/application/Espo/Controllers/Layout.php +++ b/application/Espo/Controllers/Layout.php @@ -1,4 +1,4 @@ -getContainer()->get('preferences'); - } - - protected function getEntityManager() - { - return $this->getContainer()->get('entityManager'); - } - - protected function handleUserAccess($userId) - { - if (!$this->getUser()->isAdmin()) { - if ($this->getUser()->id != $userId) { - throw new Forbidden(); - } - } - } - - public function actionPatch($params, $data) - { - return $this->actionUpdate($params, $data); - } - - public function actionUpdate($params, $data) - { - $userId = $params['id']; - $this->handleUserAccess($userId); - - $entity = $this->getEntityManager()->getEntity('Preferences', $userId); - if ($entity) { - $entity->set($data); - $this->getEntityManager()->saveEntity($entity); - return $entity->toArray(); - } - throw new Error(); - } - - public function actionRead($params) - { - $userId = $params['id']; - $this->handleUserAccess($userId); - - $entity = $this->getEntityManager()->getEntity('Preferences', $userId); - $user = $this->getEntityManager()->getEntity('User', $userId); - - $entity->set('smtpEmailAddress', $user->get('emailAddress')); - $entity->set('name', $user->get('name')); - if ($entity) { - return $entity->toArray(); - } - throw new NotFound(); - } -} - +getContainer()->get('preferences'); + } + + protected function getEntityManager() + { + return $this->getContainer()->get('entityManager'); + } + + protected function handleUserAccess($userId) + { + if (!$this->getUser()->isAdmin()) { + if ($this->getUser()->id != $userId) { + throw new Forbidden(); + } + } + } + + public function actionPatch($params, $data) + { + return $this->actionUpdate($params, $data); + } + + public function actionUpdate($params, $data) + { + $userId = $params['id']; + $this->handleUserAccess($userId); + + $entity = $this->getEntityManager()->getEntity('Preferences', $userId); + if ($entity) { + $entity->set($data); + $this->getEntityManager()->saveEntity($entity); + return $entity->toArray(); + } + throw new Error(); + } + + public function actionRead($params) + { + $userId = $params['id']; + $this->handleUserAccess($userId); + + $entity = $this->getEntityManager()->getEntity('Preferences', $userId); + $user = $this->getEntityManager()->getEntity('User', $userId); + + $entity->set('smtpEmailAddress', $user->get('emailAddress')); + $entity->set('name', $user->get('name')); + if ($entity) { + return $entity->toArray(); + } + throw new NotFound(); + } +} + diff --git a/application/Espo/Controllers/Role.php b/application/Espo/Controllers/Role.php index 1e428dc4c5..bfc39e6c09 100644 --- a/application/Espo/Controllers/Role.php +++ b/application/Espo/Controllers/Role.php @@ -1,4 +1,4 @@ -getConfig()->getData($this->getUser()->isAdmin()); - } - - public function actionUpdate($params, $data) - { - return $this->actionPatch($params, $data); - } - - public function actionPatch($params, $data) - { - $result = $this->getConfig()->setData($data, $this->getUser()->isAdmin()); - if ($result === false) { - throw new Error('Cannot save settings'); - } - return $this->getConfig()->getData($this->getUser()->isAdmin()); - } -} +getConfig()->getData($this->getUser()->isAdmin()); + } + + public function actionUpdate($params, $data) + { + return $this->actionPatch($params, $data); + } + + public function actionPatch($params, $data) + { + $result = $this->getConfig()->setData($data, $this->getUser()->isAdmin()); + if ($result === false) { + throw new Error('Cannot save settings'); + } + return $this->getConfig()->getData($this->getUser()->isAdmin()); + } +} diff --git a/application/Espo/Controllers/Stream.php b/application/Espo/Controllers/Stream.php index 45a7807de5..5f03a9f55f 100644 --- a/application/Espo/Controllers/Stream.php +++ b/application/Espo/Controllers/Stream.php @@ -1,4 +1,4 @@ -user = $user; - $this->fileManager = $fileManager; - - if (!$this->user->isFetched()) { - throw new Error(); - } - - $this->user->loadLinkMultipleField('teams'); - - $this->cacheFile = 'data/cache/application/acl/' . $user->id . '.php'; - - if ($config->get('useCache') && file_exists($this->cacheFile)) { - $cached = include $this->cacheFile; - } else { - $this->load(); - $this->initSolid(); - if ($config->get('useCache')) { - $this->buildCache(); - } - } - } - - public function checkScope($scope, $action = null, $isOwner = null, $inTeam = null) - { - if (array_key_exists($scope, $this->data)) { - if ($this->data[$scope] === false) { - return false; - } - if ($this->data[$scope] === true) { - return true; - } - if (!is_null($action)) { - if (array_key_exists($action, $this->data[$scope])) { - $value = $this->data[$scope][$action]; - - if ($value === 'all' || $value === true) { - return true; - } - - if (!$value || $value === 'no') { - return false; - } - - if (is_null($isOwner)) { - return true; - } - - if ($isOwner) { - if ($value === 'own' || $value === 'team') { - return true; - } - } - - if ($inTeam) { - if ($value === 'team') { - return true; - } - } - - return false; - } - } - return true; - } - return true; - } - - public function toArray() - { - return $this->data; - } - - public function check($subject, $action = null, $isOwner = null, $inTeam = null) - { - if ($this->user->isAdmin()) { - return true; - } - if (is_string($subject)) { - return $this->checkScope($subject, $action, $isOwner, $inTeam); - } else { - $entity = $subject; - $entityName = $entity->getEntityName(); - return $this->checkScope($entityName, $action, $this->checkIsOwner($entity), $this->checkInTeam($entity)); - } - } - - public function checkReadOnlyTeam($scope) - { - if (isset($this->data[$scope]) && isset($this->data[$scope]['read'])) { - return $this->data[$scope]['read'] === 'team'; - } - return false; - } - - public function checkReadOnlyOwn($scope) - { - if ($this->user->isAdmin()) { - return false; - } - if (isset($this->data[$scope]) && isset($this->data[$scope]['read'])) { - return $this->data[$scope]['read'] === 'own'; - } - return false; - } - - public function checkIsOwner($entity) - { - if ($this->user->isAdmin()) { - return false; - } - $userId = $this->user->id; - if ($userId === $entity->get('assignedUserId') || $userId === $entity->get('createdById')) { - return true; - } - return false; - } - - public function checkInTeam($entity) - { - $userTeamIds = $this->user->get('teamsIds'); - $teamIds = $entity->get('teamsIds'); - - foreach ($userTeamIds as $id) { - if (in_array($id, $teamIds)) { - return true; - } - } - return false; - } - - private function load() - { - $aclTables = array(); - - $userRoles = $this->user->get('roles'); - - foreach ($userRoles as $role) { - $aclTables[] = json_decode($role->get('data')); - } - - $teams = $this->user->get('teams'); - foreach ($teams as $team) { - $teamRoles = $team->get('roles'); - foreach ($teamRoles as $role) { - $aclTables[] = json_decode($role->get('data')); - } - } - - $this->data = $this->merge($aclTables); - } - - private function initSolid() - { - $this->data['User'] = array( - 'read' => 'all', - 'edit' => 'no', - 'delete' => 'no', - ); - $this->data['Team'] = array( - 'read' => 'all', - 'edit' => 'no', - 'delete' => 'no', - ); - $this->data['Role'] = false; - $this->data['Note'] = array( - 'read' => 'own', - 'edit' => 'own', - 'delete' => 'own', - ); - } - - private function merge($tables) - { - $data = array(); - foreach ($tables as $table) { - foreach ($table as $scope => $row) { - if ($row == false) { - if (!isset($data[$scope])) { - $data[$scope] = false; - } - } else { - if (!isset($data[$scope])) { - $data[$scope] = array(); - } - if ($data[$scope] == false) { - $data[$scope] = array(); - } - foreach ($row as $action => $level) { - if (!isset($data[$scope][$action])) { - $data[$scope][$action] = $level; - } else { - if (array_search($data[$scope][$action], $this->levelList) > array_search($level, $this->levelList)) { - $data[$scope][$action] = $level; - } - } - } - } - } - } - return $data; - } - - private function buildCache() - { - $contents = '<' . '?'. 'php return ' . var_export($this->data, true) . ';'; - $this->fileManager->putContents($this->cacheFile, $contents); - } -} - +user = $user; + $this->fileManager = $fileManager; + + if (!$this->user->isFetched()) { + throw new Error(); + } + + $this->user->loadLinkMultipleField('teams'); + + $this->cacheFile = 'data/cache/application/acl/' . $user->id . '.php'; + + if ($config->get('useCache') && file_exists($this->cacheFile)) { + $cached = include $this->cacheFile; + } else { + $this->load(); + $this->initSolid(); + if ($config->get('useCache')) { + $this->buildCache(); + } + } + } + + public function checkScope($scope, $action = null, $isOwner = null, $inTeam = null) + { + if (array_key_exists($scope, $this->data)) { + if ($this->data[$scope] === false) { + return false; + } + if ($this->data[$scope] === true) { + return true; + } + if (!is_null($action)) { + if (array_key_exists($action, $this->data[$scope])) { + $value = $this->data[$scope][$action]; + + if ($value === 'all' || $value === true) { + return true; + } + + if (!$value || $value === 'no') { + return false; + } + + if (is_null($isOwner)) { + return true; + } + + if ($isOwner) { + if ($value === 'own' || $value === 'team') { + return true; + } + } + + if ($inTeam) { + if ($value === 'team') { + return true; + } + } + + return false; + } + } + return true; + } + return true; + } + + public function toArray() + { + return $this->data; + } + + public function check($subject, $action = null, $isOwner = null, $inTeam = null) + { + if ($this->user->isAdmin()) { + return true; + } + if (is_string($subject)) { + return $this->checkScope($subject, $action, $isOwner, $inTeam); + } else { + $entity = $subject; + $entityName = $entity->getEntityName(); + return $this->checkScope($entityName, $action, $this->checkIsOwner($entity), $this->checkInTeam($entity)); + } + } + + public function checkReadOnlyTeam($scope) + { + if (isset($this->data[$scope]) && isset($this->data[$scope]['read'])) { + return $this->data[$scope]['read'] === 'team'; + } + return false; + } + + public function checkReadOnlyOwn($scope) + { + if ($this->user->isAdmin()) { + return false; + } + if (isset($this->data[$scope]) && isset($this->data[$scope]['read'])) { + return $this->data[$scope]['read'] === 'own'; + } + return false; + } + + public function checkIsOwner($entity) + { + if ($this->user->isAdmin()) { + return false; + } + $userId = $this->user->id; + if ($userId === $entity->get('assignedUserId') || $userId === $entity->get('createdById')) { + return true; + } + return false; + } + + public function checkInTeam($entity) + { + $userTeamIds = $this->user->get('teamsIds'); + $teamIds = $entity->get('teamsIds'); + + foreach ($userTeamIds as $id) { + if (in_array($id, $teamIds)) { + return true; + } + } + return false; + } + + private function load() + { + $aclTables = array(); + + $userRoles = $this->user->get('roles'); + + foreach ($userRoles as $role) { + $aclTables[] = json_decode($role->get('data')); + } + + $teams = $this->user->get('teams'); + foreach ($teams as $team) { + $teamRoles = $team->get('roles'); + foreach ($teamRoles as $role) { + $aclTables[] = json_decode($role->get('data')); + } + } + + $this->data = $this->merge($aclTables); + } + + private function initSolid() + { + $this->data['User'] = array( + 'read' => 'all', + 'edit' => 'no', + 'delete' => 'no', + ); + $this->data['Team'] = array( + 'read' => 'all', + 'edit' => 'no', + 'delete' => 'no', + ); + $this->data['Role'] = false; + $this->data['Note'] = array( + 'read' => 'own', + 'edit' => 'own', + 'delete' => 'own', + ); + } + + private function merge($tables) + { + $data = array(); + foreach ($tables as $table) { + foreach ($table as $scope => $row) { + if ($row == false) { + if (!isset($data[$scope])) { + $data[$scope] = false; + } + } else { + if (!isset($data[$scope])) { + $data[$scope] = array(); + } + if ($data[$scope] == false) { + $data[$scope] = array(); + } + foreach ($row as $action => $level) { + if (!isset($data[$scope][$action])) { + $data[$scope][$action] = $level; + } else { + if (array_search($data[$scope][$action], $this->levelList) > array_search($level, $this->levelList)) { + $data[$scope][$action] = $level; + } + } + } + } + } + } + return $data; + } + + private function buildCache() + { + $contents = '<' . '?'. 'php return ' . var_export($this->data, true) . ';'; + $this->fileManager->putContents($this->cacheFile, $contents); + } +} + diff --git a/application/Espo/Core/Container.php b/application/Espo/Core/Container.php index accdf49be0..3870cf8504 100644 --- a/application/Espo/Core/Container.php +++ b/application/Espo/Core/Container.php @@ -1,194 +1,194 @@ -data[$name])) { - $this->load($name); - } - return $this->data[$name]; - } - - private function load($name) - { - $loadMethod = 'load' . ucfirst($name); - if (method_exists($this, $loadMethod)) { - $obj = $this->$loadMethod(); - $this->data[$name] = $obj; - } else { - //external loader class \Espo\Core\Loaders\ or \Espo\Custom\Core\Loaders\ with load() method - $className = '\Espo\Custom\Core\Loaders\\'.ucfirst($name); - if (!class_exists($className)) { - $className = '\Espo\Core\Loaders\\'.ucfirst($name); - } - - if (class_exists($className)) { - $loadClass = new $className($this); - $this->data[$name] = $loadClass->load(); - } - } - - // TODO throw an exception - return null; - } - - private function loadSlim() - { - //return new \Slim\Slim(); - return new \Espo\Core\Utils\Api\Slim(); - } - - private function loadFileManager() - { - return new \Espo\Core\Utils\File\Manager( - array( - 'defaultPermissions' => $this->get('config')->get('defaultPermissions'), - ) - ); - } - - private function loadPreferences() - { - return $this->get('entityManager')->getEntity('Preferences', $this->get('user')->id); - } - - private function loadConfig() - { - return new \Espo\Core\Utils\Config( - new \Espo\Core\Utils\File\Manager() - ); - } - - private function loadHookManager() - { - return new \Espo\Core\HookManager( - $this - ); - } - - private function loadOutput() - { - return new \Espo\Core\Utils\Api\Output( - $this->get('slim') - ); - } - - private function loadMailSender() - { - return new \Espo\Core\Mail\Sender( - $this->get('config') - ); - } - - private function loadServiceFactory() - { - return new \Espo\Core\ServiceFactory( - $this - ); - } - - private function loadSelectManagerFactory() - { - return new \Espo\Core\SelectManagerFactory( - $this->get('entityManager'), - $this->get('user'), - $this->get('acl'), - $this->get('metadata') - ); - } - - private function loadMetadata() - { - return new \Espo\Core\Utils\Metadata( - $this->get('config'), - $this->get('fileManager') - ); - } - - private function loadLayout() - { - return new \Espo\Core\Utils\Layout( - $this->get('fileManager'), - $this->get('metadata') - ); - } - - private function loadAcl() - { - return new \Espo\Core\Acl( - $this->get('user'), - $this->get('config'), - $this->get('fileManager') - ); - } - - private function loadSchema() - { - return new \Espo\Core\Utils\Database\Schema\Schema( - $this->get('config'), - $this->get('metadata'), - $this->get('fileManager'), - $this->get('entityManager'), - $this->get('classParser') - ); - } - - private function loadClassParser() - { - return new \Espo\Core\Utils\File\ClassParser( - $this->get('fileManager'), - $this->get('config'), - $this->get('metadata') - ); - } - - private function loadI18n() - { - return new \Espo\Core\Utils\I18n( - $this->get('fileManager'), - $this->get('config'), - $this->get('preferences') - ); - } - - public function setUser($user) - { - $this->data['user'] = $user; - } -} - +data[$name])) { + $this->load($name); + } + return $this->data[$name]; + } + + private function load($name) + { + $loadMethod = 'load' . ucfirst($name); + if (method_exists($this, $loadMethod)) { + $obj = $this->$loadMethod(); + $this->data[$name] = $obj; + } else { + //external loader class \Espo\Core\Loaders\ or \Espo\Custom\Core\Loaders\ with load() method + $className = '\Espo\Custom\Core\Loaders\\'.ucfirst($name); + if (!class_exists($className)) { + $className = '\Espo\Core\Loaders\\'.ucfirst($name); + } + + if (class_exists($className)) { + $loadClass = new $className($this); + $this->data[$name] = $loadClass->load(); + } + } + + // TODO throw an exception + return null; + } + + private function loadSlim() + { + //return new \Slim\Slim(); + return new \Espo\Core\Utils\Api\Slim(); + } + + private function loadFileManager() + { + return new \Espo\Core\Utils\File\Manager( + array( + 'defaultPermissions' => $this->get('config')->get('defaultPermissions'), + ) + ); + } + + private function loadPreferences() + { + return $this->get('entityManager')->getEntity('Preferences', $this->get('user')->id); + } + + private function loadConfig() + { + return new \Espo\Core\Utils\Config( + new \Espo\Core\Utils\File\Manager() + ); + } + + private function loadHookManager() + { + return new \Espo\Core\HookManager( + $this + ); + } + + private function loadOutput() + { + return new \Espo\Core\Utils\Api\Output( + $this->get('slim') + ); + } + + private function loadMailSender() + { + return new \Espo\Core\Mail\Sender( + $this->get('config') + ); + } + + private function loadServiceFactory() + { + return new \Espo\Core\ServiceFactory( + $this + ); + } + + private function loadSelectManagerFactory() + { + return new \Espo\Core\SelectManagerFactory( + $this->get('entityManager'), + $this->get('user'), + $this->get('acl'), + $this->get('metadata') + ); + } + + private function loadMetadata() + { + return new \Espo\Core\Utils\Metadata( + $this->get('config'), + $this->get('fileManager') + ); + } + + private function loadLayout() + { + return new \Espo\Core\Utils\Layout( + $this->get('fileManager'), + $this->get('metadata') + ); + } + + private function loadAcl() + { + return new \Espo\Core\Acl( + $this->get('user'), + $this->get('config'), + $this->get('fileManager') + ); + } + + private function loadSchema() + { + return new \Espo\Core\Utils\Database\Schema\Schema( + $this->get('config'), + $this->get('metadata'), + $this->get('fileManager'), + $this->get('entityManager'), + $this->get('classParser') + ); + } + + private function loadClassParser() + { + return new \Espo\Core\Utils\File\ClassParser( + $this->get('fileManager'), + $this->get('config'), + $this->get('metadata') + ); + } + + private function loadI18n() + { + return new \Espo\Core\Utils\I18n( + $this->get('fileManager'), + $this->get('config'), + $this->get('preferences') + ); + } + + public function setUser($user) + { + $this->data['user'] = $user; + } +} + diff --git a/application/Espo/Core/ControllerManager.php b/application/Espo/Core/ControllerManager.php index 3bed206fd2..e253241d21 100644 --- a/application/Espo/Core/ControllerManager.php +++ b/application/Espo/Core/ControllerManager.php @@ -1,114 +1,114 @@ -container = $container; - - $this->config = $this->container->get('config'); - $this->metadata = $this->container->get('metadata'); - } - - protected function getConfig() - { - return $this->config; - } - - protected function getMetadata() - { - return $this->metadata; - } - - public function process($controllerName, $actionName, $params, $data, $request) - { - $customeClassName = '\\Espo\\Custom\\Controllers\\' . Util::normilizeClassName($controllerName); - if (class_exists($customeClassName)) { - $controllerClassName = $customeClassName; - } else { - $moduleName = $this->metadata->getScopeModuleName($controllerName); - if ($moduleName) { - $controllerClassName = '\\Espo\\Modules\\' . $moduleName . '\\Controllers\\' . Util::normilizeClassName($controllerName); - } else { - $controllerClassName = '\\Espo\\Controllers\\' . Util::normilizeClassName($controllerName); - } - } - - if ($data) { - $data = json_decode($data); - } - - - if ($data instanceof \stdClass) { - $data = get_object_vars($data); - } - - if (!class_exists($controllerClassName)) { - throw new NotFound("Controller '$controllerName' is not found"); - } - - $controller = new $controllerClassName($this->container); - - if ($actionName == 'index') { - $actionName = $controllerClassName::$defaultAction; - } - - $actionNameUcfirst = ucfirst($actionName); - - $beforeMethodName = 'before' . $actionNameUcfirst; - if (method_exists($controller, $beforeMethodName)) { - $controller->$beforeMethodName($params, $data, $request); - } - $actionMethodName = 'action' . $actionNameUcfirst; - - if (!method_exists($controller, $actionMethodName)) { - throw new NotFound("Action '$actionMethodName' does not exist in controller '$controller'"); - } - - $result = $controller->$actionMethodName($params, $data, $request); - - $afterMethodName = 'after' . $actionNameUcfirst; - if (method_exists($controller, $afterMethodName)) { - $controller->$afterMethodName($params, $data, $request); - } - - if (is_array($result) || is_bool($result)) { - return \Espo\Core\Utils\Json::encode($result); - } - - return $result; - } - -} - +container = $container; + + $this->config = $this->container->get('config'); + $this->metadata = $this->container->get('metadata'); + } + + protected function getConfig() + { + return $this->config; + } + + protected function getMetadata() + { + return $this->metadata; + } + + public function process($controllerName, $actionName, $params, $data, $request) + { + $customeClassName = '\\Espo\\Custom\\Controllers\\' . Util::normilizeClassName($controllerName); + if (class_exists($customeClassName)) { + $controllerClassName = $customeClassName; + } else { + $moduleName = $this->metadata->getScopeModuleName($controllerName); + if ($moduleName) { + $controllerClassName = '\\Espo\\Modules\\' . $moduleName . '\\Controllers\\' . Util::normilizeClassName($controllerName); + } else { + $controllerClassName = '\\Espo\\Controllers\\' . Util::normilizeClassName($controllerName); + } + } + + if ($data) { + $data = json_decode($data); + } + + + if ($data instanceof \stdClass) { + $data = get_object_vars($data); + } + + if (!class_exists($controllerClassName)) { + throw new NotFound("Controller '$controllerName' is not found"); + } + + $controller = new $controllerClassName($this->container); + + if ($actionName == 'index') { + $actionName = $controllerClassName::$defaultAction; + } + + $actionNameUcfirst = ucfirst($actionName); + + $beforeMethodName = 'before' . $actionNameUcfirst; + if (method_exists($controller, $beforeMethodName)) { + $controller->$beforeMethodName($params, $data, $request); + } + $actionMethodName = 'action' . $actionNameUcfirst; + + if (!method_exists($controller, $actionMethodName)) { + throw new NotFound("Action '$actionMethodName' does not exist in controller '$controller'"); + } + + $result = $controller->$actionMethodName($params, $data, $request); + + $afterMethodName = 'after' . $actionNameUcfirst; + if (method_exists($controller, $afterMethodName)) { + $controller->$afterMethodName($params, $data, $request); + } + + if (is_array($result) || is_bool($result)) { + return \Espo\Core\Utils\Json::encode($result); + } + + return $result; + } + +} + diff --git a/application/Espo/Core/Controllers/Base.php b/application/Espo/Core/Controllers/Base.php index 357ee4071a..1ddc32dd98 100644 --- a/application/Espo/Core/Controllers/Base.php +++ b/application/Espo/Core/Controllers/Base.php @@ -1,4 +1,4 @@ -getContainer()->get('entityManager'); - } - - protected function getRecordService() - { - if ($this->getServiceFactory()->checkExists($this->name)) { - $service = $this->getServiceFactory()->create($this->name); - } else { - $service = $this->getServiceFactory()->create('Record'); - $service->setEntityName($this->name); - } - - return $service; - } - - public function actionRead($params) - { - $id = $params['id']; - $entity = $this->getRecordService()->getEntity($id); - - if (empty($entity)) { - throw new NotFound(); - } - - return $entity->toArray(); - } - - public function actionPatch($params, $data) - { - return $this->actionUpdate($params, $data); - } - - public function actionCreate($params, $data) - { - if (!$this->getAcl()->check($this->name, 'edit')) { - throw new Forbidden(); - } - - $service = $this->getRecordService(); - - if ($entity = $service->createEntity($data)) { - return $entity->toArray(); - } - - throw new Error(); - } - - public function actionUpdate($params, $data) - { - if (!$this->getAcl()->check($this->name, 'edit')) { - throw new Forbidden(); - } - - $id = $params['id']; - - if ($entity = $this->getRecordService()->updateEntity($id, $data)) { - return $entity->toArray(); - } - - throw new Error(); - } - - public function actionList($params, $data, $request) - { - if (!$this->getAcl()->check($this->name, 'read')) { - throw new Forbidden(); - } - - $where = $request->get('where'); - $offset = $request->get('offset'); - $maxSize = $request->get('maxSize'); - $asc = $request->get('asc') === 'true'; - $sortBy = $request->get('sortBy'); - $q = $request->get('q'); - - $result = $this->getRecordService()->findEntities(array( - 'where' => $where, - 'offset' => $offset, - 'maxSize' => $maxSize, - 'asc' => $asc, - 'sortBy' => $sortBy, - 'q' => $q, - )); - - return array( - 'total' => $result['total'], - 'list' => $result['collection']->toArray() - ); - } - - public function actionListLinked($params, $data, $request) - { - $id = $params['id']; - $link = $params['link']; - - $where = $request->get('where'); - $offset = $request->get('offset'); - $maxSize = $request->get('maxSize'); - $asc = $request->get('asc') === 'true'; - $sortBy = $request->get('sortBy'); - $q = $request->get('q'); - - $result = $this->getRecordService()->findLinkedEntities($id, $link, array( - 'where' => $where, - 'offset' => $offset, - 'maxSize' => $maxSize, - 'asc' => $asc, - 'sortBy' => $sortBy, - 'q' => $q, - )); - - - return array( - 'total' => $result['total'], - 'list' => $result['collection']->toArray() - ); - } - - public function actionDelete($params) - { - $id = $params['id']; - - if ($this->getRecordService()->deleteEntity($id)) { - return true; - } - throw new Error(); - } - - public function actionExport($params, $data, $request) - { - // TODO move to service - - if (!$this->getAcl()->check($this->name, 'read')) { - throw new Forbidden(); - } - - $ids = $request->get('ids'); - $where = $request->get('where'); - - return array( - 'id' => $this->getRecordService()->export($ids, $where) - ); - } - - public function actionMassUpdate($params, $data) - { - if (!$this->getAcl()->check($this->name, 'edit')) { - throw new Forbidden(); - } - - $ids = $data['ids']; - $where = $data['where']; - $attributes = $data['attributes']; - - $idsUpdated = $this->getRecordService()->massUpdate($attributes, $ids, $where); - - return $idsUpdated; - } - - public function actionMassDelete($params, $data) - { - if (!$this->getAcl()->check($this->name, 'delete')) { - throw new Forbidden(); - } - - $ids = $data['ids']; - $where = $data['where']; - - $idsDeleted = $this->getRecordService()->massDelete($ids, $where); - - return $idsDeleted; - } - - public function actionCreateLink($params, $data) - { - $id = $params['id']; - $link = $params['link']; - - $foreignIds = array(); - if (isset($data['id'])) { - $foreignIds[] = $data['id']; - } - if (isset($data['ids']) && is_array($data['ids'])) { - foreach ($data['ids'] as $foreignId) { - $foreignIds[] = $foreignId; - } - } - - $result = false; - foreach ($foreignIds as $foreignId) { - if ($this->getRecordService()->linkEntity($id, $link, $foreignId)) { - $result = $result || true; - } - } - if ($result) { - return true; - } - - throw new Error(); - } - - public function actionRemoveLink($params, $data) - { - $id = $params['id']; - $link = $params['link']; - - $foreignIds = array(); - if (isset($data['id'])) { - $foreignIds[] = $data['id']; - } - if (isset($data['ids']) && is_array($data['ids'])) { - foreach ($data['ids'] as $foreignId) { - $foreignIds[] = $foreignId; - } - } - - $result = false; - foreach ($foreignIds as $foreignId) { - if ($this->getRecordService()->unlinkEntity($id, $link, $foreignId)) { - $result = $result || true; - } - } - if ($result) { - return true; - } - - throw new Error(); - } - - public function actionFollow($params) - { - if (!$this->getAcl()->check($this->name, 'read')) { - throw new Forbidden(); - } - $id = $params['id']; - return $this->getRecordService()->follow($id); - } - - public function actionUnfollow($params) - { - if (!$this->getAcl()->check($this->name, 'read')) { - throw new Forbidden(); - } - $id = $params['id']; - return $this->getRecordService()->unfollow($id); - } -} - +getContainer()->get('entityManager'); + } + + protected function getRecordService() + { + if ($this->getServiceFactory()->checkExists($this->name)) { + $service = $this->getServiceFactory()->create($this->name); + } else { + $service = $this->getServiceFactory()->create('Record'); + $service->setEntityName($this->name); + } + + return $service; + } + + public function actionRead($params) + { + $id = $params['id']; + $entity = $this->getRecordService()->getEntity($id); + + if (empty($entity)) { + throw new NotFound(); + } + + return $entity->toArray(); + } + + public function actionPatch($params, $data) + { + return $this->actionUpdate($params, $data); + } + + public function actionCreate($params, $data) + { + if (!$this->getAcl()->check($this->name, 'edit')) { + throw new Forbidden(); + } + + $service = $this->getRecordService(); + + if ($entity = $service->createEntity($data)) { + return $entity->toArray(); + } + + throw new Error(); + } + + public function actionUpdate($params, $data) + { + if (!$this->getAcl()->check($this->name, 'edit')) { + throw new Forbidden(); + } + + $id = $params['id']; + + if ($entity = $this->getRecordService()->updateEntity($id, $data)) { + return $entity->toArray(); + } + + throw new Error(); + } + + public function actionList($params, $data, $request) + { + if (!$this->getAcl()->check($this->name, 'read')) { + throw new Forbidden(); + } + + $where = $request->get('where'); + $offset = $request->get('offset'); + $maxSize = $request->get('maxSize'); + $asc = $request->get('asc') === 'true'; + $sortBy = $request->get('sortBy'); + $q = $request->get('q'); + + $result = $this->getRecordService()->findEntities(array( + 'where' => $where, + 'offset' => $offset, + 'maxSize' => $maxSize, + 'asc' => $asc, + 'sortBy' => $sortBy, + 'q' => $q, + )); + + return array( + 'total' => $result['total'], + 'list' => $result['collection']->toArray() + ); + } + + public function actionListLinked($params, $data, $request) + { + $id = $params['id']; + $link = $params['link']; + + $where = $request->get('where'); + $offset = $request->get('offset'); + $maxSize = $request->get('maxSize'); + $asc = $request->get('asc') === 'true'; + $sortBy = $request->get('sortBy'); + $q = $request->get('q'); + + $result = $this->getRecordService()->findLinkedEntities($id, $link, array( + 'where' => $where, + 'offset' => $offset, + 'maxSize' => $maxSize, + 'asc' => $asc, + 'sortBy' => $sortBy, + 'q' => $q, + )); + + + return array( + 'total' => $result['total'], + 'list' => $result['collection']->toArray() + ); + } + + public function actionDelete($params) + { + $id = $params['id']; + + if ($this->getRecordService()->deleteEntity($id)) { + return true; + } + throw new Error(); + } + + public function actionExport($params, $data, $request) + { + // TODO move to service + + if (!$this->getAcl()->check($this->name, 'read')) { + throw new Forbidden(); + } + + $ids = $request->get('ids'); + $where = $request->get('where'); + + return array( + 'id' => $this->getRecordService()->export($ids, $where) + ); + } + + public function actionMassUpdate($params, $data) + { + if (!$this->getAcl()->check($this->name, 'edit')) { + throw new Forbidden(); + } + + $ids = $data['ids']; + $where = $data['where']; + $attributes = $data['attributes']; + + $idsUpdated = $this->getRecordService()->massUpdate($attributes, $ids, $where); + + return $idsUpdated; + } + + public function actionMassDelete($params, $data) + { + if (!$this->getAcl()->check($this->name, 'delete')) { + throw new Forbidden(); + } + + $ids = $data['ids']; + $where = $data['where']; + + $idsDeleted = $this->getRecordService()->massDelete($ids, $where); + + return $idsDeleted; + } + + public function actionCreateLink($params, $data) + { + $id = $params['id']; + $link = $params['link']; + + $foreignIds = array(); + if (isset($data['id'])) { + $foreignIds[] = $data['id']; + } + if (isset($data['ids']) && is_array($data['ids'])) { + foreach ($data['ids'] as $foreignId) { + $foreignIds[] = $foreignId; + } + } + + $result = false; + foreach ($foreignIds as $foreignId) { + if ($this->getRecordService()->linkEntity($id, $link, $foreignId)) { + $result = $result || true; + } + } + if ($result) { + return true; + } + + throw new Error(); + } + + public function actionRemoveLink($params, $data) + { + $id = $params['id']; + $link = $params['link']; + + $foreignIds = array(); + if (isset($data['id'])) { + $foreignIds[] = $data['id']; + } + if (isset($data['ids']) && is_array($data['ids'])) { + foreach ($data['ids'] as $foreignId) { + $foreignIds[] = $foreignId; + } + } + + $result = false; + foreach ($foreignIds as $foreignId) { + if ($this->getRecordService()->unlinkEntity($id, $link, $foreignId)) { + $result = $result || true; + } + } + if ($result) { + return true; + } + + throw new Error(); + } + + public function actionFollow($params) + { + if (!$this->getAcl()->check($this->name, 'read')) { + throw new Forbidden(); + } + $id = $params['id']; + return $this->getRecordService()->follow($id); + } + + public function actionUnfollow($params) + { + if (!$this->getAcl()->check($this->name, 'read')) { + throw new Forbidden(); + } + $id = $params['id']; + return $this->getRecordService()->unfollow($id); + } +} + diff --git a/application/Espo/Core/Cron/ScheduledJob.php b/application/Espo/Core/Cron/ScheduledJob.php index 40e0b83849..94b927db21 100644 --- a/application/Espo/Core/Cron/ScheduledJob.php +++ b/application/Espo/Core/Cron/ScheduledJob.php @@ -1,4 +1,4 @@ -container = $container; - - $this->config = $this->container->get('config'); - $this->fileManager = $this->container->get('fileManager'); - - $this->scheduledJobCron = new \Espo\Core\Cron\ScheduledJob( $this->container ); - $this->serviceCron = new \Espo\Core\Cron\Service( $this->container->get('serviceFactory')); - - $this->jobService = $this->container->get('serviceFactory')->create('job'); - $this->scheduledJobService = $this->container->get('serviceFactory')->create('scheduledJob'); - } - - protected function getContainer() - { - return $this->container; - } - - protected function getConfig() - { - return $this->config; - } - - protected function getFileManager() - { - return $this->fileManager; - } - - protected function getJobService() - { - return $this->jobService; - } - - protected function getScheduledJobService() - { - return $this->scheduledJobService; - } - - protected function getScheduledJobCron() - { - return $this->scheduledJobCron; - } - - protected function getServiceCron() - { - return $this->serviceCron; - } - - - protected function getLastRunTime() - { - $lastRunTime = $this->getFileManager()->getContents($this->lastRunTime); - if (!is_int($lastRunTime)) { - $lastRunTime = time() - (intval($this->getConfig()->get('cron.minExecutionTime')) + 60); - } - - return $lastRunTime; - } - - protected function setLastRunTime($time) - { - return $this->getFileManager()->putContentsPHP($this->lastRunTime, $time); - } - - protected function checkLastRunTime() - { - $currentTime = time(); - $lastRunTime = $this->getLastRunTime(); - $minTime = $this->getConfig()->get('cron.minExecutionTime'); - - if ($currentTime > ($lastRunTime + $minTime) ) { - return true; - } - - return false; - } - - - public function run() - { - if (!$this->checkLastRunTime()) { - $GLOBALS['log']->info('Cron Manager: Stop cron running, too frequency execution'); - return; //stop cron running, too frequency execution - } - - $this->setLastRunTime(time()); - - //Check scheduled jobs and create related jobs - $this->createJobsFromScheduledJobs(); - - - $pendingJobs = $this->getJobService()->getPendingJobs(); - - foreach ($pendingJobs as $job) { - - $this->getJobService()->updateEntity($job['id'], array( - 'status' => 'Running', - )); - - $isSuccess = true; - - try { - if (!empty($job['scheduled_job_id'])) { - $this->getScheduledJobCron()->run($job); - } else { - $this->getServiceCron()->run($job); - } - } catch (\Exception $e) { - $isSuccess = false; - $GLOBALS['log']->info('Failed job running, job ['.$job['id'].']. Error Details: '.$e->getMessage()); - } - - $status = $isSuccess ? 'Success' : 'Failed'; - - $this->getJobService()->updateEntity($job['id'], array( - 'status' => $status, - )); - - //set status in the schedulerJobLog - if (!empty($job['scheduled_job_id'])) { - $this->getScheduledJobService()->addLogRecord($job['scheduled_job_id'], $status); - } - } - - } - - /** - * Check scheduled jobs and create related jobs - * @return array List of created Jobs - */ - protected function createJobsFromScheduledJobs() - { - $activeScheduledJobs = $this->getScheduledJobService()->getActiveJobs(); - - $createdJobs = array(); - foreach ($activeScheduledJobs as $scheduledJob) { - - $scheduling = $scheduledJob['scheduling']; - - $cronExpression = \Cron\CronExpression::factory($scheduling); - - try { - //$nextDate = $cronExpression->getNextRunDate()->format('Y-m-d H:i:s'); - $prevDate = $cronExpression->getPreviousRunDate()->format('Y-m-d H:i:s'); - } catch (\Exception $e) { - $GLOBALS['log']->error('ScheduledJob ['.$scheduledJob['id'].']: CronExpression - Impossible CRON expression ['.$scheduling.']'); - continue; - } - - if ($cronExpression->isDue()) { - $prevDate = date('Y-m-d H:i:00'); - } - - $existsJob = $this->getJobService()->getJobByScheduledJob($scheduledJob['id'], $prevDate); - - if (!isset($existsJob) || empty($existsJob)) { - //create a job - $data = array( - 'name' => $scheduledJob['name'], - 'status' => 'Pending', - 'scheduledJobId' => $scheduledJob['id'], - 'executeTime' => $prevDate, - 'method' => $scheduledJob['job'], - ); - $createdJobs[] = $this->getJobService()->createEntity($data); - } - } - - return $createdJobs; - } -} - +container = $container; + + $this->config = $this->container->get('config'); + $this->fileManager = $this->container->get('fileManager'); + + $this->scheduledJobCron = new \Espo\Core\Cron\ScheduledJob( $this->container ); + $this->serviceCron = new \Espo\Core\Cron\Service( $this->container->get('serviceFactory')); + + $this->jobService = $this->container->get('serviceFactory')->create('job'); + $this->scheduledJobService = $this->container->get('serviceFactory')->create('scheduledJob'); + } + + protected function getContainer() + { + return $this->container; + } + + protected function getConfig() + { + return $this->config; + } + + protected function getFileManager() + { + return $this->fileManager; + } + + protected function getJobService() + { + return $this->jobService; + } + + protected function getScheduledJobService() + { + return $this->scheduledJobService; + } + + protected function getScheduledJobCron() + { + return $this->scheduledJobCron; + } + + protected function getServiceCron() + { + return $this->serviceCron; + } + + + protected function getLastRunTime() + { + $lastRunTime = $this->getFileManager()->getContents($this->lastRunTime); + if (!is_int($lastRunTime)) { + $lastRunTime = time() - (intval($this->getConfig()->get('cron.minExecutionTime')) + 60); + } + + return $lastRunTime; + } + + protected function setLastRunTime($time) + { + return $this->getFileManager()->putContentsPHP($this->lastRunTime, $time); + } + + protected function checkLastRunTime() + { + $currentTime = time(); + $lastRunTime = $this->getLastRunTime(); + $minTime = $this->getConfig()->get('cron.minExecutionTime'); + + if ($currentTime > ($lastRunTime + $minTime) ) { + return true; + } + + return false; + } + + + public function run() + { + if (!$this->checkLastRunTime()) { + $GLOBALS['log']->info('Cron Manager: Stop cron running, too frequency execution'); + return; //stop cron running, too frequency execution + } + + $this->setLastRunTime(time()); + + //Check scheduled jobs and create related jobs + $this->createJobsFromScheduledJobs(); + + + $pendingJobs = $this->getJobService()->getPendingJobs(); + + foreach ($pendingJobs as $job) { + + $this->getJobService()->updateEntity($job['id'], array( + 'status' => 'Running', + )); + + $isSuccess = true; + + try { + if (!empty($job['scheduled_job_id'])) { + $this->getScheduledJobCron()->run($job); + } else { + $this->getServiceCron()->run($job); + } + } catch (\Exception $e) { + $isSuccess = false; + $GLOBALS['log']->info('Failed job running, job ['.$job['id'].']. Error Details: '.$e->getMessage()); + } + + $status = $isSuccess ? 'Success' : 'Failed'; + + $this->getJobService()->updateEntity($job['id'], array( + 'status' => $status, + )); + + //set status in the schedulerJobLog + if (!empty($job['scheduled_job_id'])) { + $this->getScheduledJobService()->addLogRecord($job['scheduled_job_id'], $status); + } + } + + } + + /** + * Check scheduled jobs and create related jobs + * @return array List of created Jobs + */ + protected function createJobsFromScheduledJobs() + { + $activeScheduledJobs = $this->getScheduledJobService()->getActiveJobs(); + + $createdJobs = array(); + foreach ($activeScheduledJobs as $scheduledJob) { + + $scheduling = $scheduledJob['scheduling']; + + $cronExpression = \Cron\CronExpression::factory($scheduling); + + try { + //$nextDate = $cronExpression->getNextRunDate()->format('Y-m-d H:i:s'); + $prevDate = $cronExpression->getPreviousRunDate()->format('Y-m-d H:i:s'); + } catch (\Exception $e) { + $GLOBALS['log']->error('ScheduledJob ['.$scheduledJob['id'].']: CronExpression - Impossible CRON expression ['.$scheduling.']'); + continue; + } + + if ($cronExpression->isDue()) { + $prevDate = date('Y-m-d H:i:00'); + } + + $existsJob = $this->getJobService()->getJobByScheduledJob($scheduledJob['id'], $prevDate); + + if (!isset($existsJob) || empty($existsJob)) { + //create a job + $data = array( + 'name' => $scheduledJob['name'], + 'status' => 'Pending', + 'scheduledJobId' => $scheduledJob['id'], + 'executeTime' => $prevDate, + 'method' => $scheduledJob['job'], + ); + $createdJobs[] = $this->getJobService()->createEntity($data); + } + } + + return $createdJobs; + } +} + diff --git a/application/Espo/Core/EntryPointManager.php b/application/Espo/Core/EntryPointManager.php index 5b5b593eda..fa24a67fa5 100644 --- a/application/Espo/Core/EntryPointManager.php +++ b/application/Espo/Core/EntryPointManager.php @@ -1,4 +1,4 @@ - 'application/Espo/Hooks', - 'modulePath' => 'application/Espo/Modules/{*}/Hooks', - 'customPath' => 'custom/Espo/Custom/Hooks', - ); - - - public function __construct(Container $container) - { - $this->container = $container; - $this->loadHooks(); - } - - 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->data = $this->getFileManager()->getContents($this->cacheFile); - return; - } - - $metadata = $this->container->get('metadata'); - - $this->data = $this->getHookData($this->paths['corePath']); - - foreach ($metadata->getModuleList() as $moduleName) { - $modulePath = str_replace('{*}', $moduleName, $this->paths['modulePath']); - $this->data = array_merge($this->data, $this->getHookData($modulePath)); - } - - $this->data = array_merge($this->data, $this->getHookData($this->paths['customPath'])); - - if ($this->getConfig()->get('useCache')) { - $this->getFileManager()->putContentsPHP($this->cacheFile, $this->data); - } - } - - public function process($scope, $hookName, $injection = null) - { - if ($scope != 'Common') { - $this->process('Common', $hookName, $injection); - } - - if (!empty($this->data[$scope])) { - if (!empty($this->data[$scope][$hookName])) { - foreach ($this->data[$scope][$hookName] as $className) { - if (empty($this->hooks[$className])) { - $this->hooks[$className] = $this->createHookByClassName($className); - } - $hook = $this->hooks[$className]; - $hook->$hookName($injection); - } - } - } - } - - public function createHookByClassName($className) - { - if (class_exists($className)) { - $hook = new $className(); - $dependencies = $hook->getDependencyList(); - foreach ($dependencies as $name) { - $hook->inject($name, $this->container->get($name)); - } - return $hook; - } - 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/Modules/Crm/Hooks') - * - * @return array - */ - protected function getHookData($hookDirs) - { - if (is_string($hookDirs)) { - $hookDirs = (array) $hookDirs; - } - - $hooks = array(); - - foreach ($hookDirs as $hookDir) { - - if (file_exists($hookDir)) { - $fileList = $this->getFileManager()->getFileList($hookDir, 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::getClassName($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; - } - -} - + 'application/Espo/Hooks', + 'modulePath' => 'application/Espo/Modules/{*}/Hooks', + 'customPath' => 'custom/Espo/Custom/Hooks', + ); + + + public function __construct(Container $container) + { + $this->container = $container; + $this->loadHooks(); + } + + 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->data = $this->getFileManager()->getContents($this->cacheFile); + return; + } + + $metadata = $this->container->get('metadata'); + + $this->data = $this->getHookData($this->paths['corePath']); + + foreach ($metadata->getModuleList() as $moduleName) { + $modulePath = str_replace('{*}', $moduleName, $this->paths['modulePath']); + $this->data = array_merge($this->data, $this->getHookData($modulePath)); + } + + $this->data = array_merge($this->data, $this->getHookData($this->paths['customPath'])); + + if ($this->getConfig()->get('useCache')) { + $this->getFileManager()->putContentsPHP($this->cacheFile, $this->data); + } + } + + public function process($scope, $hookName, $injection = null) + { + if ($scope != 'Common') { + $this->process('Common', $hookName, $injection); + } + + if (!empty($this->data[$scope])) { + if (!empty($this->data[$scope][$hookName])) { + foreach ($this->data[$scope][$hookName] as $className) { + if (empty($this->hooks[$className])) { + $this->hooks[$className] = $this->createHookByClassName($className); + } + $hook = $this->hooks[$className]; + $hook->$hookName($injection); + } + } + } + } + + public function createHookByClassName($className) + { + if (class_exists($className)) { + $hook = new $className(); + $dependencies = $hook->getDependencyList(); + foreach ($dependencies as $name) { + $hook->inject($name, $this->container->get($name)); + } + return $hook; + } + 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/Modules/Crm/Hooks') + * + * @return array + */ + protected function getHookData($hookDirs) + { + if (is_string($hookDirs)) { + $hookDirs = (array) $hookDirs; + } + + $hooks = array(); + + foreach ($hookDirs as $hookDir) { + + if (file_exists($hookDir)) { + $fileList = $this->getFileManager()->getFileList($hookDir, 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::getClassName($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 14bbc37353..e3627788be 100644 --- a/application/Espo/Core/Hooks/Base.php +++ b/application/Espo/Core/Hooks/Base.php @@ -1,95 +1,95 @@ -init(); - } - - protected function init() - { - } - - public function getDependencyList() - { - return $this->dependencies; - } - - protected function getInjection($name) - { - return $this->injections[$name]; - } - - public function inject($name, $object) - { - $this->injections[$name] = $object; - } - - protected function getEntityManager() - { - return $this->injections['entityManager']; - } - - protected function getUser() - { - return $this->injections['user']; - } - - protected function getAcl() - { - return $this->injections['acl']; - } - - protected function getConfig() - { - return $this->injections['config']; - } - - protected function getMetadata() - { - return $this->injections['metadata']; - } - - protected function getRepository() - { - return $this->getEntityManager()->getRepository($this->entityName); - } -} - +init(); + } + + protected function init() + { + } + + public function getDependencyList() + { + return $this->dependencies; + } + + protected function getInjection($name) + { + return $this->injections[$name]; + } + + public function inject($name, $object) + { + $this->injections[$name] = $object; + } + + protected function getEntityManager() + { + return $this->injections['entityManager']; + } + + protected function getUser() + { + return $this->injections['user']; + } + + protected function getAcl() + { + return $this->injections['acl']; + } + + protected function getConfig() + { + return $this->injections['config']; + } + + protected function getMetadata() + { + return $this->injections['metadata']; + } + + protected function getRepository() + { + return $this->getEntityManager()->getRepository($this->entityName); + } +} + diff --git a/application/Espo/Core/Interfaces/Injectable.php b/application/Espo/Core/Interfaces/Injectable.php index 0ee7d2c52d..9185de91d8 100644 --- a/application/Espo/Core/Interfaces/Injectable.php +++ b/application/Espo/Core/Interfaces/Injectable.php @@ -1,4 +1,4 @@ -container; - } - - protected function getEntityManager() - { - return $this->getContainer()->get('entityManager'); - } - - protected function getServiceFactory() - { - return $this->getContainer()->get('serviceFactory'); - } - - protected function getConfig() - { - return $this->getContainer()->get('config'); - } - - protected function getMetadata() - { - return $this->getContainer()->get('metadata'); - } - - protected function getUser() - { - return $this->getContainer()->get('user'); - } - - public function __construct(Container $container) - { - $this->container = $container; - } - - abstract public function run(); - -} - +container; + } + + protected function getEntityManager() + { + return $this->getContainer()->get('entityManager'); + } + + protected function getServiceFactory() + { + return $this->getContainer()->get('serviceFactory'); + } + + protected function getConfig() + { + return $this->getContainer()->get('config'); + } + + protected function getMetadata() + { + return $this->getContainer()->get('metadata'); + } + + protected function getUser() + { + return $this->getContainer()->get('user'); + } + + public function __construct(Container $container) + { + $this->container = $container; + } + + abstract public function run(); + +} + diff --git a/application/Espo/Core/Loaders/EntityManager.php b/application/Espo/Core/Loaders/EntityManager.php index 1984ddb2bb..dd625977cb 100644 --- a/application/Espo/Core/Loaders/EntityManager.php +++ b/application/Espo/Core/Loaders/EntityManager.php @@ -1,63 +1,63 @@ -container = $container; - } - - protected function getContainer() - { - return $this->container; - } - - public function load() - { - $config = $this->getContainer()->get('config'); - - $params = array( - 'host' => $config->get('database.host'), - 'dbname' => $config->get('database.dbname'), - 'user' => $config->get('database.user'), - 'password' => $config->get('database.password'), - 'metadata' => $this->getContainer()->get('metadata')->getOrmMetadata(), - 'repositoryFactoryClassName' => '\\Espo\\Core\\ORM\\RepositoryFactory', - ); - - $entityManager = new \Espo\Core\ORM\EntityManager($params); - $entityManager->setEspoMetadata($this->getContainer()->get('metadata')); - $entityManager->setHookManager($this->getContainer()->get('hookManager')); - $entityManager->setContainer($this->getContainer()); - - return $entityManager; - } -} - +container = $container; + } + + protected function getContainer() + { + return $this->container; + } + + public function load() + { + $config = $this->getContainer()->get('config'); + + $params = array( + 'host' => $config->get('database.host'), + 'dbname' => $config->get('database.dbname'), + 'user' => $config->get('database.user'), + 'password' => $config->get('database.password'), + 'metadata' => $this->getContainer()->get('metadata')->getOrmMetadata(), + 'repositoryFactoryClassName' => '\\Espo\\Core\\ORM\\RepositoryFactory', + ); + + $entityManager = new \Espo\Core\ORM\EntityManager($params); + $entityManager->setEspoMetadata($this->getContainer()->get('metadata')); + $entityManager->setHookManager($this->getContainer()->get('hookManager')); + $entityManager->setContainer($this->getContainer()); + + return $entityManager; + } +} + diff --git a/application/Espo/Core/Mail/Sender.php b/application/Espo/Core/Mail/Sender.php index dd09952f21..af180a20b8 100644 --- a/application/Espo/Core/Mail/Sender.php +++ b/application/Espo/Core/Mail/Sender.php @@ -1,234 +1,234 @@ -config = $config; - $this->useGlobal(); - } - - public function resetParams() - { - $this->params = array(); - return $this; - } - - public function setParams(array $params = array()) - { - $this->params = array_merge($this->params, $params); - return $this; - } - - public function useSmtp(array $params = array()) - { - $this->isGlobal = false; - $this->params = $params; - - $this->transport = new SmtpTransport(); - - $opts = array( - 'name' => 'admin', - 'host' => $params['server'], - 'port' => $params['port'], - 'connection_config' => array() - ); - if ($params['auth']) { - $opts['connection_class'] = 'login'; - $opts['connection_config']['username'] = $params['username']; - $opts['connection_config']['password'] = $params['password']; - } - if ($params['security']) { - $opts['connection_config']['ssl'] = strtolower($params['security']); - } - - $options = new SmtpOptions($opts); - $this->transport->setOptions($options); - - return $this; - } - - public function useGlobal() - { - $this->params = array(); - if ($this->isGlobal) { - return $this; - } - - $this->transport = new SmtpTransport(); - - $config = $this->config; - - $opts = array( - 'name' => 'admin', - 'host' => $config->get('smtpServer'), - 'port' => $config->get('smtpPort'), - 'connection_config' => array() - ); - if ($config->get('smtpAuth')) { - $opts['connection_class'] = 'login'; - $opts['connection_config']['username'] = $config->get('smtpUsername'); - $opts['connection_config']['password'] = $config->get('smtpPassword'); - } - if ($config->get('smtpSecurity')) { - $opts['connection_config']['ssl'] = strtolower($config->get('smtpSecurity')); - } - - $options = new SmtpOptions($opts); - $this->transport->setOptions($options); - - $this->isGlobal = true; - - return $this; - } - - public function send(Email $email) - { - $message = new Message(); - - $config = $this->config; - - if ($email->get('from')) { - $fromName = null; - if (!empty($this->params['fromName'])) { - $fromName = $this->params['fromName']; - } else { - $fromName = $config->get('outboundEmailFromName'); - } - $message->addFrom(trim($email->get('from')), $fromName); - } else { - if (!empty($this->params['fromAddress'])) { - $fromAddress = $this->params['fromAddress']; - } else { - if (!$config->get('outboundEmailFromAddress')) { - throw new Error('outboundEmailFromAddress is not specified in config.'); - } - $fromAddress = $config->get('outboundEmailFromAddress'); - } - - if (!empty($this->params['fromName'])) { - $fromName = $this->params['fromName']; - } else { - $fromName = $config->get('outboundEmailFromName'); - } - - $message->addFrom($fromAddress, $fromName); - } - - $value = $email->get('to'); - if ($value) { - $arr = explode(';', $value); - if (is_array($arr)) { - foreach ($arr as $address) { - $message->addTo(trim($address)); - } - } - } - - $value = $email->get('cc'); - if ($value) { - $arr = explode(';', $value); - if (is_array($arr)) { - foreach ($arr as $address) { - $message->addCC(trim($address)); - } - } - } - - $value = $email->get('bcc'); - if ($value) { - $arr = explode(';', $value); - if (is_array($arr)) { - foreach ($arr as $address) { - $message->addBCC(trim($address)); - } - } - } - - $message->setSubject($email->get('name')); - - $body = new MimeMessage; - $parts = array(); - - $bodyPart = new MimePart($email->get('body')); - - if ($email->get('isHtml')) { - $bodyPart->type = 'text/html'; - } else { - $bodyPart->type = 'text/plain'; - } - - $parts[] = $bodyPart; - - $aCollection = $email->get('attachments'); - if (!empty($aCollection)) { - foreach ($aCollection as $a) { - $fileName = 'data/upload/' . $a->id; - $attachment = new MimePart(file_get_contents($fileName)); - $attachment->disposition = Mime::DISPOSITION_ATTACHMENT; - $attachment->encoding = Mime::ENCODING_BASE64; - $attachment->filename = $a->get('name'); - if ($a->get('type')) { - $attachment->type = $a->get('type'); - } - $parts[] = $attachment; - } - } - - $body->setParts($parts); - $message->setBody($body); - - try { - $this->transport->send($message); - $email->set('status', 'Sent'); - $email->set('dateSent', date("Y-m-d H:i:s")); - } catch (\Exception $e) { - throw new Error($e->getMessage(), 500); - } - - $this->useGlobal(); - } -} - +config = $config; + $this->useGlobal(); + } + + public function resetParams() + { + $this->params = array(); + return $this; + } + + public function setParams(array $params = array()) + { + $this->params = array_merge($this->params, $params); + return $this; + } + + public function useSmtp(array $params = array()) + { + $this->isGlobal = false; + $this->params = $params; + + $this->transport = new SmtpTransport(); + + $opts = array( + 'name' => 'admin', + 'host' => $params['server'], + 'port' => $params['port'], + 'connection_config' => array() + ); + if ($params['auth']) { + $opts['connection_class'] = 'login'; + $opts['connection_config']['username'] = $params['username']; + $opts['connection_config']['password'] = $params['password']; + } + if ($params['security']) { + $opts['connection_config']['ssl'] = strtolower($params['security']); + } + + $options = new SmtpOptions($opts); + $this->transport->setOptions($options); + + return $this; + } + + public function useGlobal() + { + $this->params = array(); + if ($this->isGlobal) { + return $this; + } + + $this->transport = new SmtpTransport(); + + $config = $this->config; + + $opts = array( + 'name' => 'admin', + 'host' => $config->get('smtpServer'), + 'port' => $config->get('smtpPort'), + 'connection_config' => array() + ); + if ($config->get('smtpAuth')) { + $opts['connection_class'] = 'login'; + $opts['connection_config']['username'] = $config->get('smtpUsername'); + $opts['connection_config']['password'] = $config->get('smtpPassword'); + } + if ($config->get('smtpSecurity')) { + $opts['connection_config']['ssl'] = strtolower($config->get('smtpSecurity')); + } + + $options = new SmtpOptions($opts); + $this->transport->setOptions($options); + + $this->isGlobal = true; + + return $this; + } + + public function send(Email $email) + { + $message = new Message(); + + $config = $this->config; + + if ($email->get('from')) { + $fromName = null; + if (!empty($this->params['fromName'])) { + $fromName = $this->params['fromName']; + } else { + $fromName = $config->get('outboundEmailFromName'); + } + $message->addFrom(trim($email->get('from')), $fromName); + } else { + if (!empty($this->params['fromAddress'])) { + $fromAddress = $this->params['fromAddress']; + } else { + if (!$config->get('outboundEmailFromAddress')) { + throw new Error('outboundEmailFromAddress is not specified in config.'); + } + $fromAddress = $config->get('outboundEmailFromAddress'); + } + + if (!empty($this->params['fromName'])) { + $fromName = $this->params['fromName']; + } else { + $fromName = $config->get('outboundEmailFromName'); + } + + $message->addFrom($fromAddress, $fromName); + } + + $value = $email->get('to'); + if ($value) { + $arr = explode(';', $value); + if (is_array($arr)) { + foreach ($arr as $address) { + $message->addTo(trim($address)); + } + } + } + + $value = $email->get('cc'); + if ($value) { + $arr = explode(';', $value); + if (is_array($arr)) { + foreach ($arr as $address) { + $message->addCC(trim($address)); + } + } + } + + $value = $email->get('bcc'); + if ($value) { + $arr = explode(';', $value); + if (is_array($arr)) { + foreach ($arr as $address) { + $message->addBCC(trim($address)); + } + } + } + + $message->setSubject($email->get('name')); + + $body = new MimeMessage; + $parts = array(); + + $bodyPart = new MimePart($email->get('body')); + + if ($email->get('isHtml')) { + $bodyPart->type = 'text/html'; + } else { + $bodyPart->type = 'text/plain'; + } + + $parts[] = $bodyPart; + + $aCollection = $email->get('attachments'); + if (!empty($aCollection)) { + foreach ($aCollection as $a) { + $fileName = 'data/upload/' . $a->id; + $attachment = new MimePart(file_get_contents($fileName)); + $attachment->disposition = Mime::DISPOSITION_ATTACHMENT; + $attachment->encoding = Mime::ENCODING_BASE64; + $attachment->filename = $a->get('name'); + if ($a->get('type')) { + $attachment->type = $a->get('type'); + } + $parts[] = $attachment; + } + } + + $body->setParts($parts); + $message->setBody($body); + + try { + $this->transport->send($message); + $email->set('status', 'Sent'); + $email->set('dateSent', date("Y-m-d H:i:s")); + } catch (\Exception $e) { + throw new Error($e->getMessage(), 500); + } + + $this->useGlobal(); + } +} + diff --git a/application/Espo/Core/ORM/DB/MysqlMapper.php b/application/Espo/Core/ORM/DB/MysqlMapper.php index 62dabcfe52..78ef574046 100644 --- a/application/Espo/Core/ORM/DB/MysqlMapper.php +++ b/application/Espo/Core/ORM/DB/MysqlMapper.php @@ -1,4 +1,4 @@ -injections[$name] = $object; - } - - protected function getInjection($name) - { - return $this->injections[$name]; - } - - public function getDependencyList() - { - return $this->dependencies; - } - - protected function getMetadata() - { - return $this->getInjection('metadata'); - } - - protected function handleSelectParams(&$params, $entityName = false) - { - $this->handleEmailAddressParams($params, $entityName); - } - - protected function handleEmailAddressParams(&$params, $entityName = false) - { - if (empty($entityName)) { - $entityName = $this->entityName; - } - - $defs = $this->getEntityManager()->getMetadata()->get($entityName); - if (!empty($defs['relations']) && array_key_exists('emailAddresses', $defs['relations'])) { - if (empty($params['leftJoins'])) { - $params['leftJoins'] = array(); - } - if (empty($params['whereClause'])) { - $params['whereClause'] = array(); - } - if (empty($params['joinConditions'])) { - $params['joinConditions'] = array(); - } - $params['distinct'] = true; - $params['leftJoins'] = array('emailAddresses'); - $params['joinConditions'] = array( - 'emailAddresses' => array( - 'primary' => 1 - ) - ); - } - } - - protected function beforeRemove(Entity $entity) - { - parent::beforeRemove($entity); - $this->getEntityManager()->getHookManager()->process($this->entityName, 'beforeRemove', $entity); - } - - protected function afterRemove(Entity $entity) - { - parent::afterRemove($entity); - $this->getEntityManager()->getHookManager()->process($this->entityName, 'afterRemove', $entity); - } - - public function remove(Entity $entity) - { - $this->getEntityManager()->getHookManager()->process($this->entityName, 'beforeRemove', $entity); - - $result = parent::remove($entity); - if ($result) { - $this->getEntityManager()->getHookManager()->process($this->entityName, 'afterRemove', $entity); - } - return $result; - } - - protected function beforeSave(Entity $entity) - { - parent::beforeSave($entity); - $this->getEntityManager()->getHookManager()->process($this->entityName, 'beforeSave', $entity); - } - - protected function afterSave(Entity $entity) - { - parent::afterSave($entity); - $this->getEntityManager()->getHookManager()->process($this->entityName, 'afterSave', $entity); - } - - public function save(Entity $entity) - { - $nowString = date('Y-m-d H:i:s', time()); - $restoreData = array(); - - if ($entity->isNew()) { - if (!$entity->has('id')) { - $entity->set('id', uniqid()); - } - - if ($entity->hasField('createdAt')) { - $entity->set('createdAt', $nowString); - } - if ($entity->hasField('createdById')) { - $entity->set('createdById', $this->entityManager->getUser()->id); - } - - if ($entity->has('modifiedById')) { - $restoreData['modifiedById'] = $entity->get('modifiedById'); - } - if ($entity->has('modifiedAt')) { - $restoreData['modifiedAt'] = $entity->get('modifiedAt'); - } - $entity->clear('modifiedById'); - $entity->clear('modifiedAt'); - } else { - if ($entity->hasField('modifiedAt')) { - $entity->set('modifiedAt', $nowString); - } - if ($entity->hasField('modifiedById')) { - $entity->set('modifiedById', $this->entityManager->getUser()->id); - } - - if ($entity->has('createdById')) { - $restoreData['createdById'] = $entity->get('createdById'); - } - if ($entity->has('createdAt')) { - $restoreData['createdAt'] = $entity->get('createdAt'); - } - $entity->clear('createdById'); - $entity->clear('createdAt'); - } - $result = parent::save($entity); - - $entity->set($restoreData); - - $this->handleEmailAddressSave($entity); - $this->handleSpecifiedRelations($entity); - - return $result; - } - - protected function handleEmailAddressSave(Entity $entity) - { - if ($entity->hasRelation('emailAddresses') && $entity->hasField('emailAddress')) { - $email = $entity->get('emailAddress'); - $pdo = $this->getPDO(); - - $emailAddressRepository = $this->getEntityManager()->getRepository('EmailAddress'); - - if (!empty($email)) { - if ($email != $entity->getFetched('emailAddress')) { - - $emailAddressNew = $emailAddressRepository->where(array('lower' => strtolower($email)))->findOne(); - $isNewEmailAddress = false; - if (!$emailAddressNew) { - $emailAddressNew = $emailAddressRepository->get(); - $emailAddressNew->set('name', $email); - $emailAddressRepository->save($emailAddressNew); - $isNewEmailAddress = true; - } - - $emailOld = $entity->getFetched('emailAddress'); - if (!empty($emailOld)) { - $emailAddressOld = $emailAddressRepository->where(array('lower' => strtolower($emailOld)))->findOne(); - $this->unrelate($entity, 'emailAddresses', $emailAddressOld); - } - $this->relate($entity, 'emailAddresses', $emailAddressNew); - - $query = " - UPDATE entity_email_address - SET `primary` = 1 - WHERE - entity_id = ".$pdo->quote($entity->id)." AND - entity_type = ".$pdo->quote($this->entityName)." AND - email_address_id = ".$pdo->quote($emailAddressNew->id)." - "; - $sth = $pdo->prepare($query); - $sth->execute(); - } - } else { - $emailOld = $entity->getFetched('emailAddress'); - if (!empty($emailOld)) { - $emailAddressOld = $emailAddressRepository->where(array('lower' => strtolower($emailOld)))->findOne(); - $this->unrelate($entity, 'emailAddresses', $emailAddressOld); - } - } - } - } - - protected function handleSpecifiedRelations(Entity $entity) - { - $relationTypes = array($entity::HAS_MANY, $entity::MANY_MANY, $entity::HAS_CHILDREN); - foreach ($entity->getRelations() as $name => $defs) { - if (in_array($defs['type'], $relationTypes)) { - $fieldName = $name . 'Ids'; - if ($entity->has($fieldName)) { - $specifiedIds = $entity->get($fieldName); - if (is_array($specifiedIds)) { - $toRemoveIds = array(); - $existingIds = array(); - foreach ($entity->get($name) as $foreignEntity) { - $existingIds[] = $foreignEntity->id; - } - foreach ($existingIds as $id) { - if (!in_array($id, $specifiedIds)) { - $toRemoveIds[] = $id; - } - } - foreach ($specifiedIds as $id) { - if (!in_array($id, $existingIds)) { - $this->relate($entity, $name, $id); - } - } - foreach ($toRemoveIds as $id) { - $this->unrelate($entity, $name, $id); - } - } - } - } - } - } -} - +injections[$name] = $object; + } + + protected function getInjection($name) + { + return $this->injections[$name]; + } + + public function getDependencyList() + { + return $this->dependencies; + } + + protected function getMetadata() + { + return $this->getInjection('metadata'); + } + + protected function handleSelectParams(&$params, $entityName = false) + { + $this->handleEmailAddressParams($params, $entityName); + } + + protected function handleEmailAddressParams(&$params, $entityName = false) + { + if (empty($entityName)) { + $entityName = $this->entityName; + } + + $defs = $this->getEntityManager()->getMetadata()->get($entityName); + if (!empty($defs['relations']) && array_key_exists('emailAddresses', $defs['relations'])) { + if (empty($params['leftJoins'])) { + $params['leftJoins'] = array(); + } + if (empty($params['whereClause'])) { + $params['whereClause'] = array(); + } + if (empty($params['joinConditions'])) { + $params['joinConditions'] = array(); + } + $params['distinct'] = true; + $params['leftJoins'] = array('emailAddresses'); + $params['joinConditions'] = array( + 'emailAddresses' => array( + 'primary' => 1 + ) + ); + } + } + + protected function beforeRemove(Entity $entity) + { + parent::beforeRemove($entity); + $this->getEntityManager()->getHookManager()->process($this->entityName, 'beforeRemove', $entity); + } + + protected function afterRemove(Entity $entity) + { + parent::afterRemove($entity); + $this->getEntityManager()->getHookManager()->process($this->entityName, 'afterRemove', $entity); + } + + public function remove(Entity $entity) + { + $this->getEntityManager()->getHookManager()->process($this->entityName, 'beforeRemove', $entity); + + $result = parent::remove($entity); + if ($result) { + $this->getEntityManager()->getHookManager()->process($this->entityName, 'afterRemove', $entity); + } + return $result; + } + + protected function beforeSave(Entity $entity) + { + parent::beforeSave($entity); + $this->getEntityManager()->getHookManager()->process($this->entityName, 'beforeSave', $entity); + } + + protected function afterSave(Entity $entity) + { + parent::afterSave($entity); + $this->getEntityManager()->getHookManager()->process($this->entityName, 'afterSave', $entity); + } + + public function save(Entity $entity) + { + $nowString = date('Y-m-d H:i:s', time()); + $restoreData = array(); + + if ($entity->isNew()) { + if (!$entity->has('id')) { + $entity->set('id', uniqid()); + } + + if ($entity->hasField('createdAt')) { + $entity->set('createdAt', $nowString); + } + if ($entity->hasField('createdById')) { + $entity->set('createdById', $this->entityManager->getUser()->id); + } + + if ($entity->has('modifiedById')) { + $restoreData['modifiedById'] = $entity->get('modifiedById'); + } + if ($entity->has('modifiedAt')) { + $restoreData['modifiedAt'] = $entity->get('modifiedAt'); + } + $entity->clear('modifiedById'); + $entity->clear('modifiedAt'); + } else { + if ($entity->hasField('modifiedAt')) { + $entity->set('modifiedAt', $nowString); + } + if ($entity->hasField('modifiedById')) { + $entity->set('modifiedById', $this->entityManager->getUser()->id); + } + + if ($entity->has('createdById')) { + $restoreData['createdById'] = $entity->get('createdById'); + } + if ($entity->has('createdAt')) { + $restoreData['createdAt'] = $entity->get('createdAt'); + } + $entity->clear('createdById'); + $entity->clear('createdAt'); + } + $result = parent::save($entity); + + $entity->set($restoreData); + + $this->handleEmailAddressSave($entity); + $this->handleSpecifiedRelations($entity); + + return $result; + } + + protected function handleEmailAddressSave(Entity $entity) + { + if ($entity->hasRelation('emailAddresses') && $entity->hasField('emailAddress')) { + $email = $entity->get('emailAddress'); + $pdo = $this->getPDO(); + + $emailAddressRepository = $this->getEntityManager()->getRepository('EmailAddress'); + + if (!empty($email)) { + if ($email != $entity->getFetched('emailAddress')) { + + $emailAddressNew = $emailAddressRepository->where(array('lower' => strtolower($email)))->findOne(); + $isNewEmailAddress = false; + if (!$emailAddressNew) { + $emailAddressNew = $emailAddressRepository->get(); + $emailAddressNew->set('name', $email); + $emailAddressRepository->save($emailAddressNew); + $isNewEmailAddress = true; + } + + $emailOld = $entity->getFetched('emailAddress'); + if (!empty($emailOld)) { + $emailAddressOld = $emailAddressRepository->where(array('lower' => strtolower($emailOld)))->findOne(); + $this->unrelate($entity, 'emailAddresses', $emailAddressOld); + } + $this->relate($entity, 'emailAddresses', $emailAddressNew); + + $query = " + UPDATE entity_email_address + SET `primary` = 1 + WHERE + entity_id = ".$pdo->quote($entity->id)." AND + entity_type = ".$pdo->quote($this->entityName)." AND + email_address_id = ".$pdo->quote($emailAddressNew->id)." + "; + $sth = $pdo->prepare($query); + $sth->execute(); + } + } else { + $emailOld = $entity->getFetched('emailAddress'); + if (!empty($emailOld)) { + $emailAddressOld = $emailAddressRepository->where(array('lower' => strtolower($emailOld)))->findOne(); + $this->unrelate($entity, 'emailAddresses', $emailAddressOld); + } + } + } + } + + protected function handleSpecifiedRelations(Entity $entity) + { + $relationTypes = array($entity::HAS_MANY, $entity::MANY_MANY, $entity::HAS_CHILDREN); + foreach ($entity->getRelations() as $name => $defs) { + if (in_array($defs['type'], $relationTypes)) { + $fieldName = $name . 'Ids'; + if ($entity->has($fieldName)) { + $specifiedIds = $entity->get($fieldName); + if (is_array($specifiedIds)) { + $toRemoveIds = array(); + $existingIds = array(); + foreach ($entity->get($name) as $foreignEntity) { + $existingIds[] = $foreignEntity->id; + } + foreach ($existingIds as $id) { + if (!in_array($id, $specifiedIds)) { + $toRemoveIds[] = $id; + } + } + foreach ($specifiedIds as $id) { + if (!in_array($id, $existingIds)) { + $this->relate($entity, $name, $id); + } + } + foreach ($toRemoveIds as $id) { + $this->unrelate($entity, $name, $id); + } + } + } + } + } + } +} + diff --git a/application/Espo/Core/ORM/Repository.php b/application/Espo/Core/ORM/Repository.php index d6bcd122d2..a6386eae43 100644 --- a/application/Espo/Core/ORM/Repository.php +++ b/application/Espo/Core/ORM/Repository.php @@ -1,4 +1,4 @@ -getDependencyList(); - foreach ($dependencies as $name) { - $repository->inject($name, $this->entityManager->getContainer()->get($name)); - } - return $repository; - } -} - + ************************************************************************/ + +namespace Espo\Core\ORM; + +class RepositoryFactory extends \Espo\ORM\RepositoryFactory +{ + protected $defaultRepositoryClassName = '\\Espo\\Core\\ORM\\Repositories\\RDB'; + + public function create($name) + { + $repository = parent::create($name); + + $dependencies = $repository->getDependencyList(); + foreach ($dependencies as $name) { + $repository->inject($name, $this->entityManager->getContainer()->get($name)); + } + return $repository; + } +} + diff --git a/application/Espo/Core/SelectManagerFactory.php b/application/Espo/Core/SelectManagerFactory.php index 577f3460b0..dd035fe8b8 100644 --- a/application/Espo/Core/SelectManagerFactory.php +++ b/application/Espo/Core/SelectManagerFactory.php @@ -1,4 +1,4 @@ -entityManager = $entityManager; - $this->user = $user; - $this->acl = $acl; - $this->metadata = $metadata; - } - - public function setEntityName($entityName) - { - $this->entityName = $entityName; - } - - protected function limit($params, &$result) - { - if (isset($params['offset']) && !is_null($params['offset'])) { - $result['offset'] = $params['offset']; - } - if (isset($params['maxSize']) && !is_null($params['maxSize'])) { - $result['limit'] = $params['maxSize']; - } - } - - protected function order($params, &$result) - { - if (!empty($params['sortBy'])) { - $result['orderBy'] = $params['sortBy']; - $type = $this->metadata->get("entityDefs.{$this->entityName}.fields." . $result['orderBy'] . ".type"); - if ($type == 'link') { - $result['orderBy'] .= 'Name'; - } else if ($type == 'linkParent') { - $result['orderBy'] .= 'Type'; - } - } - if (isset($params['asc'])) { - if ($params['asc']) { - $result['order'] = 'ASC'; - } else { - $result['order'] = 'DESC'; - } - } - } - - protected function where($params, &$result) - { - if (!empty($params['where']) && is_array($params['where'])) { - $where = array(); - - foreach ($params['where'] as $item) { - if ($item['type'] == 'boolFilters' && is_array($item['value'])) { - foreach ($item['value'] as $filter) { - $p = $this->getBoolFilterWhere($filter); - if (!empty($p)) { - $params['where'][] = $p; - } - } - } - } - - $linkedWith = array(); - $ignoreList = array('linkedWith', 'boolFilters'); - foreach ($params['where'] as $item) { - if (!in_array($item['type'], $ignoreList)) { - $part = $this->getWherePart($item); - if (!empty($part)) { - $where[] = $part; - } - } else { - if ($item['type'] == 'linkedWith') { - $linkedWith[$item['field']] = $item['value']; - } - } - } - - if (!empty($linkedWith)) { - $joins = array(); - - $part = array(); - foreach ($linkedWith as $link => $ids) { - $joins[] = $link; - $defs = $this->entityManager->getMetadata()->get($this->entityName); - - $entityName = $defs['relations'][$link]['entity']; - if ($entityName) { - $part[$entityName . '.id'] = $ids; - } - } - - if (!empty($part)) { - $where[] = $part; - } - $result['joins'] = $joins; - $result['distinct'] = true; - - } - - $result['whereClause'] = $where; - } - } - - protected function q($params, &$result) - { - if (!empty($params['q'])) { - if (empty($result['whereClause'])) { - $result['whereClause'] = array(); - } - $result['whereClause']['name*'] = $params['q'] . '%'; - } - } - - protected function access(&$result) - { - if ($this->acl->checkReadOnlyOwn($this->entityName)) { - - if (!array_key_exists('whereClause', $result)) { - $result['whereClause'] = array(); - } - $result['whereClause']['assignedUserId'] = $this->user->id; - } - if ($this->acl->checkReadOnlyTeam($this->entityName)) { - if (!array_key_exists('whereClause', $result)) { - $result['whereClause'] = array(); - } - if (!array_key_exists('joins', $result)) { - $result['joins'] = array(); - } - if (!in_array('teams', $result['joins'])) { - $result['joins'][] = 'teams'; - } - - $result['whereClause']['Team.id'] = $this->user->get('teamsIds'); - } - } - - public function getAclParams() - { - $result = array(); - $this->access($result); - return $result; - } - - public function getSelectParams(array $params, $withAcl = false) - { - $result = array(); - - $this->order($params, $result); - $this->limit($params, $result); - $this->where($params, $result); - $this->q($params, $result); - - if ($withAcl) { - $this->access($result); - } - - return $result; - } - - protected function getWherePart($item) - { - $part = array(); - - if (!empty($item['type'])) { - switch ($item['type']) { - case 'or': - case 'and': - if (is_array($item['value'])) { - $arr = array(); - foreach ($item['value'] as $i) { - $a = $this->getWherePart($i); - foreach ($a as $left => $right) { - if (!empty($right)) { - $arr[$left] = $right; - } - } - } - $part[strtoupper($item['type'])] = $arr; - } - break; - case 'like': - $part[$item['field'] . '*'] = $item['value']; - break; - case 'equals': - case 'on': - $part[$item['field'] . '='] = $item['value']; - break; - case 'notEquals': - case 'notOn': - $part[$item['field'] . '!='] = $item['value']; - break; - case 'greaterThan': - case 'after': - $part[$item['field'] . '>'] = $item['value']; - break; - case 'lessThan': - case 'before': - $part[$item['field'] . '<'] = $item['value']; - break; - case 'greaterThanOrEquals': - $part[$item['field'] . '>='] = $item['value']; - break; - case 'lessThanOrEquals': - $part[$item['field'] . '<'] = $item['value']; - break; - case 'in': - $part[$item['field'] . '='] = $item['value']; - break; - case 'notIn': - $part[$item['field'] . '!='] = $item['value']; - break; - case 'isBetween': - if (is_array($item['value'])) { - $part['AND'] = array( - $item['field'] . '>=' => $item['value'][0], - $item['field'] . '<=' => $item['value'][1], - ); - } - break; - } - } - - return $part; - } - - protected function getBoolFilterWhere($filterName, $entityName) - { - $method = 'getBoolFilterWhere' . ucfirst($filterName); - if (method_exists($this, $method)) { - return $this->$method(); - } - } - - protected function getBoolFilterWhereOnlyMy() - { - return array( - 'type' => 'equals', - 'field' => 'assignedUserId', - 'value' => $this->user->id, - ); - } -} - +entityManager = $entityManager; + $this->user = $user; + $this->acl = $acl; + $this->metadata = $metadata; + } + + public function setEntityName($entityName) + { + $this->entityName = $entityName; + } + + protected function limit($params, &$result) + { + if (isset($params['offset']) && !is_null($params['offset'])) { + $result['offset'] = $params['offset']; + } + if (isset($params['maxSize']) && !is_null($params['maxSize'])) { + $result['limit'] = $params['maxSize']; + } + } + + protected function order($params, &$result) + { + if (!empty($params['sortBy'])) { + $result['orderBy'] = $params['sortBy']; + $type = $this->metadata->get("entityDefs.{$this->entityName}.fields." . $result['orderBy'] . ".type"); + if ($type == 'link') { + $result['orderBy'] .= 'Name'; + } else if ($type == 'linkParent') { + $result['orderBy'] .= 'Type'; + } + } + if (isset($params['asc'])) { + if ($params['asc']) { + $result['order'] = 'ASC'; + } else { + $result['order'] = 'DESC'; + } + } + } + + protected function where($params, &$result) + { + if (!empty($params['where']) && is_array($params['where'])) { + $where = array(); + + foreach ($params['where'] as $item) { + if ($item['type'] == 'boolFilters' && is_array($item['value'])) { + foreach ($item['value'] as $filter) { + $p = $this->getBoolFilterWhere($filter); + if (!empty($p)) { + $params['where'][] = $p; + } + } + } + } + + $linkedWith = array(); + $ignoreList = array('linkedWith', 'boolFilters'); + foreach ($params['where'] as $item) { + if (!in_array($item['type'], $ignoreList)) { + $part = $this->getWherePart($item); + if (!empty($part)) { + $where[] = $part; + } + } else { + if ($item['type'] == 'linkedWith') { + $linkedWith[$item['field']] = $item['value']; + } + } + } + + if (!empty($linkedWith)) { + $joins = array(); + + $part = array(); + foreach ($linkedWith as $link => $ids) { + $joins[] = $link; + $defs = $this->entityManager->getMetadata()->get($this->entityName); + + $entityName = $defs['relations'][$link]['entity']; + if ($entityName) { + $part[$entityName . '.id'] = $ids; + } + } + + if (!empty($part)) { + $where[] = $part; + } + $result['joins'] = $joins; + $result['distinct'] = true; + + } + + $result['whereClause'] = $where; + } + } + + protected function q($params, &$result) + { + if (!empty($params['q'])) { + if (empty($result['whereClause'])) { + $result['whereClause'] = array(); + } + $result['whereClause']['name*'] = $params['q'] . '%'; + } + } + + protected function access(&$result) + { + if ($this->acl->checkReadOnlyOwn($this->entityName)) { + + if (!array_key_exists('whereClause', $result)) { + $result['whereClause'] = array(); + } + $result['whereClause']['assignedUserId'] = $this->user->id; + } + if ($this->acl->checkReadOnlyTeam($this->entityName)) { + if (!array_key_exists('whereClause', $result)) { + $result['whereClause'] = array(); + } + if (!array_key_exists('joins', $result)) { + $result['joins'] = array(); + } + if (!in_array('teams', $result['joins'])) { + $result['joins'][] = 'teams'; + } + + $result['whereClause']['Team.id'] = $this->user->get('teamsIds'); + } + } + + public function getAclParams() + { + $result = array(); + $this->access($result); + return $result; + } + + public function getSelectParams(array $params, $withAcl = false) + { + $result = array(); + + $this->order($params, $result); + $this->limit($params, $result); + $this->where($params, $result); + $this->q($params, $result); + + if ($withAcl) { + $this->access($result); + } + + return $result; + } + + protected function getWherePart($item) + { + $part = array(); + + if (!empty($item['type'])) { + switch ($item['type']) { + case 'or': + case 'and': + if (is_array($item['value'])) { + $arr = array(); + foreach ($item['value'] as $i) { + $a = $this->getWherePart($i); + foreach ($a as $left => $right) { + if (!empty($right)) { + $arr[$left] = $right; + } + } + } + $part[strtoupper($item['type'])] = $arr; + } + break; + case 'like': + $part[$item['field'] . '*'] = $item['value']; + break; + case 'equals': + case 'on': + $part[$item['field'] . '='] = $item['value']; + break; + case 'notEquals': + case 'notOn': + $part[$item['field'] . '!='] = $item['value']; + break; + case 'greaterThan': + case 'after': + $part[$item['field'] . '>'] = $item['value']; + break; + case 'lessThan': + case 'before': + $part[$item['field'] . '<'] = $item['value']; + break; + case 'greaterThanOrEquals': + $part[$item['field'] . '>='] = $item['value']; + break; + case 'lessThanOrEquals': + $part[$item['field'] . '<'] = $item['value']; + break; + case 'in': + $part[$item['field'] . '='] = $item['value']; + break; + case 'notIn': + $part[$item['field'] . '!='] = $item['value']; + break; + case 'isBetween': + if (is_array($item['value'])) { + $part['AND'] = array( + $item['field'] . '>=' => $item['value'][0], + $item['field'] . '<=' => $item['value'][1], + ); + } + break; + } + } + + return $part; + } + + protected function getBoolFilterWhere($filterName, $entityName) + { + $method = 'getBoolFilterWhere' . ucfirst($filterName); + if (method_exists($this, $method)) { + return $this->$method(); + } + } + + protected function getBoolFilterWhereOnlyMy() + { + return array( + 'type' => 'equals', + 'field' => 'assignedUserId', + 'value' => $this->user->id, + ); + } +} + diff --git a/application/Espo/Core/ServiceFactory.php b/application/Espo/Core/ServiceFactory.php index 3759d760a5..b55d69b22b 100644 --- a/application/Espo/Core/ServiceFactory.php +++ b/application/Espo/Core/ServiceFactory.php @@ -1,154 +1,154 @@ - 'application/Espo/Services', - 'modulePath' => 'application/Espo/Modules/{*}/Services', - 'customPath' => 'custom/Espo/Custom/Services', - ); - - protected $data; - - public function __construct(Container $container) - { - $this->container = $container; - } - - protected function init() - { - $config = $this->getContainer()->get('config'); - - if (file_exists($this->cacheFile) && $config->get('useCache')) { - $this->data = $this->getFileManager()->getContents($this->cacheFile); - } else { - $this->data = $this->getClassNameHash($this->paths['corePath']); - - foreach ($this->getContainer()->get('metadata')->getModuleList() as $moduleName) { - $path = str_replace('{*}', $moduleName, $this->paths['modulePath']); - $this->data = array_merge($this->data, $this->getClassNameHash($path)); - } - - $this->data = array_merge($this->data, $this->getClassNameHash($this->paths['customPath'])); - - if ($config->get('useCache')) { - $result = $this->getFileManager()->putContentsPHP($this->cacheFile, $this->data); - if ($result == false) { - throw new \Espo\Core\Exceptions\Error(); - } - } - } - } - - protected function getFileManager() - { - return $this->container->get('fileManager'); - } - - protected function getContainer() - { - return $this->container; - } - - protected function getClassName($name) - { - $name = Util::normilizeClassName($name); - - if (!isset($this->data)) { - $this->init(); - } - - $name = ucfirst($name); - if (isset($this->data[$name])) { - return $this->data[$name]; - } - - return false; - } - - public function checkExists($name) { - $className = $this->getClassName($name); - if (!empty($className)) { - return true; - } - } - - public function create($name) - { - $className = $this->getClassName($name); - if (empty($className)) { - throw new Error(); - } - return $this->createByClassName($className); - } - - protected function createByClassName($className) - { - if (class_exists($className)) { - $service = new $className(); - $dependencies = $service->getDependencyList(); - foreach ($dependencies as $name) { - $service->inject($name, $this->container->get($name)); - } - return $service; - } - throw new Error("Class '$className' does not exist"); - } - - // TODO delegate to another class - protected function getClassNameHash($dirs) - { - if (is_string($dirs)) { - $dirs = (array) $dirs; - } - - $data = array(); - - foreach ($dirs as $dir) { - if (file_exists($dir)) { - $fileList = $this->getFileManager()->getFileList($dir, false, '\.php$', 'file'); - foreach ($fileList as $file) { - $filePath = Util::concatPath($dir, $file); - $className = Util::getClassName($filePath); - $fileName = $this->getFileManager()->getFileName($filePath); - $data[$fileName] = $className; - } - } - } - return $data; - } -} - + 'application/Espo/Services', + 'modulePath' => 'application/Espo/Modules/{*}/Services', + 'customPath' => 'custom/Espo/Custom/Services', + ); + + protected $data; + + public function __construct(Container $container) + { + $this->container = $container; + } + + protected function init() + { + $config = $this->getContainer()->get('config'); + + if (file_exists($this->cacheFile) && $config->get('useCache')) { + $this->data = $this->getFileManager()->getContents($this->cacheFile); + } else { + $this->data = $this->getClassNameHash($this->paths['corePath']); + + foreach ($this->getContainer()->get('metadata')->getModuleList() as $moduleName) { + $path = str_replace('{*}', $moduleName, $this->paths['modulePath']); + $this->data = array_merge($this->data, $this->getClassNameHash($path)); + } + + $this->data = array_merge($this->data, $this->getClassNameHash($this->paths['customPath'])); + + if ($config->get('useCache')) { + $result = $this->getFileManager()->putContentsPHP($this->cacheFile, $this->data); + if ($result == false) { + throw new \Espo\Core\Exceptions\Error(); + } + } + } + } + + protected function getFileManager() + { + return $this->container->get('fileManager'); + } + + protected function getContainer() + { + return $this->container; + } + + protected function getClassName($name) + { + $name = Util::normilizeClassName($name); + + if (!isset($this->data)) { + $this->init(); + } + + $name = ucfirst($name); + if (isset($this->data[$name])) { + return $this->data[$name]; + } + + return false; + } + + public function checkExists($name) { + $className = $this->getClassName($name); + if (!empty($className)) { + return true; + } + } + + public function create($name) + { + $className = $this->getClassName($name); + if (empty($className)) { + throw new Error(); + } + return $this->createByClassName($className); + } + + protected function createByClassName($className) + { + if (class_exists($className)) { + $service = new $className(); + $dependencies = $service->getDependencyList(); + foreach ($dependencies as $name) { + $service->inject($name, $this->container->get($name)); + } + return $service; + } + throw new Error("Class '$className' does not exist"); + } + + // TODO delegate to another class + protected function getClassNameHash($dirs) + { + if (is_string($dirs)) { + $dirs = (array) $dirs; + } + + $data = array(); + + foreach ($dirs as $dir) { + if (file_exists($dir)) { + $fileList = $this->getFileManager()->getFileList($dir, false, '\.php$', 'file'); + foreach ($fileList as $file) { + $filePath = Util::concatPath($dir, $file); + $className = Util::getClassName($filePath); + $fileName = $this->getFileManager()->getFileName($filePath); + $data[$fileName] = $className; + } + } + } + return $data; + } +} + diff --git a/application/Espo/Core/Services/Base.php b/application/Espo/Core/Services/Base.php index 956d6191d3..f9c7a202fd 100644 --- a/application/Espo/Core/Services/Base.php +++ b/application/Espo/Core/Services/Base.php @@ -1,4 +1,4 @@ -valuesContainer['name'] = $value; - $this->set('lower', strtolower($value)); - } - -} +valuesContainer['name'] = $value; + $this->set('lower', strtolower($value)); + } + +} diff --git a/application/Espo/Entities/EmailTemplate.php b/application/Espo/Entities/EmailTemplate.php index a67fc1a6a4..9add953848 100644 --- a/application/Espo/Entities/EmailTemplate.php +++ b/application/Espo/Entities/EmailTemplate.php @@ -1,4 +1,4 @@ - $entity->get('name') - ); - } -} - + $entity->get('name') + ); + } +} + diff --git a/application/Espo/Modules/Crm/Services/Activities.php b/application/Espo/Modules/Crm/Services/Activities.php index 9289446343..9924ffc5b3 100644 --- a/application/Espo/Modules/Crm/Services/Activities.php +++ b/application/Espo/Modules/Crm/Services/Activities.php @@ -1,4 +1,4 @@ - $entity->get('firstName'), - 'lastName' => $entity->get('lastName'), - ); - } -} - + $entity->get('firstName'), + 'lastName' => $entity->get('lastName'), + ); + } +} + diff --git a/application/Espo/Modules/Crm/Services/InboundEmail.php b/application/Espo/Modules/Crm/Services/InboundEmail.php index 28963c8c99..167ab2631b 100644 --- a/application/Espo/Modules/Crm/Services/InboundEmail.php +++ b/application/Espo/Modules/Crm/Services/InboundEmail.php @@ -1,4 +1,4 @@ - $entity->get('firstName'), - 'lastName' => $entity->get('lastName'), - ); - } - - public function convert($id, $recordsData) - { - $lead = $this->getEntity($id); - - if (!$this->getAcl()->check($lead, 'edit')) { - throw new Forbidden(); - } - - $entityManager = $this->getEntityManager(); - - - if (!empty($recordsData->Account)) { - - $account = $entityManager->getEntity('Account'); - $account->set(get_object_vars($recordsData->Account)); - $entityManager->saveEntity($account); - $lead->set('createdAccountId', $account->id); - } - if (!empty($recordsData->Opportunity)) { - $opportunity = $entityManager->getEntity('Opportunity'); - $opportunity->set(get_object_vars($recordsData->Opportunity)); - if (isset($account)) { - $opportunity->set('accountId', $account->id); - } - $entityManager->saveEntity($opportunity); - $lead->set('createdOpportunityId', $opportunity->id); - } - if (!empty($recordsData->Contact)) { - $contact = $entityManager->getEntity('Contact'); - $contact->set(get_object_vars($recordsData->Contact)); - if (isset($account)) { - $contact->set('accountId', $account->id); - } - $entityManager->saveEntity($contact); - if (isset($opportunity)) { - $entityManager->getRepository('Contact')->relate($contact, 'opportunities', $opportunity); - } - $lead->set('createdContactId', $contact->id); - } - - $lead->set('status', 'Converted'); - $entityManager->saveEntity($lead); - - if ($meetings = $lead->get('meetings')) { - foreach ($meetings as $meeting) { - if (!empty($contact)) { - $entityManager->getRepository('Meeting')->relate($meeting, 'contacts', $contact); - } - if (!empty($opportunity)) { - $entityManager->getRepository('Opportunity')->relate($opportunity, 'meetings', $meeting); - } - } - } - if ($calls = $lead->get('calls')) { - foreach ($calls as $call) { - if (!empty($contact)) { - $entityManager->getRepository('Call')->relate($call, 'contacts', $contact); - } - if (!empty($opportunity)) { - $entityManager->getRepository('Opportunity')->relate($opportunity, 'calls', $call); - } - } - } - - return $lead; - } -} - + $entity->get('firstName'), + 'lastName' => $entity->get('lastName'), + ); + } + + public function convert($id, $recordsData) + { + $lead = $this->getEntity($id); + + if (!$this->getAcl()->check($lead, 'edit')) { + throw new Forbidden(); + } + + $entityManager = $this->getEntityManager(); + + + if (!empty($recordsData->Account)) { + + $account = $entityManager->getEntity('Account'); + $account->set(get_object_vars($recordsData->Account)); + $entityManager->saveEntity($account); + $lead->set('createdAccountId', $account->id); + } + if (!empty($recordsData->Opportunity)) { + $opportunity = $entityManager->getEntity('Opportunity'); + $opportunity->set(get_object_vars($recordsData->Opportunity)); + if (isset($account)) { + $opportunity->set('accountId', $account->id); + } + $entityManager->saveEntity($opportunity); + $lead->set('createdOpportunityId', $opportunity->id); + } + if (!empty($recordsData->Contact)) { + $contact = $entityManager->getEntity('Contact'); + $contact->set(get_object_vars($recordsData->Contact)); + if (isset($account)) { + $contact->set('accountId', $account->id); + } + $entityManager->saveEntity($contact); + if (isset($opportunity)) { + $entityManager->getRepository('Contact')->relate($contact, 'opportunities', $opportunity); + } + $lead->set('createdContactId', $contact->id); + } + + $lead->set('status', 'Converted'); + $entityManager->saveEntity($lead); + + if ($meetings = $lead->get('meetings')) { + foreach ($meetings as $meeting) { + if (!empty($contact)) { + $entityManager->getRepository('Meeting')->relate($meeting, 'contacts', $contact); + } + if (!empty($opportunity)) { + $entityManager->getRepository('Opportunity')->relate($opportunity, 'meetings', $meeting); + } + } + } + if ($calls = $lead->get('calls')) { + foreach ($calls as $call) { + if (!empty($contact)) { + $entityManager->getRepository('Call')->relate($call, 'contacts', $contact); + } + if (!empty($opportunity)) { + $entityManager->getRepository('Opportunity')->relate($opportunity, 'calls', $call); + } + } + } + + return $lead; + } +} + diff --git a/application/Espo/Modules/Crm/Services/Meeting.php b/application/Espo/Modules/Crm/Services/Meeting.php index 8d18e81c3e..ab81405909 100644 --- a/application/Espo/Modules/Crm/Services/Meeting.php +++ b/application/Espo/Modules/Crm/Services/Meeting.php @@ -1,4 +1,4 @@ - $entity->get('firstName'), - 'lastName' => $entity->get('lastName'), - ); - } - - public function convert($id) - { - $entityManager = $this->getEntityManager(); - $prospect = $this->getEntity($id); - - if (!$this->getAcl()->check($prospect, 'delete')) { - throw new Forbidden(); - } - if (!$this->getAcl()->check('Lead', 'read')) { - throw new Forbidden(); - } - - $lead = $entityManager->getEntity('Lead'); - $lead->set($prospect->toArray()); - - $entityManager->removeEntity($prospect); - $entityManager->saveEntity($lead); - - return $lead; - } -} - + $entity->get('firstName'), + 'lastName' => $entity->get('lastName'), + ); + } + + public function convert($id) + { + $entityManager = $this->getEntityManager(); + $prospect = $this->getEntity($id); + + if (!$this->getAcl()->check($prospect, 'delete')) { + throw new Forbidden(); + } + if (!$this->getAcl()->check('Lead', 'read')) { + throw new Forbidden(); + } + + $lead = $entityManager->getEntity('Lead'); + $lead->set($prospect->toArray()); + + $entityManager->removeEntity($prospect); + $entityManager->saveEntity($lead); + + return $lead; + } +} + diff --git a/application/Espo/ORM/DB/IMapper.php b/application/Espo/ORM/DB/IMapper.php index 03c1a305de..5c9f64ecb5 100644 --- a/application/Espo/ORM/DB/IMapper.php +++ b/application/Espo/ORM/DB/IMapper.php @@ -1,4 +1,4 @@ - '<>', - '*' => 'LIKE', - '>=' => '>=', - '<=' => '<=', - '>' => '>', - '<' => '<', - '=' => '=', - ); - - // @todo whereClause ? - protected static $selectParamList = array( - 'offset', - 'limit', - 'order', - 'orderBy', - 'customWhere', - 'customJoin', - 'joins', - 'leftJoins', - 'distinct', - 'joinConditions', - ); - - public function __construct(PDO $pdo, \Espo\ORM\EntityFactory $entityFactory) { - $this->pdo = $pdo; - $this->entityFactory = $entityFactory; - } - - public function selectById(IEntity $entity, $id, $params = array()) - { - if (!array_key_exists('whereClause', $params)) { - $params['whereClause'] = array(); - } - - $params['whereClause']['id'] = $id; - $params['whereClause']['deleted'] = 0; - - $sql = $this->createSelectQuery($entity, $params); - - $ps = $this->pdo->query($sql); - - if ($ps) { - foreach ($ps as $row) { - $entity = $this->fromRow($entity, $row); - return true; - } - } - return false; - } - - public function count(IEntity $entity, $params = array()) - { - return $this->aggregate($entity, $params, 'COUNT', 'id'); - } - - public function max(IEntity $entity, $params = array(), $field, $deleted = false) - { - return $this->aggregate($entity, $params, 'MAX', $field, true); - } - - public function min(IEntity $entity, $params = array(), $field, $deleted = false) - { - return $this->aggregate($entity, $params, 'MIN', $field, true); - } - - public function sum(IEntity $entity, $params = array()) - { - return $this->aggregate($entity, $params, 'SUM', 'id'); - } - - public function select(IEntity $entity, $params = array()) - { - $sql = $this->createSelectQuery($entity, $params); - - $dataArr = array(); - $ps = $this->pdo->query($sql); - if ($ps) { - $dataArr = $ps->fetchAll(); - } - - if ($this->returnCollection) { - $collectionClass = $this->collectionClass; - $entityArr = new $collectionClass($dataArr, $entity->getEntityName(), $this->entityFactory); - return $entityArr; - } else { - return $dataArr; - } - } - - public function aggregate(IEntity $entity, $params = array(), $aggregation, $aggregationBy, $deleted = false) - { - if (empty($aggregation) || !isset($entity->fields[$aggregationBy])) { - return false; - } - - $sql = $this->createSelectQuery($entity, $params, $aggregation, $aggregationBy, $deleted); - - $ps = $this->pdo->query($sql); - - if ($ps) { - foreach ($ps as $row) { - return $row['AggregateValue']; - } - } - return false; - } - - protected function createSelectQuery(IEntity $entity, $params = array(), $aggregation = null, $aggregationBy = null, $deleted = false) - { - $whereClause = array(); - if (array_key_exists('whereClause', $params)) { - $whereClause = $params['whereClause']; - } - - if (!$deleted) { - $whereClause = $whereClause + array('deleted' => 0); - } - - foreach (self::$selectParamList as $k) { - $$k = array_key_exists($k, $params) ? $params[$k] : null; - } - - if (empty($aggregation)) { - $selectPart = $this->getSelect($entity); - $orderPart = $this->getOrder($entity, $orderBy, $order); - } else { - $aggDist = false; - if ($distinct && $aggregation == 'COUNT') { - $aggDist = true; - } - $selectPart = $this->getAggregationSelect($entity, $aggregation, $aggregationBy, $aggDist); - } - $joinsPart = $this->getBelongsToJoins($entity); - $wherePart = $this->getWhere($entity, $whereClause); - - if (!empty($customWhere)) { - $wherePart .= ' ' . $customWhere; - } - - if (!empty($customJoin)) { - $joinsPart .= ' ' . $customJoin . ' '; - } - - if (!empty($joins) && is_array($joins)) { - $joinsRelated = $this->getJoins($entity, $joins, false, $joinConditions); - if (!empty($joinsRelated)) { - if (!empty($joinsPart)) { - $joinsPart .= ' '; - } - $joinsPart .= $joinsRelated; - } - } - - if (!empty($leftJoins) && is_array($leftJoins)) { - $joinsRelated = $this->getJoins($entity, $leftJoins, true, $joinConditions); - if (!empty($joinsRelated)) { - if (!empty($joinsPart)) { - $joinsPart .= ' '; - } - $joinsPart .= $joinsRelated; - } - } - - if (empty($aggregation)) { - return $this->composeSelectQuery($this->toDb($entity->getEntityName()), $selectPart, $joinsPart, $wherePart, $orderPart, $offset, $limit, $distinct); - } else { - return $this->composeSelectQuery($this->toDb($entity->getEntityName()), $selectPart, $joinsPart, $wherePart, null, null, null, $distinct); - } - } - - protected function getAggregationSelect(IEntity $entity, $aggregation, $aggregationBy, $distinct = false) - { - if (!isset($entity->fields[$aggregationBy])) { - return false; - } - - $aggregation = strtoupper($aggregation); - - $distinctPart = ''; - if ($distinct) { - $distinctPart = 'DISTINCT '; - } - - $selectPart = "{$aggregation}({$distinctPart}" . $this->toDb($entity->getEntityName()) . "." . $this->toDb($aggregationBy) . ") AS AggregateValue"; - return $selectPart; - } - - protected function getJoins(IEntity $entity, array $joins, $left = false, $joinConditions = array()) - { - $joinsArr = array(); - foreach ($joins as $relationName) { - $conditions = array(); - if (!empty($joinConditions[$relationName])) { - $conditions = $joinConditions[$relationName]; - } - if ($joinRelated = $this->getJoinRelated($entity, $relationName, $left, $conditions)) { - $joinsArr[] = $joinRelated; - } - } - return implode(' ', $joinsArr); - } - - - public function selectRelated(IEntity $entity, $relationName, $params = array(), $totalCount = false) - { - $relOpt = $entity->relations[$relationName]; - - if (!isset($relOpt['entity']) || !isset($relOpt['type'])) { - throw new \LogicException("Not appropriate defenition for relationship {$relationName} in " . $entity->getEntityName() . " entity"); - } - - $relEntityName = (!empty($relOpt['class'])) ? $relOpt['class'] : $relOpt['entity']; - $relEntity = $this->entityFactory->create($relEntityName); - - $whereClause = array(); - if (array_key_exists('whereClause', $params)) { - $whereClause = $params['whereClause']; - } - - $whereClause = $whereClause + array('deleted' => 0); - - foreach (self::$selectParamList as $k) { - $$k = array_key_exists($k, $params) ? $params[$k] : null; - if (is_null($$k) && isset($relOpt[$k])) { - $$k = $relOpt[$k]; - } - } - - if (!$totalCount) { - $selectPart = $this->getSelect($relEntity); - $joinsPart = $this->getBelongsToJoins($relEntity); - $orderPart = $this->getOrder($relEntity, $orderBy, $order); - - if (!empty($joins) && is_array($joins)) { - $joinsRelated = $this->getJoins($relEntity, $joins, false, $joinConditions); - if (!empty($joinsRelated)) { - if (!empty($joinsPart)) { - $joinsPart .= ' '; - } - $joinsPart .= $joinsRelated; - } - } - - if (!empty($leftJoins) && is_array($leftJoins)) { - $joinsRelated = $this->getJoins($relEntity, $leftJoins, true, $joinConditions); - if (!empty($joinsRelated)) { - if (!empty($joinsPart)) { - $joinsPart .= ' '; - } - $joinsPart .= $joinsRelated; - } - } - - } else { - $selectPart = $this->getAggregationSelect($relEntity, 'COUNT', 'id'); - $joinsPart = ''; - $orderPart = ''; - $offset = null; - $limit = null; - } - - $relType = $relOpt['type']; - - $keySet = $this->getKeys($entity, $relationName); - - $key = $keySet['key']; - $foreignKey = $keySet['foreignKey']; - - switch ($relType) { - - case IEntity::BELONGS_TO: - - $whereClause[$foreignKey] = $entity->get($key); - $wherePart = $this->getWhere($relEntity, $whereClause); - - if (!empty($customWhere)) { - $wherePart .= ' ' . $customWhere; - } - - $sql = $this->composeSelectQuery($this->toDb($relEntity->getEntityName()), $selectPart, $joinsPart, $wherePart, $orderPart, 0, 1); - $ps = $this->pdo->query($sql); - - - if ($ps) { - foreach ($ps as $row) { - if (!$totalCount) { - $relEntity = $this->fromRow($relEntity, $row); - return $relEntity; - } else { - return $row['AggregateValue']; - } - } - } - break; - - case IEntity::HAS_MANY: - case IEntity::HAS_CHILDREN: - - $whereClause[$foreignKey] = $entity->get($key); - - if ($relType == IEntity::HAS_CHILDREN) { - $foreignType = $keySet['foreignType']; - $whereClause[$foreignType] = $entity->getEntityName(); - } - - $wherePart = $this->getWhere($relEntity, $whereClause); - - if (!empty($customWhere)) { - $wherePart .= ' ' . $customWhere; - } - $dataArr = array(); - - - $sql = $this->composeSelectQuery($this->toDb($relEntity->getEntityName()), $selectPart, $joinsPart, $wherePart, $orderPart, $offset, $limit); - - $ps = $this->pdo->query($sql); - if ($ps) { - if (!$totalCount) { - $dataArr = $ps->fetchAll(); - - } else { - foreach ($ps as $row) { - return $row['AggregateValue']; - } - } - } - if ($this->returnCollection) { - $collectionClass = $this->collectionClass; - return new $collectionClass($dataArr, $relEntity->getEntityName(), $this->entityFactory); - } else { - return $dataArr; - } - break; - - case IEntity::MANY_MANY: - - $MMJoinPart = $this->getMMJoin($entity, $relationName, $keySet); - $wherePart = $this->getWhere($relEntity, $whereClause); - if ($joinsPart != '') { - $MMJoinPart = ' ' . $MMJoinPart; - } - - $dataArr = array(); - - $sql = $this->composeSelectQuery($this->toDb($relEntity->getEntityName()), $selectPart, $joinsPart . $MMJoinPart, $wherePart, $orderPart, $offset, $limit); - - $ps = $this->pdo->query($sql); - if ($ps) { - if (!$totalCount) { - $dataArr = $ps->fetchAll(); - - } else { - foreach ($ps as $row) { - return $row['AggregateValue']; - } - } - } - if ($this->returnCollection) { - $collectionClass = $this->collectionClass; - return new $collectionClass($dataArr, $relEntity->getEntityName(), $this->entityFactory); - } else { - return $dataArr; - } - break; - } - - return false; - } - - protected function getKeys(IEntity $entity, $relationName) - { - $relOpt = $entity->relations[$relationName]; - $relType = $relOpt['type']; - - switch ($relType) { - - case IEntity::BELONGS_TO: - $key = $this->toDb($entity->getEntityName()) . 'Id'; - if (isset($relOpt['key'])) { - $key = $relOpt['key']; - } - $foreignKey = 'id'; - if(isset($relOpt['foreignKey'])){ - $foreignKey = $relOpt['foreignKey']; - } - return array( - 'key' => $key, - 'foreignKey' => $foreignKey, - ); - - case IEntity::HAS_MANY: - $key = 'id'; - if (isset($relOpt['key'])){ - $key = $relOpt['key']; - } - $foreignKey = $this->toDb($entity->getEntityName()) . 'Id'; - if (isset($relOpt['foreignKey'])) { - $foreignKey = $relOpt['foreignKey']; - } - return array( - 'key' => $key, - 'foreignKey' => $foreignKey, - ); - case IEntity::HAS_CHILDREN: - $key = 'id'; - if (isset($relOpt['key'])){ - $key = $relOpt['key']; - } - $foreignKey = 'parentId'; - if (isset($relOpt['foreignKey'])) { - $foreignKey = $relOpt['foreignKey']; - } - $foreignType = 'parentType'; - if (isset($relOpt['foreignType'])) { - $foreignType = $relOpt['foreignType']; - } - return array( - 'key' => $key, - 'foreignKey' => $foreignKey, - 'foreignType' => $foreignType, - ); - - case IEntity::MANY_MANY: - $key = 'id'; - if(isset($relOpt['key'])){ - $key = $relOpt['key']; - } - $foreignKey = 'id'; - if(isset($relOpt['foreignKey'])){ - $foreignKey = $relOpt['foreignKey']; - } - $nearKey = $this->toDb($entity->getEntityName()) . 'Id'; - $distantKey = $this->toDb($relOpt['entity']) . 'Id'; - if (isset($relOpt['midKeys']) && is_array($relOpt['midKeys'])){ - $nearKey = $relOpt['midKeys'][0]; - $distantKey = $relOpt['midKeys'][1]; - } - return array( - 'key' => $key, - 'foreignKey' => $foreignKey, - 'nearKey' => $nearKey, - 'distantKey' => $distantKey, - ); - } - } - - public function countRelated(IEntity $entity, $relationName, $params = array()) - { - return $this->selectRelated($entity, $relationName, $params, true); - } - - public function relate(IEntity $entityFrom, $relationName, IEntity $entityTo) - { - $this->addRelation($entityFrom, $relationName, null, $entityTo); - } - - public function unrelate(IEntity $entityFrom, $relationName, IEntity $entityTo) - { - $this->removeRelation($entityFrom, $relationName, null, false, $entityTo); - } - - public function addRelation(IEntity $entity, $relationName, $id = null, $relEntity = null) - { - if (!is_null($relEntity)) { - $id = $relEntity->id; - } - - if (empty($id) || empty($relationName)) { - return false; - } - - $relOpt = $entity->relations[$relationName]; - - if (!isset($relOpt['entity']) || !isset($relOpt['type'])) { - throw new \LogicException("Not appropriate defenition for relationship {$relationName} in " . $entity->getEntityName() . " entity"); - } - - $relType = $relOpt['type']; - - $className = (!empty($relOpt['class'])) ? $relOpt['class'] : $relOpt['entity']; - - if (is_null($relEntity)) { - $relEntity = $this->entityFactory->create($className); - $relEntity->id = $id; - } - - $keySet = $this->getKeys($entity, $relationName); - - switch ($relType) { - case IEntity::BELONGS_TO: - case IEntity::HAS_ONE: - return false; - break; - - case IEntity::HAS_CHILDREN: - case IEntity::HAS_MANY: - $key = $keySet['key']; - $foreignKey = $keySet['foreignKey']; - - if ($this->count($relEntity, array('whereClause' => array('id' => $id))) > 0) { - - $setPart = $this->toDb($foreignKey) . " = " . $this->pdo->quote($entity->get($key)); - - if ($relType == IEntity::HAS_CHILDREN) { - $foreignType = $keySet['foreignType']; - $setPart .= ", " . $this->toDb($foreignType) . " = " . $this->pdo->quote($entity->getEntityName()); - } - - $wherePart = $this->getWhere($relEntity, array('id' => $id, 'deleted' => 0)); - $sql = $this->composeUpdateQuery($this->toDb($relEntity->getEntityName()), $setPart, $wherePart); - - if ($this->pdo->query($sql)) { - return true; - } - } else { - return false; - } - break; - - case IEntity::MANY_MANY: - $key = $keySet['key']; - $foreignKey = $keySet['foreignKey']; - $nearKey = $keySet['nearKey']; - $distantKey = $keySet['distantKey']; - - if ($this->count($relEntity, array('whereClause' => array('id' => $id))) > 0) { - $relTable = $this->toDb($relOpt['relationName']); - - $wherePart = - $this->toDb($nearKey) . " = " . $this->pdo->quote($entity->id) . " ". - "AND " . $this->toDb($distantKey) . " = " . $this->pdo->quote($relEntity->id); - if (!empty($relOpt['conditions']) && is_array($relOpt['conditions'])) { - foreach ($relOpt['conditions'] as $f => $v) { - $wherePart .= " AND " . $this->toDb($f) . " = " . $this->pdo->quote($v); - } - } - - $sql = $this->composeSelectQuery($relTable, '*', '', $wherePart); - - $ps = $this->pdo->query($sql); - - if ($ps->rowCount() == 0) { - $fieldsPart = $this->toDb($nearKey) . ", " . $this->toDb($distantKey); - $valuesPart = $this->pdo->quote($entity->id) . ", " . $this->pdo->quote($relEntity->id); - - if (!empty($relOpt['conditions']) && is_array($relOpt['conditions'])) { - foreach ($relOpt['conditions'] as $f => $v) { - $fieldsPart .= ", " . $this->toDb($f); - $valuesPart .= ", " . $this->pdo->quote($v); - } - } - - $sql = $this->composeInsertQuery($relTable, $fieldsPart, $valuesPart); - - if ($this->pdo->query($sql)) { - return true; - } - } else { - $setPart = 'deleted = 0'; - $wherePart = - $this->toDb($nearKey) . " = " . $this->pdo->quote($entity->id) . " - AND " . $this->toDb($distantKey) . " = " . $this->pdo->quote($relEntity->id) . " - "; - - if (!empty($relOpt['conditions']) && is_array($relOpt['conditions'])) { - foreach ($relOpt['conditions'] as $f => $v) { - $wherePart .= " AND " . $this->toDb($f) . " = " . $this->pdo->quote($v); - } - } - - $sql = $this->composeUpdateQuery($relTable, $setPart, $wherePart); - if ($this->pdo->query($sql)) { - return true; - } - } - } else { - return false; - } - break; - } - } - - public function removeRelation(IEntity $entity, $relationName, $id = null, $all = false, IEntity $relEntity = null) - { - if (!is_null($relEntity)) { - $id = $relEntity->id; - } - - if (empty($id) && empty($all) || empty($relationName)) { - return false; - } - - $relOpt = $entity->relations[$relationName]; - - if (!isset($relOpt['entity']) || !isset($relOpt['type'])) { - throw new \LogicException("Not appropriate defenition for relationship {$relationName} in " . $entity->getEntityName() . " entity"); - } - - $relType = $relOpt['type']; - - $className = (!empty($relOpt['class'])) ? $relOpt['class'] : $relOpt['entity']; - - if (is_null($relEntity)) { - $relEntity = $this->entityFactory->create($className); - $relEntity->id = $id; - } - - $keySet = $this->getKeys($entity, $relationName); - - switch ($relType) { - - case IEntity::BELONGS_TO: - /*$foreignKey = $keySet['foreignKey']; - $relEntity->$foreignKey = null; - $this-> - break;*/ - - case IEntity::HAS_ONE: - return false; - - - case IEntity::HAS_MANY: - case IEntity::HAS_CHILDREN: - $key = $keySet['key']; - $foreignKey = $keySet['foreignKey']; - - $setPart = $this->toDb($foreignKey) . " = " . "NULL"; - - $whereClause = array('deleted' => 0); - if (empty($all)) { - $whereClause['id'] = $id; - } else { - $whereClause[$foreignKey] = $entity->id; - } - - if ($relType == IEntity::HAS_CHILDREN) { - $foreignType = $keySet['foreignType']; - $whereClause[$foreignType] = $entity->getEntityName(); - } - - $wherePart = $this->getWhere($relEntity, $whereClause); - $sql = $this->composeUpdateQuery($this->toDb($relEntity->getEntityName()), $setPart, $wherePart); - if ($this->pdo->query($sql)) { - return true; - } - break; - - case IEntity::MANY_MANY: - $key = $keySet['key']; - $foreignKey = $keySet['foreignKey']; - $nearKey = $keySet['nearKey']; - $distantKey = $keySet['distantKey']; - - $relTable = $this->toDb($relOpt['relationName']); - - $setPart = 'deleted = 1'; - $wherePart = $this->toDb($nearKey) . " = " . $this->pdo->quote($entity->id); - - - if (empty($all)) { - $wherePart .= " AND " . $this->toDb($distantKey) . " = " . $this->pdo->quote($id) . ""; - } - - if (!empty($relOpt['conditions']) && is_array($relOpt['conditions'])) { - foreach ($relOpt['conditions'] as $f => $v) { - $wherePart .= " AND " . $this->toDb($f) . " = " . $this->pdo->quote($v); - } - } - - $sql = $this->composeUpdateQuery($relTable, $setPart, $wherePart); - - if ($this->pdo->query($sql)) { - return true; - } - break; - } - } - - public function removeAllRelations(IEntity $entity, $relationName) - { - $this->removeRelation($entity, $relationName, null, true); - } - - protected function quote($value) - { - if (is_null($value)) { - return 'NULL'; - } else { - return $this->pdo->quote($value); - } - } - - public function insert(IEntity $entity) - { - $dataArr = $this->toArray($entity); - - $fieldArr = array(); - $valArr = array(); - foreach ($dataArr as $field => $value) { - $fieldArr[] = $this->toDb($field); - $valArr[] = $this->quote($value); - } - $fieldsPart = implode(", ", $fieldArr); - $valuesPart = implode(", ", $valArr); - - $sql = $this->composeInsertQuery($this->toDb($entity->getEntityName()), $fieldsPart, $valuesPart); - - if ($this->pdo->query($sql)) { - return $entity->id; - } - - return false; - } - - public function update(IEntity $entity) - { - $dataArr = $this->toArray($entity); - - $setArr = array(); - foreach ($dataArr as $field => $value) { - if ($field == 'id') { - continue; - } - if ($entity->fields[$field]['type'] == IEntity::FOREIGN) { - continue; - } - - if ($entity->getFetched($field) === $value) { - continue; - } - - $setArr[] = $this->toDb($field) . " = " . $this->quote($value); - } - - if (count($setArr) == 0) { - return $entity->id; - } - - $setPart = implode(', ', $setArr); - $wherePart = $this->getWhere($entity, array('id' => $entity->id, 'deleted' => 0)); - - $sql = $this->composeUpdateQuery($this->toDb($entity->getEntityName()), $setPart, $wherePart); - - if ($this->pdo->query($sql)) { - return $entity->id; - } - - return false; - } - - public function delete(IEntity $entity) - { - $entity->set('deleted', true); - return $this->update($entity); - } - - protected function toArray(IEntity $entity, $onlyStorable = true) - { - $arr = array(); - foreach ($entity->fields as $field => $fieldDefs) { - if ($entity->has($field)) { - if ($onlyStorable) { - if (!empty($fieldDefs['notStorable']) || isset($fieldDefs['source']) && $fieldDefs['source'] != 'db') - continue; - if ($fieldDefs['type'] == IEntity::FOREIGN) - continue; - } - $arr[$field] = $entity->get($field); - } - } - return $arr; - } - - protected function fromRow(IEntity $entity, $data) - { - $entity->set($data); - return $entity; - } - - protected function getAlias(IEntity $entity, $key) - { - if (!isset($this->aliasesCache[$entity->getEntityName()])) { - $this->aliasesCache[$entity->getEntityName()] = $this->getTableAliases($entity); - } - - if (isset($this->aliasesCache[$entity->getEntityName()][$key])) { - return $this->aliasesCache[$entity->getEntityName()][$key]; - } else { - return false; - } - } - - protected function getTableAliases(IEntity $entity) - { - $aliases = array(); - $c = 0; - - $occuranceHash = array(); - - foreach ($entity->relations as $name => $r) { - if ($r['type'] == IEntity::BELONGS_TO) { - $key = $r['key']; - $table = $this->toDb($r['entity']); - - if (!array_key_exists($key, $aliases)) { - if (array_key_exists($table, $occuranceHash)) { - $occuranceHash[$table]++; - } else { - $occuranceHash[$table] = 0; - } - $suffix = '_f'; - if ($occuranceHash[$table] > 0) { - $suffix .= '_' . $occuranceHash[$table]; - } - - $aliases[$key] = $table . $suffix; - } - } - } - - return $aliases; - } - - protected function getFieldPath(IEntity $entity, $field) - { - if (isset($entity->fields[$field])) { - $f = $entity->fields[$field]; - - if (isset($f['source'])) { - if ($f['source'] != 'db') { - return false; - } - } - - if (!empty($f['notStorable'])) { - return false; - } - - $fieldPath = ''; - - switch($f['type']) { - case 'foreign': - if (isset($f['relation'])) { - $relationName = $f['relation']; - - $keySet = $this->getKeys($entity, $relationName); - $key = $keySet['key']; - - $foreigh = $f['foreign']; - - if (is_array($foreigh)) { - foreach ($foreigh as $i => $value) { - if ($value == ' ') { - $foreigh[$i] = '\' \''; - } else { - $foreigh[$i] = $this->getAlias($entity, $key) . '.' . $this->toDb($value); - } - } - $fieldPath = 'TRIM(CONCAT(' . implode(', ', $foreigh). '))'; - } else { - $fieldPath = $this->getAlias($entity, $key) . '.' . $this->toDb($foreigh); - } - } - break; - default: - $fieldPath = $this->toDb($entity->getEntityName()) . '.' . $this->toDb($field) ; - } - - return $fieldPath; - } - - return false; - } - - protected function getWhere(IEntity $entity, $whereClause, $sqlOp = 'AND') - { - $whereParts = array(); - - foreach ($whereClause as $field => $value) { - - if (is_int($field)) { - $field = 'AND'; - } - - if (!in_array($field, self::$sqlOperators)) { - - $inRelated = false; - - if (strpos($field, '.') !== false) { - list($entityName, $field) = array_map('trim', explode('.', $field)); - $entityName = preg_replace('/[^A-Za-z0-9_]+/', '', $entityName); - $field = preg_replace('/[^A-Za-z0-9_]+/', '', $field); - $inRelated = true; - } - - $operator = '='; - - if (!preg_match('/^[a-z0-9]+$/i', $field)) { - foreach (self::$comparisonOperators as $op => $opDb) { - if (strpos($field, $op) !== false) { - $field = trim(str_replace($op, '', $field)); - $operator = $opDb; - break; - } - } - } - - if (!$inRelated) { - - if (!isset($entity->fields[$field])) { - continue; - } - - $fieldDefs = $entity->fields[$field]; - - if (!empty($fieldDefs['where']) && !empty($fieldDefs['where'][$operator])) { - $whereParts[] = str_replace('{text}', $value, $fieldDefs['where'][$operator]); - } else { - if ($fieldDefs['type'] == IEntity::FOREIGN) { - $leftPart = ''; - if (isset($fieldDefs['relation'])) { - $relationName = $fieldDefs['relation']; - if (isset($entity->relations[$relationName])) { - $keySet = $this->getKeys($entity, $relationName); - $key = $keySet['key']; - - $alias = $this->getAlias($entity, $key); - if ($alias) { - $leftPart = $alias . '.' . $this->toDb($fieldDefs['foreign']); - } - } - } - } else { - $leftPart = $this->toDb($entity->getEntityName()) . '.' . $this->toDb($field); - } - } - } else { - $leftPart = $this->toDb($entityName) . '.' . $this->toDb($field); - } - - if (!empty($leftPart)) { - if (!is_array($value)) { - $whereParts[] = $leftPart . " " . $operator . " " . $this->pdo->quote($value); - - } else { - $valArr = $value; - foreach ($valArr as $k => $v) { - $valArr[$k] = $this->pdo->quote($valArr[$k]); - } - $oppose = ''; - if ($operator == '<>') { - $oppose = 'NOT'; - } - $whereParts[] = $leftPart . " {$oppose} IN " . "(" . implode(',', $valArr) . ")"; - } - } - } else { - $whereParts[] = "(" . $this->getWhere($entity, $value, $field) . ")"; - } - } - return implode(" " . $sqlOp . " ", $whereParts); - } - - protected function getBelongsToJoins(IEntity $entity) - { - $joinsArr = array(); - - foreach ($entity->relations as $relationName => $r) { - if ($r['type'] == IEntity::BELONGS_TO) { - $keySet = $this->getKeys($entity, $relationName); - $key = $keySet['key']; - $foreignKey = $keySet['foreignKey']; - - $alias = $this->getAlias($entity, $key); - - if ($alias) { - $joinsArr[] = - "LEFT JOIN `" . $this->toDb($r['entity']) . "` AS `" . $alias . "` ON ". - $this->toDb($entity->getEntityName()) . "." . $this->toDb($key) . " = " . $alias . "." . $this->toDb($foreignKey); - } - } - } - - return implode(' ', $joinsArr); - } - - protected function getJoinRelated(IEntity $entity, $relationName, $left = false, $conditions = array()) - { - $relOpt = $entity->relations[$relationName]; - $keySet = $this->getKeys($entity, $relationName); - - $pre = ($left) ? 'LEFT ' : ''; - - if ($relOpt['type'] == IEntity::MANY_MANY) { - - $key = $keySet['key']; - $foreignKey = $keySet['foreignKey']; - $nearKey = $keySet['nearKey']; - $distantKey = $keySet['distantKey']; - - $relTable = $this->toDb($relOpt['relationName']); - $distantTable = $this->toDb($relOpt['entity']); - - $join = - "{$pre}JOIN `{$relTable}` ON {$this->toDb($entity->getEntityName())}." . $this->toDb($key) . " = {$relTable}." . $this->toDb($nearKey) - . " AND " - . "{$relTable}.deleted = " . $this->pdo->quote(0); - - if (!empty($relOpt['conditions']) && is_array($relOpt['conditions'])) { - $conditions = array_merge($conditions, $relOpt['conditions']); - } - foreach ($conditions as $f => $v) { - $join .= " AND {$relTable}." . $this->toDb($f) . " = " . $this->pdo->quote($v); - } - - $join .= " {$pre}JOIN `{$distantTable}` ON {$distantTable}." . $this->toDb($foreignKey) . " = {$relTable}." . $this->toDb($distantKey) - . " AND " - . "{$distantTable}.deleted = " . $this->pdo->quote(0) . ""; - - return $join; - } - - if ($relOpt['type'] == IEntity::HAS_MANY) { - - $foreignKey = $keySet['foreignKey']; - $distantTable = $this->toDb($relOpt['entity']); - - // TODO conditions - - $join = - "{$pre}JOIN `{$distantTable}` ON {$this->toDb($entity->getEntityName())}." . $this->toDb('id') . " = {$distantTable}." . $this->toDb($foreignKey) - . " AND " - . "{$distantTable}.deleted = " . $this->pdo->quote(0) . ""; - - return $join; - } - - return false; - } - - protected function getMMJoin(IEntity $entity, $relationName, $keySet = false) - { - $relOpt = $entity->relations[$relationName]; - - if (empty($keySet)) { - $keySet = $this->getKeys($entity, $relationName); - } - - $key = $keySet['key']; - $foreignKey = $keySet['foreignKey']; - $nearKey = $keySet['nearKey']; - $distantKey = $keySet['distantKey']; - - $relTable = $this->toDb($relOpt['relationName']); - $distantTable = $this->toDb($relOpt['entity']); - - $join = - "JOIN `{$relTable}` ON {$distantTable}." . $this->toDb($foreignKey) . " = {$relTable}." . $this->toDb($distantKey) - . " AND " - . "{$relTable}." . $this->toDb($nearKey) . " = " . $this->pdo->quote($entity->get($key)) - . " AND " - . "{$relTable}.deleted = " . $this->pdo->quote(0) . ""; - - if (!empty($relOpt['conditions']) && is_array($relOpt['conditions'])) { - foreach ($relOpt['conditions'] as $f => $v) { - $join .= " AND {$relTable}." . $this->toDb($f) . " = " . $this->pdo->quote($v); - } - } - - return $join; - } - - protected function getSelect(IEntity $entity, $fields = null) - { - $select = ""; - $arr = array(); - $specifiedList = is_array($fields) ? true : false; - - foreach ($entity->fields as $field => $fieldDefs) { - if ($specifiedList) { - if (!in_array($field, $fields)) { - continue; - } - } - - if (!empty($fieldDefs['select'])) { - $fieldPath = $fieldDefs['select']; - } else { - if (!empty($fieldDefs['notStorable'])) { - continue; - } - $fieldPath = $this->getFieldPath($entity, $field); - } - - $arr[] = $fieldPath . ' AS `' . $field . '`'; - } - - $select = implode(', ', $arr); - - return $select; - } - - protected function getOrder(IEntity $entity, $orderBy = null, $order = null) - { - $orderStr = ""; - - if (!is_null($orderBy)) { - if (!is_null($order)) { - $order = strtoupper($order); - if (!in_array($order, array('ASC', 'DESC'))) { - $order = 'ASC'; - } - } else { - $order = 'ASC'; - } - - $fieldDefs = $entity->fields[$orderBy]; - if (!empty($fieldDefs['orderBy'])) { - $orderPart = str_replace('{direction}', $order, $fieldDefs['orderBy']); - $orderStr .= "ORDER BY {$orderPart}"; - } else { - $fieldPath = $this->getFieldPath($entity, $orderBy); - if ($fieldDefs['type'] == iEntity::FOREIGN) { - - } else { - - } - $orderStr .= "ORDER BY {$fieldPath} " . $order; - } - } - - return $orderStr; - } - - protected function composeInsertQuery($table, $fields, $values) - { - $sql = "INSERT INTO `{$table}`"; - $sql .= " ({$fields})"; - if (!is_array($values)) { - $sql .= " VALUES ({$values})"; - } else { - $sql .= " VALUES (" . implode("), (", $values) . ")"; - } - - return $sql; - } - - protected function composeUpdateQuery($table, $set, $where) - { - $sql = "UPDATE `{$table}` SET {$set} WHERE {$where}"; - - return $sql; - } - - abstract protected function composeSelectQuery($table, $select, $joins = '', $where = '', $order = '', $offset = null, $limit = null, $distinct = null); - - abstract protected function toDb($field); - - public function setReturnCollection($returnCollection) - { - $this->returnCollection = $returnCollection; - } - - public function setCollectionClass($collectionClass) - { - $this->collectionClass = $collectionClass; - } -} - - + ************************************************************************/ + +namespace Espo\ORM\DB; + +use Espo\ORM\Entity; +use Espo\ORM\IEntity; +use Espo\ORM\EntityFactory; +use PDO; + +/** + * Abstraction for DB. + * Mapping of Entity to DB. + * Should be used internally only. + */ +abstract class Mapper implements IMapper +{ + public $pdo; + + protected $entityFactroy; + + protected $fieldsMapCache = array(); + protected $aliasesCache = array(); + + protected $returnCollection = true; + + protected $collectionClass = "\\Espo\\ORM\\EntityCollection"; + + protected static $sqlOperators = array( + 'OR', + 'AND', + ); + + protected static $comparisonOperators = array( + '!=' => '<>', + '*' => 'LIKE', + '>=' => '>=', + '<=' => '<=', + '>' => '>', + '<' => '<', + '=' => '=', + ); + + // @todo whereClause ? + protected static $selectParamList = array( + 'offset', + 'limit', + 'order', + 'orderBy', + 'customWhere', + 'customJoin', + 'joins', + 'leftJoins', + 'distinct', + 'joinConditions', + ); + + public function __construct(PDO $pdo, \Espo\ORM\EntityFactory $entityFactory) { + $this->pdo = $pdo; + $this->entityFactory = $entityFactory; + } + + public function selectById(IEntity $entity, $id, $params = array()) + { + if (!array_key_exists('whereClause', $params)) { + $params['whereClause'] = array(); + } + + $params['whereClause']['id'] = $id; + $params['whereClause']['deleted'] = 0; + + $sql = $this->createSelectQuery($entity, $params); + + $ps = $this->pdo->query($sql); + + if ($ps) { + foreach ($ps as $row) { + $entity = $this->fromRow($entity, $row); + return true; + } + } + return false; + } + + public function count(IEntity $entity, $params = array()) + { + return $this->aggregate($entity, $params, 'COUNT', 'id'); + } + + public function max(IEntity $entity, $params = array(), $field, $deleted = false) + { + return $this->aggregate($entity, $params, 'MAX', $field, true); + } + + public function min(IEntity $entity, $params = array(), $field, $deleted = false) + { + return $this->aggregate($entity, $params, 'MIN', $field, true); + } + + public function sum(IEntity $entity, $params = array()) + { + return $this->aggregate($entity, $params, 'SUM', 'id'); + } + + public function select(IEntity $entity, $params = array()) + { + $sql = $this->createSelectQuery($entity, $params); + + $dataArr = array(); + $ps = $this->pdo->query($sql); + if ($ps) { + $dataArr = $ps->fetchAll(); + } + + if ($this->returnCollection) { + $collectionClass = $this->collectionClass; + $entityArr = new $collectionClass($dataArr, $entity->getEntityName(), $this->entityFactory); + return $entityArr; + } else { + return $dataArr; + } + } + + public function aggregate(IEntity $entity, $params = array(), $aggregation, $aggregationBy, $deleted = false) + { + if (empty($aggregation) || !isset($entity->fields[$aggregationBy])) { + return false; + } + + $sql = $this->createSelectQuery($entity, $params, $aggregation, $aggregationBy, $deleted); + + $ps = $this->pdo->query($sql); + + if ($ps) { + foreach ($ps as $row) { + return $row['AggregateValue']; + } + } + return false; + } + + protected function createSelectQuery(IEntity $entity, $params = array(), $aggregation = null, $aggregationBy = null, $deleted = false) + { + $whereClause = array(); + if (array_key_exists('whereClause', $params)) { + $whereClause = $params['whereClause']; + } + + if (!$deleted) { + $whereClause = $whereClause + array('deleted' => 0); + } + + foreach (self::$selectParamList as $k) { + $$k = array_key_exists($k, $params) ? $params[$k] : null; + } + + if (empty($aggregation)) { + $selectPart = $this->getSelect($entity); + $orderPart = $this->getOrder($entity, $orderBy, $order); + } else { + $aggDist = false; + if ($distinct && $aggregation == 'COUNT') { + $aggDist = true; + } + $selectPart = $this->getAggregationSelect($entity, $aggregation, $aggregationBy, $aggDist); + } + $joinsPart = $this->getBelongsToJoins($entity); + $wherePart = $this->getWhere($entity, $whereClause); + + if (!empty($customWhere)) { + $wherePart .= ' ' . $customWhere; + } + + if (!empty($customJoin)) { + $joinsPart .= ' ' . $customJoin . ' '; + } + + if (!empty($joins) && is_array($joins)) { + $joinsRelated = $this->getJoins($entity, $joins, false, $joinConditions); + if (!empty($joinsRelated)) { + if (!empty($joinsPart)) { + $joinsPart .= ' '; + } + $joinsPart .= $joinsRelated; + } + } + + if (!empty($leftJoins) && is_array($leftJoins)) { + $joinsRelated = $this->getJoins($entity, $leftJoins, true, $joinConditions); + if (!empty($joinsRelated)) { + if (!empty($joinsPart)) { + $joinsPart .= ' '; + } + $joinsPart .= $joinsRelated; + } + } + + if (empty($aggregation)) { + return $this->composeSelectQuery($this->toDb($entity->getEntityName()), $selectPart, $joinsPart, $wherePart, $orderPart, $offset, $limit, $distinct); + } else { + return $this->composeSelectQuery($this->toDb($entity->getEntityName()), $selectPart, $joinsPart, $wherePart, null, null, null, $distinct); + } + } + + protected function getAggregationSelect(IEntity $entity, $aggregation, $aggregationBy, $distinct = false) + { + if (!isset($entity->fields[$aggregationBy])) { + return false; + } + + $aggregation = strtoupper($aggregation); + + $distinctPart = ''; + if ($distinct) { + $distinctPart = 'DISTINCT '; + } + + $selectPart = "{$aggregation}({$distinctPart}" . $this->toDb($entity->getEntityName()) . "." . $this->toDb($aggregationBy) . ") AS AggregateValue"; + return $selectPart; + } + + protected function getJoins(IEntity $entity, array $joins, $left = false, $joinConditions = array()) + { + $joinsArr = array(); + foreach ($joins as $relationName) { + $conditions = array(); + if (!empty($joinConditions[$relationName])) { + $conditions = $joinConditions[$relationName]; + } + if ($joinRelated = $this->getJoinRelated($entity, $relationName, $left, $conditions)) { + $joinsArr[] = $joinRelated; + } + } + return implode(' ', $joinsArr); + } + + + public function selectRelated(IEntity $entity, $relationName, $params = array(), $totalCount = false) + { + $relOpt = $entity->relations[$relationName]; + + if (!isset($relOpt['entity']) || !isset($relOpt['type'])) { + throw new \LogicException("Not appropriate defenition for relationship {$relationName} in " . $entity->getEntityName() . " entity"); + } + + $relEntityName = (!empty($relOpt['class'])) ? $relOpt['class'] : $relOpt['entity']; + $relEntity = $this->entityFactory->create($relEntityName); + + $whereClause = array(); + if (array_key_exists('whereClause', $params)) { + $whereClause = $params['whereClause']; + } + + $whereClause = $whereClause + array('deleted' => 0); + + foreach (self::$selectParamList as $k) { + $$k = array_key_exists($k, $params) ? $params[$k] : null; + if (is_null($$k) && isset($relOpt[$k])) { + $$k = $relOpt[$k]; + } + } + + if (!$totalCount) { + $selectPart = $this->getSelect($relEntity); + $joinsPart = $this->getBelongsToJoins($relEntity); + $orderPart = $this->getOrder($relEntity, $orderBy, $order); + + if (!empty($joins) && is_array($joins)) { + $joinsRelated = $this->getJoins($relEntity, $joins, false, $joinConditions); + if (!empty($joinsRelated)) { + if (!empty($joinsPart)) { + $joinsPart .= ' '; + } + $joinsPart .= $joinsRelated; + } + } + + if (!empty($leftJoins) && is_array($leftJoins)) { + $joinsRelated = $this->getJoins($relEntity, $leftJoins, true, $joinConditions); + if (!empty($joinsRelated)) { + if (!empty($joinsPart)) { + $joinsPart .= ' '; + } + $joinsPart .= $joinsRelated; + } + } + + } else { + $selectPart = $this->getAggregationSelect($relEntity, 'COUNT', 'id'); + $joinsPart = ''; + $orderPart = ''; + $offset = null; + $limit = null; + } + + $relType = $relOpt['type']; + + $keySet = $this->getKeys($entity, $relationName); + + $key = $keySet['key']; + $foreignKey = $keySet['foreignKey']; + + switch ($relType) { + + case IEntity::BELONGS_TO: + + $whereClause[$foreignKey] = $entity->get($key); + $wherePart = $this->getWhere($relEntity, $whereClause); + + if (!empty($customWhere)) { + $wherePart .= ' ' . $customWhere; + } + + $sql = $this->composeSelectQuery($this->toDb($relEntity->getEntityName()), $selectPart, $joinsPart, $wherePart, $orderPart, 0, 1); + $ps = $this->pdo->query($sql); + + + if ($ps) { + foreach ($ps as $row) { + if (!$totalCount) { + $relEntity = $this->fromRow($relEntity, $row); + return $relEntity; + } else { + return $row['AggregateValue']; + } + } + } + break; + + case IEntity::HAS_MANY: + case IEntity::HAS_CHILDREN: + + $whereClause[$foreignKey] = $entity->get($key); + + if ($relType == IEntity::HAS_CHILDREN) { + $foreignType = $keySet['foreignType']; + $whereClause[$foreignType] = $entity->getEntityName(); + } + + $wherePart = $this->getWhere($relEntity, $whereClause); + + if (!empty($customWhere)) { + $wherePart .= ' ' . $customWhere; + } + $dataArr = array(); + + + $sql = $this->composeSelectQuery($this->toDb($relEntity->getEntityName()), $selectPart, $joinsPart, $wherePart, $orderPart, $offset, $limit); + + $ps = $this->pdo->query($sql); + if ($ps) { + if (!$totalCount) { + $dataArr = $ps->fetchAll(); + + } else { + foreach ($ps as $row) { + return $row['AggregateValue']; + } + } + } + if ($this->returnCollection) { + $collectionClass = $this->collectionClass; + return new $collectionClass($dataArr, $relEntity->getEntityName(), $this->entityFactory); + } else { + return $dataArr; + } + break; + + case IEntity::MANY_MANY: + + $MMJoinPart = $this->getMMJoin($entity, $relationName, $keySet); + $wherePart = $this->getWhere($relEntity, $whereClause); + if ($joinsPart != '') { + $MMJoinPart = ' ' . $MMJoinPart; + } + + $dataArr = array(); + + $sql = $this->composeSelectQuery($this->toDb($relEntity->getEntityName()), $selectPart, $joinsPart . $MMJoinPart, $wherePart, $orderPart, $offset, $limit); + + $ps = $this->pdo->query($sql); + if ($ps) { + if (!$totalCount) { + $dataArr = $ps->fetchAll(); + + } else { + foreach ($ps as $row) { + return $row['AggregateValue']; + } + } + } + if ($this->returnCollection) { + $collectionClass = $this->collectionClass; + return new $collectionClass($dataArr, $relEntity->getEntityName(), $this->entityFactory); + } else { + return $dataArr; + } + break; + } + + return false; + } + + protected function getKeys(IEntity $entity, $relationName) + { + $relOpt = $entity->relations[$relationName]; + $relType = $relOpt['type']; + + switch ($relType) { + + case IEntity::BELONGS_TO: + $key = $this->toDb($entity->getEntityName()) . 'Id'; + if (isset($relOpt['key'])) { + $key = $relOpt['key']; + } + $foreignKey = 'id'; + if(isset($relOpt['foreignKey'])){ + $foreignKey = $relOpt['foreignKey']; + } + return array( + 'key' => $key, + 'foreignKey' => $foreignKey, + ); + + case IEntity::HAS_MANY: + $key = 'id'; + if (isset($relOpt['key'])){ + $key = $relOpt['key']; + } + $foreignKey = $this->toDb($entity->getEntityName()) . 'Id'; + if (isset($relOpt['foreignKey'])) { + $foreignKey = $relOpt['foreignKey']; + } + return array( + 'key' => $key, + 'foreignKey' => $foreignKey, + ); + case IEntity::HAS_CHILDREN: + $key = 'id'; + if (isset($relOpt['key'])){ + $key = $relOpt['key']; + } + $foreignKey = 'parentId'; + if (isset($relOpt['foreignKey'])) { + $foreignKey = $relOpt['foreignKey']; + } + $foreignType = 'parentType'; + if (isset($relOpt['foreignType'])) { + $foreignType = $relOpt['foreignType']; + } + return array( + 'key' => $key, + 'foreignKey' => $foreignKey, + 'foreignType' => $foreignType, + ); + + case IEntity::MANY_MANY: + $key = 'id'; + if(isset($relOpt['key'])){ + $key = $relOpt['key']; + } + $foreignKey = 'id'; + if(isset($relOpt['foreignKey'])){ + $foreignKey = $relOpt['foreignKey']; + } + $nearKey = $this->toDb($entity->getEntityName()) . 'Id'; + $distantKey = $this->toDb($relOpt['entity']) . 'Id'; + if (isset($relOpt['midKeys']) && is_array($relOpt['midKeys'])){ + $nearKey = $relOpt['midKeys'][0]; + $distantKey = $relOpt['midKeys'][1]; + } + return array( + 'key' => $key, + 'foreignKey' => $foreignKey, + 'nearKey' => $nearKey, + 'distantKey' => $distantKey, + ); + } + } + + public function countRelated(IEntity $entity, $relationName, $params = array()) + { + return $this->selectRelated($entity, $relationName, $params, true); + } + + public function relate(IEntity $entityFrom, $relationName, IEntity $entityTo) + { + $this->addRelation($entityFrom, $relationName, null, $entityTo); + } + + public function unrelate(IEntity $entityFrom, $relationName, IEntity $entityTo) + { + $this->removeRelation($entityFrom, $relationName, null, false, $entityTo); + } + + public function addRelation(IEntity $entity, $relationName, $id = null, $relEntity = null) + { + if (!is_null($relEntity)) { + $id = $relEntity->id; + } + + if (empty($id) || empty($relationName)) { + return false; + } + + $relOpt = $entity->relations[$relationName]; + + if (!isset($relOpt['entity']) || !isset($relOpt['type'])) { + throw new \LogicException("Not appropriate defenition for relationship {$relationName} in " . $entity->getEntityName() . " entity"); + } + + $relType = $relOpt['type']; + + $className = (!empty($relOpt['class'])) ? $relOpt['class'] : $relOpt['entity']; + + if (is_null($relEntity)) { + $relEntity = $this->entityFactory->create($className); + $relEntity->id = $id; + } + + $keySet = $this->getKeys($entity, $relationName); + + switch ($relType) { + case IEntity::BELONGS_TO: + case IEntity::HAS_ONE: + return false; + break; + + case IEntity::HAS_CHILDREN: + case IEntity::HAS_MANY: + $key = $keySet['key']; + $foreignKey = $keySet['foreignKey']; + + if ($this->count($relEntity, array('whereClause' => array('id' => $id))) > 0) { + + $setPart = $this->toDb($foreignKey) . " = " . $this->pdo->quote($entity->get($key)); + + if ($relType == IEntity::HAS_CHILDREN) { + $foreignType = $keySet['foreignType']; + $setPart .= ", " . $this->toDb($foreignType) . " = " . $this->pdo->quote($entity->getEntityName()); + } + + $wherePart = $this->getWhere($relEntity, array('id' => $id, 'deleted' => 0)); + $sql = $this->composeUpdateQuery($this->toDb($relEntity->getEntityName()), $setPart, $wherePart); + + if ($this->pdo->query($sql)) { + return true; + } + } else { + return false; + } + break; + + case IEntity::MANY_MANY: + $key = $keySet['key']; + $foreignKey = $keySet['foreignKey']; + $nearKey = $keySet['nearKey']; + $distantKey = $keySet['distantKey']; + + if ($this->count($relEntity, array('whereClause' => array('id' => $id))) > 0) { + $relTable = $this->toDb($relOpt['relationName']); + + $wherePart = + $this->toDb($nearKey) . " = " . $this->pdo->quote($entity->id) . " ". + "AND " . $this->toDb($distantKey) . " = " . $this->pdo->quote($relEntity->id); + if (!empty($relOpt['conditions']) && is_array($relOpt['conditions'])) { + foreach ($relOpt['conditions'] as $f => $v) { + $wherePart .= " AND " . $this->toDb($f) . " = " . $this->pdo->quote($v); + } + } + + $sql = $this->composeSelectQuery($relTable, '*', '', $wherePart); + + $ps = $this->pdo->query($sql); + + if ($ps->rowCount() == 0) { + $fieldsPart = $this->toDb($nearKey) . ", " . $this->toDb($distantKey); + $valuesPart = $this->pdo->quote($entity->id) . ", " . $this->pdo->quote($relEntity->id); + + if (!empty($relOpt['conditions']) && is_array($relOpt['conditions'])) { + foreach ($relOpt['conditions'] as $f => $v) { + $fieldsPart .= ", " . $this->toDb($f); + $valuesPart .= ", " . $this->pdo->quote($v); + } + } + + $sql = $this->composeInsertQuery($relTable, $fieldsPart, $valuesPart); + + if ($this->pdo->query($sql)) { + return true; + } + } else { + $setPart = 'deleted = 0'; + $wherePart = + $this->toDb($nearKey) . " = " . $this->pdo->quote($entity->id) . " + AND " . $this->toDb($distantKey) . " = " . $this->pdo->quote($relEntity->id) . " + "; + + if (!empty($relOpt['conditions']) && is_array($relOpt['conditions'])) { + foreach ($relOpt['conditions'] as $f => $v) { + $wherePart .= " AND " . $this->toDb($f) . " = " . $this->pdo->quote($v); + } + } + + $sql = $this->composeUpdateQuery($relTable, $setPart, $wherePart); + if ($this->pdo->query($sql)) { + return true; + } + } + } else { + return false; + } + break; + } + } + + public function removeRelation(IEntity $entity, $relationName, $id = null, $all = false, IEntity $relEntity = null) + { + if (!is_null($relEntity)) { + $id = $relEntity->id; + } + + if (empty($id) && empty($all) || empty($relationName)) { + return false; + } + + $relOpt = $entity->relations[$relationName]; + + if (!isset($relOpt['entity']) || !isset($relOpt['type'])) { + throw new \LogicException("Not appropriate defenition for relationship {$relationName} in " . $entity->getEntityName() . " entity"); + } + + $relType = $relOpt['type']; + + $className = (!empty($relOpt['class'])) ? $relOpt['class'] : $relOpt['entity']; + + if (is_null($relEntity)) { + $relEntity = $this->entityFactory->create($className); + $relEntity->id = $id; + } + + $keySet = $this->getKeys($entity, $relationName); + + switch ($relType) { + + case IEntity::BELONGS_TO: + /*$foreignKey = $keySet['foreignKey']; + $relEntity->$foreignKey = null; + $this-> + break;*/ + + case IEntity::HAS_ONE: + return false; + + + case IEntity::HAS_MANY: + case IEntity::HAS_CHILDREN: + $key = $keySet['key']; + $foreignKey = $keySet['foreignKey']; + + $setPart = $this->toDb($foreignKey) . " = " . "NULL"; + + $whereClause = array('deleted' => 0); + if (empty($all)) { + $whereClause['id'] = $id; + } else { + $whereClause[$foreignKey] = $entity->id; + } + + if ($relType == IEntity::HAS_CHILDREN) { + $foreignType = $keySet['foreignType']; + $whereClause[$foreignType] = $entity->getEntityName(); + } + + $wherePart = $this->getWhere($relEntity, $whereClause); + $sql = $this->composeUpdateQuery($this->toDb($relEntity->getEntityName()), $setPart, $wherePart); + if ($this->pdo->query($sql)) { + return true; + } + break; + + case IEntity::MANY_MANY: + $key = $keySet['key']; + $foreignKey = $keySet['foreignKey']; + $nearKey = $keySet['nearKey']; + $distantKey = $keySet['distantKey']; + + $relTable = $this->toDb($relOpt['relationName']); + + $setPart = 'deleted = 1'; + $wherePart = $this->toDb($nearKey) . " = " . $this->pdo->quote($entity->id); + + + if (empty($all)) { + $wherePart .= " AND " . $this->toDb($distantKey) . " = " . $this->pdo->quote($id) . ""; + } + + if (!empty($relOpt['conditions']) && is_array($relOpt['conditions'])) { + foreach ($relOpt['conditions'] as $f => $v) { + $wherePart .= " AND " . $this->toDb($f) . " = " . $this->pdo->quote($v); + } + } + + $sql = $this->composeUpdateQuery($relTable, $setPart, $wherePart); + + if ($this->pdo->query($sql)) { + return true; + } + break; + } + } + + public function removeAllRelations(IEntity $entity, $relationName) + { + $this->removeRelation($entity, $relationName, null, true); + } + + protected function quote($value) + { + if (is_null($value)) { + return 'NULL'; + } else { + return $this->pdo->quote($value); + } + } + + public function insert(IEntity $entity) + { + $dataArr = $this->toArray($entity); + + $fieldArr = array(); + $valArr = array(); + foreach ($dataArr as $field => $value) { + $fieldArr[] = $this->toDb($field); + $valArr[] = $this->quote($value); + } + $fieldsPart = implode(", ", $fieldArr); + $valuesPart = implode(", ", $valArr); + + $sql = $this->composeInsertQuery($this->toDb($entity->getEntityName()), $fieldsPart, $valuesPart); + + if ($this->pdo->query($sql)) { + return $entity->id; + } + + return false; + } + + public function update(IEntity $entity) + { + $dataArr = $this->toArray($entity); + + $setArr = array(); + foreach ($dataArr as $field => $value) { + if ($field == 'id') { + continue; + } + if ($entity->fields[$field]['type'] == IEntity::FOREIGN) { + continue; + } + + if ($entity->getFetched($field) === $value) { + continue; + } + + $setArr[] = $this->toDb($field) . " = " . $this->quote($value); + } + + if (count($setArr) == 0) { + return $entity->id; + } + + $setPart = implode(', ', $setArr); + $wherePart = $this->getWhere($entity, array('id' => $entity->id, 'deleted' => 0)); + + $sql = $this->composeUpdateQuery($this->toDb($entity->getEntityName()), $setPart, $wherePart); + + if ($this->pdo->query($sql)) { + return $entity->id; + } + + return false; + } + + public function delete(IEntity $entity) + { + $entity->set('deleted', true); + return $this->update($entity); + } + + protected function toArray(IEntity $entity, $onlyStorable = true) + { + $arr = array(); + foreach ($entity->fields as $field => $fieldDefs) { + if ($entity->has($field)) { + if ($onlyStorable) { + if (!empty($fieldDefs['notStorable']) || isset($fieldDefs['source']) && $fieldDefs['source'] != 'db') + continue; + if ($fieldDefs['type'] == IEntity::FOREIGN) + continue; + } + $arr[$field] = $entity->get($field); + } + } + return $arr; + } + + protected function fromRow(IEntity $entity, $data) + { + $entity->set($data); + return $entity; + } + + protected function getAlias(IEntity $entity, $key) + { + if (!isset($this->aliasesCache[$entity->getEntityName()])) { + $this->aliasesCache[$entity->getEntityName()] = $this->getTableAliases($entity); + } + + if (isset($this->aliasesCache[$entity->getEntityName()][$key])) { + return $this->aliasesCache[$entity->getEntityName()][$key]; + } else { + return false; + } + } + + protected function getTableAliases(IEntity $entity) + { + $aliases = array(); + $c = 0; + + $occuranceHash = array(); + + foreach ($entity->relations as $name => $r) { + if ($r['type'] == IEntity::BELONGS_TO) { + $key = $r['key']; + $table = $this->toDb($r['entity']); + + if (!array_key_exists($key, $aliases)) { + if (array_key_exists($table, $occuranceHash)) { + $occuranceHash[$table]++; + } else { + $occuranceHash[$table] = 0; + } + $suffix = '_f'; + if ($occuranceHash[$table] > 0) { + $suffix .= '_' . $occuranceHash[$table]; + } + + $aliases[$key] = $table . $suffix; + } + } + } + + return $aliases; + } + + protected function getFieldPath(IEntity $entity, $field) + { + if (isset($entity->fields[$field])) { + $f = $entity->fields[$field]; + + if (isset($f['source'])) { + if ($f['source'] != 'db') { + return false; + } + } + + if (!empty($f['notStorable'])) { + return false; + } + + $fieldPath = ''; + + switch($f['type']) { + case 'foreign': + if (isset($f['relation'])) { + $relationName = $f['relation']; + + $keySet = $this->getKeys($entity, $relationName); + $key = $keySet['key']; + + $foreigh = $f['foreign']; + + if (is_array($foreigh)) { + foreach ($foreigh as $i => $value) { + if ($value == ' ') { + $foreigh[$i] = '\' \''; + } else { + $foreigh[$i] = $this->getAlias($entity, $key) . '.' . $this->toDb($value); + } + } + $fieldPath = 'TRIM(CONCAT(' . implode(', ', $foreigh). '))'; + } else { + $fieldPath = $this->getAlias($entity, $key) . '.' . $this->toDb($foreigh); + } + } + break; + default: + $fieldPath = $this->toDb($entity->getEntityName()) . '.' . $this->toDb($field) ; + } + + return $fieldPath; + } + + return false; + } + + protected function getWhere(IEntity $entity, $whereClause, $sqlOp = 'AND') + { + $whereParts = array(); + + foreach ($whereClause as $field => $value) { + + if (is_int($field)) { + $field = 'AND'; + } + + if (!in_array($field, self::$sqlOperators)) { + + $inRelated = false; + + if (strpos($field, '.') !== false) { + list($entityName, $field) = array_map('trim', explode('.', $field)); + $entityName = preg_replace('/[^A-Za-z0-9_]+/', '', $entityName); + $field = preg_replace('/[^A-Za-z0-9_]+/', '', $field); + $inRelated = true; + } + + $operator = '='; + + if (!preg_match('/^[a-z0-9]+$/i', $field)) { + foreach (self::$comparisonOperators as $op => $opDb) { + if (strpos($field, $op) !== false) { + $field = trim(str_replace($op, '', $field)); + $operator = $opDb; + break; + } + } + } + + if (!$inRelated) { + + if (!isset($entity->fields[$field])) { + continue; + } + + $fieldDefs = $entity->fields[$field]; + + if (!empty($fieldDefs['where']) && !empty($fieldDefs['where'][$operator])) { + $whereParts[] = str_replace('{text}', $value, $fieldDefs['where'][$operator]); + } else { + if ($fieldDefs['type'] == IEntity::FOREIGN) { + $leftPart = ''; + if (isset($fieldDefs['relation'])) { + $relationName = $fieldDefs['relation']; + if (isset($entity->relations[$relationName])) { + $keySet = $this->getKeys($entity, $relationName); + $key = $keySet['key']; + + $alias = $this->getAlias($entity, $key); + if ($alias) { + $leftPart = $alias . '.' . $this->toDb($fieldDefs['foreign']); + } + } + } + } else { + $leftPart = $this->toDb($entity->getEntityName()) . '.' . $this->toDb($field); + } + } + } else { + $leftPart = $this->toDb($entityName) . '.' . $this->toDb($field); + } + + if (!empty($leftPart)) { + if (!is_array($value)) { + $whereParts[] = $leftPart . " " . $operator . " " . $this->pdo->quote($value); + + } else { + $valArr = $value; + foreach ($valArr as $k => $v) { + $valArr[$k] = $this->pdo->quote($valArr[$k]); + } + $oppose = ''; + if ($operator == '<>') { + $oppose = 'NOT'; + } + $whereParts[] = $leftPart . " {$oppose} IN " . "(" . implode(',', $valArr) . ")"; + } + } + } else { + $whereParts[] = "(" . $this->getWhere($entity, $value, $field) . ")"; + } + } + return implode(" " . $sqlOp . " ", $whereParts); + } + + protected function getBelongsToJoins(IEntity $entity) + { + $joinsArr = array(); + + foreach ($entity->relations as $relationName => $r) { + if ($r['type'] == IEntity::BELONGS_TO) { + $keySet = $this->getKeys($entity, $relationName); + $key = $keySet['key']; + $foreignKey = $keySet['foreignKey']; + + $alias = $this->getAlias($entity, $key); + + if ($alias) { + $joinsArr[] = + "LEFT JOIN `" . $this->toDb($r['entity']) . "` AS `" . $alias . "` ON ". + $this->toDb($entity->getEntityName()) . "." . $this->toDb($key) . " = " . $alias . "." . $this->toDb($foreignKey); + } + } + } + + return implode(' ', $joinsArr); + } + + protected function getJoinRelated(IEntity $entity, $relationName, $left = false, $conditions = array()) + { + $relOpt = $entity->relations[$relationName]; + $keySet = $this->getKeys($entity, $relationName); + + $pre = ($left) ? 'LEFT ' : ''; + + if ($relOpt['type'] == IEntity::MANY_MANY) { + + $key = $keySet['key']; + $foreignKey = $keySet['foreignKey']; + $nearKey = $keySet['nearKey']; + $distantKey = $keySet['distantKey']; + + $relTable = $this->toDb($relOpt['relationName']); + $distantTable = $this->toDb($relOpt['entity']); + + $join = + "{$pre}JOIN `{$relTable}` ON {$this->toDb($entity->getEntityName())}." . $this->toDb($key) . " = {$relTable}." . $this->toDb($nearKey) + . " AND " + . "{$relTable}.deleted = " . $this->pdo->quote(0); + + if (!empty($relOpt['conditions']) && is_array($relOpt['conditions'])) { + $conditions = array_merge($conditions, $relOpt['conditions']); + } + foreach ($conditions as $f => $v) { + $join .= " AND {$relTable}." . $this->toDb($f) . " = " . $this->pdo->quote($v); + } + + $join .= " {$pre}JOIN `{$distantTable}` ON {$distantTable}." . $this->toDb($foreignKey) . " = {$relTable}." . $this->toDb($distantKey) + . " AND " + . "{$distantTable}.deleted = " . $this->pdo->quote(0) . ""; + + return $join; + } + + if ($relOpt['type'] == IEntity::HAS_MANY) { + + $foreignKey = $keySet['foreignKey']; + $distantTable = $this->toDb($relOpt['entity']); + + // TODO conditions + + $join = + "{$pre}JOIN `{$distantTable}` ON {$this->toDb($entity->getEntityName())}." . $this->toDb('id') . " = {$distantTable}." . $this->toDb($foreignKey) + . " AND " + . "{$distantTable}.deleted = " . $this->pdo->quote(0) . ""; + + return $join; + } + + return false; + } + + protected function getMMJoin(IEntity $entity, $relationName, $keySet = false) + { + $relOpt = $entity->relations[$relationName]; + + if (empty($keySet)) { + $keySet = $this->getKeys($entity, $relationName); + } + + $key = $keySet['key']; + $foreignKey = $keySet['foreignKey']; + $nearKey = $keySet['nearKey']; + $distantKey = $keySet['distantKey']; + + $relTable = $this->toDb($relOpt['relationName']); + $distantTable = $this->toDb($relOpt['entity']); + + $join = + "JOIN `{$relTable}` ON {$distantTable}." . $this->toDb($foreignKey) . " = {$relTable}." . $this->toDb($distantKey) + . " AND " + . "{$relTable}." . $this->toDb($nearKey) . " = " . $this->pdo->quote($entity->get($key)) + . " AND " + . "{$relTable}.deleted = " . $this->pdo->quote(0) . ""; + + if (!empty($relOpt['conditions']) && is_array($relOpt['conditions'])) { + foreach ($relOpt['conditions'] as $f => $v) { + $join .= " AND {$relTable}." . $this->toDb($f) . " = " . $this->pdo->quote($v); + } + } + + return $join; + } + + protected function getSelect(IEntity $entity, $fields = null) + { + $select = ""; + $arr = array(); + $specifiedList = is_array($fields) ? true : false; + + foreach ($entity->fields as $field => $fieldDefs) { + if ($specifiedList) { + if (!in_array($field, $fields)) { + continue; + } + } + + if (!empty($fieldDefs['select'])) { + $fieldPath = $fieldDefs['select']; + } else { + if (!empty($fieldDefs['notStorable'])) { + continue; + } + $fieldPath = $this->getFieldPath($entity, $field); + } + + $arr[] = $fieldPath . ' AS `' . $field . '`'; + } + + $select = implode(', ', $arr); + + return $select; + } + + protected function getOrder(IEntity $entity, $orderBy = null, $order = null) + { + $orderStr = ""; + + if (!is_null($orderBy)) { + if (!is_null($order)) { + $order = strtoupper($order); + if (!in_array($order, array('ASC', 'DESC'))) { + $order = 'ASC'; + } + } else { + $order = 'ASC'; + } + + $fieldDefs = $entity->fields[$orderBy]; + if (!empty($fieldDefs['orderBy'])) { + $orderPart = str_replace('{direction}', $order, $fieldDefs['orderBy']); + $orderStr .= "ORDER BY {$orderPart}"; + } else { + $fieldPath = $this->getFieldPath($entity, $orderBy); + if ($fieldDefs['type'] == iEntity::FOREIGN) { + + } else { + + } + $orderStr .= "ORDER BY {$fieldPath} " . $order; + } + } + + return $orderStr; + } + + protected function composeInsertQuery($table, $fields, $values) + { + $sql = "INSERT INTO `{$table}`"; + $sql .= " ({$fields})"; + if (!is_array($values)) { + $sql .= " VALUES ({$values})"; + } else { + $sql .= " VALUES (" . implode("), (", $values) . ")"; + } + + return $sql; + } + + protected function composeUpdateQuery($table, $set, $where) + { + $sql = "UPDATE `{$table}` SET {$set} WHERE {$where}"; + + return $sql; + } + + abstract protected function composeSelectQuery($table, $select, $joins = '', $where = '', $order = '', $offset = null, $limit = null, $distinct = null); + + abstract protected function toDb($field); + + public function setReturnCollection($returnCollection) + { + $this->returnCollection = $returnCollection; + } + + public function setCollectionClass($collectionClass) + { + $this->collectionClass = $collectionClass; + } +} + + diff --git a/application/Espo/ORM/DB/MysqlMapper.php b/application/Espo/ORM/DB/MysqlMapper.php index aebd3ab392..74c8a21042 100644 --- a/application/Espo/ORM/DB/MysqlMapper.php +++ b/application/Espo/ORM/DB/MysqlMapper.php @@ -1,4 +1,4 @@ -fieldsMapCache)) { - return $this->fieldsMapCache[$field]; - - } else { - $dbField = strtolower(preg_replace('/(?<=[a-z])([A-Z])/', '_$1', $field)); - $this->fieldsMapCache[$field] = $dbField; - return $dbField; - } - } -} -?> + ************************************************************************/ + +namespace Espo\ORM\DB; + +use Espo\ORM\Entity; +use Espo\ORM\Classes\EntityCollection; +use PDO; + +/** + * Abstraction for MySQL DB. + * Mapping of Entity to DB. + * Should be used internally only. + */ +class MysqlMapper extends Mapper +{ + protected function composeSelectQuery($table, $select, $joins = '', $where = '', $order = '', $offset = null, $limit = null, $distinct = null) + { + $sql = "SELECT"; + + if (!empty($distinct)) { + $sql .= " DISTINCT"; + } + + $sql .= " {$select} FROM `{$table}`"; + + if (!empty($joins)) { + $sql .= " {$joins}"; + } + + if (!empty($where)) { + $sql .= " WHERE {$where}"; + } + + if (!empty($order)) { + $sql .= " {$order}"; + } + + if (is_null($offset) && !is_null($limit)) { + $offset = 0; + } + + if (!is_null($offset) && !is_null($limit)) { + $offset = intval($offset); + $limit = intval($limit); + $sql .= " LIMIT {$offset}, {$limit}"; + } + + return $sql; + } + + protected function toDb($field) + { + if (array_key_exists($field, $this->fieldsMapCache)) { + return $this->fieldsMapCache[$field]; + + } else { + $dbField = strtolower(preg_replace('/(?<=[a-z])([A-Z])/', '_$1', $field)); + $this->fieldsMapCache[$field] = $dbField; + return $dbField; + } + } +} +?> diff --git a/application/Espo/ORM/Entity.php b/application/Espo/ORM/Entity.php index 3fd26f7f30..2ddee67e5d 100644 --- a/application/Espo/ORM/Entity.php +++ b/application/Espo/ORM/Entity.php @@ -1,4 +1,4 @@ -container = $data; - $this->entityName = $entityName; - $this->entityFactory = $entityFactory; - } - - public function rewind() - { - $this->position = 0; - - while (!$this->valid() && $this->position < count($this->container)) { - $this->position ++; - } - } - - public function current() - { - return $this->getEntityByOffset($this->position); - } - - public function key() - { - return $this->position; - } - - public function next() - { - do { - $this->position ++; - $next = false; - if (!$this->valid() && $this->position < count($this->container)) { - $next = true; - } - } while ($next); - } - - public function valid() - { - return isset($this->container[$this->position]); - } - - public function offsetExists($offset) - { - return isset($this->container[$offset]); - } - - public function offsetGet($offset) - { - if (!isset($this->container[$offset])) { - return null; - } - return $this->getEntityByOffset($offset); - } - - public function offsetSet($offset, $value) - { - if (!($value instanceof Entity)) { - throw new \InvalidArgumentException('Only Entity is allowed to be added to EntityCollection.'); - } - - if (is_null($offset)) { - $this->container[] = $value; - } else { - $this->container[$offset] = $value; - } - } - - public function offsetUnset($offset) - { - unset($this->container[$offset]); - } - - public function count() - { - return count($this->container); - } - - public function seek($offset) - { - $this->position = $offset; - if (!$this->valid()) { - throw new \OutOfBoundsException("Invalid seek offset ($offset)."); - } - } - - public function append(Entity $entity) - { - $this->container[] = $entity; - } - - private function getEntityByOffset($offset) - { - $value = $this->container[$offset]; - - if ($value instanceof Entity) { - return $value; - } else if (is_array($value)) { - $this->container[$offset] = $this->buildEntityFromArray($value); - } else { - return null; - } - - return $this->container[$offset]; - } - - protected function buildEntityFromArray(array $dataArray) - { - $entity = $this->entityFactory->create($this->entityName); - $entity->set($dataArray); - $entity->setAsFetched(); - return $entity; - } - - public function getEntityName() - { - return $this->entityName; - } - - public function getInnerContainer() - { - return $this->container; - } - - public function merge(EntityCollection $collection) - { - $newData = $this->container; - $incomingData = $collection->getInnerContainer(); - - foreach ($incomingData as $v) { - if (!$this->contains($v)) { - $this->container[] = $v; - } - } - } - - public function contains($value) - { - if ($this->indexOf($value) !== false) { - return true; - } - return false; - } - - public function indexOf($value) - { - $index = 0; - if (is_array($value)) { - foreach ($this->container as $v) { - if (is_array($v)) { - if ($value['id'] == $v['id']) { - return $index; - } - } else if ($v instanceof Entity) { - if ($value['id'] == $v->id) { - return $index; - } - } - $index ++; - } - } else if ($value instanceof Entity) { - foreach ($this->container as $v) { - if (is_array($v)) { - if ($value->id == $v['id']) { - return $index; - } - } else if ($v instanceof Entity) { - if ($value === $v) { - return $index; - } - } - $index ++; - } - } - return false; - } - - public function toArray() - { - $arr = array(); - foreach ($this as $entity) { - $arr[] = $entity->toArray(); - } - return $arr; - } - -} - + ************************************************************************/ + +namespace Espo\ORM; + +class EntityCollection implements \Iterator, \Countable, \ArrayAccess, \SeekableIterator +{ + private $entityFactory = null; + + private $entityName; + + private $position = 0; + + protected $container = array(); + + public function __construct($data = array(), $entityName, EntityFactory $entityFactory = null) + { + $this->container = $data; + $this->entityName = $entityName; + $this->entityFactory = $entityFactory; + } + + public function rewind() + { + $this->position = 0; + + while (!$this->valid() && $this->position < count($this->container)) { + $this->position ++; + } + } + + public function current() + { + return $this->getEntityByOffset($this->position); + } + + public function key() + { + return $this->position; + } + + public function next() + { + do { + $this->position ++; + $next = false; + if (!$this->valid() && $this->position < count($this->container)) { + $next = true; + } + } while ($next); + } + + public function valid() + { + return isset($this->container[$this->position]); + } + + public function offsetExists($offset) + { + return isset($this->container[$offset]); + } + + public function offsetGet($offset) + { + if (!isset($this->container[$offset])) { + return null; + } + return $this->getEntityByOffset($offset); + } + + public function offsetSet($offset, $value) + { + if (!($value instanceof Entity)) { + throw new \InvalidArgumentException('Only Entity is allowed to be added to EntityCollection.'); + } + + if (is_null($offset)) { + $this->container[] = $value; + } else { + $this->container[$offset] = $value; + } + } + + public function offsetUnset($offset) + { + unset($this->container[$offset]); + } + + public function count() + { + return count($this->container); + } + + public function seek($offset) + { + $this->position = $offset; + if (!$this->valid()) { + throw new \OutOfBoundsException("Invalid seek offset ($offset)."); + } + } + + public function append(Entity $entity) + { + $this->container[] = $entity; + } + + private function getEntityByOffset($offset) + { + $value = $this->container[$offset]; + + if ($value instanceof Entity) { + return $value; + } else if (is_array($value)) { + $this->container[$offset] = $this->buildEntityFromArray($value); + } else { + return null; + } + + return $this->container[$offset]; + } + + protected function buildEntityFromArray(array $dataArray) + { + $entity = $this->entityFactory->create($this->entityName); + $entity->set($dataArray); + $entity->setAsFetched(); + return $entity; + } + + public function getEntityName() + { + return $this->entityName; + } + + public function getInnerContainer() + { + return $this->container; + } + + public function merge(EntityCollection $collection) + { + $newData = $this->container; + $incomingData = $collection->getInnerContainer(); + + foreach ($incomingData as $v) { + if (!$this->contains($v)) { + $this->container[] = $v; + } + } + } + + public function contains($value) + { + if ($this->indexOf($value) !== false) { + return true; + } + return false; + } + + public function indexOf($value) + { + $index = 0; + if (is_array($value)) { + foreach ($this->container as $v) { + if (is_array($v)) { + if ($value['id'] == $v['id']) { + return $index; + } + } else if ($v instanceof Entity) { + if ($value['id'] == $v->id) { + return $index; + } + } + $index ++; + } + } else if ($value instanceof Entity) { + foreach ($this->container as $v) { + if (is_array($v)) { + if ($value->id == $v['id']) { + return $index; + } + } else if ($v instanceof Entity) { + if ($value === $v) { + return $index; + } + } + $index ++; + } + } + return false; + } + + public function toArray() + { + $arr = array(); + foreach ($this as $entity) { + $arr[] = $entity->toArray(); + } + return $arr; + } + +} + diff --git a/application/Espo/ORM/EntityFactory.php b/application/Espo/ORM/EntityFactory.php index 52b7b1e4a5..deb0a358cc 100644 --- a/application/Espo/ORM/EntityFactory.php +++ b/application/Espo/ORM/EntityFactory.php @@ -1,4 +1,4 @@ -params = $params; - - $this->metadata = new Metadata(); - - if (!empty($params['metadata'])) { - $this->setMetadata($params['metadata']); - } - - $entityFactoryClassName = '\\Espo\\ORM\\EntityFactory'; - if (!empty($params['entityFactoryClassName'])) { - $entityFactoryClassName = $params['entityFactoryClassName']; - } - $this->entityFactory = new $entityFactoryClassName($this, $this->metadata); - - - $repositoryFactoryClassName = '\\Espo\\ORM\\RepositoryFactory'; - if (!empty($params['repositoryFactoryClassName'])) { - $repositoryFactoryClassName = $params['repositoryFactoryClassName']; - } - $this->repositoryFactory = new $repositoryFactoryClassName($this, $this->entityFactory); - - $this->init(); - } - - public function getMapper($className) - { - if (empty($this->mappers[$className])) { - $this->mappers[$className] = new $className($this->getPDO(), $this->entityFactory); - } - return $this->mappers[$className]; - } - - protected function initPDO() - { - $params = $this->params; - - $this->pdo = new \PDO('mysql:host='.$params['host'].';dbname=' . $params['dbname'], $params['user'], $params['password']); - $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); - } - - public function getEntity($name, $id = null) - { - return $this->getRepository($name)->get($id); - } - - public function saveEntity(Entity $entity) - { - $entityName = $entity->getEntityName(); - return $this->getRepository($entityName)->save($entity); - } - - public function removeEntity(Entity $entity) - { - $entityName = $entity->getEntityName(); - return $this->getRepository($entityName)->remove($entity); - } - - public function getRepository($name) - { - if (empty($this->repositoryHash[$name])) { - $this->repositoryHash[$name] = $this->repositoryFactory->create($name); - } - return $this->repositoryHash[$name]; - } - - public function setMetadata(array $data) - { - $this->metadata->setData($data); - } - - public function getMetadata() - { - return $this->metadata; - } - - public function getPDO() - { - if (empty($this->pdo)) { - $this->initPDO(); - } - return $this->pdo; - } - - public function normalizeRepositoryName($name) - { - return $name; - } - - public function normalizeEntityName($name) - { - return $name; - } - - public function createCollection($entityName, $data = array()) - { - $seed = $this->getEntity($entityName); - $collection = new EntityCollection($data, $seed, $this->entityFactory); - return $collection; - } - - protected function init() - { - } -} - +params = $params; + + $this->metadata = new Metadata(); + + if (!empty($params['metadata'])) { + $this->setMetadata($params['metadata']); + } + + $entityFactoryClassName = '\\Espo\\ORM\\EntityFactory'; + if (!empty($params['entityFactoryClassName'])) { + $entityFactoryClassName = $params['entityFactoryClassName']; + } + $this->entityFactory = new $entityFactoryClassName($this, $this->metadata); + + + $repositoryFactoryClassName = '\\Espo\\ORM\\RepositoryFactory'; + if (!empty($params['repositoryFactoryClassName'])) { + $repositoryFactoryClassName = $params['repositoryFactoryClassName']; + } + $this->repositoryFactory = new $repositoryFactoryClassName($this, $this->entityFactory); + + $this->init(); + } + + public function getMapper($className) + { + if (empty($this->mappers[$className])) { + $this->mappers[$className] = new $className($this->getPDO(), $this->entityFactory); + } + return $this->mappers[$className]; + } + + protected function initPDO() + { + $params = $this->params; + + $this->pdo = new \PDO('mysql:host='.$params['host'].';dbname=' . $params['dbname'], $params['user'], $params['password']); + $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + } + + public function getEntity($name, $id = null) + { + return $this->getRepository($name)->get($id); + } + + public function saveEntity(Entity $entity) + { + $entityName = $entity->getEntityName(); + return $this->getRepository($entityName)->save($entity); + } + + public function removeEntity(Entity $entity) + { + $entityName = $entity->getEntityName(); + return $this->getRepository($entityName)->remove($entity); + } + + public function getRepository($name) + { + if (empty($this->repositoryHash[$name])) { + $this->repositoryHash[$name] = $this->repositoryFactory->create($name); + } + return $this->repositoryHash[$name]; + } + + public function setMetadata(array $data) + { + $this->metadata->setData($data); + } + + public function getMetadata() + { + return $this->metadata; + } + + public function getPDO() + { + if (empty($this->pdo)) { + $this->initPDO(); + } + return $this->pdo; + } + + public function normalizeRepositoryName($name) + { + return $name; + } + + public function normalizeEntityName($name) + { + return $name; + } + + public function createCollection($entityName, $data = array()) + { + $seed = $this->getEntity($entityName); + $collection = new EntityCollection($data, $seed, $this->entityFactory); + return $collection; + } + + protected function init() + { + } +} + diff --git a/application/Espo/ORM/IEntity.php b/application/Espo/ORM/IEntity.php index 1a1afc80b9..b17069fb56 100644 --- a/application/Espo/ORM/IEntity.php +++ b/application/Espo/ORM/IEntity.php @@ -1,4 +1,4 @@ -entityManager = $entityManager; - $this->entityFactroy = $entityFactroy; - } - - public function create($name) - { - $className = $this->entityManager->normalizeRepositoryName($name); - - if (!class_exists($className)) { - $className = $this->defaultRepositoryClassName; - } - - $repository = new $className($name, $this->entityManager, $this->entityFactroy); - return $repository; - } - - protected function normalizeName($name) - { - return $name; - } - - public function setDefaultRepositoryClassName($defaultRepositoryClassName) - { - $this->defaultRepositoryClassName = $defaultRepositoryClassName; - } -} - + ************************************************************************/ + +namespace Espo\ORM; + +class RepositoryFactory +{ + protected $entityFactroy; + + protected $entityManager; + + protected $defaultRepositoryClassName = '\\Espo\\ORM\\Repository'; + + public function __construct(EntityManager $entityManager, EntityFactory $entityFactroy) + { + $this->entityManager = $entityManager; + $this->entityFactroy = $entityFactroy; + } + + public function create($name) + { + $className = $this->entityManager->normalizeRepositoryName($name); + + if (!class_exists($className)) { + $className = $this->defaultRepositoryClassName; + } + + $repository = new $className($name, $this->entityManager, $this->entityFactroy); + return $repository; + } + + protected function normalizeName($name) + { + return $name; + } + + public function setDefaultRepositoryClassName($defaultRepositoryClassName) + { + $this->defaultRepositoryClassName = $defaultRepositoryClassName; + } +} + diff --git a/application/Espo/Repositories/Email.php b/application/Espo/Repositories/Email.php index d42c58e5bb..5bbeb83f58 100644 --- a/application/Espo/Repositories/Email.php +++ b/application/Espo/Repositories/Email.php @@ -1,4 +1,4 @@ -dependencies[] = 'fileManager'; - } - - protected function getFileManager() - { - return $this->injections['fileManager']; - } - - public function parse($id, array $params = array(), $copyAttachments = false) - { - $emailTemplate = $this->getEntity($id); - if (empty($emailTemplate)) { - throw new NotFound(); - } - - $entityList = array(); - if (!empty($params['entityHash']) && is_array($params['entityHash'])) { - $entityList = $params['entityHash']; - } - - if (!empty($params['emailAddress'])) { - $emailAddress = $this->getEntityManager()->getRepository('EmailAddress')->where(array( - 'lower' => $params['emailAddress'] - ))->findOne(); - - - if (!empty($emailAddress)) { - $pdo = $this->getEntityManager()->getPDO(); - $sql = " - SELECT * FROM `entity_email_address` - WHERE - `primary` = 1 AND `deleted` = 0 AND `email_address_id` = " . $pdo->quote($emailAddress->id). " - "; - $sth = $pdo->prepare($sql); - $sth->execute(); - if ($row = $sth->fetch()) { - if (!empty($row['entity_id'])) { - $entity = $this->getEntityManager()->getEntity($row['entity_type'], $row['entity_id']); - if (!empty($entity::$person)) { - $entityList['Person'] = $entity; - } - } - } - } - } - - if (!empty($params['parentId']) && !empty($params['parentType'])) { - $parent = $this->getEntityManager()->getEntity($params['parentType'], $params['parentId']); - if (!empty($parent)) { - $entityList[$params['parentType']] = $parent; - $entityList['Parent'] = $parent; - - if (empty($entityList['Person']) && !empty($parent::$person)) { - $entityList['Person'] = $parent; - } - } - } - - $subject = $emailTemplate->get('subject'); - $body = $emailTemplate->get('body'); - - foreach ($entityList as $type => $entity) { - $subject = $this->parseText($type, $entity, $subject); - } - foreach ($entityList as $type => $entity) { - $body = $this->parseText($type, $entity, $body); - } - - $attachmentsIds = array(); - - if ($copyAttachments) { - $attachmentList = $emailTemplate->get('attachments'); - if (!empty($attachmentList)) { - foreach ($attachmentList as $attachment) { - $clone = $this->getEntityManager()->getEntity('Attachment'); - $data = $attachment->toArray(); - unset($data['parentType']); - unset($data['parentId']); - unset($data['id']); - $clone->set($data); - $this->getEntityManager()->saveEntity($clone); - - $contents = $this->getFileManager()->getContents('data/upload/' . $attachment->id); - if (empty($contents)) { - continue; - } - $this->getFileManager()->putContents('data/upload/' . $clone->id, $contents); - - $attachmentsIds[] = $clone->id; - } - } - } - - return array( - 'subject' => $subject, - 'body' => $body, - 'attachmentsIds' => $attachmentsIds, - 'isHtml' => $emailTemplate->get('isHtml') - ); - } - - protected function parseText($type, Entity $entity, $text) - { - $fields = array_keys($entity->getFields()); - foreach ($fields as $field) { - $text = str_replace('{' . $type . '.' . $field . '}', $entity->get($field), $text); - } - return $text; - } -} - +dependencies[] = 'fileManager'; + } + + protected function getFileManager() + { + return $this->injections['fileManager']; + } + + public function parse($id, array $params = array(), $copyAttachments = false) + { + $emailTemplate = $this->getEntity($id); + if (empty($emailTemplate)) { + throw new NotFound(); + } + + $entityList = array(); + if (!empty($params['entityHash']) && is_array($params['entityHash'])) { + $entityList = $params['entityHash']; + } + + if (!empty($params['emailAddress'])) { + $emailAddress = $this->getEntityManager()->getRepository('EmailAddress')->where(array( + 'lower' => $params['emailAddress'] + ))->findOne(); + + + if (!empty($emailAddress)) { + $pdo = $this->getEntityManager()->getPDO(); + $sql = " + SELECT * FROM `entity_email_address` + WHERE + `primary` = 1 AND `deleted` = 0 AND `email_address_id` = " . $pdo->quote($emailAddress->id). " + "; + $sth = $pdo->prepare($sql); + $sth->execute(); + if ($row = $sth->fetch()) { + if (!empty($row['entity_id'])) { + $entity = $this->getEntityManager()->getEntity($row['entity_type'], $row['entity_id']); + if (!empty($entity::$person)) { + $entityList['Person'] = $entity; + } + } + } + } + } + + if (!empty($params['parentId']) && !empty($params['parentType'])) { + $parent = $this->getEntityManager()->getEntity($params['parentType'], $params['parentId']); + if (!empty($parent)) { + $entityList[$params['parentType']] = $parent; + $entityList['Parent'] = $parent; + + if (empty($entityList['Person']) && !empty($parent::$person)) { + $entityList['Person'] = $parent; + } + } + } + + $subject = $emailTemplate->get('subject'); + $body = $emailTemplate->get('body'); + + foreach ($entityList as $type => $entity) { + $subject = $this->parseText($type, $entity, $subject); + } + foreach ($entityList as $type => $entity) { + $body = $this->parseText($type, $entity, $body); + } + + $attachmentsIds = array(); + + if ($copyAttachments) { + $attachmentList = $emailTemplate->get('attachments'); + if (!empty($attachmentList)) { + foreach ($attachmentList as $attachment) { + $clone = $this->getEntityManager()->getEntity('Attachment'); + $data = $attachment->toArray(); + unset($data['parentType']); + unset($data['parentId']); + unset($data['id']); + $clone->set($data); + $this->getEntityManager()->saveEntity($clone); + + $contents = $this->getFileManager()->getContents('data/upload/' . $attachment->id); + if (empty($contents)) { + continue; + } + $this->getFileManager()->putContents('data/upload/' . $clone->id, $contents); + + $attachmentsIds[] = $clone->id; + } + } + } + + return array( + 'subject' => $subject, + 'body' => $body, + 'attachmentsIds' => $attachmentsIds, + 'isHtml' => $emailTemplate->get('isHtml') + ); + } + + protected function parseText($type, Entity $entity, $text) + { + $fields = array_keys($entity->getFields()); + foreach ($fields as $field) { + $text = str_replace('{' . $type . '.' . $field . '}', $entity->get($field), $text); + } + return $text; + } +} + diff --git a/application/Espo/Services/GlobalSearch.php b/application/Espo/Services/GlobalSearch.php index 40a0f8d6af..675284e905 100644 --- a/application/Espo/Services/GlobalSearch.php +++ b/application/Espo/Services/GlobalSearch.php @@ -1,4 +1,4 @@ - 'Y-m-d', - 'DD-MM-YYYY' => 'd-m-Y', - 'MM-DD-YYYY' => 'm-d-Y', - 'MM/DD/YYYY' => 'm/d/Y', - 'DD/MM/YYYY' => 'd/m/Y', - 'DD.MM.YYYY' => 'd.m.Y', - 'MM.DD.YYYY' => 'm.d.Y', - 'YYYY.MM.DD' => 'Y.m.d', - ); - - protected $timeFormatsMap = array( - 'HH:mm' => 'H:i', - 'hh:mm a' => 'G:i a', - 'hh:mma' => 'G:ia', - 'hh:mm A' => 'G:iA', - 'hh:mmA' => 'G:iA', - ); - - protected function getSelectManagerFactory() - { - return $this->injections['selectManagerFactory']; - } - - protected function getEntityManager() - { - return $this->injections['entityManager']; - } - - protected function getUser() - { - return $this->injections['user']; - } - - protected function getAcl() - { - return $this->injections['acl']; - } - - protected function getMetadata() - { - return $this->injections['metadata']; - } - - protected function getConfig() - { - return $this->injections['config']; - } - - protected function getServiceFactory() - { - return $this->injections['serviceFactory']; - } - - public function import($scope, array $fields, $contents, array $params = array()) - { - $delimiter = ','; - if (!empty($params['fieldDelimiter'])) { - $delimiter = $params['fieldDelimiter']; - } - $enclosure = '"'; - if (!empty($params['textQualifier'])) { - $enclosure = $params['textQualifier']; - } - - - $lines = explode("\n", $contents); - - $result = array( - 'importedIds' => array(), - 'updatedIds' => array(), - 'duplicateIds' => array(), - ); - - foreach ($lines as $i => $line) { - if ($i == 0 && !empty($params['headerRow'])) { - continue; - } - $arr = str_getcsv($line, $delimiter, $enclosure); - if (count($arr) == 1 && empty($arr[0])) { - continue; - } - $r = $this->importRow($scope, $fields, $arr, $params); - if (!empty($r['imported'])) { - $result['importedIds'][] = $r['id']; - } - if (!empty($r['updated'])) { - $result['updatedIds'][] = $r['id']; - } - if (!empty($r['duplicate'])) { - $result['duplicateIds'][] = $r['id']; - } - } - - return array( - 'countCreated' => count($result['importedIds']), - 'countUpdated' => count($result['updatedIds']), - 'duplicateIds' => $result['duplicateIds'], - ); - } - - public function importRow($scope, array $fields, array $row, array $params = array()) - { - // TODO create related records or related if exists, e.g. Account from accountName (skip users) - // Duplicate check - - $id = null; - if (!empty($params['action'])) { - if ($params['action'] == 'createAndUpdate' && in_array('id', $fields)) { - $i = array_search('id', $fields); - $id = $row[$i]; - if (empty($id)) { - $id = null; - } - } - } - - - $entity = $this->getEntityManager()->getEntity($scope, $id); - - $entity->set('assignedUserId', $this->getUser()->id); - - if (!empty($params['defaultValues'])) { - $entity->set(get_object_vars($params['defaultValues'])); - } - - $fieldsDefs = $entity->getFields(); - $relDefs = $entity->getRelations(); - - foreach ($fields as $i => $field) { - if (!empty($field)) { - if ($field == 'id') { - continue; - } - $value = $row[$i]; - if (array_key_exists($field, $fieldsDefs)) { - if ($value !== '') { - $entity->set($field, $this->parseValue($entity, $field, $value, $params)); - } - } - } - } - - - - foreach ($fields as $i => $field) { - if (array_key_exists($field, $fieldsDefs) && $fieldsDefs[$field]['type'] == Entity::FOREIGN) { - if ($entity->has($field)) { - $relation = $fieldsDefs[$field]['relation']; - if ($field == $relation . 'Name' && !$entity->has($relation . 'Id') && array_key_exists($relation, $relDefs)) { - if ($relDefs[$relation]['type'] == Entity::BELONGS_TO) { - $name = $entity->get($field); - $scope = $relDefs[$relation]['entity']; - $found = $this->getEntityManager()->getRepository($scope)->where(array('name' => $name))->findOne(); - - if ($found) { - $entity->set($relation . 'Id', $found->id); - } else { - if (!in_array($scope, 'User', 'Team')) { - - // TODO create related record with name $name and relate - } - } - } - } - } - } - - } - - $result = array(); - - $a = $entity->toArray(); - - if ($this->getEntityManager()->saveEntity($entity)) { - $result['id'] = $entity->id; - if (empty($id)) { - $result['imported'] = true; - } else { - $result['updated'] = true; - } - } - return $result; - } - - protected function parseValue(Entity $entity, $field, $value, $params = array()) - { - $decimalMark = '.'; - if (!empty($params['decimalMark'])) { - $decimalMark = $params['decimalMark']; - } - - $defaultCurrency = 'USD'; - if (!empty($params['defaultCurrency'])) { - $dateFormat = $params['defaultCurrency']; - } - - $dateFormat = 'Y-m-d'; - if (!empty($params['dateFormat'])) { - $dateFormat = $params['dateFormat']; - } - - $timeFormat = 'H:i'; - if (!empty($params['timeFormat'])) { - $timeFormat = $params['timeFormat']; - } - - $fieldDefs = $entity->getFields(); - - if (!empty($fieldDefs[$field])) { - $type = $fieldDefs[$field]['type']; - - switch ($type) { - case Entity::DATE: - $dt = \DateTime::createFromFormat($dateFormat, $value); - if ($dt) { - return $dt->format('Y-m-d'); - } - break; - case Entity::DATETIME: - $dt = \DateTime::createFromFormat($dateFormat . ' ' . $timeFormat, $value); - if ($dt) { - return $dt->format('Y-m-d H:i'); - } - break; - case Entity::FLOAT: - $currencyField = $field . 'Currency'; - if ($entity->hasField($currencyField)) { - if (!$entity->has($currencyField)) { - $entity->set($currencyField, $defaultCurrency); - } - } - - $a = explode($decimalMark, $value); - $a[0] = preg_replace('/[^A-Za-z0-9\-]/', '', $a[0]); - - if (count($a) > 1) { - return $a[0] . '.' . $a[1]; - } else { - return $a[0]; - } - break; - } - - } - return $value; - } -} - + 'Y-m-d', + 'DD-MM-YYYY' => 'd-m-Y', + 'MM-DD-YYYY' => 'm-d-Y', + 'MM/DD/YYYY' => 'm/d/Y', + 'DD/MM/YYYY' => 'd/m/Y', + 'DD.MM.YYYY' => 'd.m.Y', + 'MM.DD.YYYY' => 'm.d.Y', + 'YYYY.MM.DD' => 'Y.m.d', + ); + + protected $timeFormatsMap = array( + 'HH:mm' => 'H:i', + 'hh:mm a' => 'G:i a', + 'hh:mma' => 'G:ia', + 'hh:mm A' => 'G:iA', + 'hh:mmA' => 'G:iA', + ); + + protected function getSelectManagerFactory() + { + return $this->injections['selectManagerFactory']; + } + + protected function getEntityManager() + { + return $this->injections['entityManager']; + } + + protected function getUser() + { + return $this->injections['user']; + } + + protected function getAcl() + { + return $this->injections['acl']; + } + + protected function getMetadata() + { + return $this->injections['metadata']; + } + + protected function getConfig() + { + return $this->injections['config']; + } + + protected function getServiceFactory() + { + return $this->injections['serviceFactory']; + } + + public function import($scope, array $fields, $contents, array $params = array()) + { + $delimiter = ','; + if (!empty($params['fieldDelimiter'])) { + $delimiter = $params['fieldDelimiter']; + } + $enclosure = '"'; + if (!empty($params['textQualifier'])) { + $enclosure = $params['textQualifier']; + } + + + $lines = explode("\n", $contents); + + $result = array( + 'importedIds' => array(), + 'updatedIds' => array(), + 'duplicateIds' => array(), + ); + + foreach ($lines as $i => $line) { + if ($i == 0 && !empty($params['headerRow'])) { + continue; + } + $arr = str_getcsv($line, $delimiter, $enclosure); + if (count($arr) == 1 && empty($arr[0])) { + continue; + } + $r = $this->importRow($scope, $fields, $arr, $params); + if (!empty($r['imported'])) { + $result['importedIds'][] = $r['id']; + } + if (!empty($r['updated'])) { + $result['updatedIds'][] = $r['id']; + } + if (!empty($r['duplicate'])) { + $result['duplicateIds'][] = $r['id']; + } + } + + return array( + 'countCreated' => count($result['importedIds']), + 'countUpdated' => count($result['updatedIds']), + 'duplicateIds' => $result['duplicateIds'], + ); + } + + public function importRow($scope, array $fields, array $row, array $params = array()) + { + // TODO create related records or related if exists, e.g. Account from accountName (skip users) + // Duplicate check + + $id = null; + if (!empty($params['action'])) { + if ($params['action'] == 'createAndUpdate' && in_array('id', $fields)) { + $i = array_search('id', $fields); + $id = $row[$i]; + if (empty($id)) { + $id = null; + } + } + } + + + $entity = $this->getEntityManager()->getEntity($scope, $id); + + $entity->set('assignedUserId', $this->getUser()->id); + + if (!empty($params['defaultValues'])) { + $entity->set(get_object_vars($params['defaultValues'])); + } + + $fieldsDefs = $entity->getFields(); + $relDefs = $entity->getRelations(); + + foreach ($fields as $i => $field) { + if (!empty($field)) { + if ($field == 'id') { + continue; + } + $value = $row[$i]; + if (array_key_exists($field, $fieldsDefs)) { + if ($value !== '') { + $entity->set($field, $this->parseValue($entity, $field, $value, $params)); + } + } + } + } + + + + foreach ($fields as $i => $field) { + if (array_key_exists($field, $fieldsDefs) && $fieldsDefs[$field]['type'] == Entity::FOREIGN) { + if ($entity->has($field)) { + $relation = $fieldsDefs[$field]['relation']; + if ($field == $relation . 'Name' && !$entity->has($relation . 'Id') && array_key_exists($relation, $relDefs)) { + if ($relDefs[$relation]['type'] == Entity::BELONGS_TO) { + $name = $entity->get($field); + $scope = $relDefs[$relation]['entity']; + $found = $this->getEntityManager()->getRepository($scope)->where(array('name' => $name))->findOne(); + + if ($found) { + $entity->set($relation . 'Id', $found->id); + } else { + if (!in_array($scope, 'User', 'Team')) { + + // TODO create related record with name $name and relate + } + } + } + } + } + } + + } + + $result = array(); + + $a = $entity->toArray(); + + if ($this->getEntityManager()->saveEntity($entity)) { + $result['id'] = $entity->id; + if (empty($id)) { + $result['imported'] = true; + } else { + $result['updated'] = true; + } + } + return $result; + } + + protected function parseValue(Entity $entity, $field, $value, $params = array()) + { + $decimalMark = '.'; + if (!empty($params['decimalMark'])) { + $decimalMark = $params['decimalMark']; + } + + $defaultCurrency = 'USD'; + if (!empty($params['defaultCurrency'])) { + $dateFormat = $params['defaultCurrency']; + } + + $dateFormat = 'Y-m-d'; + if (!empty($params['dateFormat'])) { + $dateFormat = $params['dateFormat']; + } + + $timeFormat = 'H:i'; + if (!empty($params['timeFormat'])) { + $timeFormat = $params['timeFormat']; + } + + $fieldDefs = $entity->getFields(); + + if (!empty($fieldDefs[$field])) { + $type = $fieldDefs[$field]['type']; + + switch ($type) { + case Entity::DATE: + $dt = \DateTime::createFromFormat($dateFormat, $value); + if ($dt) { + return $dt->format('Y-m-d'); + } + break; + case Entity::DATETIME: + $dt = \DateTime::createFromFormat($dateFormat . ' ' . $timeFormat, $value); + if ($dt) { + return $dt->format('Y-m-d H:i'); + } + break; + case Entity::FLOAT: + $currencyField = $field . 'Currency'; + if ($entity->hasField($currencyField)) { + if (!$entity->has($currencyField)) { + $entity->set($currencyField, $defaultCurrency); + } + } + + $a = explode($decimalMark, $value); + $a[0] = preg_replace('/[^A-Za-z0-9\-]/', '', $a[0]); + + if (count($a) > 1) { + return $a[0] . '.' . $a[1]; + } else { + return $a[0]; + } + break; + } + + } + return $value; + } +} + diff --git a/application/Espo/Services/Job.php b/application/Espo/Services/Job.php index 4f38863900..d53cba4f03 100644 --- a/application/Espo/Services/Job.php +++ b/application/Espo/Services/Job.php @@ -1,4 +1,4 @@ -entityName)) { - $name = get_class($this); - if (preg_match('@\\\\([\w]+)$@', $name, $matches)) { - $name = $matches[1]; - } - if ($name != 'Record') { - $this->entityName = $name; - } - } - } - - public function setEntityName($entityName) - { - $this->entityName = $entityName; - } - - protected function getEntityManager() - { - return $this->injections['entityManager']; - } - - protected function getServiceFactory() - { - return $this->injections['serviceFactory']; - } - - protected function getSelectManagerFactory() - { - return $this->injections['selectManagerFactory']; - } - - protected function getUser() - { - return $this->injections['user']; - } - - protected function getAcl() - { - return $this->injections['acl']; - } - - protected function getFileManager() - { - return $this->injections['fileManager']; - } - - protected function getConfig() - { - return $this->injections['config']; - } - - protected function getMetadata() - { - return $this->injections['metadata']; - } - - protected function getRepository() - { - return $this->getEntityManager()->getRepository($this->entityName); - } - - public function getEntity($id = null) - { - $entity = $this->getRepository()->get($id); - if (!empty($entity) && !empty($id)) { - $this->loadLinkMultipleFields($entity); - $this->loadParentNameFields($entity); - $this->loadIsFollowed($entity); - - if (!$this->getAcl()->check($entity, 'read')) { - throw new Forbidden(); - } - } - return $entity; - } - - protected function getStreamService() - { - if (empty($this->streamService)) { - $this->streamService = $this->getServiceFactory()->create('Stream'); - } - return $this->streamService; - } - - protected function loadIsFollowed(Entity $entity) - { - if ($this->getStreamService()->checkIsFollowed($entity)) { - $entity->set('isFollowed', true); - } else { - $entity->set('isFollowed', false); - } - } - - protected function loadLinkMultipleFields(Entity $entity) - { - $fieldDefs = $this->getMetadata()->get('entityDefs.' . $entity->getEntityName() . '.fields', array()); - foreach ($fieldDefs as $field => $defs) { - if ($defs['type'] == 'linkMultiple') { - $entity->loadLinkMultipleField($field); - } - } - } - - protected function loadParentNameFields(Entity $entity) - { - $fieldDefs = $this->getMetadata()->get('entityDefs.' . $entity->getEntityName() . '.fields', array()); - foreach ($fieldDefs as $field => $defs) { - if ($defs['type'] == 'linkParent') { - $id = $entity->get($field . 'Id'); - $scope = $entity->get($field . 'Type'); - - if ($scope) { - if ($foreignEntity = $this->getEntityManager()->getEntity($scope, $id)) { - $entity->set($field . 'Name', $foreignEntity->get('name')); - } - } - } - } - } - - protected function getSelectManager($entityName) - { - return $this->getSelectManagerFactory()->create($entityName); - } - - protected function storeEntity(Entity $entity) - { - return $this->getRepository()->save($entity); - } - - protected function isValid($entity) - { - $fieldDefs = $entity->getFields(); - if ($entity->hasField('name') && !empty($fieldDefs['name']['required'])) { - if (!$entity->get('name')) { - return false; - } - } - return true; - } - - protected function stripTags($string) - { - return strip_tags($string, '