isFromTeams filter

This commit is contained in:
yuri
2016-01-29 14:54:46 +02:00
parent eafb8bd2cb
commit 9f7043a85d
25 changed files with 414 additions and 102 deletions
+76 -43
View File
@@ -52,6 +52,8 @@ class Base
private $userTimeZone = null;
protected $additionalFilterTypeList = ['linkedWith', 'inCategory', 'isUserFromTeams'];
const MIN_LENGTH_FOR_CONTENT_SEARCH = 4;
public function __construct($entityManager, \Espo\Entities\User $user, Acl $acl, $metadata)
@@ -158,77 +160,108 @@ class Base
}
}
$linkedWith = array();
$inCategory = array();
$ignoreList = ['linkedWith', 'inCategory', 'bool', 'primary'];
$ignoreTypeList = array_merge(['bool', 'primary'], $this->additionalFilterTypeList);
$additionalFilters = array();
foreach ($where as $item) {
if (!in_array($item['type'], $ignoreList)) {
$type = $item['type'];
if (!in_array($type, $ignoreTypeList)) {
$part = $this->getWherePart($item);
if (!empty($part)) {
$whereClause[] = $part;
}
} else {
if ($item['type'] == 'linkedWith' && !empty($item['value'])) {
$linkedWith[$item['field']] = $item['value'];
} else if ($item['type'] == 'inCategory' && !empty($item['value'])) {
$inCategory[$item['field']] = $item['value'];
if (in_array($type, $this->additionalFilterTypeList)) {
if (!empty($item['value'])) {
$methodName = 'apply' . ucfirst($type);
if (method_exists($this, $methodName)) {;
$this->$methodName($item['field'], $item['value'], $result);
}
}
}
}
}
$result['whereClause'] = array_merge($result['whereClause'], $whereClause);
if (!empty($linkedWith)) {
$this->handleLinkedWith($linkedWith, $result);
}
if (!empty($inCategory)) {
$this->handleInCategory($inCategory, $result);
}
}
protected function handleLinkedWith($linkedWith, &$result)
protected function applyLinkedWith($link, $idsValue, &$result)
{
$joins = [];
$part = array();
foreach ($linkedWith as $link => $idsValue) {
if (is_array($idsValue) && count($idsValue) == 1) {
$idsValue = $idsValue[0];
}
$relDefs = $this->getSeed()->getRelations();
if (is_array($idsValue) && count($idsValue) == 1) {
$idsValue = $idsValue[0];
}
if (!empty($relDefs[$link])) {
$defs = $relDefs[$link];
if ($defs['type'] == 'manyMany') {
$joins[] = $link;
if (!empty($defs['midKeys'])) {
$key = $defs['midKeys'][1];
$part[$link . 'Middle.' . $key] = $idsValue;
}
} else if ($defs['type'] == 'belongsTo') {
if (!empty($defs['key'])) {
$key = $defs['key'];
$part[$key] = $idsValue;
}
}
$seed = $this->getSeed();
if (!$seed->hasRelation($link)) return;
$relDefs = $this->getSeed()->getRelations();
$relationType = $seed->getRelationType($link);
$defs = $relDefs[$link];
if ($relationType == 'manyMany') {
$this->addJoin($link, $result);
$midKeys = $seed->getRelationParam($link, 'midKeys');
if (!empty($midKeys)) {
$key = $midKeys[1];
$part[$link . 'Middle.' . $key] = $idsValue;
}
} else if ($relationType== 'belongsTo') {
$key = $seed->getRelationParam($link, 'key');
if (!empty($key)) {
$part[$key] = $idsValue;
}
} else {
return;
}
if (!empty($part)) {
$result['whereClause'][] = $part;
}
$result['joins'] = array_merge($result['joins'], $joins);
$result['joins'] = array_unique($result['joins']);
$result['distinct'] = true;
$this->setDistinct(true, $result);
}
protected function handleInCategory($inCategory, &$result)
protected function applyIsUserFromTeams($link, $idsValue, &$result)
{
foreach ($inCategory as $link => $value) {
$this->applyInCategory($link, $value, $result);
if (is_array($idsValue) && count($idsValue) == 1) {
$idsValue = $idsValue[0];
}
$query = $this->getEntityManager()->getQuery();
$seed = $this->getSeed();
$relDefs = $seed->getRelations();
if (!$seed->hasRelation($link)) return;
$relationType = $seed->getRelationType($link);
if ($relationType == 'belongsTo') {
$key = $seed->getRelationParam($link, 'key');
$aliasName = 'usersTeams' . ucfirst($link);
$result['customJoin'] .= "
JOIN team_user AS {$aliasName}Middle ON {$aliasName}Middle.user_id = ".$query->toDb($seed->getEntityType()).".".$query->toDb($key)." AND {$aliasName}Middle.deleted = 0
JOIN team AS {$aliasName} ON {$aliasName}.deleted = 0 AND {$aliasName}Middle.team_id = {$aliasName}.id
";
$result['whereClause'][] = array(
$aliasName . 'Middle.teamId' => $idsValue
);
} else {
return;
}
$this->setDistinct(true, $result);
}
public function applyInCategory($link, $value, &$result)
@@ -17,15 +17,18 @@
},
"createdBy": {
"type": "link",
"readOnly": true
"readOnly": true,
"view": "views/fields/user"
},
"modifiedBy": {
"type": "link",
"readOnly": true
"readOnly": true,
"view": "views/fields/user"
},
"assignedUser": {
"type": "link",
"required": true
"required": true,
"view": "views/fields/user"
},
"teams": {
"type": "linkMultiple"
@@ -22,11 +22,13 @@
},
"createdBy": {
"type": "link",
"readOnly": true
"readOnly": true,
"view": "views/fields/user"
},
"modifiedBy": {
"type": "link",
"readOnly": true
"readOnly": true,
"view": "views/fields/user"
},
"teams": {
"type": "linkMultiple"
@@ -59,15 +59,18 @@
},
"createdBy": {
"type": "link",
"readOnly": true
"readOnly": true,
"view": "views/fields/user"
},
"modifiedBy": {
"type": "link",
"readOnly": true
"readOnly": true,
"view": "views/fields/user"
},
"assignedUser": {
"type": "link",
"required": true
"required": false,
"view": "views/fields/user"
},
"teams": {
"type": "linkMultiple"
@@ -126,14 +126,17 @@
},
"createdBy": {
"type": "link",
"readOnly": true
"readOnly": true,
"view": "views/fields/user"
},
"modifiedBy": {
"type": "link",
"readOnly": true
"readOnly": true,
"view": "views/fields/user"
},
"assignedUser": {
"type": "link"
"type": "link",
"view": "views/fields/assigned-user"
},
"teams": {
"type": "linkMultiple"
@@ -97,15 +97,18 @@
},
"createdBy": {
"type": "link",
"readOnly": true
"readOnly": true,
"view": "views/fields/user"
},
"modifiedBy": {
"type": "link",
"readOnly": true
"readOnly": true,
"view": "views/fields/user"
},
"assignedUser": {
"type": "link",
"required": true
"required": true,
"view": "views/fields/user"
},
"teams": {
"type": "linkMultiple"
@@ -34,14 +34,17 @@
},
"createdBy": {
"type": "link",
"readOnly": true
"readOnly": true,
"view": "views/fields/user"
},
"modifiedBy": {
"type": "link",
"readOnly": true
"readOnly": true,
"view": "views/fields/user"
},
"assignedUser": {
"type": "link"
"type": "link",
"view": "views/fields/user"
},
"teams": {
"type": "linkMultiple"
@@ -60,14 +60,17 @@
},
"createdBy": {
"type": "link",
"readOnly": true
"readOnly": true,
"view": "views/fields/user"
},
"modifiedBy": {
"type": "link",
"readOnly": true
"readOnly": true,
"view": "views/fields/user"
},
"assignedUser": {
"type": "link"
"type": "link",
"view": "views/fields/user"
},
"teams": {
"type": "linkMultiple"
@@ -116,14 +116,17 @@
},
"createdBy": {
"type": "link",
"readOnly": true
"readOnly": true,
"view": "views/fields/user"
},
"modifiedBy": {
"type": "link",
"readOnly": true
"readOnly": true,
"view": "views/fields/user"
},
"assignedUser": {
"type": "link"
"type": "link",
"view": "views/fields/assigned-user"
},
"teams": {
"type": "linkMultiple"
@@ -50,14 +50,17 @@
},
"createdBy": {
"type": "link",
"readOnly": true
"readOnly": true,
"view": "views/fields/user"
},
"modifiedBy": {
"type": "link",
"readOnly": true
"readOnly": true,
"view": "views/fields/user"
},
"assignedUser": {
"type": "link"
"type": "link",
"view": "views/fields/user"
},
"teams": {
"type": "linkMultiple"
@@ -94,17 +94,20 @@
},
"createdBy": {
"type": "link",
"readOnly": true
"readOnly": true,
"view": "views/fields/user"
},
"modifiedBy": {
"type": "link",
"readOnly": true
"readOnly": true,
"view": "views/fields/user"
},
"accountName": {
"type": "varchar"
},
"assignedUser": {
"type": "link"
"type": "link",
"view": "views/fields/user"
},
"acceptanceStatus": {
"type": "varchar",
@@ -92,15 +92,18 @@
},
"createdBy": {
"type": "link",
"readOnly": true
"readOnly": true,
"view": "views/fields/user"
},
"modifiedBy": {
"type": "link",
"readOnly": true
"readOnly": true,
"view": "views/fields/user"
},
"assignedUser": {
"type": "link",
"required": true
"required": true,
"view": "views/fields/user"
},
"teams": {
"type": "linkMultiple"
@@ -80,15 +80,18 @@
},
"createdBy": {
"type": "link",
"readOnly": true
"readOnly": true,
"view": "views/fields/user"
},
"modifiedBy": {
"type": "link",
"readOnly": true
"readOnly": true,
"view": "views/fields/user"
},
"assignedUser": {
"type": "link",
"required": false
"required": false,
"view": "views/fields/user"
},
"teams": {
"type": "linkMultiple"
@@ -73,15 +73,17 @@
},
"createdBy": {
"type": "link",
"readOnly": true
"readOnly": true,
"view": "views/fields/user"
},
"modifiedBy": {
"type": "link",
"readOnly": true
"readOnly": true,
"view": "views/fields/user"
},
"assignedUser": {
"type": "link",
"required": true
"view": "views/fields/user"
},
"teams": {
"type": "linkMultiple"
@@ -22,14 +22,17 @@
},
"createdBy": {
"type": "link",
"readOnly": true
"readOnly": true,
"view": "views/fields/user"
},
"modifiedBy": {
"type": "link",
"readOnly": true
"readOnly": true,
"view": "views/fields/user"
},
"assignedUser": {
"type": "link"
"type": "link",
"view": "views/fields/user"
},
"teams": {
"type": "linkMultiple"
@@ -71,15 +71,18 @@
},
"createdBy": {
"type": "link",
"readOnly": true
"readOnly": true,
"view": "views/fields/user"
},
"modifiedBy": {
"type": "link",
"readOnly": true
"readOnly": true,
"view": "views/fields/user"
},
"assignedUser": {
"type": "link",
"required": true
"required": true,
"view": "views/fields/user"
},
"teams": {
"type": "linkMultiple"
@@ -447,7 +447,8 @@
"is": "Is",
"isEmpty": "Is Empty",
"isNotEmpty": "Is Not Empty",
"isOneOf": "Is One Of"
"isOneOf": "Is One Of",
"isFromTeams": "Is From Team"
},
"varcharSearchRanges": {
"equals": "Equals",
@@ -171,15 +171,18 @@
},
"createdBy": {
"type": "link",
"readOnly": true
"readOnly": true,
"view": "views/fields/user"
},
"modifiedBy": {
"type": "link",
"readOnly": true
"readOnly": true,
"view": "views/fields/user"
},
"assignedUser": {
"type": "link",
"required": false
"required": false,
"view": "views/fields/user"
},
"replied": {
"type": "link",
@@ -37,11 +37,13 @@
},
"modifiedAt": {
"type": "datetime",
"readOnly": true
"readOnly": true,
"view": "views/fields/user"
},
"createdBy": {
"type": "link",
"readOnly": true
"readOnly": true,
"view": "views/fields/user"
},
"modifiedBy": {
"type": "link",
@@ -91,7 +91,8 @@
},
"modifiedBy": {
"type": "link",
"readOnly": true
"readOnly": true,
"view": "views/fields/user"
},
"createdAt": {
"type": "datetime",
@@ -99,7 +100,8 @@
},
"createdBy": {
"type": "link",
"readOnly": true
"readOnly": true,
"view": "views/fields/user"
}
},
"links": {
@@ -13,7 +13,7 @@
</div>
<div class="one-of-container hidden">
<div class="link-container list-group">
<div class="link-one-of-container link-container list-group">
</div>
<div class="input-group add-team">
@@ -0,0 +1,36 @@
<select class="form-control search-type input-sm" name="{{name}}-type">
{{options searchParams.typeOptions searchParams.typeFront field='searchRanges'}}
</select>
<div class="primary">
<div class="input-group">
<input class="form-control input-sm" type="text" name="{{nameName}}" value="{{searchParams.valueName}}" autocomplete="off" placeholder="{{translate 'Select'}}">
<span class="input-group-btn">
<button type="button" class="btn btn-sm btn-default" data-action="selectLink" tabindex="-1" title="{{translate 'Select'}}"><i class="glyphicon glyphicon-arrow-up"></i></button>
<button type="button" class="btn btn-sm btn-default" data-action="clearLink" tabindex="-1"><i class="glyphicon glyphicon-remove"></i></button>
</span>
</div>
<input type="hidden" name="{{idName}}" value="{{searchParams.value}}">
</div>
<div class="one-of-container hidden">
<div class="link-one-of-container link-container list-group">
</div>
<div class="input-group add-team">
<input class="form-control input-sm element-one-of" type="text" name="" value="" autocomplete="off" placeholder="{{translate 'Select'}}">
<span class="input-group-btn">
<button data-action="selectLinkOneOf" class="btn btn-default btn-sm" type="button" tabindex="-1" title="{{translate 'Select'}}"><span class="glyphicon glyphicon-arrow-up"></span></button>
</span>
</div>
</div>
<div class="teams-container hidden">
<div class="link-teams-container link-container list-group">
</div>
<div class="input-group add-team">
<input class="form-control input-sm element-teams" type="text" name="" value="" autocomplete="off" placeholder="{{translate 'Select'}}">
<span class="input-group-btn">
<button data-action="selectLinkTeams" class="btn btn-default btn-sm" type="button" tabindex="-1" title="{{translate 'Select'}}"><span class="glyphicon glyphicon-arrow-up"></span></button>
</span>
</div>
</div>
+2 -2
View File
@@ -367,11 +367,11 @@ Espo.define('views/fields/link', 'views/fields/base', function (Dep) {
},
deleteLinkOneOfHtml: function (id) {
this.$el.find('.link-container .link-' + id).remove();
this.$el.find('.link-one-of-container .link-' + id).remove();
},
addLinkOneOfHtml: function (id, name) {
var $container = this.$el.find('.link-container');
var $container = this.$el.find('.link-one-of-container');
var $el = $('<div />').addClass('link-' + id).addClass('list-group-item');
$el.html(name + '&nbsp');
$el.prepend('<a href="javascript:" class="pull-right" data-id="' + id + '" data-action="clearLinkOneOf"><span class="glyphicon glyphicon-remove"></a>');
@@ -26,13 +26,13 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
Espo.define('Views.Fields.UserWithAvatar', 'Views.Fields.Link', function (Dep) {
Espo.define('views/fields/user-with-avatar', 'views/fields/user', function (Dep) {
return Dep.extend({
listTemplate: 'fields.user-with-avatar.detail',
listTemplate: 'fields/user-with-avatar/detail',
detailTemplate: 'fields.user-with-avatar.detail',
detailTemplate: 'fields/user-with-avatar/detail',
data: function () {
var o = _.extend({}, Dep.prototype.data.call(this));
@@ -54,7 +54,7 @@ Espo.define('Views.Fields.UserWithAvatar', 'Views.Fields.Link', function (Dep) {
t = Date.now();
}
return '<img class="avatar avatar-link" width="14" src="?entryPoint=avatar&size=small&id=' + this.model.get(this.idName) + '&t='+t+'">';
},
}
});
});
+192
View File
@@ -0,0 +1,192 @@
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2015 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/fields/user', 'views/fields/link', function (Dep) {
return Dep.extend({
searchTemplate: 'fields/user/search',
setupSearch: function () {
Dep.prototype.setupSearch.call(this);
this.searchParams.typeOptions.push('isFromTeams');
this.searchParams.teamIdList = this.searchParams.teamIdList || [];
this.searchParams.teamNameHash = this.searchParams.teamNameHash || {};
this.events['click a[data-action="clearLinkTeams"]'] = function (e) {
var id = $(e.currentTarget).data('id').toString();
this.deleteLinkTeams(id);
};
this.addActionHandler('selectLinkTeams', function () {
this.notify('Loading...');
var viewName = this.getMetadata().get('clientDefs.Team.modalViews.select') || 'views/modals/select-records';
this.createView('dialog', viewName, {
scope: 'Team',
createButton: false,
multiple: true
}, function (view) {
view.render();
this.notify(false);
this.listenToOnce(view, 'select', function (models) {
if (Object.prototype.toString.call(models) !== '[object Array]') {
models = [models];
}
models.forEach(function (model) {
this.addLinkTeams(model.id, model.get('name'));
}, this);
});
}, this);
});
this.events['click a[data-action="clearLinkTeams"]'] = function (e) {
var id = $(e.currentTarget).data('id').toString();
this.deleteLinkTeams(id);
};
},
handleSearchType: function (type) {
Dep.prototype.handleSearchType.call(this, type);
if (type === 'isFromTeams') {
this.$el.find('div.teams-container').removeClass('hidden');
} else {
this.$el.find('div.teams-container').addClass('hidden');
}
},
afterRender: function () {
Dep.prototype.afterRender.call(this);
if (this.mode == 'search') {
var $elemeneTeams = this.$el.find('input.element-teams');
$elemeneTeams.autocomplete({
serviceUrl: function (q) {
return 'Team?sortBy=name&maxCount=' + this.AUTOCOMPLETE_RESULT_MAX_COUNT;
}.bind(this),
minChars: 1,
paramName: 'q',
formatResult: function (suggestion) {
return suggestion.name;
},
transformResult: function (response) {
var response = JSON.parse(response);
var list = [];
response.list.forEach(function(item) {
list.push({
id: item.id,
name: item.name,
data: item.id,
value: item.name
});
}, this);
return {
suggestions: list
};
}.bind(this),
onSelect: function (s) {
this.addLinkTeams(s.id, s.name);
$elemeneTeams.val('');
}.bind(this)
});
this.once('render', function () {
$elemeneTeams.autocomplete('dispose');
}, this);
this.once('remove', function () {
$elemeneTeams.autocomplete('dispose');
}, this);
var type = this.$el.find('select.search-type').val();
if (type == 'isFromTeams') {
this.searchParams.teamIdList.forEach(function (id) {
this.addLinkTeamsHtml(id, this.searchParams.teamNameHash[id]);
}, this);
}
}
},
deleteLinkTeams: function (id) {
this.deleteLinkTeamsHtml(id);
var index = this.searchParams.teamIdList.indexOf(id);
if (index > -1) {
this.searchParams.teamIdList.splice(index, 1);
}
delete this.searchParams.teamNameHash[id];
},
addLinkTeams: function (id, name) {
if (!~this.searchParams.teamIdList.indexOf(id)) {
this.searchParams.teamIdList.push(id);
this.searchParams.teamNameHash[id] = name;
this.addLinkTeamsHtml(id, name);
}
},
deleteLinkTeamsHtml: function (id) {
this.$el.find('.link-teams-container .link-' + id).remove();
},
addLinkTeamsHtml: function (id, name) {
var $container = this.$el.find('.link-teams-container');
var $el = $('<div />').addClass('link-' + id).addClass('list-group-item');
$el.html(name + '&nbsp');
$el.prepend('<a href="javascript:" class="pull-right" data-id="' + id + '" data-action="clearLinkTeams"><span class="glyphicon glyphicon-remove"></a>');
$container.append($el);
return $el;
},
fetchSearch: function () {
var type = this.$el.find('select.search-type').val();
if (type == 'isFromTeams') {
var data = {
type: 'isUserFromTeams',
typeFront: type,
field: this.name,
value: this.searchParams.teamIdList,
teamIdList: this.searchParams.teamIdList,
teamNameHash: this.searchParams.teamNameHash
};
return data;
}
return Dep.prototype.fetchSearch.call(this);
}
});
});