mirror of
https://github.com/espocrm/espocrm.git
synced 2026-03-04 16:07:01 +00:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
06395f3ff5 | ||
|
|
4065ec5477 | ||
|
|
31d45a0583 | ||
|
|
696faa4468 | ||
|
|
625876f123 | ||
|
|
974cd05276 | ||
|
|
fb5db991a3 | ||
|
|
3dafb7e922 | ||
|
|
ee640273a1 | ||
|
|
b92de17f72 | ||
|
|
abc48ca76f | ||
|
|
46443aae7e | ||
|
|
a88c3283d6 | ||
|
|
2b9653ee0b | ||
|
|
2cf79abdb1 | ||
|
|
20a000a46c | ||
|
|
95bfd5bace | ||
|
|
da736008f1 | ||
|
|
1de98d9616 | ||
|
|
31a6143cc3 | ||
|
|
8502a84a3f | ||
|
|
2234678728 |
@@ -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 = [];
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
82
application/Espo/Core/Utils/Database/DBAL/Schema/Index.php
Normal file
82
application/Espo/Core/Utils/Database/DBAL/Schema/Index.php
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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}}
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
|
||||
|
||||
@@ -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,
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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,6 +1,6 @@
|
||||
{
|
||||
"name": "espocrm",
|
||||
"version": "5.3.2",
|
||||
"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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user