Compare commits

...

47 Commits

Author SHA1 Message Date
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
yuri 92fedd2c2e Merge branch 'hotfix/5.3.2' of ssh://172.20.0.1/var/git/espo/backend 2018-07-12 10:09:31 +03:00
Taras Machyshyn 0ec450d59d Schema bug fixes 2018-07-12 10:09:01 +03:00
yuri f0a1634f90 version 2018-07-12 10:03:04 +03:00
Taras Machyshyn 5b5f5c8ab7 Schema bug fixes 2018-07-11 15:16:59 +03:00
yuri 19d37e8081 text filter disabling 2018-07-11 11:04:01 +03:00
yuri d8ab10fd75 textFilterDisabled param 2018-07-11 10:37:17 +03:00
yuri 312de11a15 smtp local host name 2018-07-10 15:53:15 +03:00
yuri 47a22042b2 fix select records 2 2018-07-10 13:13:07 +03:00
yuri 31cea9e36e fix select records 2018-07-10 12:26:37 +03:00
yuri 2b983ad880 fix attachment multiple 2018-07-10 12:11:45 +03:00
yuri 21b0c2b2eb attachment multiple in list view 2018-07-10 11:58:11 +03:00
yuri 59b4aa61e4 not sortable by default 2018-07-10 11:36:20 +03:00
yuri 25d91a1e73 Merge branch 'hotfix/5.3.1' of ssh://172.20.0.1/var/git/espo/backend into hotfix/5.3.1 2018-07-09 13:36:24 +03:00
yuri 6750d32bd6 skip printing x status reason for pdo exception 2018-07-09 13:30:36 +03:00
yuri 70b8fc9ac9 foreign field: supporting number field 2018-07-09 12:52:55 +03:00
yuri 38a1be5ae3 version 2018-07-09 12:04:26 +03:00
yuri 1d9e9752c8 mass remove memory usage 2018-07-09 12:03:45 +03:00
yuri eb37c1dc47 fix email text filter by email 2018-07-09 11:58:31 +03:00
yuri 1df6ec7f0c mass update improvement memory usage 2018-07-09 11:52:33 +03:00
yuri 1d6ee3e030 clean up 2018-07-09 11:37:39 +03:00
yuri 6eb80b747c login view name in clientDefs 2018-07-09 11:14:28 +03:00
yuri 539c0d22d7 convert lead skip disabled entity types 2018-07-09 10:54:29 +03:00
yuri 8e7d607ad4 fix dynamic logic not has 2018-07-09 10:49:44 +03:00
yuri 59e15eb71f fix select all results modal 2018-07-09 10:48:36 +03:00
Taras Machyshyn c1ba6d5330 Metadata improvements 2018-07-06 17:02:19 +03:00
60 changed files with 703 additions and 201 deletions
+3 -3
View File
@@ -128,7 +128,7 @@ class Application
$slim->run();
} catch (\Exception $e) {
$container->get('output')->processError($e->getMessage(), $e->getCode(), true);
$container->get('output')->processError($e->getMessage(), $e->getCode(), true, $e);
}
}
@@ -177,7 +177,7 @@ class Application
try {
$auth = $this->createAuth();
} catch (\Exception $e) {
$container->get('output')->processError($e->getMessage(), $e->getCode());
$container->get('output')->processError($e->getMessage(), $e->getCode(), false, $e);
}
$apiAuth = $this->createApiAuth($auth);
@@ -227,7 +227,7 @@ class Application
$result = $controllerManager->process($controllerName, $actionName, $params, $data, $slim->request());
$container->get('output')->render($result);
} catch (\Exception $e) {
$container->get('output')->processError($e->getMessage(), $e->getCode());
$container->get('output')->processError($e->getMessage(), $e->getCode(), false, $e);
}
});
+8 -2
View File
@@ -89,8 +89,12 @@ class Sender
$this->transport = new SmtpTransport();
$config = $this->config;
$localHostName = $config->get('smtpLocalHostName', gethostname());
$opts = array(
'name' => 'admin',
'name' => $localHostName,
'host' => $params['server'],
'port' => $params['port'],
'connection_config' => array()
@@ -132,8 +136,10 @@ class Sender
$config = $this->config;
$localHostName = $config->get('smtpLocalHostName', gethostname());
$opts = array(
'name' => 'admin',
'name' => $localHostName,
'host' => $config->get('smtpServer'),
'port' => $config->get('smtpPort'),
'connection_config' => array()
+28 -6
View File
@@ -392,7 +392,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 +1536,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 +1563,10 @@ class Base
}
if ($useFullTextSearch) {
$textFilter = str_replace(['(', ')'], '', $textFilter);
if (
$isAuxiliaryUse
$isAuxiliaryUse && mb_strpos($textFilter, '*') === false
||
mb_strpos($textFilter, ' ') === false
&&
@@ -1575,6 +1579,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 +1633,21 @@ class Base
$forceFullTextSearch = true;
}
$textFilterForFullTextSearch = $textFilter;
$skipWidlcards = false;
if (!$useFullTextSearch) {
if (mb_strpos($textFilter, '*') !== false) {
$skipWidlcards = true;
$textFilter = str_replace('*', '%', $textFilter);
} else {
$textFilterForFullTextSearch .= '*';
}
$textFilterForFullTextSearch = str_replace('%', '*', $textFilterForFullTextSearch);
}
$fullTextSearchData = $this->getFullTextSearchDataForTextFilter($textFilter, !$useFullTextSearch);
$fullTextSearchData = $this->getFullTextSearchDataForTextFilter($textFilterForFullTextSearch, !$useFullTextSearch);
$fullTextGroup = [];
@@ -1689,7 +1711,7 @@ class Base
}
if (!$forceFullTextSearch) {
$this->applyAdditionalToTextFilterGroup($textFilter, $group);
$this->applyAdditionalToTextFilterGroup($textFilter, $group, $result);
}
if (!empty($fullTextGroup)) {
@@ -1707,7 +1729,7 @@ class Base
];
}
protected function applyAdditionalToTextFilterGroup($textFilter, &$group)
protected function applyAdditionalToTextFilterGroup($textFilter, &$group, &$result)
{
}
+13 -4
View File
@@ -42,6 +42,9 @@ class Output
500 => 'Internal Server Error',
);
protected $ignorePrintXStatusReasonExceptionClassNameList = [
'PDOException'
];
public function __construct(\Espo\Core\Utils\Api\Slim $slim)
{
@@ -69,7 +72,7 @@ class Output
echo $data;
}
public function processError($message = 'Error', $code = 500, $isPrint = false)
public function processError($message = 'Error', $code = 500, $isPrint = false, $exception = null)
{
$currentRoute = $this->getSlim()->router()->getCurrentRoute();
@@ -79,7 +82,7 @@ class Output
$GLOBALS['log']->error('API ['.$this->getSlim()->request()->getMethod().']:'.$currentRoute->getPattern().', Params:'.print_r($currentRoute->getParams(), true).', InputData: '.$inputData.' - '.$message);
}
$this->displayError($message, $code, $isPrint);
$this->displayError($message, $code, $isPrint, $exception);
}
/**
@@ -90,15 +93,21 @@ class Output
*
* @return void
*/
public function displayError($text, $statusCode = 500, $isPrint = false)
public function displayError($text, $statusCode = 500, $isPrint = false, $exception = null)
{
$GLOBALS['log']->error('Display Error: '.$text.', Code: '.$statusCode.' URL: '.$_SERVER['REQUEST_URI']);
ob_clean();
if (!empty( $this->slim)) {
$toPrintXStatusReason = true;
if ($exception && in_array(get_class($exception), $this->ignorePrintXStatusReasonExceptionClassNameList)) {
$toPrintXStatusReason = false;
}
$this->getSlim()->response()->setStatus($statusCode);
$this->getSlim()->response()->headers->set('X-Status-Reason', $text);
if ($toPrintXStatusReason) {
$this->getSlim()->response()->headers->set('X-Status-Reason', $text);
}
if ($isPrint) {
$status = $this->getCodeDesc($statusCode);
@@ -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);
}
}
@@ -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;
}
}
@@ -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;
}
}
@@ -28,9 +28,9 @@
************************************************************************/
namespace Espo\Core\Utils\Database\DBAL\Schema;
class Schema extends \Doctrine\DBAL\Schema\Schema
{
/**
* Creates a new table.
*
@@ -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;
}
}
@@ -40,7 +40,8 @@ class AttachmentMultiple extends Base
'type' => 'jsonArray',
'notStorable' => true,
'orderBy' => [['createdAt', 'ASC'], ['name', 'ASC']],
'isLinkMultipleIdList' => true
'isLinkMultipleIdList' => true,
'relation' => $fieldName
),
$fieldName.'Names' => array(
'type' => 'jsonObject',
@@ -181,7 +181,7 @@ class Converter
$schema = $this->getSchema(true);
$indexList = SchemaUtils::getIndexList($ormMeta);
$fieldListExceededIndexMaxLength = SchemaUtils::getFieldListExceededIndexMaxLength($ormMeta, $this->getMaxIndexLength(), $indexList);
$fieldListExceededIndexMaxLength = SchemaUtils::getFieldListExceededIndexMaxLength($ormMeta, $this->getMaxIndexLength());
$tables = array();
foreach ($ormMeta as $entityName => $entityParams) {
@@ -110,7 +110,7 @@ class Utils
$fields = array();
if (!isset($indexList)) {
$indexList = static::getIndexList($ormMeta);
$indexList = static::getIndexList($ormMeta, ['fulltext']);
}
foreach ($indexList as $entityName => $indexes) {
+16
View File
@@ -358,6 +358,14 @@ class Metadata
*/
public function saveCustom($key1, $key2, $data)
{
if (is_object($data)) {
foreach ($data as $key => $item) {
if ($item == new \stdClass()) {
unset($data->$key);
}
}
}
$filePath = array($this->paths['customPath'], $key1, $key2.'.json');
$changedData = Json::encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
@@ -380,6 +388,14 @@ class Metadata
*/
public function set($key1, $key2, $data)
{
if (is_array($data)) {
foreach ($data as $key => $item) {
if (is_array($item) && empty($item)) {
unset($data[$key]);
}
}
}
$newData = array(
$key1 => array(
$key2 => $data,
@@ -82,8 +82,7 @@
"view": "views/fields/teams"
},
"attachments": {
"type": "attachmentMultiple",
"layoutListDisabled": true
"type": "attachmentMultiple"
}
},
"links": {
@@ -66,7 +66,8 @@
"distinct": true
}
},
"trim": true
"trim": true,
"textFilterDisabled": true
},
"description": {
"type": "text"
@@ -130,7 +131,8 @@
"layoutFiltersDisabled": true,
"exportDisabled": true,
"importDisabled": true,
"view": "crm:views/contact/fields/account-role"
"view": "crm:views/contact/fields/account-role",
"textFilterDisabled": true
},
"accountIsInactive": {
"type": "bool",
@@ -34,7 +34,8 @@
},
"order": {
"type": "int",
"disableFormatting": true
"disableFormatting": true,
"textFilterDisabled": true
},
"description": {
"type": "text"
@@ -27,7 +27,8 @@
"order": {
"type": "int",
"required": true,
"disableFormatting": true
"disableFormatting": true,
"textFilterDisabled": true
},
"teams": {
"type": "linkMultiple",
@@ -99,8 +99,7 @@
},
"attachments": {
"type": "attachmentMultiple",
"sourceList": ["Document"],
"layoutListDisabled": true
"sourceList": ["Document"]
}
},
"links": {
@@ -31,28 +31,33 @@
"type": "varchar",
"notStorable": true,
"required": true,
"view": "views/email/fields/from-address-varchar"
"view": "views/email/fields/from-address-varchar",
"textFilterDisabled": true
},
"to": {
"type": "varchar",
"notStorable": true,
"required": true,
"view": "views/email/fields/email-address-varchar"
"view": "views/email/fields/email-address-varchar",
"textFilterDisabled": true
},
"cc": {
"type": "varchar",
"notStorable": true,
"view": "views/email/fields/email-address-varchar"
"view": "views/email/fields/email-address-varchar",
"textFilterDisabled": true
},
"bcc": {
"type": "varchar",
"notStorable": true,
"view": "views/email/fields/email-address-varchar"
"view": "views/email/fields/email-address-varchar",
"textFilterDisabled": true
},
"replyTo": {
"type": "varchar",
"notStorable": true,
"view": "views/email/fields/email-address-varchar"
"view": "views/email/fields/email-address-varchar",
"textFilterDisabled": true
},
"personStringData": {
"type": "varchar",
@@ -98,7 +103,8 @@
"folderId": {
"type": "varchar",
"notStorable": true,
"default": false
"default": false,
"textFilterDisabled": true
},
"isUsers": {
"type": "bool",
@@ -137,7 +143,8 @@
"messageIdInternal": {
"type": "varchar",
"maxLength": 300,
"readOnly": true
"readOnly": true,
"textFilterDisabled": true
},
"emailAddress": {
"type": "base",
@@ -146,7 +153,8 @@
},
"fromEmailAddress": {
"type": "link",
"view": "views/email/fields/from-email-address"
"view": "views/email/fields/from-email-address",
"textFilterDisabled": true
},
"toEmailAddresses": {
"type": "linkMultiple"
@@ -85,6 +85,7 @@
},
"collection": {
"sortBy": "createdAt",
"asc": false
"asc": false,
"textFilterFields": ["name", "bodyPlain", "body", "subject"]
}
}
@@ -8,7 +8,8 @@
"order": {
"type": "int",
"minValue": 1,
"required": true
"required": true,
"textFilterDisabled": true
},
"description": {
"type": "text"
File diff suppressed because one or more lines are too long
@@ -96,7 +96,8 @@
},
"trim": true,
"layoutDetailDisabled": true,
"layoutListDisabled": true
"layoutListDisabled": true,
"textFilterDisabled": true
},
"emailAddress": {
"type": "email",
@@ -31,6 +31,7 @@
],
"filter": true,
"notCreatable": false,
"notSortable": true,
"fieldDefs":{
"type":"jsonArray"
},
@@ -30,10 +30,8 @@
"layoutRelationshipsDisabled": true,
"relationName": "attachments"
},
"notSortable": true,
"filter": true,
"fieldDefs": {
"layoutListDisabled": true
},
"hookClassName": "\\Espo\\Core\\Utils\\FieldManager\\Hooks\\AttachmentMultipleType",
"personalData": true
}
@@ -13,7 +13,7 @@
"required": true
}
],
"fieldTypeList": ["varchar", "enum", "enumInt", "enumFloat", "int", "float", "website", "date", "datetime", "text"],
"fieldTypeList": ["varchar", "enum", "enumInt", "enumFloat", "int", "float", "website", "date", "datetime", "text", "number"],
"filter": true,
"notCreatable": false,
"fieldDefs": {
@@ -1,5 +1,6 @@
{
"notCreatable": true,
"notMergeable": true,
"notSortable": true,
"filter": false
}
@@ -23,8 +23,6 @@
"names"
],
"notCreatable": true,
"filter": true,
"fieldDefs": {
"layoutListDisabled": true
}
"notSortable": true,
"filter": true
}
@@ -14,6 +14,7 @@
],
"filter": false,
"notCreatable": true,
"notSortable": true,
"fieldDefs": {
"notStorable": true
}
@@ -26,6 +26,7 @@
],
"filter": true,
"notCreatable": false,
"notSortable": true,
"fieldDefs":{
"type":"jsonArray"
},
@@ -6,6 +6,7 @@
"default":false
}
],
"notSortable": true,
"notCreatable": true,
"filter": false
}
+1 -1
View File
@@ -270,7 +270,7 @@ class Email extends \Espo\Core\SelectManagers\Base
);
}
protected function applyAdditionalToTextFilterGroup($textFilter, &$group)
protected function applyAdditionalToTextFilterGroup($textFilter, &$group, &$result)
{
if (strlen($textFilter) >= self::MIN_LENGTH_FOR_CONTENT_SEARCH) {
$emailAddressId = $this->getEmailAddressIdByValue($textFilter);
@@ -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
}
}
+3 -1
View File
@@ -29,6 +29,8 @@
namespace Espo\Services;
use Espo\Core\Utils\Database\Schema\Utils as SchemaUtils;
class MysqlCharacter extends \Espo\Core\Services\Base
{
protected function init()
@@ -63,7 +65,7 @@ class MysqlCharacter extends \Espo\Core\Services\Base
$sth->execute();
}
$fieldListExceededIndexMaxLength = \Espo\Core\Utils\Database\Schema\Utils::getFieldListExceededIndexMaxLength($ormMeta, $maxIndexLength);
$fieldListExceededIndexMaxLength = SchemaUtils::getFieldListExceededIndexMaxLength($ormMeta, $maxIndexLength);
foreach ($ormMeta as $entityName => $entityParams) {
+22 -18
View File
@@ -1352,9 +1352,17 @@ class Record extends \Espo\Core\Services\Base
$selectParams = $this->getSelectParams($p);
$collection = $repository->find($selectParams);
$this->getEntityManager()->getRepository($this->getEntityType())->handleSelectParams($selectParams);
$sql = $this->getEntityManager()->getQuery()->createSelectQuery($this->getEntityType(), $selectParams);
$sth = $this->getEntityManager()->getPdo()->prepare($sql);
$sth->execute();
while ($dataRow = $sth->fetch(\PDO::FETCH_ASSOC)) {
$entity = $this->getEntityManager()->getEntityFactory()->create($this->getEntityType());
$entity->set($dataRow);
$entity->setAsFetched();
foreach ($collection as $entity) {
if ($this->getAcl()->check($entity, 'edit') && $this->checkEntityForMassUpdate($entity, $data)) {
$entity->set($data);
if ($this->checkAssignment($entity)) {
@@ -1427,10 +1435,19 @@ class Record extends \Espo\Core\Services\Base
}
$selectParams = $this->getSelectParams($p);
$skipTextColumns['skipTextColumns'] = true;
$collection = $repository->find($selectParams);
$selectParams['skipTextColumns'] = true;
$this->getEntityManager()->getRepository($this->getEntityType())->handleSelectParams($selectParams);
$sql = $this->getEntityManager()->getQuery()->createSelectQuery($this->getEntityType(), $selectParams);
$sth = $this->getEntityManager()->getPdo()->prepare($sql);
$sth->execute();
while ($dataRow = $sth->fetch(\PDO::FETCH_ASSOC)) {
$entity = $this->getEntityManager()->getEntityFactory()->create($this->getEntityType());
$entity->set($dataRow);
$entity->setAsFetched();
foreach ($collection as $entity) {
if ($this->getAcl()->check($entity, 'delete') && $this->checkEntityForMassRemove($entity)) {
if ($repository->remove($entity)) {
$idsRemoved[] = $entity->id;
@@ -1761,19 +1778,6 @@ class Record extends \Espo\Core\Services\Base
}
}
/*foreach ($collection as $entity) {
$this->loadAdditionalFieldsForExport($entity);
if (method_exists($exportObj, 'loadAdditionalFields')) {
$exportObj->loadAdditionalFields($entity, $fieldList);
}
$row = array();
foreach ($attributeList as $attribute) {
$value = $this->getAttributeFromEntityForExport($entity, $attribute);
$row[$attribute] = $value;
}
$arr[] = $row;
}*/
if (is_null($attributeList)) {
$attributeList = [];
}
@@ -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'));
@@ -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 = '';
@@ -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) {
@@ -81,6 +81,7 @@ Espo.define('crm:views/lead/convert', 'view', function (Dep) {
if (scope == 'Account' && this.getConfig().get('b2cMode')) {
return;
}
if (this.getMetadata().get(['scopes', scope, 'disabled'])) return;
if (this.getAcl().check(scope, 'edit')) {
scopeList.push(scope);
}
+1 -1
View File
@@ -85,7 +85,7 @@
<header>{{translate 'Disabled' scope='Admin'}}</header>
<ul class="disabled connected">
{{#each disabledFields}}
<li draggable="true" data-name="{{name}}">
<li draggable="true" {{#each ../dataAttributeList}}data-{{toDom this}}="{{prop ../this this}}" {{/each}}>
<div class="left">
<label>{{label}}</label>
</div>
@@ -1,6 +1,9 @@
{{#if value}}
{{{value}}}
{{else}}
{{{translate 'None'}}}
{{/if}}
{{#if valueIsSet}}
{{{translate 'None'}}}
{{else}}
...
{{/if}}
{{/if}}
@@ -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}}
+2 -1
View File
@@ -30,7 +30,8 @@ Espo.define('controllers/base', 'controller', function (Dep) {
return Dep.extend({
login: function () {
this.entire('views/login', {}, function (login) {
var viewName = this.getMetadata().get(['clientDefs', 'App', 'loginView']) || 'views/login';
this.entire(viewName, {}, function (login) {
login.render();
login.on('login', function (data) {
this.trigger('login', data);
+1 -1
View File
@@ -176,7 +176,7 @@ Espo.define('dynamic-logic', [], function () {
} else if (type === 'contains' || type === 'has') {
if (!setValue) return false;
return !!~setValue.indexOf(value);
} else if (type === 'notContains' || type === 'hasNot') {
} else if (type === 'notContains' || type === 'notHas') {
if (!setValue) return true;
return !~setValue.indexOf(value);
} else if (type === 'greaterThan') {
@@ -285,6 +285,9 @@ Espo.define('views/admin/entity-manager/modals/edit-entity', ['views/modal', 'mo
if (this.getMetadata().get(['entityDefs', scope, 'fields', item, 'disabled'])) {
return false;
}
if (this.getMetadata().get(['entityDefs', scope, 'fields', item, 'textFilterDisabled'])) {
return false;
}
return true;
}, this);
@@ -118,6 +118,8 @@ Espo.define('views/admin/field-manager/fields/foreign/field', 'views/fields/enum
this.viewValue = 'views/fields/foreign-datetime';
} else if (type == 'text') {
this.viewValue = 'views/fields/foreign-text';
} else if (type == 'number') {
this.viewValue = 'views/fields/foreign-varchar';
} else {
this.viewValue = null;
}
+11 -3
View File
@@ -127,10 +127,18 @@ Espo.define('views/admin/layouts/list', 'views/admin/layouts/rows', function (De
duplicateLabelList.push(label);
}
labelList.push(label);
this.disabledFields.push({
name: allFields[i],
var fieldName = allFields[i];
var o = {
name: fieldName,
label: label
});
};
var fieldType = this.getMetadata().get(['entityDefs', this.scope, 'fields', fieldName, 'type']);
if (fieldType) {
if (this.getMetadata().get(['fields', fieldType, 'notSortable'])) {
o.notSortable = true;
}
}
this.disabledFields.push(o);
}
}
@@ -137,7 +137,8 @@ Espo.define('views/fields/attachment-multiple', 'views/fields/base', function (D
idValues: this.model.get(this.idsName),
idValuesString: ids ? ids.join(',') : '',
nameHash: this.model.get(this.nameHashName),
foreignScope: this.foreignScope
foreignScope: this.foreignScope,
valueIsSet: this.model.has(this.idsName)
}, Dep.prototype.data.call(this));
if (this.mode == 'edit') {
@@ -181,10 +182,6 @@ Espo.define('views/fields/attachment-multiple', 'views/fields/base', function (D
this.listenTo(this.model, 'change:' + this.nameHashName, function () {
this.nameHash = _.clone(this.model.get(this.nameHashName)) || {};
}.bind(this));
if (!this.model.get(this.idsName)) {
this.clearIds(true);
}
},
setupSearch: function () {
@@ -231,7 +228,7 @@ Espo.define('views/fields/attachment-multiple', 'views/fields/base', function (D
},
removeId: function (id) {
var arr = _.clone(this.model.get(this.idsName));
var arr = _.clone(this.model.get(this.idsName) || []);
var i = arr.indexOf(id);
arr.splice(i, 1);
this.model.set(this.idsName, arr);
@@ -583,7 +580,7 @@ Espo.define('views/fields/attachment-multiple', 'views/fields/base', function (D
validateRequired: function () {
if (this.isRequired()) {
if (this.model.get(this.idsName).length == 0) {
if ((this.model.get(this.idsName) || []).length == 0) {
var msg = this.translate('fieldIsRequired', 'messages').replace('{field}', this.getLabelText());
this.showValidationMessage(msg, 'label');
return true;
@@ -601,7 +598,7 @@ Espo.define('views/fields/attachment-multiple', 'views/fields/base', function (D
fetch: function () {
var data = {};
data[this.idsName] = this.model.get(this.idsName);
data[this.idsName] = this.model.get(this.idsName) || [];
return data;
},
+11 -7
View File
@@ -423,19 +423,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 +451,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 +461,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({
@@ -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);
},
+10 -1
View File
@@ -134,6 +134,9 @@ Espo.define('views/modals/select-records', ['views/modal', 'search-manager'], fu
this.header = iconHtml + this.header;
this.waitForView('list');
if (this.searchPanel) {
this.waitForView('search');
}
this.getCollectionFactory().create(this.scope, function (collection) {
collection.maxSize = this.getConfig().get('recordsPerPageSmall') || 5;
@@ -143,8 +146,8 @@ Espo.define('views/modals/select-records', ['views/modal', 'search-manager'], fu
this.defaultAsc = collection.asc;
this.loadSearch();
this.wait(true);
this.loadList();
collection.fetch();
}, this);
},
@@ -217,12 +220,18 @@ Espo.define('views/modals/select-records', ['views/modal', 'search-manager'], fu
this.disableButton('select');
}
}, this);
this.listenTo(view, 'select-all-results', function () {
this.enableButton('select');
}, this);
}
view.getSelectAttributeList(function (selectAttributeList) {
if (selectAttributeList) {
this.collection.data.select = selectAttributeList.join(',');
}
this.listenToOnce(view, 'after:build-rows', function () {
this.wait(false);
}, this);
this.collection.fetch();
}.bind(this));
});
+83 -73
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) {
@@ -321,6 +321,8 @@ Espo.define('views/record/list', 'view', function (Dep) {
}
this.$el.find('.list > table tbody tr').removeClass('active');
this.trigger('select-all-results');
},
unselectAllResult: function () {
@@ -331,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);
},
@@ -467,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) {
@@ -483,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 () {
@@ -679,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);
@@ -692,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) {
@@ -756,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;
@@ -788,7 +790,7 @@ Espo.define('views/record/list', 'view', function (Dep) {
||
this.getAcl().get('exportPermission') === 'no'
) {
this.removeMassAction('export');
this.removeMassAction('export');
}
if (
@@ -968,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,
@@ -1055,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;
@@ -1069,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 {
@@ -1099,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 () {
@@ -1234,6 +1242,7 @@ Espo.define('views/record/list', 'view', function (Dep) {
if (built == count) {
func();
this.wait(false);
this.trigger('after:build-rows');
}
}.bind(this));
}, this);
@@ -1247,6 +1256,7 @@ Espo.define('views/record/list', 'view', function (Dep) {
} else {
if (typeof callback == 'function') {
callback();
this.trigger('after:build-rows');
}
}
},
+3 -3
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);
+2 -1
View File
@@ -1078,7 +1078,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;
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "espocrm",
"version": "5.3.0",
"version": "5.3.3",
"description": "",
"main": "index.php",
"repository": {
@@ -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);
}
}
@@ -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']));
}
}
@@ -0,0 +1,11 @@
{
"collection": {
"fullTextSearch": true,
"sortBy": "createdAt",
"asc": false,
"textFilterFields": [
"name",
"emailAddress"
]
}
}
@@ -0,0 +1,11 @@
{
"collection": {
"sortBy": "createdAt",
"asc": false,
"textFilterFields": [
"name",
"emailAddress"
],
"fullTextSearch": true
}
}
@@ -0,0 +1,12 @@
{
"collection": {
"sortBy": "dateSent",
"asc": false,
"textFilterFields": [
"name",
"bodyPlain",
"body"
],
"fullTextSearch": true
}
}