mirror of
https://github.com/espocrm/espocrm.git
synced 2026-03-09 20:27:01 +00:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd45f07e54 | ||
|
|
a35f9625af | ||
|
|
f5245ef3eb | ||
|
|
4ad9ef770b | ||
|
|
acbc74d858 | ||
|
|
8da3350523 | ||
|
|
496879dd79 | ||
|
|
84cda80fd8 | ||
|
|
50a81b1247 | ||
|
|
f0f225f349 | ||
|
|
d49fff1289 | ||
|
|
abaa1b302b | ||
|
|
0eaa7825ea | ||
|
|
1028b7c5b3 | ||
|
|
f319e5219d | ||
|
|
0daae5ced5 | ||
|
|
abee63d269 | ||
|
|
be33d90986 | ||
|
|
3a7cac824d | ||
|
|
7d616b075f | ||
|
|
b718765138 | ||
|
|
3104be7c99 |
@@ -113,11 +113,31 @@ class SumRelatedType extends \Espo\Core\Formula\Functions\Base
|
||||
$foreignSelectManager->addJoin($foreignLink, $selectParams);
|
||||
}
|
||||
|
||||
$selectParams['groupBy'] = [$foreignLink . '.id'];
|
||||
if (!empty($selectParams['distinct'])) {
|
||||
$sqSelectParams = $selectParams;
|
||||
|
||||
$selectParams['whereClause'][] = [
|
||||
$foreignLink . '.id' => $entity->id
|
||||
];
|
||||
$sqSelectParams['whereClause'][] = [
|
||||
$foreignLink . '.id' => $entity->id
|
||||
];
|
||||
|
||||
$sqSelectParams['select'] = ['id'];
|
||||
unset($sqSelectParams['distinct']);
|
||||
unset($sqSelectParams['orderBy']);
|
||||
unset($sqSelectParams['order']);
|
||||
|
||||
$selectParams['whereClause'][] = [
|
||||
'id=s' => [
|
||||
'entityType' => $foreignEntityType,
|
||||
'selectParams' => $sqSelectParams,
|
||||
]
|
||||
];
|
||||
} else {
|
||||
$selectParams['whereClause'][] = [
|
||||
$foreignLink . '.id' => $entity->id
|
||||
];
|
||||
}
|
||||
|
||||
$selectParams['groupBy'] = [$foreignLink . '.id'];
|
||||
|
||||
$entityManager->getRepository($foreignEntityType)->handleSelectParams($selectParams);
|
||||
|
||||
|
||||
@@ -115,10 +115,12 @@ class Parser
|
||||
$braceCounter = 0;
|
||||
|
||||
for ($i = 0; $i < strlen($string); $i++) {
|
||||
$isStringStart = false;
|
||||
if ($string[$i] === "'" && ($i === 0 || $string[$i - 1] !== "\\")) {
|
||||
if (!$isString) {
|
||||
$isString = true;
|
||||
$isSingleQuote = true;
|
||||
$isStringStart = true;
|
||||
} else {
|
||||
if ($isSingleQuote) {
|
||||
$isString = false;
|
||||
@@ -127,6 +129,7 @@ class Parser
|
||||
} else if ($string[$i] === "\"" && ($i === 0 || $string[$i - 1] !== "\\")) {
|
||||
if (!$isString) {
|
||||
$isString = true;
|
||||
$isStringStart = true;
|
||||
$isSingleQuote = false;
|
||||
} else {
|
||||
if (!$isSingleQuote) {
|
||||
@@ -137,6 +140,8 @@ class Parser
|
||||
if ($isString) {
|
||||
if ($string[$i] === '(' || $string[$i] === ')') {
|
||||
$modifiedString[$i] = '_';
|
||||
} else if (!$isStringStart) {
|
||||
$modifiedString[$i] = ' ';
|
||||
}
|
||||
} else {
|
||||
if ($string[$i] === '(') {
|
||||
@@ -176,6 +181,16 @@ class Parser
|
||||
|
||||
$this->processStrings($expression, $modifiedExpression, $splitterIndexList, true);
|
||||
|
||||
$this->stripComments($expression, $modifiedExpression);
|
||||
|
||||
foreach ($splitterIndexList as $i => $index) {
|
||||
if ($expression[$index] !== ';') {
|
||||
unset($splitterIndexList[$i]);
|
||||
}
|
||||
}
|
||||
|
||||
$splitterIndexList = array_values($splitterIndexList);
|
||||
|
||||
$expressionOutOfBraceList = [];
|
||||
|
||||
for ($i = 0; $i < strlen($modifiedExpression); $i++) {
|
||||
@@ -402,6 +417,43 @@ class Parser
|
||||
}
|
||||
}
|
||||
|
||||
protected function stripComments(&$expression, &$modifiedExpression)
|
||||
{
|
||||
$commentIndexStart = null;
|
||||
|
||||
for ($i = 0; $i < strlen($modifiedExpression); $i++) {
|
||||
if (is_null($commentIndexStart)) {
|
||||
if ($modifiedExpression[$i] === '/' && $i < strlen($modifiedExpression) - 1 && $modifiedExpression[$i + 1] === '/') {
|
||||
$commentIndexStart = $i;
|
||||
}
|
||||
} else {
|
||||
if ($modifiedExpression[$i] === "\n" || $i === strlen($modifiedExpression) - 1) {
|
||||
for ($j = $commentIndexStart; $j <= $i; $j++) {
|
||||
$modifiedExpression[$j] = ' ';
|
||||
$expression[$j] = ' ';
|
||||
}
|
||||
$commentIndexStart = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ($i = 0; $i < strlen($modifiedExpression) - 1; $i++) {
|
||||
if (is_null($commentIndexStart)) {
|
||||
if ($modifiedExpression[$i] === '/' && $modifiedExpression[$i + 1] === '*') {
|
||||
$commentIndexStart = $i;
|
||||
}
|
||||
} else {
|
||||
if ($modifiedExpression[$i] === '*' && $modifiedExpression[$i + 1] === '/') {
|
||||
for ($j = $commentIndexStart; $j <= $i + 1; $j++) {
|
||||
$modifiedExpression[$j] = ' ';
|
||||
$expression[$j] = ' ';
|
||||
}
|
||||
$commentIndexStart = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function parseArgumentListFromFunctionContent($functionContent)
|
||||
{
|
||||
$functionContent = trim($functionContent);
|
||||
|
||||
@@ -35,26 +35,33 @@ use Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Utils\File\Manager as FileManager;
|
||||
use Espo\Core\Utils\DateTime;
|
||||
use Espo\Core\Utils\NumberUtil;
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Core\Utils\Language;
|
||||
use Espo\Core\Utils\Metadata;
|
||||
use Espo\ORM\EntityManager;
|
||||
|
||||
require('vendor/zordius/lightncandy/src/lightncandy.php');
|
||||
|
||||
class Htmlizer
|
||||
{
|
||||
protected $fileManager;
|
||||
|
||||
protected $dateTime;
|
||||
|
||||
protected $config;
|
||||
|
||||
protected $acl;
|
||||
|
||||
protected $entityManager;
|
||||
|
||||
protected $metadata;
|
||||
|
||||
protected $language;
|
||||
|
||||
public function __construct(FileManager $fileManager, DateTime $dateTime, NumberUtil $number, $acl = null, $entityManager = null, $metadata = null, $language = null)
|
||||
public function __construct(
|
||||
FileManager $fileManager,
|
||||
DateTime $dateTime,
|
||||
NumberUtil $number,
|
||||
$acl = null,
|
||||
?EntityManager $entityManager = null,
|
||||
?Metadata $metadata = null,
|
||||
?Language $language = null,
|
||||
?Config $config = null
|
||||
)
|
||||
{
|
||||
$this->fileManager = $fileManager;
|
||||
$this->dateTime = $dateTime;
|
||||
@@ -63,6 +70,7 @@ class Htmlizer
|
||||
$this->entityManager = $entityManager;
|
||||
$this->metadata = $metadata;
|
||||
$this->language = $language;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
protected function getAcl()
|
||||
@@ -92,7 +100,7 @@ class Htmlizer
|
||||
return $value;
|
||||
}
|
||||
|
||||
protected function getDataFromEntity(Entity $entity, $skipLinks = false, $level = 0)
|
||||
protected function getDataFromEntity(Entity $entity, $skipLinks = false, $level = 0, ?string $template = null)
|
||||
{
|
||||
$data = $entity->toArray();
|
||||
|
||||
@@ -118,15 +126,32 @@ class Htmlizer
|
||||
|
||||
if (!$skipLinks && $level === 0) {
|
||||
foreach ($relationList as $relation) {
|
||||
if (!$entity->hasLinkMultipleField($relation)) continue;
|
||||
$collection = null;
|
||||
|
||||
$collection = $entity->getLinkMultipleCollection($relation);
|
||||
$data[$relation] = $collection;
|
||||
if ($entity->hasLinkMultipleField($relation)) {
|
||||
$toLoad = true;
|
||||
$collection = $entity->getLinkCollection($relation);
|
||||
} else {
|
||||
if (
|
||||
$template && $entity->getRelationType($relation, ['hasMany', 'manyMany', 'hasChildren']) &&
|
||||
mb_stripos($template, '{{#each '.$relation.'}}') !== false
|
||||
) {
|
||||
$limit = 100;
|
||||
if ($this->config) {
|
||||
$limit = $this->config->get('htmlizerLinkLimit') ?? $limit;
|
||||
}
|
||||
$collection = $entity->getLinkCollection($relation, ['limit' => $limit]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($collection) {
|
||||
$data[$relation] = $collection;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
if ($value instanceof \Espo\ORM\EntityCollection) {
|
||||
if ($value instanceof \Espo\ORM\ICollection) {
|
||||
$skipAttributeList[] = $key;
|
||||
$collection = $value;
|
||||
$list = [];
|
||||
@@ -280,7 +305,7 @@ class Htmlizer
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
},
|
||||
],
|
||||
'hbhelpers' => [
|
||||
'ifEqual' => function () {
|
||||
@@ -331,7 +356,7 @@ class Htmlizer
|
||||
$this->fileManager->removeFile($fileName);
|
||||
}
|
||||
|
||||
$data = $this->getDataFromEntity($entity, $skipLinks);
|
||||
$data = $this->getDataFromEntity($entity, $skipLinks, 0, $template);
|
||||
|
||||
if (!array_key_exists('today', $data)) {
|
||||
$data['today'] = $this->dateTime->getTodayString();
|
||||
|
||||
@@ -212,14 +212,18 @@ class Importer
|
||||
if ($parser->checkMessageAttribute($message, 'in-Reply-To') && $parser->getMessageAttribute($message, 'in-Reply-To')) {
|
||||
$arr = explode(' ', $parser->getMessageAttribute($message, 'in-Reply-To'));
|
||||
$inReplyTo = $arr[0];
|
||||
$replied = $this->getEntityManager()->getRepository('Email')->where(array(
|
||||
'messageId' => $inReplyTo
|
||||
))->findOne();
|
||||
if ($replied) {
|
||||
$email->set('repliedId', $replied->id);
|
||||
$repliedTeamIdList = $replied->getLinkMultipleIdList('teams');
|
||||
foreach ($repliedTeamIdList as $repliedTeamId) {
|
||||
$email->addLinkMultipleId('teams', $repliedTeamId);
|
||||
|
||||
if ($inReplyTo) {
|
||||
if ($inReplyTo[0] !== '<') $inReplyTo = '<' . $inReplyTo . '>';
|
||||
$replied = $this->getEntityManager()->getRepository('Email')->where(array(
|
||||
'messageId' => $inReplyTo
|
||||
))->findOne();
|
||||
if ($replied) {
|
||||
$email->set('repliedId', $replied->id);
|
||||
$repliedTeamIdList = $replied->getLinkMultipleIdList('teams');
|
||||
foreach ($repliedTeamIdList as $repliedTeamId) {
|
||||
$email->addLinkMultipleId('teams', $repliedTeamId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +91,13 @@ class MailMimeParser
|
||||
|
||||
public function getMessageMessageId($message)
|
||||
{
|
||||
return $this->getMessageAttribute($message, 'Message-ID');
|
||||
$messageId = $this->getMessageAttribute($message, 'Message-ID');
|
||||
|
||||
if ($messageId && strlen($messageId) && $messageId[0] !== '<') {
|
||||
$messageId = '<' . $messageId . '>';
|
||||
}
|
||||
|
||||
return $messageId;
|
||||
}
|
||||
|
||||
public function getAddressNameMap($message)
|
||||
|
||||
@@ -74,22 +74,33 @@ class Entity extends \Espo\ORM\Entity
|
||||
}
|
||||
}
|
||||
|
||||
public function getLinkMultipleCollection($field)
|
||||
public function getLinkCollection(string $link, ?array $selectParams = null)
|
||||
{
|
||||
if (!$this->hasLinkMultipleField($field)) return;
|
||||
if (!$selectParams) $selectParams = [];
|
||||
|
||||
$defs = $this->getRelationSelectParams($field);
|
||||
$relSelectParams = $this->getRelationSelectParams($link);
|
||||
|
||||
$columnAttribute = $field . 'Columns';
|
||||
$selectParams = array_merge($selectParams, $relSelectParams);
|
||||
|
||||
$selectParams['returnSthCollection'] = true;
|
||||
|
||||
$columnAttribute = $link . 'Columns';
|
||||
if ($this->hasAttribute($columnAttribute) && $this->getAttributeParam($columnAttribute, 'columns')) {
|
||||
$defs['additionalColumns'] = $this->getAttributeParam($columnAttribute, 'columns');
|
||||
$selectParams['additionalColumns'] = $this->getAttributeParam($columnAttribute, 'columns');
|
||||
}
|
||||
|
||||
$collection = $this->get($field, $defs);
|
||||
$collection = $this->get($link, $selectParams);
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
public function getLinkMultipleCollection(string $link, ?array $selectParams = null)
|
||||
{
|
||||
if (!$this->hasLinkMultipleField($link)) return;
|
||||
|
||||
return $this->getLinkCollection($link, $selectParams);
|
||||
}
|
||||
|
||||
protected function getRelationSelectParams($link)
|
||||
{
|
||||
$field = $link;
|
||||
|
||||
@@ -240,4 +240,33 @@ class Tcpdf extends \TCPDF
|
||||
$this->_out($out);
|
||||
}
|
||||
|
||||
public function Output($name = 'doc.pdf', $dest = 'I')
|
||||
{
|
||||
if ($dest === 'I' && !$this->sign && php_sapi_name() != 'cli') {
|
||||
if ($this->state < 3) {
|
||||
$this->Close();
|
||||
}
|
||||
$name = preg_replace('/[\s]+/', '_', $name);
|
||||
$name = \Espo\Core\Utils\Util::sanitizeFileName($name);
|
||||
|
||||
if (ob_get_contents()) {
|
||||
$this->Error('Some data has already been output, can\'t send PDF file');
|
||||
}
|
||||
|
||||
header('Content-Type: application/pdf');
|
||||
if (headers_sent()) {
|
||||
$this->Error('Some data has already been output to browser, can\'t send PDF file');
|
||||
}
|
||||
header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
|
||||
header('Pragma: public');
|
||||
header('Expires: Sat, 26 Jul 1997 05:00:00 GMT');
|
||||
header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
|
||||
header('Content-Disposition: inline; filename="'.$name.'"');
|
||||
TCPDF_STATIC::sendOutputData($this->getBuffer(), $this->bufferlen);
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
return parent::Output($name, $dest);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1732,6 +1732,7 @@ class Base
|
||||
case 'arrayNoneOf':
|
||||
case 'arrayIsEmpty':
|
||||
case 'arrayIsNotEmpty':
|
||||
case 'arrayAllOf':
|
||||
if (!$result) break;
|
||||
|
||||
$arrayValueAlias = 'arrayFilter' . strval(rand(10000, 99999));
|
||||
@@ -1763,6 +1764,8 @@ class Base
|
||||
$arrayValueAlias . '.attribute' => $arrayAttribute
|
||||
]], $result);
|
||||
$part[$arrayValueAlias . '.value'] = $value;
|
||||
|
||||
$this->setDistinct(true, $result);
|
||||
} else if ($type === 'arrayNoneOf') {
|
||||
if (is_null($value) || !$value && !is_array($value)) break;
|
||||
$this->addLeftJoin(['ArrayValue', $arrayValueAlias, [
|
||||
@@ -1772,6 +1775,8 @@ class Base
|
||||
$arrayValueAlias . '.value=' => $value
|
||||
]], $result);
|
||||
$part[$arrayValueAlias . '.id'] = null;
|
||||
|
||||
$this->setDistinct(true, $result);
|
||||
} else if ($type === 'arrayIsEmpty') {
|
||||
$this->addLeftJoin(['ArrayValue', $arrayValueAlias, [
|
||||
$arrayValueAlias . '.entityId:' => $idPart,
|
||||
@@ -1779,6 +1784,8 @@ class Base
|
||||
$arrayValueAlias . '.attribute' => $arrayAttribute
|
||||
]], $result);
|
||||
$part[$arrayValueAlias . '.id'] = null;
|
||||
|
||||
$this->setDistinct(true, $result);
|
||||
} else if ($type === 'arrayIsNotEmpty') {
|
||||
$this->addLeftJoin(['ArrayValue', $arrayValueAlias, [
|
||||
$arrayValueAlias . '.entityId:' => $idPart,
|
||||
@@ -1786,9 +1793,31 @@ class Base
|
||||
$arrayValueAlias . '.attribute' => $arrayAttribute
|
||||
]], $result);
|
||||
$part[$arrayValueAlias . '.id!='] = null;
|
||||
}
|
||||
|
||||
$this->setDistinct(true, $result);
|
||||
$this->setDistinct(true, $result);
|
||||
} else if ($type === 'arrayAllOf') {
|
||||
if (is_null($value) || !$value && !is_array($value)) break;
|
||||
|
||||
if (!is_array($value)) {
|
||||
$value = [$value];
|
||||
}
|
||||
|
||||
foreach ($value as $arrayValue) {
|
||||
$part[] = [
|
||||
$idPart .'=s' => [
|
||||
'entityType' => 'ArrayValue',
|
||||
'selectParams' => [
|
||||
'select' => ['entityId'],
|
||||
'whereClause' => [
|
||||
'value' => $arrayValue,
|
||||
'attribute' => $arrayAttribute,
|
||||
'entityType' => $arrayEntityType,
|
||||
],
|
||||
],
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -72,6 +72,12 @@
|
||||
"entity": "Task",
|
||||
"foreign": "parent",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"emails": {
|
||||
"type": "hasChildren",
|
||||
"entity": "Email",
|
||||
"foreign": "parent",
|
||||
"layoutRelationshipsDisabled": true
|
||||
}
|
||||
},
|
||||
"collection": {
|
||||
|
||||
@@ -54,7 +54,18 @@ class EntityManager
|
||||
|
||||
private $linkForbiddenNameList = ['posts', 'stream', 'subscription', 'followers', 'action', 'null', 'false', 'true'];
|
||||
|
||||
private $forbiddenEntityTypeNameList = ['Common', 'PortalUser', 'ApiUser', 'Timeline', 'About', 'Admin', 'Null', 'False', 'True'];
|
||||
private $forbiddenEntityTypeNameList = [
|
||||
'Common',
|
||||
'PortalUser',
|
||||
'ApiUser',
|
||||
'Timeline',
|
||||
'About',
|
||||
'Admin',
|
||||
'Null',
|
||||
'False',
|
||||
'True',
|
||||
'Base',
|
||||
];
|
||||
|
||||
public function __construct(Metadata $metadata, Language $language, File\Manager $fileManager, Config $config, Container $container = null)
|
||||
{
|
||||
|
||||
@@ -427,7 +427,11 @@ class FieldManager
|
||||
|
||||
protected function getFieldDefs($scope, $name, $default = null)
|
||||
{
|
||||
return $this->getMetadata()->get('entityDefs'.'.'.$scope.'.fields.'.$name, $default);
|
||||
$defs = $this->getMetadata()->getObjects(['entityDefs', $scope, 'fields', $name], $default);
|
||||
if (is_object($defs)) {
|
||||
return get_object_vars($defs);
|
||||
}
|
||||
return $defs;
|
||||
}
|
||||
|
||||
protected function getCustomFieldDefs($scope, $name, $default = null)
|
||||
@@ -532,7 +536,7 @@ class FieldManager
|
||||
}
|
||||
|
||||
$actualCustomFieldDefs = $this->getCustomFieldDefs($scope, $name, []);
|
||||
$actualFieldDefs = $this->getFieldDefs($scope, $name, []);
|
||||
$actualFieldDefs = $this->getFieldDefs($scope, $name, (object) []);
|
||||
$permittedParamList = array_keys($params);
|
||||
|
||||
$filteredFieldDefs = !empty($actualCustomFieldDefs) ? $actualCustomFieldDefs : [];
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
namespace Espo\ORM;
|
||||
|
||||
class EntityCollection implements \Iterator, \Countable, \ArrayAccess, \SeekableIterator
|
||||
class EntityCollection implements \Iterator, \Countable, \ArrayAccess, \SeekableIterator, ICollection
|
||||
{
|
||||
private $entityFactory = null;
|
||||
|
||||
|
||||
36
application/Espo/ORM/ICollection.php
Normal file
36
application/Espo/ORM/ICollection.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2019 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\ORM;
|
||||
|
||||
interface ICollection
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
namespace Espo\ORM;
|
||||
|
||||
class SthCollection implements \IteratorAggregate
|
||||
class SthCollection implements \IteratorAggregate, ICollection
|
||||
{
|
||||
protected $entityManager = null;
|
||||
|
||||
|
||||
@@ -608,6 +608,7 @@
|
||||
"isNot": "Is Not",
|
||||
"isNotOneOf": "None Of",
|
||||
"anyOf": "Any Of",
|
||||
"allOf": "All Of",
|
||||
"noneOf": "None Of"
|
||||
},
|
||||
"varcharSearchRanges": {
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
"account",
|
||||
"dateSent",
|
||||
"emailAddress",
|
||||
"from",
|
||||
"to",
|
||||
"isNotRead",
|
||||
"isImportant",
|
||||
"isNotReplied",
|
||||
"status",
|
||||
"parent",
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
"url": "views/fields/foreign-url",
|
||||
"date": "views/fields/foreign-date",
|
||||
"datetime": "views/fields/foreign-datetime",
|
||||
"text": "views/fields/views/fields/foreign-text",
|
||||
"text": "views/fields/foreign-text",
|
||||
"number": "views/fields/foreign-varchar",
|
||||
"bool": "views/fields/foreign-bool",
|
||||
"email": "views/fields/foreign-email",
|
||||
|
||||
@@ -14,6 +14,11 @@
|
||||
{
|
||||
"name":"readOnly",
|
||||
"type":"bool"
|
||||
},
|
||||
{
|
||||
"name": "default",
|
||||
"type": "linkMultiple",
|
||||
"view": "views/admin/field-manager/fields/link-multiple/default"
|
||||
}
|
||||
],
|
||||
"actualFields":[
|
||||
|
||||
@@ -385,27 +385,82 @@ class Email extends \Espo\Core\SelectManagers\Base
|
||||
], $result);
|
||||
}
|
||||
|
||||
|
||||
public function whereEmailAddress(string $value, array &$result)
|
||||
protected function getWherePartEmailAddressEquals($value, array &$result)
|
||||
{
|
||||
$orItem = [];
|
||||
if (!$value) {
|
||||
return ['id' => null];
|
||||
}
|
||||
|
||||
$emailAddressId = $this->getEmailAddressIdByValue($value);
|
||||
|
||||
if ($emailAddressId) {
|
||||
$this->leftJoinEmailAddress($result);
|
||||
|
||||
$orItem['fromEmailAddressId'] = $emailAddressId;
|
||||
$orItem['emailEmailAddress.emailAddressId'] = $emailAddressId;
|
||||
$result['whereClause'][] = [
|
||||
'OR' => $orItem
|
||||
];
|
||||
} else {
|
||||
if (empty($result['customWhere'])) {
|
||||
$result['customWhere'] = '';
|
||||
}
|
||||
$result['customWhere'] .= ' AND 0';
|
||||
if (!$emailAddressId) {
|
||||
return ['id' => null];
|
||||
}
|
||||
|
||||
$this->setDistinct(true, $result);
|
||||
$alias = 'emailEmailAddress' . strval(rand(10000, 99999));
|
||||
|
||||
$this->addLeftJoin([
|
||||
'EmailEmailAddress',
|
||||
$alias,
|
||||
[
|
||||
'emailId:' => 'id',
|
||||
'deleted' => false,
|
||||
]
|
||||
], $result);
|
||||
|
||||
return [
|
||||
'OR' => [
|
||||
'fromEmailAddressId' => $emailAddressId,
|
||||
$alias . '.emailAddressId' => $emailAddressId,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
protected function getWherePartFromEquals($value, array &$result)
|
||||
{
|
||||
if (!$value) {
|
||||
return ['id' => null];
|
||||
}
|
||||
|
||||
$emailAddressId = $this->getEmailAddressIdByValue($value);
|
||||
|
||||
if (!$emailAddressId) {
|
||||
return ['id' => null];
|
||||
}
|
||||
|
||||
return [
|
||||
'fromEmailAddressId' => $emailAddressId,
|
||||
];
|
||||
}
|
||||
|
||||
protected function getWherePartToEquals($value, array &$result)
|
||||
{
|
||||
if (!$value) {
|
||||
return ['id' => null];
|
||||
}
|
||||
|
||||
$emailAddressId = $this->getEmailAddressIdByValue($value);
|
||||
|
||||
if (!$emailAddressId) {
|
||||
return ['id' => null];
|
||||
}
|
||||
|
||||
$alias = 'emailEmailAddress' . strval(rand(10000, 99999));
|
||||
|
||||
$this->addLeftJoin([
|
||||
'EmailEmailAddress',
|
||||
$alias,
|
||||
[
|
||||
'emailId:' => 'id',
|
||||
'deleted' => false,
|
||||
]
|
||||
], $result);
|
||||
|
||||
return [
|
||||
$alias . '.emailAddressId' => $emailAddressId,
|
||||
$alias . '.addressType' => 'to',
|
||||
];
|
||||
}
|
||||
|
||||
protected function getWherePartIsNotRepliedIsTrue()
|
||||
|
||||
@@ -775,31 +775,6 @@ class Email extends Record
|
||||
$this->getEntityManager()->getRepository('Email')->loadNameHash($entity, $fieldList);
|
||||
}
|
||||
|
||||
protected function getSelectParams($params)
|
||||
{
|
||||
$searchByEmailAddress = false;
|
||||
if (!empty($params['where']) && is_array($params['where'])) {
|
||||
foreach ($params['where'] as $i => $p) {
|
||||
if (!empty($p['attribute']) && $p['attribute'] == 'emailAddress') {
|
||||
$searchByEmailAddress = true;
|
||||
$emailAddress = $p['value'];
|
||||
unset($params['where'][$i]);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
$selectManager = $this->getSelectManager($this->getEntityType());
|
||||
|
||||
$selectParams = $selectManager->getSelectParams($params, true);
|
||||
|
||||
if ($searchByEmailAddress) {
|
||||
$selectManager->whereEmailAddress($emailAddress, $selectParams);
|
||||
}
|
||||
|
||||
return $selectParams;
|
||||
}
|
||||
|
||||
public function copyAttachments($emailId, $parentType, $parentId)
|
||||
{
|
||||
return $this->getCopiedAttachments($emailId, $parentType, $parentId);
|
||||
|
||||
@@ -307,7 +307,8 @@ class Pdf extends \Espo\Core\Services\Base
|
||||
$this->getAcl(),
|
||||
$this->getInjection('entityManager'),
|
||||
$this->getInjection('metadata'),
|
||||
$this->getInjection('defaultLanguage')
|
||||
$this->getInjection('defaultLanguage'),
|
||||
$this->getInjection('config')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -999,8 +999,16 @@ var Bull = Bull || {};
|
||||
|
||||
this.$el = $(el).eq(0);
|
||||
this.el = this.$el[0];
|
||||
}
|
||||
},
|
||||
|
||||
propagateEvent: function () {
|
||||
this.trigger.apply(this, arguments);
|
||||
|
||||
for (var key in this.nestedViews) {
|
||||
var view = this.nestedViews[key];
|
||||
view.propagateEvent.apply(view, arguments);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
}).call(this, Bull, Backbone, _);
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="pull-right">
|
||||
<a target="_blank" href="https://www.espocrm.com/documentation/administration/server-configuration/" style="font-weight:bold;">Configuration Instructions</a>
|
||||
<a target="_blank" href="https://www.espocrm.com/documentation/administration/server-configuration/" style="font-weight:600;">Configuration Instructions</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -157,7 +157,13 @@ define('controller', [], function () {
|
||||
|
||||
storeMainView: function (key, view) {
|
||||
this.set('storedMainView-' + key, view);
|
||||
view.once('remove', function () {
|
||||
|
||||
this.listenTo(view, 'remove', function (o) {
|
||||
o = o || {};
|
||||
if (o.ignoreCleaning) return;
|
||||
|
||||
this.stopListening(view, 'remove');
|
||||
|
||||
this.clearStoredMainView(key);
|
||||
}, this);
|
||||
},
|
||||
@@ -278,6 +284,10 @@ define('controller', [], function () {
|
||||
if (master.currentViewKey) {
|
||||
this.set('storedScrollTop-' + master.currentViewKey, $(window).scrollTop());
|
||||
if (this.hasStoredMainView(master.currentViewKey)) {
|
||||
var mainView = master.getView('main');
|
||||
if (mainView) {
|
||||
mainView.propagateEvent('remove', {ignoreCleaning: true});
|
||||
}
|
||||
master.unchainView('main');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,7 +306,7 @@ define('controllers/record', 'controller', function (Dep) {
|
||||
var collectionName = this.entityType || this.name;
|
||||
if (usePreviouslyFetched) {
|
||||
if (collectionName in this.collectionMap) {
|
||||
var collection = this.collectionMap[collectionName];// = this.collectionMap[collectionName].clone();
|
||||
var collection = this.collectionMap[collectionName];
|
||||
callback.call(context, collection);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -392,6 +392,8 @@ define('views/admin/entity-manager/modals/edit-entity', ['views/modal', 'model']
|
||||
toPlural: function (string) {
|
||||
if (string.slice(-1) == 'y') {
|
||||
return string.substr(0, string.length - 1) + 'ies';
|
||||
} else if (string.slice(-1) == 's') {
|
||||
return string + 'es';
|
||||
} else {
|
||||
return string + 's';
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2019 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
define('views/admin/field-manager/fields/link-multiple/default', 'views/fields/link-multiple', function (Dep) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
data: function () {
|
||||
var defaultAttributes = this.model.get('defaultAttributes') || {};
|
||||
|
||||
var nameHash = defaultAttributes[this.options.field + 'Names'] || {};
|
||||
var idValues = defaultAttributes[this.options.field + 'Ids'] || [];
|
||||
|
||||
var data = Dep.prototype.data.call(this);
|
||||
|
||||
data.nameHash = nameHash;
|
||||
data.idValues = idValues;
|
||||
|
||||
return data;
|
||||
},
|
||||
|
||||
setup: function () {
|
||||
Dep.prototype.setup.call(this);
|
||||
this.foreignScope = this.getMetadata().get(['entityDefs', this.options.scope, 'links', this.options.field, 'entity']);
|
||||
},
|
||||
|
||||
fetch: function () {
|
||||
var data = Dep.prototype.fetch.call(this);
|
||||
|
||||
var defaultAttributes = {};
|
||||
defaultAttributes[this.options.field + 'Ids'] = data[this.idsName];
|
||||
defaultAttributes[this.options.field + 'Names'] = data[this.nameHashName];
|
||||
|
||||
if (data[this.idsName] === null || data[this.idsName].length === 0) {
|
||||
defaultAttributes = null;
|
||||
}
|
||||
|
||||
return {
|
||||
defaultAttributes: defaultAttributes
|
||||
};
|
||||
},
|
||||
|
||||
copyValuesFromModel: function () {
|
||||
var defaultAttributes = this.model.get('defaultAttributes') || {};
|
||||
|
||||
var idValues = defaultAttributes[this.options.field + 'Ids'] || [];
|
||||
var nameHash = defaultAttributes[this.options.field + 'Names'] || {};
|
||||
|
||||
this.ids = idValues;
|
||||
this.nameHash = nameHash;
|
||||
},
|
||||
|
||||
});
|
||||
});
|
||||
@@ -26,7 +26,10 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
Espo.define('views/email/fields/email-address-varchar', ['views/fields/varchar', 'views/email/fields/from-address-varchar'], function (Dep, From) {
|
||||
define(
|
||||
'views/email/fields/email-address-varchar',
|
||||
['views/fields/base', 'views/email/fields/from-address-varchar', 'views/email/fields/email-address'],
|
||||
function (Dep, From, EmailAddress) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
@@ -48,6 +51,8 @@ Espo.define('views/email/fields/email-address-varchar', ['views/fields/varchar',
|
||||
this.deleteAddress(address);
|
||||
},
|
||||
'keyup input': function (e) {
|
||||
if (this.mode === 'search') return;
|
||||
|
||||
if (e.keyCode == 188 || e.keyCode == 186 || e.keyCode == 13) {
|
||||
var $input = $(e.currentTarget);
|
||||
var address = $input.val().replace(',', '').replace(';', '').trim();
|
||||
@@ -61,6 +66,8 @@ Espo.define('views/email/fields/email-address-varchar', ['views/fields/varchar',
|
||||
}
|
||||
},
|
||||
'change input': function (e) {
|
||||
if (this.mode === 'search') return;
|
||||
|
||||
var $input = $(e.currentTarget);
|
||||
var address = $input.val().replace(',','').replace(';','').trim();
|
||||
if (~address.indexOf('@')) {
|
||||
@@ -193,6 +200,10 @@ Espo.define('views/email/fields/email-address-varchar', ['views/fields/varchar',
|
||||
this.$input.autocomplete('dispose');
|
||||
}, this);
|
||||
}
|
||||
|
||||
if (this.mode == 'search' && this.getAcl().check('Email', 'create')) {
|
||||
EmailAddress.prototype.initSearchAutocomplete.call(this);
|
||||
}
|
||||
},
|
||||
|
||||
checkEmailAddressInString: function (string) {
|
||||
@@ -275,6 +286,20 @@ Espo.define('views/email/fields/email-address-varchar', ['views/fields/varchar',
|
||||
return data;
|
||||
},
|
||||
|
||||
fetchSearch: function () {
|
||||
var value = this.$element.val().trim();
|
||||
|
||||
if (value) {
|
||||
var data = {
|
||||
type: 'equals',
|
||||
value: value,
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
getValueForDisplay: function () {
|
||||
if (this.mode == 'detail') {
|
||||
var names = [];
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
Espo.define('views/email/fields/email-address', ['views/fields/base'], function (Dep) {
|
||||
define('views/email/fields/email-address', ['views/fields/base'], function (Dep) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
@@ -43,50 +43,55 @@ Espo.define('views/email/fields/email-address', ['views/fields/base'], function
|
||||
this.$input = this.$el.find('input');
|
||||
|
||||
if (this.mode == 'search' && this.getAcl().check('Email', 'create')) {
|
||||
this.$input.autocomplete({
|
||||
serviceUrl: function (q) {
|
||||
return 'EmailAddress/action/searchInAddressBook?maxSize=' + this.getAutocompleteMaxCount();
|
||||
}.bind(this),
|
||||
paramName: 'q',
|
||||
minChars: 1,
|
||||
autoSelectFirst: true,
|
||||
triggerSelectOnValidInput: false,
|
||||
formatResult: function (suggestion) {
|
||||
return this.getHelper().escapeString(suggestion.name) + ' <' + this.getHelper().escapeString(suggestion.id) + '>';
|
||||
}.bind(this),
|
||||
transformResult: function (response) {
|
||||
var response = JSON.parse(response);
|
||||
var list = [];
|
||||
response.forEach(function(item) {
|
||||
list.push({
|
||||
id: item.emailAddress,
|
||||
name: item.entityName,
|
||||
emailAddress: item.emailAddress,
|
||||
entityId: item.entityId,
|
||||
entityName: item.entityName,
|
||||
entityType: item.entityType,
|
||||
data: item.emailAddress,
|
||||
value: item.emailAddress
|
||||
});
|
||||
}, this);
|
||||
return {
|
||||
suggestions: list
|
||||
};
|
||||
}.bind(this),
|
||||
onSelect: function (s) {
|
||||
this.$input.val(s.emailAddress);
|
||||
}.bind(this)
|
||||
});
|
||||
|
||||
this.once('render', function () {
|
||||
this.$input.autocomplete('dispose');
|
||||
}, this);
|
||||
this.once('remove', function () {
|
||||
this.$input.autocomplete('dispose');
|
||||
}, this);
|
||||
this.initSearchAutocomplete();
|
||||
}
|
||||
},
|
||||
|
||||
initSearchAutocomplete: function () {
|
||||
this.$input = this.$input || this.$el.find('input');
|
||||
|
||||
this.$input.autocomplete({
|
||||
serviceUrl: function (q) {
|
||||
return 'EmailAddress/action/searchInAddressBook?maxSize=' + this.getAutocompleteMaxCount();
|
||||
}.bind(this),
|
||||
paramName: 'q',
|
||||
minChars: 1,
|
||||
autoSelectFirst: true,
|
||||
triggerSelectOnValidInput: false,
|
||||
formatResult: function (suggestion) {
|
||||
return this.getHelper().escapeString(suggestion.name) + ' <' + this.getHelper().escapeString(suggestion.id) + '>';
|
||||
}.bind(this),
|
||||
transformResult: function (response) {
|
||||
var response = JSON.parse(response);
|
||||
var list = [];
|
||||
response.forEach(function(item) {
|
||||
list.push({
|
||||
id: item.emailAddress,
|
||||
name: item.entityName,
|
||||
emailAddress: item.emailAddress,
|
||||
entityId: item.entityId,
|
||||
entityName: item.entityName,
|
||||
entityType: item.entityType,
|
||||
data: item.emailAddress,
|
||||
value: item.emailAddress,
|
||||
});
|
||||
}, this);
|
||||
return {
|
||||
suggestions: list
|
||||
};
|
||||
}.bind(this),
|
||||
onSelect: function (s) {
|
||||
this.$input.val(s.emailAddress);
|
||||
}.bind(this)
|
||||
});
|
||||
|
||||
this.once('render', function () {
|
||||
this.$input.autocomplete('dispose');
|
||||
}, this);
|
||||
this.once('remove', function () {
|
||||
this.$input.autocomplete('dispose');
|
||||
}, this);
|
||||
},
|
||||
|
||||
fetchSearch: function () {
|
||||
var value = this.$element.val();
|
||||
@@ -101,8 +106,7 @@ Espo.define('views/email/fields/email-address', ['views/fields/base'], function
|
||||
return data;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -25,7 +25,11 @@
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
Espo.define('views/email/fields/from-address-varchar', 'views/fields/varchar', function (Dep) {
|
||||
|
||||
define(
|
||||
'views/email/fields/from-address-varchar',
|
||||
['views/fields/base', 'views/email/fields/email-address'],
|
||||
function (Dep, EmailAddress) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
@@ -36,6 +40,7 @@ Espo.define('views/email/fields/from-address-varchar', 'views/fields/varchar', f
|
||||
Dep.prototype.setup.call(this);
|
||||
|
||||
this.on('render', function () {
|
||||
if (this.mode === 'search') return;
|
||||
this.initAddressList();
|
||||
}, this);
|
||||
},
|
||||
@@ -74,6 +79,18 @@ Espo.define('views/email/fields/from-address-varchar', 'views/fields/varchar', f
|
||||
return data;
|
||||
},
|
||||
|
||||
afterRender: function () {
|
||||
Dep.prototype.afterRender.call(this);
|
||||
|
||||
if (this.mode == 'search' && this.getAcl().check('Email', 'create')) {
|
||||
EmailAddress.prototype.initSearchAutocomplete.call(this);
|
||||
}
|
||||
},
|
||||
|
||||
getAutocompleteMaxCount: function () {
|
||||
return EmailAddress.prototype.getAutocompleteMaxCount.call(this);
|
||||
},
|
||||
|
||||
initAddressList: function () {
|
||||
this.nameHash = {};
|
||||
this.typeHash = this.model.get('typeHash') || {};
|
||||
@@ -329,8 +346,21 @@ Espo.define('views/email/fields/from-address-varchar', 'views/fields/varchar', f
|
||||
|
||||
}, this);
|
||||
}.bind(this));
|
||||
}
|
||||
},
|
||||
|
||||
fetchSearch: function () {
|
||||
var value = this.$element.val().trim();
|
||||
|
||||
if (value) {
|
||||
var data = {
|
||||
type: 'equals',
|
||||
value: value,
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -40,7 +40,7 @@ define('views/fields/array', ['views/fields/base', 'lib!Selectize'], function (D
|
||||
|
||||
searchTemplate: 'fields/array/search',
|
||||
|
||||
searchTypeList: ['anyOf', 'noneOf', 'isEmpty', 'isNotEmpty'],
|
||||
searchTypeList: ['anyOf', 'noneOf', 'allOf', 'isEmpty', 'isNotEmpty'],
|
||||
|
||||
maxItemLength: null,
|
||||
|
||||
@@ -149,7 +149,7 @@ define('views/fields/array', ['views/fields/base', 'lib!Selectize'], function (D
|
||||
handleSearchType: function (type) {
|
||||
var $inputContainer = this.$el.find('div.input-container');
|
||||
|
||||
if (~['anyOf', 'noneOf'].indexOf(type)) {
|
||||
if (~['anyOf', 'noneOf', 'allOf'].indexOf(type)) {
|
||||
$inputContainer.removeClass('hidden');
|
||||
} else {
|
||||
$inputContainer.addClass('hidden');
|
||||
@@ -481,7 +481,7 @@ define('views/fields/array', ['views/fields/base', 'lib!Selectize'], function (D
|
||||
var arr = [];
|
||||
var arrFront = [];
|
||||
|
||||
if (~['anyOf', 'noneOf'].indexOf(type)) {
|
||||
if (~['anyOf', 'noneOf', 'allOf'].indexOf(type)) {
|
||||
var valueList = this.$element.val().split(':,:');
|
||||
if (valueList.length == 1 && valueList[0] == '') {
|
||||
valueList = [];
|
||||
@@ -515,6 +515,21 @@ define('views/fields/array', ['views/fields/base', 'lib!Selectize'], function (D
|
||||
return data;
|
||||
}
|
||||
|
||||
if (type === 'allOf') {
|
||||
var data = {
|
||||
type: 'arrayAllOf',
|
||||
value: valueList,
|
||||
data: {
|
||||
type: 'allOf',
|
||||
valueList: valueList
|
||||
}
|
||||
};
|
||||
if (!valueList.length) {
|
||||
data.value = null;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
if (type === 'isEmpty') {
|
||||
var data = {
|
||||
type: 'arrayIsEmpty',
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
Espo.define('views/fields/date', 'views/fields/base', function (Dep) {
|
||||
define('views/fields/date', 'views/fields/base', function (Dep) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
@@ -161,6 +161,7 @@ Espo.define('views/fields/date', 'views/fields/base', function (Dep) {
|
||||
weekStart: this.getDateTime().weekStart,
|
||||
autoclose: true,
|
||||
todayHighlight: true,
|
||||
keyboardNavigation: false,
|
||||
};
|
||||
|
||||
var language = this.getConfig().get('language');
|
||||
|
||||
@@ -100,13 +100,10 @@ Espo.define('views/fields/link-multiple', 'views/fields/base', function (Dep) {
|
||||
this.nameHash = Espo.Utils.clone(nameHash);
|
||||
this.ids = Espo.Utils.clone(idList);
|
||||
} else {
|
||||
this.ids = Espo.Utils.clone(this.model.get(this.idsName) || []);
|
||||
this.nameHash = Espo.Utils.clone(this.model.get(this.nameHashName) || {});
|
||||
}
|
||||
this.copyValuesFromModel(); }
|
||||
|
||||
this.listenTo(this.model, 'change:' + this.idsName, function () {
|
||||
this.ids = Espo.Utils.clone(this.model.get(this.idsName) || []);
|
||||
this.nameHash = Espo.Utils.clone(this.model.get(this.nameHashName) || {});
|
||||
this.copyValuesFromModel();
|
||||
}, this);
|
||||
|
||||
this.sortable = this.sortable || this.params.sortable;
|
||||
@@ -151,6 +148,11 @@ Espo.define('views/fields/link-multiple', 'views/fields/base', function (Dep) {
|
||||
}
|
||||
},
|
||||
|
||||
copyValuesFromModel: function () {
|
||||
this.ids = Espo.Utils.clone(this.model.get(this.idsName) || []);
|
||||
this.nameHash = Espo.Utils.clone(this.model.get(this.nameHashName) || {});
|
||||
},
|
||||
|
||||
handleSearchType: function (type) {
|
||||
if (~['anyOf', 'noneOf'].indexOf(type)) {
|
||||
this.$el.find('div.link-group-container').removeClass('hidden');
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "espocrm",
|
||||
"version": "5.7.5",
|
||||
"version": "5.7.6",
|
||||
"description": "",
|
||||
"main": "index.php",
|
||||
"repository": {
|
||||
|
||||
@@ -264,7 +264,8 @@ class Tester
|
||||
return true;
|
||||
}
|
||||
|
||||
Utils::truncateTables($configData['database']);
|
||||
//Utils::truncateTables($configData['database']);
|
||||
Utils::dropTables($configData['database']);
|
||||
$fileManager->removeInDir($this->installPath . '/data');
|
||||
$fileManager->removeInDir($this->installPath . '/custom/Espo/Custom');
|
||||
$fileManager->removeInDir($this->installPath . '/client/custom');
|
||||
|
||||
@@ -95,6 +95,37 @@ class FormulaTest extends \tests\integration\Core\BaseTestCase
|
||||
$this->assertEquals(40, $result);
|
||||
}
|
||||
|
||||
public function testSumRelated()
|
||||
{
|
||||
$em = $this->getContainer()->get('entityManager');
|
||||
$fm = $this->getContainer()->get('formulaManager');
|
||||
|
||||
$contact1 = $em->createEntity('Contact', [
|
||||
'lastName' => '1',
|
||||
]);
|
||||
$contact2 = $em->createEntity('Contact', [
|
||||
'lastName' => '2',
|
||||
]);
|
||||
|
||||
$opportunity1 = $em->createEntity('Opportunity', [
|
||||
'name' => '1',
|
||||
'amount' => 1,
|
||||
'stage' => 'Closed Won',
|
||||
'contactsIds' => [$contact1->id, $contact2->id],
|
||||
]);
|
||||
|
||||
$opportunity2 = $em->createEntity('Opportunity', [
|
||||
'name' => '2',
|
||||
'amount' => 1,
|
||||
'stage' => 'Closed Won',
|
||||
'contactsIds' => [$contact1->id, $contact2->id],
|
||||
]);
|
||||
|
||||
$script = "entity\sumRelated('opportunities', 'amountConverted', 'won')";
|
||||
$result = $fm->run($script, $contact1);
|
||||
$this->assertEquals(2, $result);
|
||||
}
|
||||
|
||||
public function testRecordExists()
|
||||
{
|
||||
$em = $this->getContainer()->get('entityManager');
|
||||
|
||||
@@ -510,7 +510,7 @@ class ParserTest extends \PHPUnit\Framework\TestCase
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
|
||||
function parseNewLine()
|
||||
function testParseNewLine()
|
||||
{
|
||||
$expression = " \n \"test\n\thello\" ";
|
||||
$expected = (object) [
|
||||
@@ -521,6 +521,117 @@ class ParserTest extends \PHPUnit\Framework\TestCase
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
|
||||
function testParseComment1()
|
||||
{
|
||||
$expression = "// \"test\"\n \"//test\" ";
|
||||
$expected = (object) [
|
||||
'type' => 'value',
|
||||
'value' => "//test"
|
||||
];
|
||||
$actual = $this->parser->parse($expression);
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
|
||||
function testParseComment2()
|
||||
{
|
||||
$expression = "\"test\" // test\n ";
|
||||
$expected = (object) [
|
||||
'type' => 'value',
|
||||
'value' => "test",
|
||||
];
|
||||
$actual = $this->parser->parse($expression);
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
|
||||
function testParseComment3()
|
||||
{
|
||||
$expression = "\"test\" /* test\n */";
|
||||
$expected = (object) [
|
||||
'type' => 'value',
|
||||
'value' => "test",
|
||||
];
|
||||
$actual = $this->parser->parse($expression);
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
|
||||
function testParseComment4()
|
||||
{
|
||||
$expression = "/* test\n */ \"/*test*/\"";
|
||||
$expected = (object) [
|
||||
'type' => 'value',
|
||||
'value' => "/*test*/",
|
||||
];
|
||||
$actual = $this->parser->parse($expression);
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
|
||||
function testParseComment5()
|
||||
{
|
||||
$expression = "\"test\" /* test\n */ /* test */ ";
|
||||
$expected = (object) [
|
||||
'type' => 'value',
|
||||
'value' => "test",
|
||||
];
|
||||
$actual = $this->parser->parse($expression);
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
|
||||
function testParseComment6()
|
||||
{
|
||||
$expression = "/* test; */ test1 = 1 + 1; test2 = 2 * 2;";
|
||||
$expected = (object) [
|
||||
'type' => 'bundle',
|
||||
'value' => [
|
||||
(object) [
|
||||
'type' => 'setAttribute',
|
||||
'value' => [
|
||||
(object) [
|
||||
'type' => 'value',
|
||||
'value' => 'test1',
|
||||
],
|
||||
(object) [
|
||||
'type' => 'numeric\summation',
|
||||
'value' => [
|
||||
(object) [
|
||||
'type' => 'value',
|
||||
'value' => 1,
|
||||
],
|
||||
(object) [
|
||||
'type' => 'value',
|
||||
'value' => 1,
|
||||
]
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
(object) [
|
||||
'type' => 'setAttribute',
|
||||
'value' => [
|
||||
(object) [
|
||||
'type' => 'value',
|
||||
'value' => 'test2',
|
||||
],
|
||||
(object) [
|
||||
'type' => 'numeric\multiplication',
|
||||
'value' => [
|
||||
(object) [
|
||||
'type' => 'value',
|
||||
'value' => 2,
|
||||
],
|
||||
(object) [
|
||||
'type' => 'value',
|
||||
'value' => 2,
|
||||
]
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
$actual = $this->parser->parse($expression);
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
|
||||
function testParseFunction()
|
||||
{
|
||||
$expression = "numeric\\summation (10, parent.amountConverted, 0.1)";
|
||||
|
||||
@@ -144,10 +144,6 @@ class ImporterTest extends \PHPUnit\Framework\TestCase
|
||||
|
||||
function testImport2()
|
||||
{
|
||||
if (extension_loaded('mailparse')) {
|
||||
$this->assertTrue(true);
|
||||
return;
|
||||
}
|
||||
|
||||
$entityManager = $this->entityManager;
|
||||
$config = $this->config;
|
||||
|
||||
@@ -74,14 +74,14 @@ class FieldManagerTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
$this->expectException('\Espo\Core\Exceptions\Conflict');
|
||||
|
||||
$data = array(
|
||||
$data = (object) [
|
||||
"type" => "varchar",
|
||||
"maxLength" => "50",
|
||||
);
|
||||
];
|
||||
|
||||
$this->objects['metadata']
|
||||
->expects($this->once())
|
||||
->method('get')
|
||||
->method('getObjects')
|
||||
->will($this->returnValue($data));
|
||||
|
||||
$this->object->create('CustomEntity', 'varName', $data);
|
||||
@@ -95,15 +95,14 @@ class FieldManagerTest extends \PHPUnit\Framework\TestCase
|
||||
"label" => "Modified Name",
|
||||
);
|
||||
|
||||
$existingData = array(
|
||||
$existingData = (object) [
|
||||
"type" => "varchar",
|
||||
"maxLength" => 50,
|
||||
"label" => "Name",
|
||||
);
|
||||
];
|
||||
|
||||
$map = array(
|
||||
['entityDefs.Account.fields.name', [], $existingData],
|
||||
[['entityDefs', 'Account', 'fields', 'name', 'type'], null, $existingData['type']],
|
||||
[['entityDefs', 'Account', 'fields', 'name', 'type'], null, $data['type']],
|
||||
['fields.varchar', null, null],
|
||||
[['fields', 'varchar', 'hookClassName'], null, null],
|
||||
);
|
||||
@@ -118,6 +117,11 @@ class FieldManagerTest extends \PHPUnit\Framework\TestCase
|
||||
->method('get')
|
||||
->will($this->returnValueMap($map));
|
||||
|
||||
$this->objects['metadata']
|
||||
->expects($this->exactly(2))
|
||||
->method('getObjects')
|
||||
->will($this->returnValue($existingData));
|
||||
|
||||
$this->objects['metadataHelper']
|
||||
->expects($this->once())
|
||||
->method('getFieldDefsByType')
|
||||
@@ -177,7 +181,6 @@ class FieldManagerTest extends \PHPUnit\Framework\TestCase
|
||||
);
|
||||
|
||||
$map = array(
|
||||
['entityDefs.Account.fields.name', [], $data],
|
||||
[['entityDefs', 'Account', 'fields', 'name', 'type'], null, $data['type']],
|
||||
['fields.varchar', null, null],
|
||||
[['fields', 'varchar', 'hookClassName'], null, null],
|
||||
@@ -240,9 +243,18 @@ class FieldManagerTest extends \PHPUnit\Framework\TestCase
|
||||
|
||||
$this->objects['metadata']
|
||||
->expects($this->exactly(2))
|
||||
->method('getObjects')
|
||||
->will($this->returnValue((object) $data));
|
||||
|
||||
$this->objects['metadata']
|
||||
->expects($this->exactly(1))
|
||||
->method('getCustom')
|
||||
->will($this->returnValue((object) []));
|
||||
|
||||
$this->objects['metadata']
|
||||
->expects($this->never())
|
||||
->method('saveCustom');
|
||||
|
||||
$this->object->update('Account', 'name', $data);
|
||||
}
|
||||
|
||||
@@ -289,7 +301,6 @@ class FieldManagerTest extends \PHPUnit\Framework\TestCase
|
||||
);
|
||||
|
||||
$map = array(
|
||||
['entityDefs.CustomEntity.fields.varName', [], $data],
|
||||
['entityDefs.CustomEntity.fields.varName.type', null, $data['type']],
|
||||
[['entityDefs', 'CustomEntity', 'fields', 'varName'], null, $data],
|
||||
['fields.varchar', null, null],
|
||||
@@ -301,6 +312,11 @@ class FieldManagerTest extends \PHPUnit\Framework\TestCase
|
||||
->method('get')
|
||||
->will($this->returnValueMap($map));
|
||||
|
||||
$this->objects['metadata']
|
||||
->expects($this->exactly(2))
|
||||
->method('getObjects')
|
||||
->will($this->returnValue((object) $data));
|
||||
|
||||
$this->objects['metadata']
|
||||
->expects($this->once())
|
||||
->method('saveCustom')
|
||||
@@ -374,8 +390,8 @@ class FieldManagerTest extends \PHPUnit\Framework\TestCase
|
||||
|
||||
$this->objects['metadata']
|
||||
->expects($this->at(0))
|
||||
->method('get')
|
||||
->will($this->returnValue($data));
|
||||
->method('getObjects')
|
||||
->will($this->returnValue((object) $data));
|
||||
|
||||
$this->objects['language']
|
||||
->expects($this->once())
|
||||
|
||||
Reference in New Issue
Block a user