Compare commits

...

40 Commits
5.3.2 ... 5.3.4

Author SHA1 Message Date
yuri
56f8e68599 fix wysiwyg width 2018-07-19 14:56:28 +03:00
yuri
e8d75808ef fix email scrolling 2018-07-19 14:43:35 +03:00
yuri
bc8bf89d6e css fixes 2018-07-19 12:14:06 +03:00
yuri
91385cc3f0 fix naming 2018-07-19 11:49:43 +03:00
yuri
ad21511c72 email list is replied fix 2018-07-19 11:44:40 +03:00
yuri
938a24f102 fix assignment email notificaiotion 2018-07-19 10:59:32 +03:00
yuri
43b041ed6c fix replied icon 2018-07-18 15:37:37 +03:00
yuri
5e3f048795 replied email icon 2018-07-18 14:57:12 +03:00
yuri
3aec3ef6d2 formula higher 2018-07-18 11:37:55 +03:00
yuri
712a450734 formula mb string functions 2018-07-18 11:35:31 +03:00
yuri
5675b6721b sort by list comma fix 2018-07-18 11:25:58 +03:00
yuri
a4e23b8adb formula parser fix 2018-07-18 10:29:06 +03:00
yuri
9d49f418c8 orm fix order list 2018-07-18 10:11:36 +03:00
yuri
ea9f70dffa global search ability to select text 2018-07-17 17:26:06 +03:00
yuri
b624accbe8 version 2018-07-17 17:07:50 +03:00
yuri
12f974635e global search fixes 2018-07-17 17:07:32 +03:00
yuri
cfce68eb7e default panel complex fields 2018-07-17 15:38:44 +03:00
yuri
db8fbd1ed3 hide default panel if empty 2018-07-17 14:39:54 +03:00
yuri
06395f3ff5 Merge branch 'hotfix/5.3.3' of ssh://172.20.0.1/var/git/espo/backend into hotfix/5.3.3 2018-07-16 15:48:49 +03:00
Taras Machyshyn
4065ec5477 Test fixes 2018-07-16 15:48:25 +03:00
yuri
31d45a0583 Merge branch 'hotfix/5.3.3' of ssh://172.20.0.1/var/git/espo/backend into hotfix/5.3.3 2018-07-16 14:20:29 +03:00
yuri
696faa4468 fix wysiwyg toolbar 2018-07-16 14:20:19 +03:00
Taras Machyshyn
625876f123 Merge branch 'hotfix/5.3.3' of ssh://172.20.0.1/var/git/espo/backend into hotfix/5.3.3 2018-07-16 14:06:54 +03:00
Taras Machyshyn
974cd05276 Integration test for fulltext index 2018-07-16 14:06:37 +03:00
yuri
fb5db991a3 fix vars 2018-07-16 13:37:00 +03:00
yuri
3dafb7e922 fix categories 2018-07-16 13:12:09 +03:00
yuri
ee640273a1 fix stream attachments 2018-07-16 10:51:55 +03:00
yuri
b92de17f72 Merge branch 'hotfix/5.3.3' of ssh://172.20.0.1/var/git/espo/backend into hotfix/5.3.3 2018-07-13 16:55:22 +03:00
Taras Machyshyn
abc48ca76f Fulltext bug fixes 2018-07-13 16:52:02 +03:00
yuri
46443aae7e fix full text search 2018-07-13 16:37:32 +03:00
yuri
a88c3283d6 full text search fix 2018-07-13 16:33:19 +03:00
yuri
2b9653ee0b replace tabs 2018-07-13 14:53:37 +03:00
yuri
2cf79abdb1 fix list view remove 2018-07-13 14:49:56 +03:00
yuri
20a000a46c fix full text search 2018-07-13 12:33:21 +03:00
ayman-alkom
95bfd5bace fix typo (#968) 2018-07-13 11:51:34 +03:00
yuri
da736008f1 version 2018-07-13 11:19:49 +03:00
yuri
1de98d9616 text filter fixes 2018-07-13 11:19:30 +03:00
yuri
31a6143cc3 email template text filter 2018-07-13 11:07:34 +03:00
yuri
8502a84a3f chart mousetick fix 2018-07-12 12:52:05 +03:00
yuri
2234678728 email search tests 2018-07-12 11:31:44 +03:00
47 changed files with 787 additions and 165 deletions

View File

@@ -48,6 +48,6 @@ class ContainsType extends \Espo\Core\Formula\Functions\Base
return false;
}
return strpos($string, $needle) !== false;
return mb_strpos($string, $needle) !== false;
}
}

View File

@@ -53,6 +53,6 @@ class LowerCaseType extends \Espo\Core\Formula\Functions\Base
$value = strval($value);
}
return strtolower($value);
return mb_strtolower($value);
}
}

View File

@@ -1,4 +1,4 @@
<?php
<?php
/************************************************************************
* This file is part of EspoCRM.
*
@@ -52,9 +52,9 @@ class SubstringType extends \Espo\Core\Formula\Functions\Base
if (count($item->value) > 2) {
$length = $this->evaluate($item->value[2]);
return substr($string, $start, $length);
return mb_substr($string, $start, $length);
} else {
return substr($string, $start);
return mb_substr($string, $start);
}
}
}

View File

@@ -53,6 +53,6 @@ class UpperCaseType extends \Espo\Core\Formula\Functions\Base
$value = strval($value);
}
return strtoupper($value);
return mb_strtoupper($value);
}
}

View File

@@ -176,6 +176,8 @@ class Parser
$this->processStrings($expression, $modifiedExpression, $splitterIndexList, true);
$expressionOutOfBraceList = [];
for ($i = 0; $i < strlen($modifiedExpression); $i++) {
if ($modifiedExpression[$i] === '(') {
$braceCounter++;
@@ -186,6 +188,11 @@ class Parser
if ($braceCounter === 0 && $i < strlen($modifiedExpression) - 1) {
$hasExcessBraces = false;
}
if ($braceCounter === 0) {
$expressionOutOfBraceList[] = true;
} else {
$expressionOutOfBraceList[] = false;
}
}
if ($braceCounter !== 0) {
throw new Error('Incorrect round brackets in expression ' . $expression . '.');
@@ -226,7 +233,13 @@ class Parser
foreach ($this->priorityList as $operationList) {
foreach ($operationList as $operator) {
$index = strpos($expression, $operator, 1);
$startFrom = 1;
while (true) {
$index = strpos($expression, $operator, $startFrom);
if ($index === false) break;
if ($expressionOutOfBraceList[$index]) break;
$startFrom = $index + 1;
}
if ($index !== false) {
$possibleRightOperator = null;
if (strlen($operator) === 1) {

View File

@@ -161,6 +161,9 @@ class Base
if ($desc) {
$list = array_reverse($list);
}
foreach ($list as $i => $listItem) {
$list[$i] = str_replace(',', '_COMMA_', $listItem);
}
$result['orderBy'] = 'LIST:' . $sortBy . ':' . implode(',', $list);
return;
}
@@ -392,7 +395,8 @@ class Base
protected function q($params, &$result)
{
if (isset($params['q']) && $params['q'] !== '') {
$this->textFilter($params['q'], $result);
$textFilter = $params['q'];
$this->textFilter($textFilter, $result);
}
}
@@ -1535,7 +1539,8 @@ class Base
if (!$fullTextSearchMinLength) {
$fullTextSearchMinLength = 0;
}
if (mb_strlen($textFilter) >= $fullTextSearchMinLength) {
$textFilterWoWildcards = str_replace('*', '', $textFilter);
if (mb_strlen($textFilterWoWildcards) >= $fullTextSearchMinLength) {
$useFullTextSearch = true;
}
}
@@ -1561,8 +1566,10 @@ class Base
}
if ($useFullTextSearch) {
$textFilter = str_replace(['(', ')'], '', $textFilter);
if (
$isAuxiliaryUse
$isAuxiliaryUse && mb_strpos($textFilter, '*') === false
||
mb_strpos($textFilter, ' ') === false
&&
@@ -1575,6 +1582,18 @@ class Base
$function = 'MATCH_NATURAL_LANGUAGE';
} else {
$function = 'MATCH_BOOLEAN';
$textFilter = str_replace('@', '*', $textFilter);
}
while (strpos($textFilter, '**')) {
$textFilter = str_replace('**', '*', $textFilter);
$textFilter = trim($textFilter);
}
while (mb_substr($textFilter, -2) === ' *') {
$textFilter = mb_substr($textFilter, 0, mb_strlen($textFilter) - 2);
$textFilter = trim($textFilter);
}
$fullTextSearchColumnSanitizedList = [];
@@ -1617,15 +1636,34 @@ class Base
$forceFullTextSearch = true;
}
$textFilterForFullTextSearch = $textFilter;
$skipWidlcards = false;
if (!$useFullTextSearch) {
if (mb_strpos($textFilter, '*') !== false) {
$skipWidlcards = true;
$textFilter = str_replace('*', '%', $textFilter);
if (mb_strpos($textFilter, '*') !== false) {
$skipWidlcards = true;
$textFilter = str_replace('*', '%', $textFilter);
} else {
if (!$useFullTextSearch) {
$textFilterForFullTextSearch .= '*';
}
}
$fullTextSearchData = $this->getFullTextSearchDataForTextFilter($textFilter, !$useFullTextSearch);
$textFilterForFullTextSearch = str_replace('%', '*', $textFilterForFullTextSearch);
$skipFullTextSearch = false;
if (!$forceFullTextSearch) {
if (mb_strpos($textFilterForFullTextSearch, '*') === 0) {
$skipFullTextSearch = true;
} else if (mb_strpos($textFilterForFullTextSearch, ' *') !== false) {
$skipFullTextSearch = true;
}
}
$fullTextSearchData = null;
if (!$skipFullTextSearch) {
$fullTextSearchData = $this->getFullTextSearchDataForTextFilter($textFilterForFullTextSearch, !$useFullTextSearch);
}
$fullTextGroup = [];

View File

@@ -25,16 +25,19 @@
*
* 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\Core\Utils\Database\DBAL\Driver\PDOMySql;
class Driver extends \Doctrine\DBAL\Driver\PDOMySql\Driver
class Driver extends \Doctrine\DBAL\Driver\PDOMySql\Driver
{
public function getDatabasePlatform()
{
return new \Espo\Core\Utils\Database\DBAL\Platforms\MySqlPlatform();
}
public function getSchemaManager(\Doctrine\DBAL\Connection $conn)
{
return new \Espo\Core\Utils\Database\DBAL\Schema\MySqlSchemaManager($conn);
}
}

View File

@@ -0,0 +1,82 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2018 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://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\Core\Utils\Database\DBAL\Schema;
use Doctrine\DBAL\Schema\Index as DBALIndex;
class Index extends \Doctrine\DBAL\Schema\Index
{
public function addFlag($flag)
{
$this->_flags[strtolower($flag)] = true;
return $this;
}
public function hasFlag($flag)
{
return isset($this->_flags[strtolower($flag)]);
}
public function removeFlag($flag)
{
unset($this->_flags[strtolower($flag)]);
}
public function isFullfilledBy(DBALIndex $other)
{
if (count($other->getColumns()) != count($this->getColumns())) {
return false;
}
$sameColumns = $this->spansColumns($other->getColumns());
if ($sameColumns) {
$flags = $this->getFlags();
$otherFlags = $other->getFlags();
if ( ! $this->isUnique() && !$this->isPrimary() && $flags === $otherFlags) {
return true;
} else if ($other->isPrimary() != $this->isPrimary()) {
return false;
} else if ($other->isUnique() != $this->isUnique()) {
return false;
}
if (count($flags) != count($otherFlags) || array_diff($flags, $otherFlags) !== array_diff($otherFlags, $flags)) {
return false;
}
return true;
}
return false;
}
}

View File

@@ -0,0 +1,144 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2018 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://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\Core\Utils\Database\DBAL\Schema;
use Doctrine\DBAL\Events;
use Doctrine\DBAL\Event\SchemaIndexDefinitionEventArgs;
class MySqlSchemaManager extends \Doctrine\DBAL\Schema\MySqlSchemaManager
{
public function createSchema()
{
$sequences = array();
if ($this->_platform->supportsSequences()) {
$sequences = $this->listSequences();
}
$tables = $this->listTables();
return new Schema($tables, $sequences, $this->createSchemaConfig());
}
public function listTables()
{
$tableNames = $this->listTableNames();
$tables = array();
foreach ($tableNames as $tableName) {
$tables[] = $this->listTableDetails($tableName);
}
return $tables;
}
public function listTableDetails($tableName)
{
$columns = $this->listTableColumns($tableName);
$foreignKeys = array();
if ($this->_platform->supportsForeignKeyConstraints()) {
$foreignKeys = $this->listTableForeignKeys($tableName);
}
$indexes = $this->listTableIndexes($tableName);
return new Table($tableName, $columns, $indexes, $foreignKeys, false, array());
}
public function listTableIndexes($table)
{
$sql = $this->_platform->getListTableIndexesSQL($table, $this->_conn->getDatabase());
$tableIndexes = $this->_conn->fetchAll($sql);
return $this->_getPortableTableIndexesList($tableIndexes, $table);
}
protected function _getPortableTableIndexesList($tableIndexes, $tableName=null)
{
foreach($tableIndexes as $k => $v) {
$v = array_change_key_case($v, CASE_LOWER);
if($v['key_name'] == 'PRIMARY') {
$v['primary'] = true;
} else {
$v['primary'] = false;
}
if (strpos($v['index_type'], 'FULLTEXT') !== false) {
$v['flags'] = array('FULLTEXT');
}
$tableIndexes[$k] = $v;
}
$result = array();
foreach($tableIndexes as $tableIndex) {
$indexName = $keyName = $tableIndex['key_name'];
if ($tableIndex['primary']) {
$keyName = 'primary';
}
$keyName = strtolower($keyName);
if (!isset($result[$keyName])) {
$result[$keyName] = array(
'name' => $indexName,
'columns' => array($tableIndex['column_name']),
'unique' => $tableIndex['non_unique'] ? false : true,
'primary' => $tableIndex['primary'],
'flags' => isset($tableIndex['flags']) ? $tableIndex['flags'] : array(),
);
} else {
$result[$keyName]['columns'][] = $tableIndex['column_name'];
}
}
$eventManager = $this->_platform->getEventManager();
$indexes = array();
foreach($result as $indexKey => $data) {
$index = null;
$defaultPrevented = false;
if (null !== $eventManager && $eventManager->hasListeners(Events::onSchemaIndexDefinition)) {
$eventArgs = new SchemaIndexDefinitionEventArgs($data, $tableName, $this->_conn);
$eventManager->dispatchEvent(Events::onSchemaIndexDefinition, $eventArgs);
$defaultPrevented = $eventArgs->isDefaultPrevented();
$index = $eventArgs->getIndex();
}
if ( ! $defaultPrevented) {
$index = new Index($data['name'], $data['columns'], $data['unique'], $data['primary'], $data['flags']);
}
if ($index) {
$indexes[$indexKey] = $index;
}
}
return $indexes;
}
}

View File

@@ -28,9 +28,9 @@
************************************************************************/
namespace Espo\Core\Utils\Database\DBAL\Schema;
class Schema extends \Doctrine\DBAL\Schema\Schema
{
/**
* Creates a new table.
*

View File

@@ -28,11 +28,12 @@
************************************************************************/
namespace Espo\Core\Utils\Database\DBAL\Schema;
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Schema\SchemaException;
class Table extends \Doctrine\DBAL\Schema\Table
{
/**
* @param string $columnName
* @param string $typeName
@@ -49,4 +50,35 @@ class Table extends \Doctrine\DBAL\Schema\Table
return $column;
}
public function addIndex(array $columnNames, $indexName = null, array $flags = array())
{
if($indexName == null) {
$indexName = $this->_generateIdentifierName(
array_merge(array($this->getName()), $columnNames), "idx", $this->_getMaxIdentifierLength()
);
}
return $this->_createIndex($columnNames, $indexName, false, false, $flags);
}
private function _createIndex(array $columnNames, $indexName, $isUnique, $isPrimary, array $flags = array())
{
if (preg_match('(([^a-zA-Z0-9_]+))', $indexName)) {
throw SchemaException::indexNameInvalid($indexName);
}
foreach ($columnNames as $columnName => $indexColOptions) {
if (is_numeric($columnName) && is_string($indexColOptions)) {
$columnName = $indexColOptions;
}
if ( ! $this->hasColumn($columnName)) {
throw SchemaException::columnDoesNotExist($columnName, $this->_name);
}
}
$this->_addIndex(new Index($indexName, $columnNames, $isUnique, $isPrimary, $flags));
return $this;
}
}

View File

@@ -58,18 +58,32 @@ class AssignmentEmailNotification extends \Espo\Core\Hooks\Base
foreach ($userIdList as $userId) {
if (in_array($userId, $fetchedAssignedUserIdList)) continue;
if ($this->getUser()->id === $userId) continue;
if (!$this->isNotSelfAssignment($entity, $userId)) continue;
$this->createJob($entity, $userId);
}
} else {
$userId = $entity->get('assignedUserId');
if (!empty($userId) && $userId != $this->getUser()->id && $entity->isAttributeChanged('assignedUserId')) {
if (!empty($userId) && $entity->isAttributeChanged('assignedUserId') && $this->isNotSelfAssignment($entity, $userId)) {
$this->createJob($entity, $userId);
}
}
}
}
protected function isNotSelfAssignment(Entity $entity, $assignedUserId)
{
if ($entity->hasAttribute('createdById') && $entity->hasAttribute('modifiedById')) {
if ($entity->isNew()) {
$isNotSelfAssignment = $assignedUserId !== $entity->get('createdById');
} else {
$isNotSelfAssignment = $assignedUserId !== $entity->get('modifiedById');
}
} else {
$isNotSelfAssignment = $assignedUserId !== $this->getUser()->id;
}
return $isNotSelfAssignment;
}
protected function createJob(Entity $entity, $userId)
{
$job = $this->getEntityManager()->getEntity('Job');

View File

@@ -576,7 +576,13 @@ abstract class Base
if (strpos($orderBy, 'LIST:') === 0) {
list($l, $field, $list) = explode(':', $orderBy);
$fieldPath = $this->getFieldPathForOrderBy($entity, $field);
$part = "FIELD(" . $fieldPath . ", '" . implode("', '", array_reverse(explode(",", $list))) . "') DESC";
$listQuoted = [];
$list = array_reverse(explode(',', $list));
foreach ($list as $i => $listItem) {
$listItem = str_replace('_COMMA_', ',', $listItem);
$listQuoted[] = $this->quote($listItem);
}
$part = "FIELD(" . $fieldPath . ", " . implode(", ", $listQuoted) . ") DESC";
return $part;
}

View File

@@ -62,7 +62,8 @@
"personStringData": {
"type": "varchar",
"notStorable": true,
"disabled": true
"disabled": true,
"view": "views/email/fields/person-string-data"
},
"isRead": {
"type": "bool",

View File

@@ -85,6 +85,7 @@
},
"collection": {
"sortBy": "createdAt",
"asc": false
"asc": false,
"textFilterFields": ["name", "bodyPlain", "body", "subject"]
}
}

File diff suppressed because one or more lines are too long

View File

@@ -31,24 +31,6 @@ namespace Espo\SelectManagers;
class EmailTemplate extends \Espo\Core\SelectManagers\Base
{
protected function textFilter($textFilter, &$result)
{
$d = array();
$d['name*'] = '' . $textFilter . '%';
$d['subject*'] = '%' . $textFilter . '%';
if (strlen($textFilter) >= self::MIN_LENGTH_FOR_CONTENT_SEARCH) {
$d['bodyPlain*'] = '%' . $textFilter . '%';
$d['body*'] = '%' . $textFilter . '%';
}
$result['whereClause'][] = array(
'OR' => $d
);
}
protected function filterActual(&$result)
{
@@ -58,4 +40,3 @@ class EmailTemplate extends \Espo\Core\SelectManagers\Base
}
}

View File

@@ -135,7 +135,7 @@ Espo.define('crm:views/dashlets/opportunities-by-stage', 'crm:views/dashlets/abs
mouse: {
track: true,
relative: true,
position: 's',
position: 'w',
lineColor: this.hoverColor,
trackFormatter: function (obj) {
var label = (obj.series.label || self.translate('None'));

View File

@@ -160,6 +160,7 @@ Espo.define('crm:views/dashlets/sales-by-month', 'crm:views/dashlets/abstract/ch
track: true,
relative: true,
lineColor: this.hoverColor,
position: 's',
trackFormatter: function (obj) {
var i = parseInt(obj.x);
var value = '';

View File

@@ -137,7 +137,7 @@ Espo.define('crm:views/dashlets/sales-pipeline', 'crm:views/dashlets/abstract/ch
mouse: {
track: true,
relative: true,
position: 'ne',
position: 'n',
lineColor: this.hoverColor,
trackFormatter: function (obj) {
if (obj.x >= self.chartData.length) {

View File

@@ -0,0 +1,4 @@
<span class="list-icon-container pull-right"{{#unless isReplied}} style="visibility: hidden;"{{/unless}}>
<span class="glyphicon glyphicon-share-alt small text-muted icon-flip-horizontal" title="{{translate 'isReplied' category='fields' scope='Email'}}"></span>
</span>
<span title="{{value}}">{{value}}</span>

View File

@@ -1,5 +1,7 @@
{{#if hasAttachment}}
<span class="glyphicon glyphicon-paperclip small text-muted pull-right"></span>
<span class="list-icon-container pull-right">
<span class="glyphicon glyphicon-paperclip small text-muted" title="{{translate 'hasAttachment' category='fields' scope='Email'}}"></span>
</span>
{{/if}}
{{#unless isRead}}<strong>{{/unless}}
<a href="#{{model.name}}/view/{{model.id}}" class="link{{#if isImportant}} text-warning{{/if}}" data-id="{{model.id}}" title="{{value}}">{{value}}</a>

View File

@@ -1,6 +1,6 @@
{{#unless isPlain}}
{{#if useIframe}}
<iframe frameborder="0" style="width: 100%;" class="hidden" scrolling="no"></iframe>
<iframe frameborder="0" style="width: 100%; overflow-x: hidden; overflow-y: hidden;" class="hidden"></iframe>
{{else}}
<div class="html-container">{{{value}}}</div>
{{/if}}

View File

@@ -1,6 +1,6 @@
<div class="search-container">{{{search}}}</div>
<div class="row">
<div class="row row-list-container">
{{#unless categoriesDisabled}}
<div class="categories-container{{#unless categoriesDisabled}} col-md-3 col-sm-4{{else}} col-md-12{{/unless}}">{{{categories}}}</div>
{{/unless}}
@@ -9,6 +9,6 @@
{{#if createButton}}
<div class="button-container">
<button class="btn btn-default" data-action="create">{{translate 'Create'}}</button>
<button class="btn btn-default" data-action="create">{{createText}}</button>
</div>
{{/if}}

View File

@@ -1 +1 @@
<input type="checkbox" class="record-checkbox" data-id="{{model.id}}">
<span class="record-checkbox-container"><input type="checkbox" class="record-checkbox" data-id="{{model.id}}"></span>

View File

@@ -1,3 +1,4 @@
{{#if fieldList.length}}
<div class="row">
{{#each fieldList}}
<div class="cell form-group col-sm-6 col-md-12{{#if hidden}} hidden-cell{{/if}}" data-name="{{name}}">
@@ -8,21 +9,26 @@
</div>
{{/each}}
</div>
{{/if}}
{{#unless complexDateFieldsDisabled}}
<div class="row">
{{#if hasComplexCreated}}
<div class="cell form-group col-sm-6 col-md-12" data-name="complexCreated">
<label class="control-label" data-name="complexCreated"><span class="label-text">{{translate 'Created'}}</span></label>
<div class="field" data-name="complexCreated">
<span data-name="createdAt" class="field">{{{createdAtField}}}</span> <span class="text-muted">&raquo;</span> <span data-name="createdBy" class="field">{{{createdByField}}}</span>
</div>
</div>
{{/if}}
{{#if hasComplexModified}}
<div class="cell form-group col-sm-6 col-md-12" data-name="complexModified">
<label class="control-label" data-name="complexModified"><span class="label-text">{{translate 'Modified'}}</span></label>
<div class="field" data-name="complexModified">
<span data-name="modifiedAt" class="field">{{{modifiedAtField}}}</span> <span class="text-muted">&raquo;</span> <span data-name="modifiedBy" class="field">{{{modifiedByField}}}</span>
</div>
</div>
{{/if}}
</div>
{{/unless}}

View File

@@ -1,3 +1,4 @@
{{#if fieldList.length}}
<div class="row">
{{#each fieldList}}
<div class="cell form-group col-sm-6 col-md-12{{#if hidden}} hidden-cell{{/if}}" data-name="{{name}}">
@@ -8,3 +9,4 @@
</div>
{{/each}}
</div>
{{/if}}

View File

@@ -35,7 +35,8 @@ Espo.define('views/admin/entity-manager/record/edit-formula', 'views/record/base
setup: function () {
Dep.prototype.setup.call(this);
this.createField('beforeSaveCustomScript', 'views/fields/formula', {
targetEntityType: this.options.targetEntityType
targetEntityType: this.options.targetEntityType,
height: 500
}, 'edit');
}

View File

@@ -0,0 +1,42 @@
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2018 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://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.
************************************************************************/
Espo.define('views/email/fields/person-string-data', 'views/fields/varchar', function (Dep) {
return Dep.extend({
listTemplate: 'email/fields/person-string-data/list',
data: function () {
var data = Dep.prototype.data.call(this);
data.isReplied = this.model.get('isReplied');
return data;
}
});
});

View File

@@ -37,6 +37,7 @@ Espo.define('views/email/fields/subject', 'views/fields/varchar', function (Dep)
data.isRead = (this.model.get('sentById') === this.getUser().id) || this.model.get('isRead');
data.isImportant = this.model.get('isImportant');
data.hasAttachment = this.model.get('hasAttachment');
data.isReplied = this.model.get('isReplied');
if (!data.isNotEmpty) {
if (

View File

@@ -208,10 +208,23 @@ Espo.define('views/fields/wysiwyg', ['views/fields/text', 'lib!Summernote'], fun
documentElement.write(body);
documentElement.close();
var $body = $iframe.contents().find('html body');
var $document = $(documentElement);
var processWidth = function () {
var bodyElement = $body.get(0);
if (bodyElement) {
if (bodyElement.clientWidth !== iframeElement.scrollWidth) {
iframeElement.style.height = (iframeElement.scrollHeight + 20) + 'px';
}
}
};
var increaseHeightStep = 10;
var processIncreaseHeight = function (iteration, previousDiff) {
$body.css('height', '');
iteration = iteration || 0;
if (iteration > 200) {
@@ -224,6 +237,8 @@ Espo.define('views/fields/wysiwyg', ['views/fields/text', 'lib!Summernote'], fun
if (typeof previousDiff !== 'undefined') {
if (diff === previousDiff) {
$body.css('height', (iframeElement.clientHeight - increaseHeightStep) + 'px');
processWidth();
return;
}
}
@@ -232,6 +247,8 @@ Espo.define('views/fields/wysiwyg', ['views/fields/text', 'lib!Summernote'], fun
var height = iframeElement.scrollHeight + increaseHeightStep;
iframeElement.style.height = height + 'px';
processIncreaseHeight(iteration, diff);
} else {
processWidth();
}
};
@@ -241,7 +258,6 @@ Espo.define('views/fields/wysiwyg', ['views/fields/text', 'lib!Summernote'], fun
overflowY: 'hidden',
overflowX: 'hidden'
});
$iframe.attr('scrolling', 'no');
iframeElement.style.height = '0px';
} else {
@@ -265,7 +281,6 @@ Espo.define('views/fields/wysiwyg', ['views/fields/text', 'lib!Summernote'], fun
overflowY: 'hidden',
overflowX: 'scroll'
});
$iframe.attr('scrolling', 'yes');
}
};
@@ -423,19 +438,21 @@ Espo.define('views/fields/wysiwyg', ['views/fields/text', 'lib!Summernote'], fun
onScrollEdit: function (e) {
var $target = $(e.target);
var toolbarHeight = this.$toolbar.height();
var top;
var toolbarWidth = this.$toolbar.parent().width();
var edgeTop, edgeTopAbsolute;
if ($target.get(0) === window.document) {
var $buttonContainer = $target.find('.detail-button-container:not(.hidden)');
var offset = $buttonContainer.offset();
if (offset) {
var edgeTop = offset.top + $buttonContainer.height();
var edgeTopAbsolute = edgeTop - $(window).scrollTop();
edgeTop = offset.top + $buttonContainer.height();
edgeTopAbsolute = edgeTop - $(window).scrollTop();
}
} else {
var offset = $target.offset();
if (offset) {
var edgeTop = offset.top;
var edgeTopAbsolute = edgeTop;
edgeTop = offset.top;
edgeTopAbsolute = edgeTop - $(window).scrollTop();
}
}
@@ -449,7 +466,8 @@ Espo.define('views/fields/wysiwyg', ['views/fields/text', 'lib!Summernote'], fun
if (toStick) {
this.$toolbar.css({
top: edgeTopAbsolute + 'px'
top: edgeTopAbsolute + 'px',
width: toolbarWidth + 'px'
});
this.$toolbar.addClass('sticked');
this.$area.css({
@@ -458,7 +476,8 @@ Espo.define('views/fields/wysiwyg', ['views/fields/text', 'lib!Summernote'], fun
});
} else {
this.$toolbar.css({
top: ''
top: '',
width: ''
});
this.$toolbar.removeClass('sticked');
this.$area.css({

View File

@@ -92,15 +92,17 @@ Espo.define('views/global-search/global-search', 'view', function (Dep) {
$document = $(document);
$document.on('mouseup.global-search', function (e) {
if (e.which !== 1) return;
if (!$container.is(e.target) && $container.has(e.target).length === 0) {
this.closePanel();
}
}.bind(this));
$document.on('click.global-search', function (e) {
if (e.target.tagName == 'A' && $(e.target).data('action') != 'showMore') {
setTimeout(function () {
this.closePanel();
}.bind(this), 100);
return;
}
if (!$container.is(e.target) && $container.has(e.target).length === 0) {
this.closePanel();
}
}.bind(this));
},
@@ -112,11 +114,10 @@ Espo.define('views/global-search/global-search', 'view', function (Dep) {
if (this.hasView('panel')) {
this.getView('panel').remove();
};
$document.off('mouseup.global-search');
$container.remove();
},
$document.off('mouseup.global-search');
$document.off('click.global-search');
$container.remove();
}
});
});

View File

@@ -50,15 +50,19 @@ Espo.define('views/modals/select-records-with-categories', ['views/modals/select
this.scope = this.entityType = this.options.scope || this.scope;
this.categoryScope = this.categoryScope || this.scope + 'Category';
Dep.prototype.setup.call(this);
this.categoriesDisabled = this.categoriesDisabled ||
this.getMetadata().get('scopes.' + this.categoryScope + '.disabled') ||
!this.getAcl().checkScope(this.categoryScope);
Dep.prototype.setup.call(this);
},
loadList: function () {
this.loadCategories();
if (!this.categoriesDisabled) {
this.loadCategories();
}
Dep.prototype.loadList.call(this);
},

View File

@@ -153,7 +153,7 @@ Espo.define('views/record/list', 'view', function (Dep) {
var method = 'massAction' + Espo.Utils.upperCaseFirst(action);
if (method in this) {
this[method]();
this[method]();
} else {
this.massAction(action);
}
@@ -311,9 +311,9 @@ Espo.define('views/record/list', 'view', function (Dep) {
this.$el.find('input.select-all').prop('checked', true);
this.massActionList.forEach(function(item) {
if (!~this.checkAllResultMassActionList.indexOf(item)) {
this.$el.find('div.list-buttons-container .actions li a.mass-action[data-action="'+item+'"]').parent().addClass('hidden');
}
if (!~this.checkAllResultMassActionList.indexOf(item)) {
this.$el.find('div.list-buttons-container .actions li a.mass-action[data-action="'+item+'"]').parent().addClass('hidden');
}
}, this);
if (this.checkAllResultMassActionList.length) {
@@ -333,9 +333,9 @@ Espo.define('views/record/list', 'view', function (Dep) {
this.massActionList.forEach(function(item) {
if (!~this.checkAllResultMassActionList.indexOf(item)) {
this.$el.find('div.list-buttons-container .actions li a.mass-action[data-action="'+item+'"]').parent().removeClass('hidden');
}
if (!~this.checkAllResultMassActionList.indexOf(item)) {
this.$el.find('div.list-buttons-container .actions li a.mass-action[data-action="'+item+'"]').parent().removeClass('hidden');
}
}, this);
},
@@ -469,11 +469,11 @@ Espo.define('views/record/list', 'view', function (Dep) {
var ids = [];
var data = {};
if (this.allResultIsChecked) {
data.where = this.collection.getWhere();
data.where = this.collection.getWhere();
data.selectData = this.collection.data || {};
data.byWhere = true;
data.byWhere = true;
} else {
data.ids = ids;
data.ids = ids;
}
for (var i in this.checkedList) {
@@ -485,45 +485,45 @@ Espo.define('views/record/list', 'view', function (Dep) {
type: 'POST',
data: JSON.stringify(data)
}).done(function (result) {
result = result || {};
var count = result.count;
if (this.allResultIsChecked) {
if (count) {
this.unselectAllResult();
this.listenToOnce(this.collection, 'sync', function () {
var msg = 'massRemoveResult';
if (count == 1) {
msg = 'massRemoveResultSingle'
}
Espo.Ui.success(this.translate(msg, 'messages').replace('{count}', count));
}, this);
this.collection.fetch();
Espo.Ui.notify(false);
} else {
Espo.Ui.warning(self.translate('noRecordsRemoved', 'messages'));
}
} else {
var idsRemoved = result.ids || [];
if (count) {
idsRemoved.forEach(function (id) {
Espo.Ui.notify(false);
this.checkedList = [];
result = result || {};
var count = result.count;
if (this.allResultIsChecked) {
if (count) {
this.unselectAllResult();
this.listenToOnce(this.collection, 'sync', function () {
var msg = 'massRemoveResult';
if (count == 1) {
msg = 'massRemoveResultSingle'
}
Espo.Ui.success(this.translate(msg, 'messages').replace('{count}', count));
}, this);
this.collection.fetch();
Espo.Ui.notify(false);
} else {
Espo.Ui.warning(self.translate('noRecordsRemoved', 'messages'));
}
} else {
var idsRemoved = result.ids || [];
if (count) {
idsRemoved.forEach(function (id) {
Espo.Ui.notify(false);
this.collection.trigger('model-removing', id);
this.removeRecordFromList(id);
this.uncheckRecord(id, null, true);
}, this);
var msg = 'massRemoveResult';
if (count == 1) {
msg = 'massRemoveResultSingle'
}
Espo.Ui.success(self.translate(msg, 'messages').replace('{count}', count));
} else {
Espo.Ui.warning(self.translate('noRecordsRemoved', 'messages'));
}
}
}, this);
var msg = 'massRemoveResult';
if (count == 1) {
msg = 'massRemoveResultSingle'
}
Espo.Ui.success(self.translate(msg, 'messages').replace('{count}', count));
} else {
Espo.Ui.warning(self.translate('noRecordsRemoved', 'messages'));
}
}
}.bind(this));
}, this);
}, this);
},
massActionPrintPdf: function () {
@@ -681,12 +681,12 @@ Espo.define('views/record/list', 'view', function (Dep) {
Espo.Ui.warning(this.translate('noRecordsUpdated', 'messages'));
}
if (allResultIsChecked) {
this.selectAllResult();
} else {
ids.forEach(function (id) {
this.checkRecord(id);
}, this);
}
this.selectAllResult();
} else {
ids.forEach(function (id) {
this.checkRecord(id);
}, this);
}
}.bind(this));
this.collection.fetch();
}, this);
@@ -694,16 +694,16 @@ Espo.define('views/record/list', 'view', function (Dep) {
},
massActionExport: function () {
if (!this.getConfig().get('exportDisabled') || this.getUser().get('isAdmin')) {
this.export();
}
if (!this.getConfig().get('exportDisabled') || this.getUser().get('isAdmin')) {
this.export();
}
},
removeMassAction: function (item) {
var index = this.massActionList.indexOf(item);
if (~index) {
this.massActionList.splice(index, 1);
}
var index = this.massActionList.indexOf(item);
if (~index) {
this.massActionList.splice(index, 1);
}
},
addMassAction: function (item, allResult) {
@@ -758,9 +758,9 @@ Espo.define('views/record/list', 'view', function (Dep) {
var checkAllResultMassActionList = [];
this.checkAllResultMassActionList.forEach(function (item) {
if (~this.massActionList.indexOf(item)) {
checkAllResultMassActionList.push(item);
}
if (~this.massActionList.indexOf(item)) {
checkAllResultMassActionList.push(item);
}
}, this);
this.checkAllResultMassActionList = checkAllResultMassActionList;
@@ -790,7 +790,7 @@ Espo.define('views/record/list', 'view', function (Dep) {
||
this.getAcl().get('exportPermission') === 'no'
) {
this.removeMassAction('export');
this.removeMassAction('export');
}
if (
@@ -970,13 +970,13 @@ Espo.define('views/record/list', 'view', function (Dep) {
var defs = [];
for (var i in this.listLayout) {
var width = false;
var width = false;
if ('width' in this.listLayout[i] && this.listLayout[i].width !== null) {
width = this.listLayout[i].width + '%';
} else if ('widthPx' in this.listLayout[i]) {
width = this.listLayout[i].widthPx;
}
} else if ('widthPx' in this.listLayout[i]) {
width = this.listLayout[i].widthPx;
}
var item = {
name: this.listLayout[i].name,
@@ -1013,7 +1013,7 @@ Espo.define('views/record/list', 'view', function (Dep) {
layout.push({
name: 'r-checkboxField',
columnName: 'r-checkbox',
template: 'record.list-checkbox'
template: 'record/list-checkbox'
});
}
@@ -1057,7 +1057,7 @@ Espo.define('views/record/list', 'view', function (Dep) {
return layout;
},
checkRecord: function (id, $target) {
checkRecord: function (id, $target, isSilent) {
$target = $target || this.$el.find('.record-checkbox[data-id="' + id + '"]');
if (!$target.size()) return;
@@ -1071,24 +1071,28 @@ Espo.define('views/record/list', 'view', function (Dep) {
$target.closest('tr').addClass('active');
this.handleAfterCheck();
this.handleAfterCheck(isSilent);
},
uncheckRecord: function (id, $target) {
uncheckRecord: function (id, $target, isSilent) {
$target = $target || this.$el.find('.record-checkbox[data-id="' + id + '"]');
$target.get(0).checked = false;
if ($target.get(0)) {
$target.get(0).checked = false;
}
var index = this.checkedList.indexOf(id);
if (index != -1) {
this.checkedList.splice(index, 1);
}
$target.closest('tr').removeClass('active');
if ($target.get(0)) {
$target.closest('tr').removeClass('active');
}
this.handleAfterCheck();
this.handleAfterCheck(isSilent);
},
handleAfterCheck: function () {
handleAfterCheck: function (isSilent) {
if (this.checkedList.length) {
this.$el.find('.actions-button').removeAttr('disabled');
} else {
@@ -1101,7 +1105,9 @@ Espo.define('views/record/list', 'view', function (Dep) {
this.$el.find('.select-all').prop('checked', false);
}
this.trigger('check');
if (!isSilent) {
this.trigger('check');
}
},
getRowActionsDefs: function () {

View File

@@ -34,28 +34,44 @@ Espo.define('views/record/panels/default-side', 'views/record/panels/side', func
data: function () {
var data = Dep.prototype.data.call(this);
if (this.complexCreatedDisabled && this.complexModifiedDisabled) {
if (this.complexCreatedDisabled && this.complexModifiedDisabled || (!this.hasComplexCreated && !this.hasComplexModified)) {
data.complexDateFieldsDisabled = true;
}
data.hasComplexCreated = this.hasComplexCreated;
data.hasComplexModified = this.hasComplexModified;
return data;
},
setup: function () {
Dep.prototype.setup.call(this);
this.hasComplexCreated =
!!this.getMetadata().get(['entityDefs', this.model.name, 'fields', 'createdAt'])
&&
!!this.getMetadata().get(['entityDefs', this.model.name, 'fields', 'createdBy']);
this.hasComplexModified =
!!this.getMetadata().get(['entityDefs', this.model.name, 'fields', 'modifiedAt'])
&&
!!this.getMetadata().get(['entityDefs', this.model.name, 'fields', 'modifiedBy']);
if (!this.complexCreatedDisabled) {
this.createField('createdBy', null, null, null, true);
this.createField('createdAt', null, null, null, true);
if (!this.model.get('createdById')) {
this.recordViewObject.hideField('complexCreated');
if (this.hasComplexCreated) {
this.createField('createdBy', null, null, null, true);
this.createField('createdAt', null, null, null, true);
if (!this.model.get('createdById')) {
this.recordViewObject.hideField('complexCreated');
}
}
} else {
this.recordViewObject.hideField('complexCreated');
}
if (!this.complexModifiedDisabled) {
this.createField('modifiedBy', null, null, null, true);
this.createField('modifiedAt', null, null, null, true);
if (this.hasComplexModified) {
this.createField('modifiedBy', null, null, null, true);
this.createField('modifiedAt', null, null, null, true);
}
if (!this.model.get('modifiedById')) {
this.recordViewObject.hideField('complexModified');
}
@@ -63,13 +79,13 @@ Espo.define('views/record/panels/default-side', 'views/record/panels/side', func
this.recordViewObject.hideField('complexModified');
}
if (!this.complexCreatedDisabled) {
if (!this.complexCreatedDisabled && this.hasComplexCreated) {
this.listenTo(this.model, 'change:createdById', function () {
if (!this.model.get('createdById')) return;
this.recordViewObject.showField('complexCreated');
}, this);
}
if (!this.complexModifiedDisabled) {
if (!this.complexModifiedDisabled && this.hasComplexModified) {
this.listenTo(this.model, 'change:modifiedById', function () {
if (!this.model.get('modifiedById')) return;
this.recordViewObject.showField('complexModified');

View File

@@ -120,6 +120,12 @@ Espo.define('views/record/panels/side', 'view', function (Dep) {
this.createFields();
},
afterRender: function () {
if (this.$el.children().size() === 0) {
this.$el.parent().addClass('hidden');
}
},
setupFields: function () {
},

View File

@@ -105,7 +105,7 @@ Espo.define('views/stream/panel', ['views/record/panels/relationship', 'lib!Text
if ($.contains(this.$postContainer.get(0), e.target)) return;
if (this.$textarea.val() !== '') return;
var attachmentsIds = this.seed.get('attachmentsIds');
var attachmentsIds = this.seed.get('attachmentsIds') || [];
if (!attachmentsIds.length && !this.getView('attachments').isUploading) {
this.disablePostingMode();
}
@@ -363,7 +363,7 @@ Espo.define('views/stream/panel', ['views/record/panels/relationship', 'lib!Text
return;
}
if (message == '' && this.seed.get('attachmentsIds').length == 0) {
if (message == '' && (this.seed.get('attachmentsIds') || []).length == 0) {
this.notify('Post cannot be empty', 'error');
this.$textarea.prop('disabled', false);
return;
@@ -386,7 +386,7 @@ Espo.define('views/stream/panel', ['views/record/panels/relationship', 'lib!Text
}, this);
model.set('post', message);
model.set('attachmentsIds', Espo.Utils.clone(this.seed.get('attachmentsIds')));
model.set('attachmentsIds', Espo.Utils.clone(this.seed.get('attachmentsIds') || []));
model.set('type', 'Post');
model.set('isInternal', this.isInternalNoteMode);

View File

@@ -132,7 +132,14 @@ div.list-expanded > ul > li > div.expanded-row > .cell:first-child {
border-left: 0;
}
.list > table.table td > span.glyphicon.pull-right,
.list > table.table td > span.list-icon-container.pull-right {
margin-left: 5px;
}
.list > table.table td > span.list-icon-container.pull-left {
margin-right: 5px;
}
.list > ul.list-group > li.list-row .cell > span.glyphicon.pull-right {
margin-left: 5px;
margin-top: 4px;
@@ -375,6 +382,10 @@ ul.dropdown-menu > li.checkbox {
user-select: none;
}
.dropdown-menu > li > a {
line-height: @line-height-computed;
}
ul.dropdown-menu > li > a.active {
background-color: @gray-lighter;
text-decoration: none;
@@ -961,8 +972,8 @@ table.list th {
.list > table th[data-name="r-checkbox"] {
white-space: nowrap;
.select-all-container {
line-height: @line-height-base;
height: unit(@line-height-base, em);
line-height: @line-height-computed;
height: @line-height-computed;
float: left;
}
}
@@ -977,6 +988,11 @@ table.list th {
line-height: 1;
}
.list .record-checkbox-container {
height: @line-height-computed;
display: block;
}
.list > table td > div.field {
white-space: normal;
}
@@ -1078,7 +1094,8 @@ table.less-padding td.cell[data-name="buttons"] > .btn-group {
right: -11px;
}
.list-container + .button-container {
.list-container + .button-container,
.row-list-container + .button-container {
margin-top: 10px;
padding-bottom: 0;
}
@@ -1273,6 +1290,15 @@ label.attach-file-label {
transform: rotate(90deg);
}
.icon-flip-horizontal {
-moz-transform: scaleX(-1);
-webkit-transform: scaleX(-1);
-o-transform: scaleX(-1);
transform: scaleX(-1);
-ms-filter: fliph;
filter: fliph;
}
.post-container > textarea.note,
textarea.auto-height {
overflow-x: hidden;

View File

@@ -33,6 +33,8 @@
.clearfix();
}
@line-height-small-computed: floor((@font-size-base * @line-height-small));
@table-cell-padding: 8px;
@table-cell-less-padding: 3px;

View File

@@ -1,6 +1,6 @@
{
"name": "espocrm",
"version": "5.3.2",
"version": "5.3.4",
"description": "",
"main": "index.php",
"repository": {

View File

@@ -0,0 +1,82 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2018 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://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 tests\integration\Espo\Core\FulltextIndex;
use Espo\Core\Utils\Util;
class CheckCreatedIndexTest extends \tests\integration\Core\BaseTestCase
{
protected $dataFile = 'InitData.php';
protected $pathToFiles = 'Core/FulltextIndex/customFiles';
public function entitylist()
{
return [
['Email'],
['Account'],
['Contact'],
];
}
/**
* @dataProvider entitylist
*/
public function testCreatedIndexes($entityName)
{
$entityManager = $this->getContainer()->get('entityManager');
$pdo = $entityManager->getPDO();
$fulltextFieldList = $entityManager->getOrmMetadata()->get($entityName, 'fullTextSearchColumnList');
if (!$fulltextFieldList) {
$this->assertNull($fulltextFieldList);
return;
}
$query = "SHOW INDEX FROM `". Util::toCamelCase($entityName) ."` WHERE Index_type = 'FULLTEXT'";
$sth = $pdo->prepare($query);
$sth->execute();
$rowList = $sth->fetchAll(\PDO::FETCH_ASSOC);
$this->assertNotEmpty($rowList);
$result = [];
foreach ($rowList as $row) {
$result[] = Util::toCamelCase($row['Column_name']);
}
asort($fulltextFieldList);
asort($result);
$this->assertEquals($fulltextFieldList, $result);
}
}

View File

@@ -56,4 +56,30 @@ class SearchByEmailAddressTest extends \tests\integration\Core\BaseTestCase
$this->assertArrayHasKey('collection', $result);
$this->assertEquals(1, count($result['collection']));
}
public function testTextSearch()
{
$entityManager = $this->getContainer()->get('entityManager');
$email = $entityManager->getEntity('Email');
$email->set('from', 'test@test.com');
$email->set('status', 'Archived');
$email->set('name', 'Improvements to our Privacy Policy');
$email->set('body', 'name abc test');
$entityManager->saveEntity($email);
$emailService = $this->getApplication()->getContainer()->get('serviceFactory')->create('Email');
$result = $emailService->findEntities([
'textFilter' => 'name abc'
]);
$this->assertArrayHasKey('collection', $result);
$this->assertEquals(1, count($result['collection']));
$result = $emailService->findEntities([
'textFilter' => 'Improvements to our Privacy Policy'
]);
$this->assertArrayHasKey('collection', $result);
$this->assertEquals(1, count($result['collection']));
}
}

View File

@@ -0,0 +1,11 @@
{
"collection": {
"fullTextSearch": true,
"sortBy": "createdAt",
"asc": false,
"textFilterFields": [
"name",
"emailAddress"
]
}
}

View File

@@ -0,0 +1,11 @@
{
"collection": {
"sortBy": "createdAt",
"asc": false,
"textFilterFields": [
"name",
"emailAddress"
],
"fullTextSearch": true
}
}

View File

@@ -0,0 +1,12 @@
{
"collection": {
"sortBy": "dateSent",
"asc": false,
"textFilterFields": [
"name",
"bodyPlain",
"body"
],
"fullTextSearch": true
}
}

View File

@@ -77,4 +77,29 @@ class EvaluatorTest extends \PHPUnit\Framework\TestCase
$actual = $this->evaluator->process($expression);
$this->assertTrue($actual);
}
function testSummationOfMultipleIfThenElse()
{
$expression = "
ifThenElse(
true,
(1 + 0 + 1) - 1 * 0.5,
0
)
+
ifThenElse(
true,
(1 - 0) * 0.5,
0
)
+
ifThenElse(
true,
(1 - 0) * 0.5,
0
)
";
$actual = $this->evaluator->process($expression);
$this->assertEquals(2.5, $actual);
}
}

View File

@@ -305,6 +305,7 @@ class ParserTest extends \PHPUnit\Framework\TestCase
]
]
];
$this->assertEquals($expected, $actual);
$expression = "!value * 10";