diff --git a/application/Espo/Core/Container.php b/application/Espo/Core/Container.php index a4636d0f82..ed05af2ab9 100644 --- a/application/Espo/Core/Container.php +++ b/application/Espo/Core/Container.php @@ -50,7 +50,8 @@ class Container private function loadSlim() { - return new \Slim\Slim(); + //return new \Slim\Slim(); + return new \Espo\Core\Utils\Api\Slim(); } private function loadFileManager() diff --git a/application/Espo/Core/Utils/Api/Auth.php b/application/Espo/Core/Utils/Api/Auth.php index af14eb3dd5..2ae9889c64 100644 --- a/application/Espo/Core/Utils/Api/Auth.php +++ b/application/Espo/Core/Utils/Api/Auth.php @@ -2,7 +2,7 @@ namespace Espo\Core\Utils\Api; -use \Slim\Slim; +use \Espo\Core\Utils\Api\Slim; class Auth extends \Slim\Middleware { diff --git a/application/Espo/Core/Utils/Api/Output.php b/application/Espo/Core/Utils/Api/Output.php index 531244ea9b..f050272619 100644 --- a/application/Espo/Core/Utils/Api/Output.php +++ b/application/Espo/Core/Utils/Api/Output.php @@ -7,7 +7,7 @@ class Output private $slim; - public function __construct(\Slim\Slim $slim) + public function __construct(\Espo\Core\Utils\Api\Slim $slim) { $this->slim = $slim; } diff --git a/application/Espo/Core/Utils/Api/Slim.php b/application/Espo/Core/Utils/Api/Slim.php new file mode 100644 index 0000000000..73cebcfa69 --- /dev/null +++ b/application/Espo/Core/Utils/Api/Slim.php @@ -0,0 +1,60 @@ +config('debug')) { + //Apply pretty exceptions only in debug to avoid accidental information leakage in production + //$this->add(new \Slim\Middleware\PrettyExceptions()); //Espo: no needs to use this handler + } + + //Invoke middleware and application stack + $this->middleware[0]->call(); + + //Fetch status, header, and body + list($status, $headers, $body) = $this->response->finalize(); + + // Serialize cookies (with optional encryption) + \Slim\Http\Util::serializeCookies($headers, $this->response->cookies, $this->settings); + + //Send headers + if (headers_sent() === false) { + //Send status + if (strpos(PHP_SAPI, 'cgi') === 0) { + header(sprintf('Status: %s', \Slim\Http\Response::getMessageForCode($status))); + } else { + header(sprintf('HTTP/%s %s', $this->config('http.version'), \Slim\Http\Response::getMessageForCode($status))); + } + + //Send headers + foreach ($headers as $name => $value) { + $hValues = explode("\n", $value); + foreach ($hValues as $hVal) { + header("$name: $hVal", false); + } + } + } + + //Send body, but only if it isn't a HEAD request + if (!$this->request->isHead()) { + echo $body; + } + + //restore_error_handler(); + } + + +} \ No newline at end of file diff --git a/application/Espo/Core/Utils/Database/Orm/Base.php b/application/Espo/Core/Utils/Database/Orm/Base.php index e7e78cf560..4795bf7be8 100644 --- a/application/Espo/Core/Utils/Database/Orm/Base.php +++ b/application/Espo/Core/Utils/Database/Orm/Base.php @@ -8,6 +8,8 @@ class Base { private $metadata; + protected $allowParams = array(); + public function __construct(\Espo\Core\Utils\Metadata $metadata) { $this->metadata = $metadata; @@ -18,6 +20,35 @@ class Base return $this->metadata; } + + public function process($params, $foreignParams) + { + $load = $this->load($params, $foreignParams); + + $load = $this->mergeAllowedParams($load, $params); + + return $load; + } + + + private function mergeAllowedParams($load, $params) + { + if (!empty($this->allowParams)) { + $linkParams = &$load[$params['entityName']] ['relations'] [$params['link']['name']]; + + foreach ($this->allowParams as $name) { + if (isset($params['link']['params'][$name]) && !isset($linkParams[$name])) { + $linkParams[$name] = $params['link']['params'][$name]; + } + } + } + + return $load; + } + + + + protected function getForeignField($name, $entityName) { $foreignField = $this->getMetadata()->get('entityDefs.'.$entityName.'.fields.'.$name); @@ -40,4 +71,6 @@ class Base return $name; } + + } diff --git a/application/Espo/Core/Utils/Database/Orm/Converter.php b/application/Espo/Core/Utils/Database/Orm/Converter.php index 050c4d0f90..f478e80054 100644 --- a/application/Espo/Core/Utils/Database/Orm/Converter.php +++ b/application/Espo/Core/Utils/Database/Orm/Converter.php @@ -39,6 +39,8 @@ class Converter 'field' => 'foreign', //todo change "foreign" to "field" 'unique' => 'unique', 'index' => 'index', + /*'conditions' => 'conditions', + 'additionalColumns' => 'additionalColumns', */ 'default' => array( 'condition' => '^javascript:', 'conditionEquals' => false, diff --git a/application/Espo/Core/Utils/Database/Orm/RelationManager.php b/application/Espo/Core/Utils/Database/Orm/RelationManager.php index 53b74a0354..71df222cb1 100644 --- a/application/Espo/Core/Utils/Database/Orm/RelationManager.php +++ b/application/Espo/Core/Utils/Database/Orm/RelationManager.php @@ -55,6 +55,14 @@ class RelationManager return false; } + protected function isMethodExists($relationName) + { + $className = $this->getRelationClass($relationName); + + return method_exists($className, 'load'); + } + + public function process($method, $entityName, $link, $foreignLink = array()) { @@ -72,13 +80,15 @@ class RelationManager $foreignParams['targetEntity'] = $params['entityName']; //relationDefs defined in separate file - $relationName = isset($link['params']['relationName']) ? ucfirst($link['params']['relationName']) : ucfirst($method); + if (isset($link['params']['relationName']) && $this->isMethodExists($link['params']['relationName'])) { + $className = $this->getRelationClass($link['params']['relationName']); + } else if ($this->isMethodExists($method)) { + $className = $this->getRelationClass($method); + } - $className = $this->getRelationClass($relationName); - - if ($className !== false && method_exists($className, 'load')) { - $helperClass = new $className($this->metadata); - return $helperClass->load($params, $foreignParams); + if (isset($className) && $className !== false) { + $helperClass = new $className($this->metadata); + return $helperClass->process($params, $foreignParams); } //END: relationDefs defined in separate file diff --git a/application/Espo/Core/Utils/Database/Orm/Relations.php b/application/Espo/Core/Utils/Database/Orm/Relations.php deleted file mode 100644 index ac1bb558ed..0000000000 --- a/application/Espo/Core/Utils/Database/Orm/Relations.php +++ /dev/null @@ -1,252 +0,0 @@ -metadata = $metadata; - } - - protected function getMetadata() - { - return $this->metadata; - } - - protected function getForeignField($name, $entityName) - { - $foreignField = $this->getMetadata()->get('entityDefs.'.$entityName.'.fields.'.$name); - - if ($foreignField['type'] != Entity::VARCHAR) { - $fieldDefs = $this->getMetadata()->get('fields.'.$foreignField['type']); - $naming = isset($fieldDefs['naming']) ? $fieldDefs['naming'] : 'postfix'; - - if (isset($fieldDefs['actualFields']) && is_array($fieldDefs['actualFields'])) { - $foreignFieldArray = array(); - foreach($fieldDefs['actualFields'] as $fieldName) { - if ($fieldName != 'salutation') { - $foreignFieldArray[] = Util::getNaming($name, $fieldName, $naming); - } - } - return explode('|', implode('| |', $foreignFieldArray)); //add an empty string between items - } - } - - return $name; - } - - - //todo sedine in foreign fieldDefs a key for current - /*public function manyMany($params, $foreignParams) - { - return array( - $params['entityName'] => array( - 'relations' => array( - $params['link']['name'] => array( - 'type' => Entity::MANY_MANY, - 'entity' => $params['targetEntity'], - 'relationName' => $this->getJoinTable($params['entityName'], $foreignParams['entityName']), - 'key' => 'id', //todo specify 'key' - 'foreignKey' => 'id', //todo specify 'foreignKey' - 'midKeys' => array( - lcfirst($params['entityName']).'Id', - lcfirst($foreignParams['entityName']).'Id', - ), - ), - ), - ), - ); - } */ - - - /*public function hasMany($params, $foreignParams) - { - $relation = array( - $params['entityName'] => array ( - 'relations' => array( - $params['link']['name'] => array( - 'type' => Entity::HAS_MANY, - 'entity' => $params['targetEntity'], - 'foreignKey' => lcfirst($foreignParams['link']['name'].'Id'), //???: 'foreignKey' => $params['link']['name'].'Id', - ), - ), - ), - ); - - return $relation; - } */ - - /*public function belongsTo($params, $foreignParams) - { - $relation = array ( - $params['entityName'] => array ( - 'fields' => array( - $params['link']['name'].'Name' => array( - 'type' => 'foreign', - 'relation' => $params['link']['name'], - 'foreign' => $this->getForeignField('name', $foreignParams['entityName']), - ), - $params['link']['name'].'Id' => array( - 'type' => 'foreignId', - 'index' => true, - ), - ), - 'relations' => array( - $params['link']['name'] => array( - 'type' => 'belongsTo', - 'entity' => $params['targetEntity'], - 'key' => $params['link']['name'].'Id', - 'foreignKey' => 'id', //???? - ), - ), - ), - ); - - return $relation; - } */ - - /*public function hasChildren($params, $foreignParams) - { - $relation = array( - $params['entityName'] => array ( - 'relations' => array( - $params['link']['name'] => array( - 'type' => Entity::HAS_CHILDREN, - 'entity' => $params['targetEntity'], - 'foreignKey' => $foreignParams['link']['name'].'Id', //???: 'foreignKey' => $params['link']['name'].'Id', - 'foreignType' => $foreignParams['link']['name'].'Type', //???: 'foreignKey' => $params['link']['name'].'Id', - ), - ), - ), - ); - - - return $relation; - } */ - - /*public function linkParent($params, $foreignParams) - { - $relation = array(); - - $entities = isset($params['link']['params']['entities']) ? $params['link']['params']['entities'] : array($params['entityName']); - - foreach($entities as $entity) { - $relation[$entity] = array ( - 'fields' => array( - $params['link']['name'].'Id' => array( - 'type' => Entity::FOREIGN_ID, - 'index' => $params['link']['name'], - ), - $params['link']['name'].'Type' => array( - 'type' => Entity::FOREIGN_TYPE, - 'index' => $params['link']['name'], - ), - $params['link']['name'].'Name' => array( - 'type' => Entity::VARCHAR, - 'notStorable' => true, - ), - ), - ); - } - - return $relation; - } */ - - - /* public function linkMultiple($params, $foreignParams) - { - return array( - $params['entityName'] => array ( - 'fields' => array( - $params['link']['name'].'Ids' => array( - 'type' => Entity::VARCHAR, - 'notStorable' => true, - ), - $params['link']['name'].'Names' => array( - 'type' => Entity::VARCHAR, - 'notStorable' => true, - ), - ), - ), - ); - } */ - - - /*public function typePersonName($params, $foreignParams) - { - $foreignField = $this->getForeignField($params['link']['name'], $params['entityName']); - $tableName = Util::toUnderScore($params['entityName']); - - $fullList = array(); //contains empty string (" ") like delimiter - $fieldList = array(); //doesn't contain empty string (" ") like delimiter - $like = array(); - foreach($foreignField as $fieldName) { - - $fieldNameTrimmed = trim($fieldName); - if (!empty($fieldNameTrimmed)) { - $columnName = $tableName.'.'.Util::toUnderScore($fieldNameTrimmed); - - $fullList[] = $fieldList[] = $columnName; - $like[] = $columnName." LIKE '{text}'"; - } else { - $fullList[] = "'".$fieldName."'"; - } - } - - return array( - $params['entityName'] => array ( - 'fields' => array( - $params['link']['name'] => array( - 'select' => "TRIM(CONCAT(".implode(", ", $fullList)."))", - 'where' => array( - 'LIKE' => "(".implode(" OR ", $like)." OR CONCAT(".implode(", ", $fullList).") LIKE '{text}')", - ), - 'orderBy' => implode(", ", array_map(function ($item) {return $item . ' {direction}';}, $fieldList)), - ), - ), - ), - ); - }*/ - - - - //public function teamRelation($params, $foreignParams) - public function hasManyWithName($params, $foreignParams) - { - $relationKeys = explode('-', Util::fromCamelCase($params['link']['params']['relationName'])); - $midKeys = array(); - foreach($relationKeys as $key) { - $midKeys[] = $key.'Id'; - } - - return array( - $params['entityName'] => array( - 'relations' => array( - $params['link']['name'] => array( - 'type' => Entity::MANY_MANY, - 'entity' => $params['targetEntity'], - 'relationName' => lcfirst($params['link']['params']['relationName']), - 'midKeys' => $midKeys, - 'conditions' => array('entityType' => $params['entityName']), - ), - ), - ), - ); - - } - - - public function hasOne($params, $foreignParams) - { - - } - - -} diff --git a/application/Espo/Core/Utils/Database/Orm/Relations/HasMany.php b/application/Espo/Core/Utils/Database/Orm/Relations/HasMany.php index d832bb014e..e77adac122 100644 --- a/application/Espo/Core/Utils/Database/Orm/Relations/HasMany.php +++ b/application/Espo/Core/Utils/Database/Orm/Relations/HasMany.php @@ -4,6 +4,11 @@ namespace Espo\Core\Utils\Database\Orm\Relations; class HasMany extends \Espo\Core\Utils\Database\Orm\Base { + protected $allowParams = array( + 'relationName', + 'conditions', + 'additionalColumns', + ); public function load($params, $foreignParams) { @@ -23,11 +28,11 @@ class HasMany extends \Espo\Core\Utils\Database\Orm\Base $params['link']['name'] => array( 'type' => 'hasMany', 'entity' => $params['targetEntity'], - 'foreignKey' => lcfirst($foreignParams['link']['name'].'Id'), //???: 'foreignKey' => $params['link']['name'].'Id', + 'foreignKey' => lcfirst($foreignParams['link']['name'].'Id'), //???: 'foreignKey' => $params['link']['name'].'Id', ), ), ), - ); + ); return $relation; } diff --git a/application/Espo/Core/Utils/Database/Schema/Schema.php b/application/Espo/Core/Utils/Database/Schema/Schema.php index 1a957cfac3..1fad3cbbd8 100644 --- a/application/Espo/Core/Utils/Database/Schema/Schema.php +++ b/application/Espo/Core/Utils/Database/Schema/Schema.php @@ -118,7 +118,7 @@ class Schema { if ($this->getConverter()->process() === false) { return false; - } + } $currentSchema = $this->getCurrentSchema(); $metadataSchema = $this->getConverter()->getSchemaFromMetadata(); diff --git a/tests/Espo/Core/Utils/LayoutTest.php b/tests/Espo/Core/Utils/LayoutTest.php new file mode 100644 index 0000000000..972fb515ab --- /dev/null +++ b/tests/Espo/Core/Utils/LayoutTest.php @@ -0,0 +1,78 @@ +objects['config'] = $this->getMockBuilder('\\Espo\\Core\\Utils\\Config')->disableOriginalConstructor()->getMock(); + $this->objects['fileManager'] = $this->getMockBuilder('\\Espo\\Core\\Utils\\File\\Manager')->disableOriginalConstructor()->getMock(); + $this->objects['metadata'] = $this->getMockBuilder('\\Espo\\Core\\Utils\\Metadata')->disableOriginalConstructor()->getMock(); + + $this->object = new \Espo\Core\Utils\Layout($this->objects['config'], $this->objects['fileManager'], $this->objects['metadata']); + } + + protected function tearDown() + { + $this->object = NULL; + } + + + function testGetLayoutPathCore() + { + $this->objects['metadata'] + ->expects($this->exactly(2)) + ->method('getScopeModuleName') + ->will($this->returnValue(false)); + + $this->assertEquals('application/Espo/Resources/layouts/User', $this->object->getLayoutPath('User')); + $this->assertEquals('application/Espo/Custom/Resources/layouts/User', $this->object->getLayoutPath('User', true)); + } + + + function testGetLayoutPathModule() + { + $this->objects['metadata'] + ->expects($this->exactly(2)) + ->method('getScopeModuleName') + ->will($this->returnValue('Crm')); + + $this->assertEquals('application/Espo/Modules/Crm/Resources/layouts/Call', $this->object->getLayoutPath('Call')); + $this->assertEquals('application/Espo/Custom/Modules/Crm/Resources/layouts/Call', $this->object->getLayoutPath('Call', true)); + } + + function testGet() + { + $result = '[{"label":"Overview","rows":[[{"name":"userName"},{"name":"isAdmin"}],[{"name":"name"},{"name":"title"}],[{"name":"defaultTeam"}],[{"name":"emailAddress"},{"name":"phone"}]]}]'; + + $this->objects['metadata'] + ->expects($this->exactly(2)) + ->method('getScopeModuleName') + ->will($this->returnValue(false)); + + $this->objects['config'] + ->expects($this->never()) + ->method('get') + ->will($this->returnValue('application/Espo/Core/defaults')); + + $this->objects['fileManager'] + ->expects($this->exactly(1)) + ->method('getContent') + ->will($this->returnValue($result)); + + $this->assertEquals($result, $this->object->get('User', 'detail')); + } + + + +} + +?> diff --git a/vendor/slim/slim/Slim/Slim.php b/vendor/slim/slim/Slim/Slim.php index 6369b028af..a19de6885e 100644 --- a/vendor/slim/slim/Slim/Slim.php +++ b/vendor/slim/slim/Slim/Slim.php @@ -1249,12 +1249,12 @@ class Slim */ public function run() { - //set_error_handler(array('\Slim\Slim', 'handleErrors')); //Espo: no needs to use this handler + set_error_handler(array('\Slim\Slim', 'handleErrors')); //Apply final outer middleware layers if ($this->config('debug')) { //Apply pretty exceptions only in debug to avoid accidental information leakage in production - //$this->add(new \Slim\Middleware\PrettyExceptions()); //Espo: no needs to use this handler + $this->add(new \Slim\Middleware\PrettyExceptions()); } //Invoke middleware and application stack