Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 97c951484d | |||
| 2215f15aaf | |||
| 6429823cc3 | |||
| 2a71bb2ea1 | |||
| 1674c2221e | |||
| e3b448f520 | |||
| a04c973817 | |||
| 01c9939215 | |||
| 2d57824cb0 | |||
| 878385f14b | |||
| 8b82058d06 | |||
| 68e05f0935 | |||
| cb7492c042 | |||
| beb811755f | |||
| 7c7fc4ee0f | |||
| aeef25f7ec | |||
| 6856ff97b1 | |||
| 10f6f7600c | |||
| a0b678a3d9 | |||
| f78e558a33 | |||
| e205ef3370 | |||
| 8b6e4d469e | |||
| a63ec2710b | |||
| 6895fac58c | |||
| ea3c8f2d35 | |||
| 0f2c050537 | |||
| 2cd8866c73 | |||
| 86a5e19b55 | |||
| 61f435812d | |||
| 217d0f6540 | |||
| f0e7a8f029 | |||
| d2954483d9 | |||
| 999b88cba1 | |||
| 717df30063 |
+1
-5
@@ -52,8 +52,7 @@ module.exports = function (grunt) {
|
||||
'frontend/client/lib/base64.js',
|
||||
'frontend/client/lib/jquery-ui.min.js',
|
||||
'frontend/client/lib/moment.min.js',
|
||||
'frontend/client/lib/moment-timezone.min.js',
|
||||
'frontend/client/lib/moment-timezone-data.js',
|
||||
'frontend/client/lib/moment-timezone-with-data.min.js',
|
||||
'frontend/client/lib/jquery.timepicker.min.js',
|
||||
'frontend/client/lib/jquery.autocomplete.js',
|
||||
'frontend/client/lib/bootstrap.min.js',
|
||||
@@ -88,9 +87,6 @@ module.exports = function (grunt) {
|
||||
'frontend/client/src/models/user.js',
|
||||
'frontend/client/src/models/preferences.js',
|
||||
'frontend/client/src/controllers/base.js',
|
||||
'frontend/client/src/controllers/record.js',
|
||||
'frontend/client/src/controllers/role.js',
|
||||
'frontend/client/src/controllers/admin.js',
|
||||
'frontend/client/src/view.js',
|
||||
'frontend/client/src/views/base.js',
|
||||
'frontend/client/src/views/login.js',
|
||||
|
||||
@@ -182,8 +182,10 @@ class Record extends Base
|
||||
}
|
||||
|
||||
public function actionExport($params, $data, $request)
|
||||
{
|
||||
// TODO move to service
|
||||
{
|
||||
if ($this->getConfig()->get('disableExport') && !$this->getUser()->isAdmin()) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
if (!$this->getAcl()->check($this->name, 'read')) {
|
||||
throw new Forbidden();
|
||||
|
||||
@@ -56,7 +56,12 @@ class EntityManager extends \Espo\ORM\EntityManager
|
||||
public function getUser()
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
}
|
||||
|
||||
public function getEspoMetadata()
|
||||
{
|
||||
return $this->espoMetadata;
|
||||
}
|
||||
|
||||
public function setEspoMetadata($espoMetadata)
|
||||
{
|
||||
|
||||
@@ -255,6 +255,15 @@ class Base
|
||||
case 'isFalse':
|
||||
$part[$item['field'] . '='] = false;
|
||||
break;
|
||||
case 'today':
|
||||
$part[$item['field'] . '='] = date('Y-m-d');
|
||||
break;
|
||||
case 'past':
|
||||
$part[$item['field'] . '<'] = date('Y-m-d');
|
||||
break;
|
||||
case 'future':
|
||||
$part[$item['field'] . '>'] = date('Y-m-d');
|
||||
break;
|
||||
case 'between':
|
||||
if (is_array($item['value'])) {
|
||||
$part['AND'] = array(
|
||||
|
||||
@@ -53,11 +53,25 @@ class Notifications extends \Espo\Core\Hooks\Base
|
||||
WHERE entity_id = " . $pdo->quote($parentId) . " AND entity_type = " . $pdo->quote($parentType);
|
||||
$sth = $pdo->prepare($sql);
|
||||
$sth->execute();
|
||||
$userIdList = array();
|
||||
while ($row = $sth->fetch(\PDO::FETCH_ASSOC)) {
|
||||
if ($this->getUser()->id != $row['userId']) {
|
||||
$this->getNotificationService()->notifyAboutNote($row['userId'], $entity->id);
|
||||
$userIdList[] = $row['userId'];
|
||||
}
|
||||
}
|
||||
if (!empty($userIdList)) {
|
||||
$job = $this->getEntityManager()->getEntity('Job');
|
||||
$job->set(array(
|
||||
'serviceName' => 'Notification',
|
||||
'method' => 'notifyAboutNoteFromJob',
|
||||
'data' => json_encode(array(
|
||||
'userIdList' => $userIdList,
|
||||
'noteId' => $entity->id
|
||||
)),
|
||||
'executeTime' => date('Y-m-d H:i:s'),
|
||||
));
|
||||
$this->getEntityManager()->saveEntity($job);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,5 +30,10 @@
|
||||
"Set Held": "Set Held",
|
||||
"Set Not Held": "Set Not Held",
|
||||
"Send Invitations": "Send Invitations"
|
||||
},
|
||||
"presetFilters": {
|
||||
"planned": "Planned",
|
||||
"held": "Held",
|
||||
"todays": "Today's"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,5 +34,9 @@
|
||||
},
|
||||
"labels": {
|
||||
"Create Case": "Create Case"
|
||||
},
|
||||
"presetFilters": {
|
||||
"open": "Open",
|
||||
"closed": "Closed"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,5 +42,8 @@
|
||||
"Campaign": "Campaign",
|
||||
"Other": "Other"
|
||||
}
|
||||
},
|
||||
"presetFilters": {
|
||||
"active": "Active"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,13 @@
|
||||
"Create Meeting": "Create Meeting",
|
||||
"Set Held": "Set Held",
|
||||
"Set Not Held": "Set Not Held",
|
||||
"Send Invitations": "Send Invitations"
|
||||
"Send Invitations": "Send Invitations",
|
||||
"Saved as Held": "Saved as Held",
|
||||
"Saved as Not Held": "Saved as Not Held"
|
||||
},
|
||||
"presetFilters": {
|
||||
"planned": "Planned",
|
||||
"held": "Held",
|
||||
"todays": "Today's"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,5 +30,9 @@
|
||||
},
|
||||
"labels": {
|
||||
"Create Opportunity": "Create Opportunity"
|
||||
},
|
||||
"presetFilters": {
|
||||
"open": "Open",
|
||||
"won": "Won"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,5 +27,11 @@
|
||||
},
|
||||
"labels": {
|
||||
"Create Task": "Create Task"
|
||||
},
|
||||
"presetFilters": {
|
||||
"active": "Active",
|
||||
"completed": "Completed",
|
||||
"todays": "Today's",
|
||||
"overdue": "Overdue"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
["status","priority","dateStart","dateEnd"]
|
||||
["status","priority","dateStart","dateEnd"]
|
||||
|
||||
@@ -1,38 +1,80 @@
|
||||
{
|
||||
"controller": "Controllers.Record",
|
||||
"views":{
|
||||
"detail":"Crm:Call.Detail"
|
||||
},
|
||||
"sidePanels":{
|
||||
"detail":[
|
||||
{
|
||||
"name":"attendees",
|
||||
"label":"Attendees",
|
||||
"view":"Record.Panels.Side",
|
||||
"options":{
|
||||
"fields":[
|
||||
"users",
|
||||
"contacts",
|
||||
"leads"
|
||||
],
|
||||
"mode":"detail"
|
||||
}
|
||||
}
|
||||
],
|
||||
"edit":[
|
||||
{
|
||||
"name":"attendees",
|
||||
"label":"Attendees",
|
||||
"view":"Record.Panels.Side",
|
||||
"options":{
|
||||
"fields":[
|
||||
"users",
|
||||
"contacts",
|
||||
"leads"
|
||||
],
|
||||
"mode":"edit"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
"controller": "Controllers.Record",
|
||||
"views":{
|
||||
"detail":"Crm:Call.Detail"
|
||||
},
|
||||
"menu": {
|
||||
"detail": {
|
||||
"dropdown": [
|
||||
{
|
||||
"label": "Duplicate",
|
||||
"action": "duplicate",
|
||||
"acl": "edit"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"sidePanels":{
|
||||
"detail":[
|
||||
{
|
||||
"name":"attendees",
|
||||
"label":"Attendees",
|
||||
"view":"Record.Panels.Side",
|
||||
"options":{
|
||||
"fields":[
|
||||
"users",
|
||||
"contacts",
|
||||
"leads"
|
||||
],
|
||||
"mode":"detail"
|
||||
}
|
||||
}
|
||||
],
|
||||
"edit":[
|
||||
{
|
||||
"name":"attendees",
|
||||
"label":"Attendees",
|
||||
"view":"Record.Panels.Side",
|
||||
"options":{
|
||||
"fields":[
|
||||
"users",
|
||||
"contacts",
|
||||
"leads"
|
||||
],
|
||||
"mode":"edit"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"presetFilters": [
|
||||
{
|
||||
"name":"planned",
|
||||
"style": "primary",
|
||||
"data": {
|
||||
"status": {
|
||||
"type": "in",
|
||||
"value": ["Planned"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name":"held",
|
||||
"style": "success",
|
||||
"data": {
|
||||
"status": {
|
||||
"type": "in",
|
||||
"value": ["Held"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name":"todays",
|
||||
"data": {
|
||||
"dateStart": {
|
||||
"type": "today",
|
||||
"dateTime": true
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -22,5 +22,27 @@
|
||||
"view":"Crm:Record.Panels.Tasks"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"presetFilters": [
|
||||
{
|
||||
"name":"open",
|
||||
"style": "primary",
|
||||
"data": {
|
||||
"status": {
|
||||
"type": "in",
|
||||
"value": ["New", "Assigned", "Pending"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name":"closed",
|
||||
"style": "success",
|
||||
"data": {
|
||||
"status": {
|
||||
"type": "in",
|
||||
"value": ["Closed"]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -24,5 +24,17 @@
|
||||
"view":"Crm:Record.Panels.Tasks"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"presetFilters": [
|
||||
{
|
||||
"name":"active",
|
||||
"style": "primary",
|
||||
"data": {
|
||||
"status": {
|
||||
"type": "in",
|
||||
"value": ["New", "Assigned", "In Process"]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,38 +1,80 @@
|
||||
{
|
||||
"controller": "Controllers.Record",
|
||||
"views":{
|
||||
"detail":"Crm:Meeting.Detail"
|
||||
},
|
||||
"sidePanels":{
|
||||
"detail":[
|
||||
{
|
||||
"name":"attendees",
|
||||
"label":"Attendees",
|
||||
"view":"Record.Panels.Side",
|
||||
"options":{
|
||||
"fields":[
|
||||
"users",
|
||||
"contacts",
|
||||
"leads"
|
||||
],
|
||||
"mode":"detail"
|
||||
}
|
||||
}
|
||||
],
|
||||
"edit":[
|
||||
{
|
||||
"name":"attendees",
|
||||
"label":"Attendees",
|
||||
"view":"Record.Panels.Side",
|
||||
"options":{
|
||||
"fields":[
|
||||
"users",
|
||||
"contacts",
|
||||
"leads"
|
||||
],
|
||||
"mode":"edit"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
"controller": "Controllers.Record",
|
||||
"views":{
|
||||
"detail":"Crm:Meeting.Detail"
|
||||
},
|
||||
"menu": {
|
||||
"detail": {
|
||||
"dropdown": [
|
||||
{
|
||||
"label": "Duplicate",
|
||||
"action": "duplicate",
|
||||
"acl": "edit"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"sidePanels":{
|
||||
"detail":[
|
||||
{
|
||||
"name":"attendees",
|
||||
"label":"Attendees",
|
||||
"view":"Record.Panels.Side",
|
||||
"options":{
|
||||
"fields":[
|
||||
"users",
|
||||
"contacts",
|
||||
"leads"
|
||||
],
|
||||
"mode":"detail"
|
||||
}
|
||||
}
|
||||
],
|
||||
"edit":[
|
||||
{
|
||||
"name":"attendees",
|
||||
"label":"Attendees",
|
||||
"view":"Record.Panels.Side",
|
||||
"options":{
|
||||
"fields":[
|
||||
"users",
|
||||
"contacts",
|
||||
"leads"
|
||||
],
|
||||
"mode":"edit"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"presetFilters": [
|
||||
{
|
||||
"name":"planned",
|
||||
"style": "primary",
|
||||
"data": {
|
||||
"status": {
|
||||
"type": "in",
|
||||
"value": ["Planned"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name":"held",
|
||||
"style": "success",
|
||||
"data": {
|
||||
"status": {
|
||||
"type": "in",
|
||||
"value": ["Held"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name":"todays",
|
||||
"data": {
|
||||
"dateStart": {
|
||||
"type": "today",
|
||||
"dateTime": true
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -21,5 +21,27 @@
|
||||
"view":"Crm:Record.Panels.Tasks"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"presetFilters": [
|
||||
{
|
||||
"name":"open",
|
||||
"style": "primary",
|
||||
"data": {
|
||||
"stage": {
|
||||
"type": "in",
|
||||
"value": ["Prospecting", "Qualification", "Needs Analysis", "Value Proposition", "Id. Decision Makers", "Perception Analysis","Proposal/Price Quote","Negotiation/Review"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name":"won",
|
||||
"style": "success",
|
||||
"data": {
|
||||
"stage": {
|
||||
"type": "in",
|
||||
"value": ["Closed Won"]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,3 +1,48 @@
|
||||
{
|
||||
"controller": "Controllers.Record"
|
||||
"controller": "Controllers.Record",
|
||||
"presetFilters": [
|
||||
{
|
||||
"name":"active",
|
||||
"style": "primary",
|
||||
"data": {
|
||||
"status": {
|
||||
"type": "in",
|
||||
"value": ["Not Started", "Started"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name":"completed",
|
||||
"style": "success",
|
||||
"data": {
|
||||
"status": {
|
||||
"type": "in",
|
||||
"value": ["Completed"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name":"todays",
|
||||
"data": {
|
||||
"dateEnd": {
|
||||
"type": "today",
|
||||
"dateTime": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name":"overdue",
|
||||
"style": "danger",
|
||||
"data": {
|
||||
"dateEnd": {
|
||||
"type": "past",
|
||||
"dateTime": true
|
||||
},
|
||||
"status": {
|
||||
"type": "in",
|
||||
"value": ["Not Started", "Started"]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -114,6 +114,6 @@
|
||||
"collection": {
|
||||
"sortBy": "number",
|
||||
"asc": false,
|
||||
"boolFilters": ["onlyMy", "open"]
|
||||
"boolFilters": ["onlyMy"]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,6 +126,6 @@
|
||||
"collection": {
|
||||
"sortBy": "createdAt",
|
||||
"asc": false,
|
||||
"boolFilters": ["onlyMy", "open"]
|
||||
"boolFilters": ["onlyMy"]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,6 +88,6 @@
|
||||
"collection": {
|
||||
"sortBy": "createdAt",
|
||||
"asc": false,
|
||||
"boolFilters": ["onlyMy", "active"]
|
||||
"boolFilters": ["onlyMy"]
|
||||
}
|
||||
}
|
||||
|
||||
+8
-7
@@ -22,15 +22,16 @@
|
||||
|
||||
namespace Espo\Modules\Crm\SelectManagers;
|
||||
|
||||
class Opportunity extends \Espo\Core\SelectManagers\Base
|
||||
class Call extends \Espo\Core\SelectManagers\Base
|
||||
{
|
||||
protected function getBoolFilterWhereOpen()
|
||||
{
|
||||
|
||||
protected function getBoolFilterWhereOnlyMy()
|
||||
{
|
||||
return array(
|
||||
'type' => 'notIn',
|
||||
'field' => 'stage',
|
||||
'value' => array('Closed Won', 'Closed Lost'),
|
||||
'type' => 'linkedWith',
|
||||
'field' => 'users',
|
||||
'value' => array($this->user->id)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+8
-8
@@ -22,16 +22,16 @@
|
||||
|
||||
namespace Espo\Modules\Crm\SelectManagers;
|
||||
|
||||
class CaseObj extends \Espo\Core\SelectManagers\Base
|
||||
class Meeting extends \Espo\Core\SelectManagers\Base
|
||||
{
|
||||
|
||||
protected function getBoolFilterWhereOpen()
|
||||
{
|
||||
|
||||
protected function getBoolFilterWhereOnlyMy()
|
||||
{
|
||||
return array(
|
||||
'type' => 'notIn',
|
||||
'field' => 'status',
|
||||
'value' => array('Closed', 'Rejected', 'Duplicate'),
|
||||
'type' => 'linkedWith',
|
||||
'field' => 'users',
|
||||
'value' => array($this->user->id)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014 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/.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Modules\Crm\SelectManagers;
|
||||
|
||||
class Task extends \Espo\Core\SelectManagers\Base
|
||||
{
|
||||
protected function getBoolFilterWhereActive()
|
||||
{
|
||||
return array(
|
||||
'type' => 'notIn',
|
||||
'field' => 'status',
|
||||
'value' => array('Completed', 'Canceled'),
|
||||
);
|
||||
}
|
||||
|
||||
protected function getBoolFilterWhereInactive()
|
||||
{
|
||||
return array(
|
||||
'type' => 'in',
|
||||
'field' => 'status',
|
||||
'value' => array('Completed', 'Canceled'),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -313,7 +313,6 @@ class InboundEmail extends \Espo\Services\Record
|
||||
$case = $this->getEntityManager()->getEntity('Case');
|
||||
$case->populateDefaults();
|
||||
$case->set('name', $email->get('name'));
|
||||
$case->set('description', $email->get('bodyPlain'));
|
||||
|
||||
$userId = $this->getUser()->id;
|
||||
if (!empty($params['userId'])) {
|
||||
|
||||
@@ -271,7 +271,11 @@ class PhoneNumber extends \Espo\Core\ORM\Repositories\RDB
|
||||
$isNewPhoneNumber = false;
|
||||
if (!$phoneNumberNew) {
|
||||
$phoneNumberNew = $this->get();
|
||||
$phoneNumberNew->set('name', $phone);
|
||||
$phoneNumberNew->set('name', $phone);
|
||||
$defaultType = $this->getEntityManager()->getEspoMetadata()->get('entityDefs.' . $entity->getEntityName() . '.fields.phoneNumber.defaultType');
|
||||
|
||||
$phoneNumberNew->set('type', $defaultType);
|
||||
|
||||
$this->save($phoneNumberNew);
|
||||
$isNewPhoneNumber = true;
|
||||
}
|
||||
|
||||
@@ -28,5 +28,9 @@
|
||||
"labels": {
|
||||
"Create Email": "Archive Email",
|
||||
"Compose": "Compose"
|
||||
},
|
||||
"presetFilters": {
|
||||
"sent": "Sent",
|
||||
"archived": "Archived"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,7 +109,12 @@
|
||||
"You": "You",
|
||||
"you": "you",
|
||||
"change": "change",
|
||||
"Primary": "Primary"
|
||||
"Primary": "Primary",
|
||||
"Save Filters": "Save Filters",
|
||||
"Administration": "Administration",
|
||||
"Run Import": "Run Import",
|
||||
"Duplicate": "Duplicate",
|
||||
"Notifications": "Notifications"
|
||||
},
|
||||
"messages": {
|
||||
"notModified": "You have not modified the record",
|
||||
@@ -271,7 +276,10 @@
|
||||
"notOn": "Not On",
|
||||
"after": "After",
|
||||
"before": "Before",
|
||||
"between": "Between"
|
||||
"between": "Between",
|
||||
"today": "Today",
|
||||
"past": "Past",
|
||||
"future": "Future"
|
||||
},
|
||||
"intSearchRanges": {
|
||||
"equals": "Equals",
|
||||
|
||||
@@ -46,7 +46,8 @@
|
||||
"ldapSecurity": "Security",
|
||||
"ldapUserLoginFilter": "User Login Filter",
|
||||
"ldapAccountDomainNameShort": "Account Domain Name Short",
|
||||
"ldapOptReferrals": "Opt Referrals"
|
||||
"ldapOptReferrals": "Opt Referrals",
|
||||
"disableExport": "Disable Export (only admin is allowed)"
|
||||
},
|
||||
"options": {
|
||||
"weekStart": {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
[
|
||||
"emailAddress",
|
||||
"dateSent",
|
||||
"status",
|
||||
"parent",
|
||||
"body"
|
||||
]
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
{
|
||||
"label": "System",
|
||||
"rows": [
|
||||
[{"name": "useCache"},{"name": "companyLogo"}]
|
||||
[{"name": "useCache"},{"name": "companyLogo"}],
|
||||
[{"name": "disableExport"}]
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,19 +1,39 @@
|
||||
{
|
||||
"controller": "Controllers.Record",
|
||||
"model": "Models.Email",
|
||||
"views":{
|
||||
"list":"Email.List"
|
||||
},
|
||||
"menu": {
|
||||
"list": {
|
||||
"buttons": [
|
||||
{
|
||||
"label": "Compose",
|
||||
"action": "composeEmail",
|
||||
"style": "danger",
|
||||
"acl": "edit"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
"controller": "Controllers.Record",
|
||||
"model": "Models.Email",
|
||||
"views":{
|
||||
"list":"Email.List"
|
||||
},
|
||||
"menu": {
|
||||
"list": {
|
||||
"buttons": [
|
||||
{
|
||||
"label": "Compose",
|
||||
"action": "composeEmail",
|
||||
"style": "danger",
|
||||
"acl": "edit"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"presetFilters": [
|
||||
{
|
||||
"name":"archived",
|
||||
"data": {
|
||||
"status": {
|
||||
"type": "in",
|
||||
"value": ["Archived"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name":"sent",
|
||||
"data": {
|
||||
"status": {
|
||||
"type": "in",
|
||||
"value": ["Sent"]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -41,6 +41,9 @@
|
||||
"dashletOptions": {
|
||||
"type": "text"
|
||||
},
|
||||
"presetFilters": {
|
||||
"type": "text"
|
||||
},
|
||||
"smtpEmailAddress": {
|
||||
"type": "varchar",
|
||||
"readOnly": true,
|
||||
|
||||
@@ -183,6 +183,10 @@
|
||||
"ldapCreateEspoUser": {
|
||||
"type": "bool",
|
||||
"default": true
|
||||
}
|
||||
},
|
||||
"disableExport": {
|
||||
"type": "bool",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,9 +165,11 @@ class Import extends \Espo\Core\Services\Base
|
||||
$ids = array();
|
||||
if (!empty($scope) && !empty($idsToRemove)) {
|
||||
foreach ($idsToRemove as $id) {
|
||||
$entity = $this->getEntityManager()->getEntity($scope, $id);
|
||||
if ($this->getEntityManager()->removeEntity($entity)) {
|
||||
$ids[] = $id;
|
||||
$entity = $this->getEntityManager()->getEntity($scope, $id);
|
||||
if ($entity) {
|
||||
if ($this->getEntityManager()->removeEntity($entity)) {
|
||||
$ids[] = $id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,17 @@ class Notification extends \Espo\Core\Services\Base
|
||||
$this->getEntityManager()->saveEntity($notification);
|
||||
}
|
||||
|
||||
public function notifyAboutNoteFromJob($data)
|
||||
{
|
||||
$userIdList = $data['userIdList'];
|
||||
$noteId = $data['noteId'];
|
||||
|
||||
foreach ($userIdList as $userId) {
|
||||
$this->notifyAboutNote($userId, $noteId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getNotReadCount($userId)
|
||||
{
|
||||
$searchParams = array();
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
-1
@@ -1 +0,0 @@
|
||||
(function(){function t(t){function n(t){t+="";var e=t.split(":"),n=~t.indexOf("-")?-1:1,s=Math.abs(+e[0]),r=parseInt(e[1],10)||0,i=parseInt(e[2],10)||0;return n*(60*s+r+i/60)}function s(t,e,s,r,i,u,a,o,h,f){this.name=t,this.startYear=+e,this.endYear=+s,this.month=+r,this.day=+i,this.dayRule=+u,this.time=n(a),this.timeRule=+o,this.offset=n(h),this.letters=f||""}function r(t,e){this.rule=e,this.start=e.start(t)}function i(t,e){return t.isLast?-1:e.isLast?1:e.start-t.start}function u(t){this.name=t,this.rules=[]}function a(e,s,r,i,u,a){var o,h="string"==typeof u?u.split("_"):[9999];for(this.name=e,this.offset=n(s),this.ruleSet=r,this.letters=i,o=0;h.length>o;o++)h[o]=+h[o];this.until=t.utc(h).subtract("m",n(a))}function o(t,e){return t.until-e.until}function h(t){this.name=d(t),this.displayName=t,this.zones=[]}function f(t){var e,n,s;for(e in t)for(s=t[e],n=0;s.length>n;n++)l(e+" "+s[n])}function l(t){if(g[t])return g[t];var e=t.split(/\s/),n=d(e[0]),r=new s(n,e[1],e[2],e[3],e[4],e[5],e[6],e[7],e[8],e[9],e[10]);return g[t]=r,z(n).add(r),r}function d(t){return(t||"").toLowerCase().replace(/\//g,"_")}function c(t){var e,n,s;for(e in t)for(s=t[e],n=0;s.length>n;n++)p(e+" "+s[n])}function m(t){var e;for(e in t)A[d(e)]=d(t[e])}function p(t){if(b[t])return b[t];var e=t.split(/\s/),n=d(e[0]),s=new a(n,e[1],z(e[2]),e[3],e[4],e[5]);return b[t]=s,y(e[0]).add(s),s}function z(t){return t=d(t),Y[t]||(Y[t]=new u(t)),Y[t]}function y(t){var e=d(t);return A[e]&&(e=A[e]),M[e]||(M[e]=new h(t)),M[e]}function v(t){t&&(t.zones&&c(t.zones),t.rules&&f(t.rules),t.links&&m(t.links))}var R,w=t.fn.zoneName,_=t.fn.zoneAbbr,g={},Y={},b={},M={},A={},k=1,L=2,N=7,q=8;return s.prototype={contains:function(t){return t>=this.startYear&&this.endYear>=t},start:function(e){return e=Math.min(Math.max(e,this.startYear),this.endYear),t.utc([e,this.month,this.date(e),0,this.time])},date:function(t){return this.dayRule===N?this.day:this.dayRule===q?this.lastWeekday(t):this.weekdayAfter(t)},weekdayAfter:function(e){for(var n=this.day,s=t([e,this.month,1]).day(),r=this.dayRule+1-s;n>r;)r+=7;return r},lastWeekday:function(e){var n=this.day,s=n%7,r=t([e,this.month+1,1]).day(),i=t([e,this.month,1]).daysInMonth(),u=i+(s-(r-1))-7*~~(n/7);return s>=r&&(u-=7),u}},r.prototype={equals:function(t){return t&&t.rule===this.rule?864e5>Math.abs(t.start-this.start):!1}},u.prototype={add:function(t){this.rules.push(t)},ruleYears:function(t,e){var n,s,u,a=t.year(),o=[];for(n=0;this.rules.length>n;n++)s=this.rules[n],s.contains(a)?o.push(new r(a,s)):s.contains(a+1)&&o.push(new r(a+1,s));return o.push(new r(a-1,this.lastYearRule(a-1))),e&&(u=new r(a-1,e.lastRule()),u.start=e.until.clone().utc(),u.isLast=e.ruleSet!==this,o.push(u)),o.sort(i),o},rule:function(t,e,n){var s,r,i,u,a,o=this.ruleYears(t,n),h=0;for(n&&(r=n.offset+n.lastRule().offset,i=9e4*Math.abs(r)),a=o.length-1;a>-1;a--)u=s,s=o[a],s.equals(u)||(n&&!s.isLast&&i>=Math.abs(s.start-n.until)&&(h+=r-e),s.rule.timeRule===L&&(h=e),s.rule.timeRule!==k&&s.start.add("m",-h),h=s.rule.offset+e);for(a=0;o.length>a;a++)if(s=o[a],t>=s.start&&!s.isLast)return s.rule;return R},lastYearRule:function(t){var e,n,s,r=R,i=-1e30;for(e=0;this.rules.length>e;e++)n=this.rules[e],t>=n.startYear&&(s=n.start(t),s>i&&(i=s,r=n));return r}},a.prototype={rule:function(t,e){return this.ruleSet.rule(t,this.offset,e)},lastRule:function(){return this._lastRule||(this._lastRule=this.rule(this.until)),this._lastRule},format:function(t){return this.letters.replace("%s",t.letters)}},h.prototype={zoneAndRule:function(t){var e,n,s;for(t=t.clone().utc(),e=0;this.zones.length>e&&(n=this.zones[e],!(n.until>t));e++)s=n;return[n,n.rule(t,s)]},add:function(t){this.zones.push(t),this.zones.sort(o)},format:function(t){var e=this.zoneAndRule(t);return e[0].format(e[1])},offset:function(t){var e=this.zoneAndRule(t);return-(e[0].offset+e[1].offset)}},t.updateOffset=function(t){var e;t._z&&(e=t._z.offset(t),16>Math.abs(e)&&(e/=60),t.zone(e))},t.fn.tz=function(e){return e?(this._z=y(e),this._z&&t.updateOffset(this),this):this._z?this._z.displayName:void 0},t.fn.zoneName=function(){return this._z?this._z.format(this):w.call(this)},t.fn.zoneAbbr=function(){return this._z?this._z.format(this):_.call(this)},t.tz=function(){var e,n=[],s=arguments.length-1;for(e=0;s>e;e++)n[e]=arguments[e];return t.apply(null,n).tz(arguments[s])},t.tz.add=v,t.tz.addRule=l,t.tz.addZone=p,t.tz.version=e,R=l("- 0 9999 0 0 0 0 0 0"),t}var e="0.0.1";"function"==typeof define&&define.amd?define("moment-timezone",["moment"],t):"undefined"!=typeof window&&window.moment?t(window.moment):"undefined"!=typeof module&&(module.exports=t(require("./moment")))}).apply(this);
|
||||
@@ -69,31 +69,31 @@
|
||||
},
|
||||
|
||||
actionSetHeld: function () {
|
||||
if (confirm(this.translate('Are you sure?'))) {
|
||||
this.model.save({
|
||||
status: 'Held'
|
||||
}, {
|
||||
patch: true,
|
||||
success: function () {
|
||||
this.notify('Saved as Held', 'success');
|
||||
this.$el.find('button[data-action="sendInvitations"]').remove();
|
||||
}.bind(this),
|
||||
});
|
||||
}
|
||||
this.model.save({
|
||||
status: 'Held'
|
||||
}, {
|
||||
patch: true,
|
||||
success: function () {
|
||||
this.notify('Saved as Held', 'success');
|
||||
this.$el.find('button[data-action="sendInvitations"]').remove();
|
||||
this.$el.find('a[data-action="setHeld"]').remove();
|
||||
this.$el.find('a[data-action="setNotHeld"]').remove();
|
||||
}.bind(this),
|
||||
});
|
||||
},
|
||||
|
||||
actionSetNotHeld: function () {
|
||||
if (confirm(this.translate('Are you sure?'))) {
|
||||
this.model.save({
|
||||
status: 'Not Held'
|
||||
}, {
|
||||
patch: true,
|
||||
success: function () {
|
||||
this.notify('Saved as Not Held', 'success');
|
||||
this.$el.find('button[data-action="sendInvitations"]').remove();
|
||||
}.bind(this),
|
||||
});
|
||||
}
|
||||
this.model.save({
|
||||
status: 'Not Held'
|
||||
}, {
|
||||
patch: true,
|
||||
success: function () {
|
||||
this.notify('Saved as Not Held', 'success');
|
||||
this.$el.find('button[data-action="sendInvitations"]').remove();
|
||||
this.$el.find('a[data-action="setHeld"]').remove();
|
||||
this.$el.find('a[data-action="setNotHeld"]').remove();
|
||||
}.bind(this),
|
||||
});
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
@@ -69,31 +69,31 @@ Espo.define('Crm:Views.Meeting.Detail', 'Views.Detail', function (Dep) {
|
||||
},
|
||||
|
||||
actionSetHeld: function () {
|
||||
if (confirm(this.translate('Are you sure?'))) {
|
||||
this.model.save({
|
||||
status: 'Held'
|
||||
}, {
|
||||
patch: true,
|
||||
success: function () {
|
||||
this.notify('Saved as Held', 'success');
|
||||
Espo.Ui.success(this.translate('Saved as Held', 'labels', 'Meeting'));
|
||||
this.$el.find('button[data-action="sendInvitations"]').remove();
|
||||
this.$el.find('a[data-action="setHeld"]').remove();
|
||||
this.$el.find('a[data-action="setNotHeld"]').remove();
|
||||
}.bind(this),
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
actionSetNotHeld: function () {
|
||||
if (confirm(this.translate('Are you sure?'))) {
|
||||
this.model.save({
|
||||
status: 'Not Held'
|
||||
}, {
|
||||
patch: true,
|
||||
success: function () {
|
||||
this.notify('Saved as Not Held', 'success');
|
||||
Espo.Ui.success(this.translate('Saved as Not Held', 'labels', 'Meeting'));
|
||||
this.$el.find('button[data-action="sendInvitations"]').remove();
|
||||
this.$el.find('a[data-action="setHeld"]').remove();
|
||||
this.$el.find('a[data-action="setNotHeld"]').remove();
|
||||
}.bind(this),
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<div class="input-group">
|
||||
<input class="main-element form-control" type="text" name="{{name}}" value="{{value}}" autocomplete="off">
|
||||
<span class="input-group-btn">
|
||||
<button type="button" class="btn btn-default" tabindex="-1"><i class="glyphicon glyphicon-calendar"></i></button>
|
||||
<button type="button" class="btn btn-default date-picker-btn" tabindex="-1"><i class="glyphicon glyphicon-calendar"></i></button>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
<select class="form-control search-type input-sm" name="{{name}}-type">
|
||||
{{options searchParams.typeOptions searchParams.type field='dateSearchRanges'}}
|
||||
</select>
|
||||
<div class="input-group">
|
||||
<input class="main-element form-control input-sm" type="text" name="{{name}}" value="{{searchParams.value1}}" autocomplete="off">
|
||||
<div class="input-group primary">
|
||||
<input class="main-element form-control input-sm" type="text" name="{{name}}" value="{{searchParams.dateValue}}" autocomplete="off">
|
||||
<span class="input-group-btn">
|
||||
<button type="button" class="btn btn-default btn-sm" tabindex="-1"><i class="glyphicon glyphicon-calendar"></i></button>
|
||||
<button type="button" class="btn btn-default btn-sm date-picker-btn" tabindex="-1"><i class="glyphicon glyphicon-calendar"></i></button>
|
||||
</span>
|
||||
</div>
|
||||
<div class="input-group{{#ifNotEqual searchParams.type 'between'}} hide{{/ifNotEqual}} additional">
|
||||
<input class="main-element form-control input-sm" type="text" name="{{name}}-additional" value="{{searchParams.value2}}" autocomplete="off">
|
||||
<div class="input-group{{#ifNotEqual searchParams.type 'between'}} hidden{{/ifNotEqual}} additional">
|
||||
<input class="main-element form-control input-sm" type="text" name="{{name}}-additional" value="{{searchParams.dateValueTo}}" autocomplete="off">
|
||||
<span class="input-group-btn">
|
||||
<button type="button" class="btn btn-default btn-sm" tabindex="-1"><i class="glyphicon glyphicon-calendar"></i></button>
|
||||
<button type="button" class="btn btn-default btn-sm date-picker-btn" tabindex="-1"><i class="glyphicon glyphicon-calendar"></i></button>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
|
||||
<div class="row">
|
||||
<div class="input-group col-lg-6 col-md-6 col-sm-6">
|
||||
<div class="input-group">
|
||||
<input class="main-element form-control" type="text" name="{{name}}" value="{{date}}" autocomplete="off">
|
||||
<span class="input-group-btn">
|
||||
<button type="button" class="btn btn-default" tabindex="-1"><i class="glyphicon glyphicon-calendar"></i></button>
|
||||
<button type="button" class="btn btn-default date-picker-btn" tabindex="-1"><i class="glyphicon glyphicon-calendar"></i></button>
|
||||
</span>
|
||||
</div>
|
||||
<div class="input-group col-lg-6 col-md-6 col-sm-6">
|
||||
<input class="form-control" type="text" name="{{name}}-time" value="{{time}}" autocomplete="off">
|
||||
<span class="input-group-btn">
|
||||
<button type="button" class="btn btn-default" tabindex="-1"><i class="glyphicon glyphicon-time"></i></button>
|
||||
<button type="button" class="btn btn-default time-picker-btn" tabindex="-1"><i class="glyphicon glyphicon-time"></i></button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
<div class="link-container list-group">
|
||||
{{#each searchParams.nameHash}}
|
||||
<div class="link-{{@key}} list-group-item">
|
||||
{{this}}
|
||||
<a href="javascript:" class="pull-right" data-id="{{@key}}" data-action="clearLink"><span class="glyphicon glyphicon-remove"></a>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
|
||||
<div class="input-group add-team">
|
||||
|
||||
@@ -1,18 +1,13 @@
|
||||
<div class="row">
|
||||
<div class="col-sm-5">
|
||||
<div class="input-group">
|
||||
<span class="input-group-btn">
|
||||
<select class="form-control" name="{{typeName}}">
|
||||
{{options foreignScopeList foreignScope category='scopeNames'}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="input-group col-sm-7">
|
||||
<input class="main-element form-control" type="text" name="{{nameName}}" value="{{nameValue}}" autocomplete="off">
|
||||
<span class="input-group-btn">
|
||||
<button data-action="selectLink" class="btn btn-default" type="button" tabindex="-1"><i class="glyphicon glyphicon-arrow-up"></i></button>
|
||||
<button data-action="clearLink" class="btn btn-default" type="button" tabindex="-1"><i class="glyphicon glyphicon-remove"></i></button>
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
<input class="main-element form-control" type="text" name="{{nameName}}" value="{{nameValue}}" autocomplete="off">
|
||||
<span class="input-group-btn">
|
||||
<button data-action="selectLink" class="btn btn-default" type="button" tabindex="-1"><i class="glyphicon glyphicon-arrow-up"></i></button>
|
||||
<button data-action="clearLink" class="btn btn-default" type="button" tabindex="-1"><i class="glyphicon glyphicon-remove"></i></button>
|
||||
</span>
|
||||
</div>
|
||||
<input type="hidden" name="{{idName}}" value="{{idValue}}">
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<select class="form-control" name="{{typeName}}">
|
||||
<select class="form-control input-sm" name="{{typeName}}">
|
||||
{{options foreignScopeList searchParams.valueType category='scopeNames'}}
|
||||
</select>
|
||||
<div class="input-group">
|
||||
|
||||
@@ -31,5 +31,5 @@
|
||||
|
||||
<div style="padding-bottom: 10px;" class="clearfix">
|
||||
<button class="btn btn-default pull-left" data-action="back">{{translate 'Back'}}</button>
|
||||
<button class="btn btn-primary pull-right" data-action="next">{{translate 'Next'}}</button>
|
||||
<button class="btn btn-danger pull-right" data-action="next">{{translate 'Run Import'}}</button>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<div class="cell cell-name form-group">
|
||||
<label class="field-label-name control-label">{{translate 'Name' category='fields'}}</label>
|
||||
<div class="field field-name">
|
||||
{{{name}}}
|
||||
</div>
|
||||
</div>
|
||||
@@ -2,16 +2,22 @@
|
||||
<div class="row search-row">
|
||||
<div class="form-group col-sm-6">
|
||||
<div class="input-group">
|
||||
{{#if boolFilters}}
|
||||
<div class="input-group-btn">
|
||||
|
||||
{{#if leftDropdown}}
|
||||
<div class="input-group-btn">
|
||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" tabindex="-1">
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu pull-left basic-filter-menu">
|
||||
<ul class="dropdown-menu pull-left basic-filter-menu">
|
||||
{{#if presetFilters.length}}
|
||||
<li><a class="preset" tabindex="-1" href="javascript:" data-name="" data-action="selectPreset">{{translate 'All'}}</a></li>
|
||||
{{#each ../presetFilters}}
|
||||
<li><a class="preset" tabindex="-1" href="javascript:" data-name="{{name}}" data-action="selectPreset">{{#if label}}{{label}}{{else}}{{translate name category='presetFilters' scope=../../../scope}}{{/if}}</a></li>
|
||||
{{/each}}
|
||||
<li class="divider"></li>
|
||||
{{/if}}
|
||||
|
||||
{{#each boolFilters}}
|
||||
<li class="checkbox"><label><input type="checkbox" name="{{this}}" {{#ifPropEquals ../bool this true}}checked{{/ifPropEquals}}> {{translate this scope=../scope category='boolFilters'}}</label></li>
|
||||
<li class="checkbox"><label><input type="checkbox" data-role="boolFilterCheckbox" name="{{this}}" {{#ifPropEquals ../bool this true}}checked{{/ifPropEquals}}> {{translate this scope=../scope category='boolFilters'}}</label></li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</div>
|
||||
@@ -24,8 +30,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-sm-6">
|
||||
|
||||
<div class="form-group col-sm-6">
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-default" data-action="reset">
|
||||
<span class="glyphicon glyphicon-repeat"></span> {{translate 'Reset'}}
|
||||
@@ -42,8 +47,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row advanced-filters">
|
||||
<div class="advanced-filters-bar" style="margin-bottom: 12px;"></div>
|
||||
<div class="row advanced-filters hidden">
|
||||
{{#each filterList}}
|
||||
<div class="filter {{this}} col-sm-4 col-md-3">
|
||||
{{{var this ../this}}}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
<div class="form-group">
|
||||
<a href="javascript:" class="remove-filter pull-right" data-name="{{name}}"><i class="glyphicon glyphicon-remove"></i></a>
|
||||
<label class="cotrol-label">{{translate name category='fields' scope=scope}}</label>
|
||||
<label class="cotrol-label small">{{translate name category='fields' scope=scope}}</label>
|
||||
<div class="field field-{{name}}">{{{field}}}</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -60,8 +60,7 @@ _.extend(Espo.DateTime.prototype, {
|
||||
return m.format(this.internalDateFormat);
|
||||
},
|
||||
|
||||
toDisplayDate: function (string) {
|
||||
|
||||
toDisplayDate: function (string) {
|
||||
if (!string || (typeof string != 'string')) {
|
||||
return '';
|
||||
}
|
||||
@@ -97,6 +96,14 @@ _.extend(Espo.DateTime.prototype, {
|
||||
}
|
||||
return this.toMoment(string).format(this.getDateTimeFormat());
|
||||
},
|
||||
|
||||
getNowMoment: function () {
|
||||
var m = moment();
|
||||
if (this.timeZone) {
|
||||
m = m.tz(this.timeZone);
|
||||
}
|
||||
return m;
|
||||
},
|
||||
|
||||
toMoment: function (string) {
|
||||
var m = moment.utc(string, this.internalDateTimeFormat);
|
||||
|
||||
@@ -138,33 +138,54 @@
|
||||
var where = {
|
||||
field: field
|
||||
};
|
||||
if (value) {
|
||||
switch (type) {
|
||||
case 'on':
|
||||
where.type = 'between';
|
||||
var from = this.dateTime.toMoment(value).format(this.dateTime.internalDateTimeFormat);
|
||||
var to = this.dateTime.toMoment(value).add('days', 1).format(this.dateTime.internalDateTimeFormat);
|
||||
where.value = [from, to];
|
||||
break;
|
||||
case 'before':
|
||||
where.type = 'before';
|
||||
where.value = this.dateTime.toMoment(value).format(this.dateTime.internalDateTimeFormat);
|
||||
break;
|
||||
case 'after':
|
||||
where.type = 'after';
|
||||
where.value = this.dateTime.toMoment(value).format(this.dateTime.internalDateTimeFormat);
|
||||
break;
|
||||
case 'between':
|
||||
where.type = 'between';
|
||||
if (value[0] && value[1]) {
|
||||
var from = this.dateTime.toMoment(value[0]).format(this.dateTime.internalDateTimeFormat);
|
||||
var to = this.dateTime.toMoment(value[1]).format(this.dateTime.internalDateTimeFormat);
|
||||
where.value = [from, to];
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
if (!value && !~['today', 'past', 'future'].indexOf(type)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 'today':
|
||||
where.type = 'between';
|
||||
var start = this.dateTime.getNowMoment().startOf('day').utc();
|
||||
|
||||
var from = start.format(this.dateTime.internalDateTimeFormat);
|
||||
var to = start.add('days', 1).format(this.dateTime.internalDateTimeFormat);
|
||||
where.value = [from, to];
|
||||
break;
|
||||
case 'past':
|
||||
where.type = 'before';
|
||||
where.value = this.dateTime.getNowMoment().utc().format(this.dateTime.internalDateTimeFormat);
|
||||
break;
|
||||
case 'future':
|
||||
where.type = 'after';
|
||||
where.value = this.dateTime.getNowMoment().utc().format(this.dateTime.internalDateTimeFormat);
|
||||
break;
|
||||
case 'on':
|
||||
where.type = 'between';
|
||||
var start = moment(value, this.dateTime.internalDateFormat, this.timeZone).utc();
|
||||
|
||||
var from = start.format(this.dateTime.internalDateTimeFormat);
|
||||
var to = start.add('days', 1).format(this.dateTime.internalDateTimeFormat);
|
||||
|
||||
where.value = [from, to];
|
||||
break;
|
||||
case 'before':
|
||||
where.type = 'before';
|
||||
where.value = moment(value, this.dateTime.internalDateFormat, this.timeZone).utc().format(this.dateTime.internalDateTimeFormat);
|
||||
break;
|
||||
case 'after':
|
||||
where.type = 'after';
|
||||
where.value = moment(value, this.dateTime.internalDateFormat, this.timeZone).utc().format(this.dateTime.internalDateTimeFormat);
|
||||
break;
|
||||
case 'between':
|
||||
where.type = 'between';
|
||||
if (value[0] && value[1]) {
|
||||
var from = moment(value[0], this.dateTime.internalDateFormat, this.timeZone).utc().format(this.dateTime.internalDateTimeFormat);
|
||||
var to = moment(value[1], this.dateTime.internalDateFormat, this.timeZone).utc().format(this.dateTime.internalDateTimeFormat);
|
||||
where.value = [from, to];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return where;
|
||||
},
|
||||
});
|
||||
|
||||
@@ -153,6 +153,7 @@ Espo.define('Views.Dashboard', 'View', function (Dep) {
|
||||
$('#dashlets').children().first().prepend('<div id="dashlet-container-' + id + '"></div>');
|
||||
|
||||
this.getView('dashlets').createView('dashlet-' + id, 'Dashlet', {
|
||||
label: name,
|
||||
name: name,
|
||||
id: id,
|
||||
el: '#dashlet-container-' + id
|
||||
|
||||
@@ -56,6 +56,7 @@ Espo.define('Views.Dashlet', 'View', function (Dep) {
|
||||
|
||||
var bodySelector = '#dashlet-' + this.id + ' .dashlet-body';
|
||||
var module = this.getMetadata().get('dashlets.' + this.name + '.module');
|
||||
|
||||
this.createView('body', Espo.Utils.composeClassName(module, this.name, 'Dashlets'), {el: bodySelector, id: this.id});
|
||||
},
|
||||
|
||||
|
||||
@@ -269,7 +269,20 @@ Espo.define('Views.Detail', 'Views.Main', function (Dep) {
|
||||
});
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
}
|
||||
},
|
||||
|
||||
actionDuplicate: function () {
|
||||
var attributes = Espo.Utils.cloneDeep(this.model.attributes);
|
||||
delete attributes.id;
|
||||
|
||||
var url = '#' + this.scope + '/create';
|
||||
|
||||
this.getRouter().dispatch(this.scope, 'create', {
|
||||
attributes: attributes,
|
||||
});
|
||||
this.getRouter().navigate(url, {trigger: false});
|
||||
},
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ Espo.define('Views.Fields.Date', 'Views.Fields.Base', function (Dep) {
|
||||
|
||||
validations: ['required', 'date', 'after', 'before'],
|
||||
|
||||
searchTypeOptions: ['on', 'notOn', 'after', 'before', 'between'],
|
||||
searchTypeOptions: ['on', 'notOn', 'after', 'before', 'between', 'today', 'past', 'future'],
|
||||
|
||||
setup: function () {
|
||||
Dep.prototype.setup.call(this);
|
||||
@@ -40,19 +40,14 @@ Espo.define('Views.Fields.Date', 'Views.Fields.Base', function (Dep) {
|
||||
setupSearch: function () {
|
||||
this.searchParams.typeOptions = this.searchTypeOptions;
|
||||
this.events = _.extend({
|
||||
'change select.search-type': function (e) {
|
||||
var additional = this.$el.find('.additional');
|
||||
if ($(e.currentTarget).val() == 'between') {
|
||||
additional.removeClass('hide');
|
||||
} else {
|
||||
additional.addClass('hide');
|
||||
}
|
||||
'change select.search-type': function (e) {
|
||||
var type = $(e.currentTarget).val();
|
||||
this.handleSearchType(type);
|
||||
},
|
||||
}, this.events || {});
|
||||
}, this.events || {});
|
||||
|
||||
|
||||
this.searchParams.value1 = this.getDateTime().toDisplayDate(this.searchParams.value1);
|
||||
this.searchParams.value2 = this.getDateTime().toDisplayDate(this.searchParams.value2);
|
||||
this.searchParams.dateValue = this.getDateTime().toDisplayDate(this.searchParams.dateValue);
|
||||
this.searchParams.dateValueTo = this.getDateTime().toDisplayDate(this.searchParams.dateValueTo);
|
||||
},
|
||||
|
||||
getValueForDisplay: function () {
|
||||
@@ -110,24 +105,40 @@ Espo.define('Views.Fields.Date', 'Views.Fields.Base', function (Dep) {
|
||||
language: language
|
||||
};
|
||||
|
||||
var datePicker = this.$element.datepicker(options).on('show', function (e) {
|
||||
var $datePicker = this.$element.datepicker(options).on('show', function (e) {
|
||||
$('body > .datepicker.datepicker-dropdown').css('z-index', 1200);
|
||||
}.bind(this));
|
||||
|
||||
|
||||
if (this.mode == 'search') {
|
||||
var elAdd = this.$el.find('input[name="' + this.name + '-additional"]');
|
||||
elAdd.datepicker(options).on('show', function (e) {
|
||||
var $elAdd = this.$el.find('input[name="' + this.name + '-additional"]');
|
||||
$elAdd.datepicker(options).on('show', function (e) {
|
||||
$('body > .datepicker.datepicker-dropdown').css('z-index', 1200);
|
||||
}.bind(this));
|
||||
elAdd.parent().find('button').click(function (e) {
|
||||
elAdd.datepicker('show');
|
||||
$elAdd.parent().find('button.date-picker-btn').on('click', function (e) {
|
||||
$elAdd.datepicker('show');
|
||||
});
|
||||
}
|
||||
|
||||
this.$element.parent().find('button').click(function (e) {
|
||||
this.$element.parent().find('button.date-picker-btn').on('click', function (e) {
|
||||
this.$element.datepicker('show');
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
|
||||
var $searchType = this.$el.find('select.search-type');
|
||||
this.handleSearchType($searchType.val());
|
||||
}
|
||||
},
|
||||
|
||||
handleSearchType: function (type) {
|
||||
if (~['today', 'past', 'future'].indexOf(type)) {
|
||||
this.$el.find('div.primary').addClass('hidden');
|
||||
this.$el.find('div.additional').addClass('hidden');
|
||||
} else if (type == 'between') {
|
||||
this.$el.find('div.primary').removeClass('hidden');
|
||||
this.$el.find('div.additional').removeClass('hidden');
|
||||
} else {
|
||||
this.$el.find('div.primary').removeClass('hidden');
|
||||
this.$el.find('div.additional').addClass('hidden');
|
||||
}
|
||||
},
|
||||
|
||||
@@ -151,17 +162,10 @@ Espo.define('Views.Fields.Date', 'Views.Fields.Base', function (Dep) {
|
||||
var type = this.$el.find('[name="'+this.name+'-type"]').val();
|
||||
var data;
|
||||
|
||||
if (!value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type != 'between') {
|
||||
data = {
|
||||
type: type,
|
||||
value: value,
|
||||
value1: value
|
||||
};
|
||||
} else {
|
||||
if (type == 'between') {
|
||||
if (!value) {
|
||||
return false;
|
||||
}
|
||||
var valueTo = this.parseDate(this.$el.find('[name="' + this.name + '-additional"]').val());
|
||||
if (!valueTo) {
|
||||
return false;
|
||||
@@ -169,8 +173,21 @@ Espo.define('Views.Fields.Date', 'Views.Fields.Base', function (Dep) {
|
||||
data = {
|
||||
type: type,
|
||||
value: [value, valueTo],
|
||||
value1: value,
|
||||
value2: valueTo
|
||||
dateValue: value,
|
||||
dateValueTo: valueTo
|
||||
};
|
||||
} else if (~['today', 'past', 'future'].indexOf(type)) {
|
||||
data = {
|
||||
type: type
|
||||
};
|
||||
} else {
|
||||
if (!value) {
|
||||
return false;
|
||||
}
|
||||
data = {
|
||||
type: type,
|
||||
value: value,
|
||||
dateValue: value
|
||||
};
|
||||
}
|
||||
return data;
|
||||
|
||||
@@ -29,7 +29,7 @@ Espo.define('Views.Fields.Datetime', 'Views.Fields.Date', function (Dep) {
|
||||
|
||||
validations: ['required', 'datetime', 'after', 'before'],
|
||||
|
||||
searchTypeOptions: ['on', 'after', 'before', 'between'],
|
||||
searchTypeOptions: ['on', 'after', 'before', 'between', 'today', 'past', 'future'],
|
||||
|
||||
timeFormatMap: {
|
||||
'HH:mm': 'H:i',
|
||||
@@ -72,7 +72,7 @@ Espo.define('Views.Fields.Datetime', 'Views.Fields.Date', function (Dep) {
|
||||
scrollDefaultNow: true,
|
||||
timeFormat: this.timeFormatMap[this.getDateTime().timeFormat]
|
||||
});
|
||||
$time.parent().find('button').click(function () {
|
||||
$time.parent().find('button.time-picker-btn').on('click', function () {
|
||||
$time.timepicker('show');
|
||||
});
|
||||
|
||||
@@ -139,37 +139,13 @@ Espo.define('Views.Fields.Datetime', 'Views.Fields.Date', function (Dep) {
|
||||
}
|
||||
},
|
||||
|
||||
fetchSearch: function () {
|
||||
var value = this.parseDate(this.$element.val());
|
||||
|
||||
var type = this.$el.find('[name="'+this.name+'-type"]').val();
|
||||
var data;
|
||||
fetchSearch: function () {
|
||||
var data = Dep.prototype.fetchSearch.call(this);
|
||||
|
||||
if (!value) {
|
||||
return false;
|
||||
if (data) {
|
||||
data.dateTime = true;
|
||||
}
|
||||
|
||||
if (type != 'between') {
|
||||
data = {
|
||||
type: type,
|
||||
value: value,
|
||||
value1: value,
|
||||
dateTime: true,
|
||||
};
|
||||
} else {
|
||||
var valueTo = this.parseDate(this.$el.find('[name="' + this.name + '-additional"]').val());
|
||||
if (!valueTo) {
|
||||
return false;
|
||||
}
|
||||
data = {
|
||||
type: type,
|
||||
value: [value, valueTo],
|
||||
value1: value,
|
||||
value2: valueTo,
|
||||
dateTime: true,
|
||||
};
|
||||
}
|
||||
return data;
|
||||
return data;
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
@@ -66,6 +66,7 @@ Espo.define('Views.Fields.LinkMultiple', 'Views.Fields.Base', function (Dep) {
|
||||
|
||||
if (this.mode == 'search') {
|
||||
this.nameHash = Espo.Utils.clone(this.searchParams.nameHash) || {};
|
||||
this.ids = Espo.Utils.clone(this.searchParams.value) || [];
|
||||
}
|
||||
|
||||
this.listenTo(this.model, 'change:' + this.idsName, function () {
|
||||
|
||||
@@ -81,7 +81,7 @@ Espo.define('Views.Import.Step1', 'View', function (Dep) {
|
||||
|
||||
afterRender: function () {
|
||||
this.setupFormData();
|
||||
if (this.formData.fileContents) {
|
||||
if (this.getParentView().fileContents) {
|
||||
this.setFileIsLoaded();
|
||||
this.preview();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014 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/.
|
||||
************************************************************************/
|
||||
|
||||
|
||||
Espo.define('Views.Modals.SaveFilters', 'Views.Modal', function (Dep) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
cssName: 'save-filters',
|
||||
|
||||
template: 'modals.save-filters',
|
||||
|
||||
data: function () {
|
||||
return {
|
||||
dashletList: this.dashletList,
|
||||
};
|
||||
},
|
||||
|
||||
setup: function () {
|
||||
this.buttons = [
|
||||
{
|
||||
name: 'save',
|
||||
label: 'Save',
|
||||
style: 'primary',
|
||||
onClick: function (dialog) {
|
||||
this.save();
|
||||
}.bind(this)
|
||||
},
|
||||
{
|
||||
name: 'cancel',
|
||||
label: 'Cancel',
|
||||
onClick: function (dialog) {
|
||||
dialog.close();
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
this.header = this.translate('Save Filters');
|
||||
|
||||
var model = new Espo.Model();
|
||||
this.createView('name', 'Fields.Varchar', {
|
||||
el: this.options.el + ' .field-name',
|
||||
defs: {
|
||||
name: 'name',
|
||||
params: {
|
||||
required: true
|
||||
}
|
||||
},
|
||||
mode: 'edit',
|
||||
model: model
|
||||
});
|
||||
},
|
||||
|
||||
save: function () {
|
||||
var nameView = this.getView('name');
|
||||
nameView.fetchToModel();
|
||||
if (nameView.validate()) {
|
||||
return;
|
||||
}
|
||||
this.trigger('save', nameView.model.get('name'));
|
||||
return true;
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -108,6 +108,7 @@ Espo.define('Views.Modals.SelectRecords', 'Views.Modal', function (Dep) {
|
||||
collection: collection,
|
||||
el: this.containerSelector + ' .search-container',
|
||||
searchManager: searchManager,
|
||||
disableSavePreset: true,
|
||||
});
|
||||
|
||||
this.listenToOnce(collection, 'sync', function () {
|
||||
|
||||
@@ -230,28 +230,6 @@ Espo.define('Views.Record.List', 'View', function (Dep) {
|
||||
}, this);
|
||||
}.bind(this));
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'export',
|
||||
label: 'Export',
|
||||
action: function (e) {
|
||||
var ids = this.checkedList;
|
||||
var where = this.collection.where;
|
||||
|
||||
$.ajax({
|
||||
url: this.scope + '/action/export',
|
||||
type: 'GET',
|
||||
data: {
|
||||
ids: ids || null,
|
||||
where: (ids.length == 0) ? where : null,
|
||||
},
|
||||
success: function (data) {
|
||||
if ('id' in data) {
|
||||
window.location = '?entryPoint=download&id=' + data.id;
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
}
|
||||
],
|
||||
|
||||
@@ -313,6 +291,25 @@ Espo.define('Views.Record.List', 'View', function (Dep) {
|
||||
this.$el.find("a.sort").addClass('disabled');
|
||||
}
|
||||
},
|
||||
|
||||
export: function () {
|
||||
var ids = this.checkedList;
|
||||
var where = this.collection.where;
|
||||
|
||||
$.ajax({
|
||||
url: this.scope + '/action/export',
|
||||
type: 'GET',
|
||||
data: {
|
||||
ids: ids || null,
|
||||
where: (ids.length == 0) ? where : null,
|
||||
},
|
||||
success: function (data) {
|
||||
if ('id' in data) {
|
||||
window.location = '?entryPoint=download&id=' + data.id;
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
setup: function () {
|
||||
if (typeof this.collection === 'undefined') {
|
||||
@@ -339,6 +336,16 @@ Espo.define('Views.Record.List', 'View', function (Dep) {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (!this.getConfig().get('disableExport') || this.getUser().get('isAdmin')) {
|
||||
this.actions.push({
|
||||
name: 'export',
|
||||
label: 'Export',
|
||||
action: function (e) {
|
||||
this.export();
|
||||
}.bind(this)
|
||||
});
|
||||
}
|
||||
|
||||
if (this.options.actions === false) {
|
||||
this.actions = [];
|
||||
|
||||
@@ -38,8 +38,8 @@ Espo.define('Views.Record.Search', 'View', function (Dep) {
|
||||
advanced: null,
|
||||
|
||||
bool: null,
|
||||
|
||||
filterViews: null,
|
||||
|
||||
disableSavePreset: false,
|
||||
|
||||
data: function () {
|
||||
return {
|
||||
@@ -49,12 +49,19 @@ Espo.define('Views.Record.Search', 'View', function (Dep) {
|
||||
boolFilters: this.boolFilters,
|
||||
advancedFields: this.getAdvancedDefs(),
|
||||
filterList: this.getFilterList(),
|
||||
presetName: this.presetName,
|
||||
presetFilters: this.presetFilters,
|
||||
leftDropdown: this.presetFilters.length || this.boolFilters.length
|
||||
};
|
||||
},
|
||||
|
||||
setup: function () {
|
||||
this.scope = this.collection.name;
|
||||
this.searchManager = this.options.searchManager;
|
||||
|
||||
if ('disableSavePreset' in this.options) {
|
||||
this.disableSavePreset = this.options.disableSavePreset;
|
||||
}
|
||||
|
||||
this.addReadyCondition(function () {
|
||||
return this.fields != null && this.moreFields != null;
|
||||
@@ -66,15 +73,39 @@ Espo.define('Views.Record.Search', 'View', function (Dep) {
|
||||
this.moreFields = list;
|
||||
this.tryReady();
|
||||
}.bind(this));
|
||||
|
||||
this.presetFilters = _.clone(this.getMetadata().get('clientDefs.' + this.scope + '.presetFilters') || []);
|
||||
((this.getPreferences().get('presetFilters') || {})[this.scope] || []).forEach(function (item) {
|
||||
this.presetFilters.push(item);
|
||||
}, this);
|
||||
|
||||
this.getSearchData();
|
||||
this.loadSearchData();
|
||||
|
||||
this.model = new this.collection.model();
|
||||
this.model.clear();
|
||||
this.model.clear();
|
||||
|
||||
this.filterViews = [];
|
||||
this.createFilters();
|
||||
},
|
||||
|
||||
createFilters: function (callback) {
|
||||
var i = 0;
|
||||
var count = Object.keys(this.advanced).length;
|
||||
|
||||
if (count == 0) {
|
||||
if (typeof callback === 'function') {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
for (var field in this.advanced) {
|
||||
this.createFilter(field, this.advanced[field]);
|
||||
this.createFilter(field, this.advanced[field], function () {
|
||||
i++;
|
||||
if (i == count) {
|
||||
if (typeof callback === 'function') {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@@ -88,41 +119,43 @@ Espo.define('Views.Record.Search', 'View', function (Dep) {
|
||||
this.search();
|
||||
},
|
||||
'click a[data-action="addFilter"]': function (e) {
|
||||
var name = $(e.currentTarget).data('name');
|
||||
var $target = $(e.currentTarget);
|
||||
var name = $target.data('name');
|
||||
this.advanced[name] = {};
|
||||
$(e.currentTarget).closest('li').addClass('hide');
|
||||
this.$el.find('.advanced-filters').append('<div class="filter filter-' + name + ' col-sm-4 col-md-3" />');
|
||||
|
||||
this.createFilter(name, {}, function () {
|
||||
|
||||
$target.closest('li').addClass('hide');
|
||||
|
||||
this.presetName = null;
|
||||
|
||||
this.createFilter(name, {}, function () {
|
||||
this.fetch();
|
||||
this.updateSearch();
|
||||
}.bind(this));
|
||||
this.updateAddFilterButton();
|
||||
|
||||
this.managePresetFilters();
|
||||
},
|
||||
'click .advanced-filters a.remove-filter': function (e) {
|
||||
var name = $(e.currentTarget).data('name');
|
||||
'click .advanced-filters a.remove-filter': function (e) {
|
||||
var $target = $(e.currentTarget);
|
||||
var name = $target.data('name');
|
||||
|
||||
this.$el.find('ul.filter-list li[data-name="' + name + '"]').removeClass('hide');
|
||||
var container = this.getView('filter-' + name).$el.closest('div.filter');
|
||||
this.clearView('filter-' + name);
|
||||
this.clearView('filter-' + name);
|
||||
container.remove();
|
||||
delete this.advanced[name];
|
||||
this.updateAddFilterButton();
|
||||
|
||||
this.presetName = null;
|
||||
|
||||
this.updateAddFilterButton();
|
||||
|
||||
this.fetch();
|
||||
this.updateSearch();
|
||||
|
||||
this.managePresetFilters();
|
||||
},
|
||||
'click button[data-action="reset"]': function (e) {
|
||||
for (var name in this.advanced) {
|
||||
this.clearView('filter-' + name);
|
||||
}
|
||||
|
||||
this.searchManager.reset();
|
||||
this.getSearchData();
|
||||
|
||||
this.render();
|
||||
this.updateCollection();
|
||||
this.resetFilters();
|
||||
},
|
||||
'click button[data-action="refresh"]': function (e) {
|
||||
this.notify('Loading...');
|
||||
@@ -132,7 +165,137 @@ Espo.define('Views.Record.Search', 'View', function (Dep) {
|
||||
|
||||
this.collection.reset();
|
||||
this.collection.fetch();
|
||||
|
||||
},
|
||||
'click a[data-action="selectPreset"]': function (e) {
|
||||
this.presetName = $(e.currentTarget).data('name') || null;
|
||||
|
||||
this.removeFilters();
|
||||
|
||||
this.advanced = this.getPresetData();
|
||||
this.updateSearch();
|
||||
|
||||
this.managePresetFilters();
|
||||
|
||||
this.createFilters(function () {
|
||||
this.render();
|
||||
}.bind(this));
|
||||
this.updateCollection();
|
||||
},
|
||||
'click .advanced-filters-bar a[data-action="showFiltersPanel"]': function (e) {
|
||||
this.$advancedFiltersPanel.removeClass('hidden');
|
||||
},
|
||||
'click .advanced-filters-bar a[data-action="savePreset"]': function (e) {
|
||||
this.createView('savePreset', 'Modals.SaveFilters', {}, function (view) {
|
||||
view.render();
|
||||
this.listenToOnce(view, 'save', function (name) {
|
||||
this.savePreset(name);
|
||||
view.close();
|
||||
|
||||
this.removeFilters();
|
||||
this.createFilters(function () {
|
||||
this.render();
|
||||
}.bind(this));
|
||||
|
||||
}, this);
|
||||
}.bind(this));
|
||||
},
|
||||
'click .advanced-filters-bar a[data-action="removePreset"]': function (e) {
|
||||
var id = $(e.currentTarget).data('id');
|
||||
if (confirm(this.translate('Are you sure?'))) {
|
||||
this.removePreset(id);
|
||||
}
|
||||
},
|
||||
'change .search-row ul.basic-filter-menu input[data-role="boolFilterCheckbox"]': function (e) {
|
||||
e.stopPropagation();
|
||||
this.search();
|
||||
}
|
||||
},
|
||||
|
||||
removeFilters: function () {
|
||||
this.$advancedFiltersPanel.empty();
|
||||
for (var name in this.advanced) {
|
||||
this.clearView('filter-' + name);
|
||||
}
|
||||
},
|
||||
|
||||
resetFilters: function () {
|
||||
this.removeFilters();
|
||||
|
||||
this.presetName = null;
|
||||
|
||||
this.searchManager.reset();
|
||||
this.loadSearchData();
|
||||
|
||||
this.render();
|
||||
this.updateCollection();
|
||||
},
|
||||
|
||||
savePreset: function (name) {
|
||||
var id = 'f' + (Math.floor(Math.random() * 1000001)).toString();
|
||||
|
||||
this.fetch();
|
||||
this.updateSearch();
|
||||
|
||||
var presetFilters = this.getPreferences().get('presetFilters') || {};
|
||||
if (!(this.scope in presetFilters)) {
|
||||
presetFilters[this.scope] = [];
|
||||
}
|
||||
|
||||
var data = {
|
||||
id: id,
|
||||
name: id,
|
||||
label: name,
|
||||
data: this.advanced
|
||||
};
|
||||
|
||||
presetFilters[this.scope].push(data);
|
||||
|
||||
this.presetFilters.push(data);
|
||||
|
||||
this.getPreferences().set('presetFilters', presetFilters);
|
||||
this.getPreferences().save({patch: true});
|
||||
this.getPreferences().trigger('update');
|
||||
|
||||
this.presetName = id;
|
||||
},
|
||||
|
||||
removePreset: function (id) {
|
||||
var presetFilters = this.getPreferences().get('presetFilters') || {};
|
||||
if (!(this.scope in presetFilters)) {
|
||||
presetFilters[this.scope] = [];
|
||||
}
|
||||
|
||||
var list;
|
||||
list = presetFilters[this.scope];
|
||||
list.forEach(function (item, i) {
|
||||
if (item.id == id) {
|
||||
list.splice(i, 1);
|
||||
return;
|
||||
}
|
||||
}, this);
|
||||
|
||||
list = this.presetFilters;
|
||||
list.forEach(function (item, i) {
|
||||
if (item.id == id) {
|
||||
list.splice(i, 1);
|
||||
return;
|
||||
}
|
||||
}, this);
|
||||
|
||||
|
||||
this.getPreferences().set('presetFilters', presetFilters);
|
||||
this.getPreferences().save({patch: true});
|
||||
this.getPreferences().trigger('update');
|
||||
|
||||
this.presetName = null;
|
||||
this.advanced = {};
|
||||
|
||||
this.removeFilters();
|
||||
|
||||
this.render();
|
||||
this.updateSearch();
|
||||
this.updateCollection();
|
||||
},
|
||||
|
||||
updateAddFilterButton: function () {
|
||||
@@ -145,10 +308,64 @@ Espo.define('Views.Record.Search', 'View', function (Dep) {
|
||||
},
|
||||
|
||||
afterRender: function () {
|
||||
this.$el.find('ul.basic-filter-menu').click(function (e) {
|
||||
/*this.$el.find('ul.basic-filter-menu li.checkbox').on('click', function (e) {
|
||||
e.stopPropagation();
|
||||
});
|
||||
}.bind(this));*/
|
||||
this.updateAddFilterButton();
|
||||
|
||||
this.$advancedFiltersBar = this.$el.find('.advanced-filters-bar');
|
||||
this.$advancedFiltersPanel = this.$el.find('.advanced-filters');
|
||||
|
||||
this.managePresetFilters();
|
||||
},
|
||||
|
||||
managePresetFilters: function () {
|
||||
var name = this.presetName || null;
|
||||
var data = this.getPresetData();
|
||||
|
||||
this.$el.find('ul.basic-filter-menu a.preset span').remove();
|
||||
|
||||
if (name) {
|
||||
this.$advancedFiltersPanel.addClass('hidden');
|
||||
|
||||
var label = null;
|
||||
var style = null;
|
||||
var id = null;
|
||||
this.presetFilters.forEach(function (item) {
|
||||
if (item.name == this.presetName) {
|
||||
label = item.label || false;
|
||||
style = item.style || 'default';
|
||||
id = item.id;
|
||||
return;
|
||||
}
|
||||
}, this);
|
||||
label = label || this.translate(this.presetName, 'presetFilters', this.scope);
|
||||
|
||||
var barContentHtml = '<a href="javascript:" class="label label-'+style+'" data-action="showFiltersPanel">' + label + '</a>';
|
||||
if (id) {
|
||||
barContentHtml += ' <a href="javascript:" title="'+this.translate('Remove')+'" class="small" data-action="removePreset" data-id="'+id+'"><span class="glyphicon glyphicon-remove"></span></a>';
|
||||
}
|
||||
|
||||
this.$advancedFiltersBar.removeClass('hidden').html(barContentHtml);
|
||||
} else {
|
||||
this.$advancedFiltersPanel.removeClass('hidden');
|
||||
|
||||
if (Object.keys(this.advanced).length !== 0) {
|
||||
if (!this.disableSavePreset) {
|
||||
var barHtml = '<a href="javascript:" class="small" data-action="savePreset">' + this.translate('Save Filters') + '</a>';
|
||||
this.$advancedFiltersBar.removeClass('hidden').html(barHtml);
|
||||
} else {
|
||||
this.$advancedFiltersBar.addClass('hidden').empty();
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
this.$advancedFiltersBar.addClass('hidden').empty();
|
||||
}
|
||||
}
|
||||
|
||||
name = name || '';
|
||||
|
||||
this.$el.find('ul.basic-filter-menu a.preset[data-name="'+name+'"]').prepend('<span class="glyphicon glyphicon-ok pull-right"></span>');
|
||||
},
|
||||
|
||||
search: function () {
|
||||
@@ -175,21 +392,42 @@ Espo.define('Views.Record.Search', 'View', function (Dep) {
|
||||
this.collection.fetch();
|
||||
},
|
||||
|
||||
getSearchData: function () {
|
||||
|
||||
getPresetData: function () {
|
||||
var data = {};
|
||||
this.presetFilters.forEach(function (item) {
|
||||
if (item.name == this.presetName) {
|
||||
data = _.clone(item.data);
|
||||
return;
|
||||
}
|
||||
}, this);
|
||||
return data;
|
||||
},
|
||||
|
||||
loadSearchData: function () {
|
||||
var searchData = this.searchManager.get();
|
||||
this.filter = searchData.filter;
|
||||
this.basic = _.clone(searchData.basic);
|
||||
this.advanced = _.clone(searchData.advanced);
|
||||
|
||||
if ('presetName' in searchData) {
|
||||
this.presetName = searchData.presetName;
|
||||
}
|
||||
|
||||
if (this.presetName) {
|
||||
this.advanced = this.getPresetData();
|
||||
} else {
|
||||
this.advanced = _.clone(searchData.advanced);
|
||||
}
|
||||
this.bool = searchData.bool;
|
||||
},
|
||||
|
||||
createFilter: function (name, params, callback) {
|
||||
createFilter: function (name, params, callback, noRender) {
|
||||
params = params || {};
|
||||
|
||||
var rendered = false;
|
||||
if (this.isRendered()) {
|
||||
rendered = true;
|
||||
Espo.Ui.notify('Loading...');
|
||||
this.$advancedFiltersPanel.append('<div class="filter filter-' + name + ' col-sm-4 col-md-3" />');
|
||||
}
|
||||
|
||||
this.createView('filter-' + name, 'Search.Filter', {
|
||||
@@ -198,18 +436,14 @@ Espo.define('Views.Record.Search', 'View', function (Dep) {
|
||||
params: params,
|
||||
el: this.options.el + ' .filter-' + name
|
||||
}, function (view) {
|
||||
if (typeof callback != 'undefined') {
|
||||
if (typeof callback === 'function') {
|
||||
view.once('after:render', function () {
|
||||
callback();
|
||||
});
|
||||
}
|
||||
if (rendered) {
|
||||
if (rendered && !noRender) {
|
||||
view.render();
|
||||
Espo.Ui.notify(false);
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
@@ -240,6 +474,7 @@ Espo.define('Views.Record.Search', 'View', function (Dep) {
|
||||
basic: this.basic,
|
||||
advanced: this.advanced,
|
||||
bool: this.bool,
|
||||
presetName: this.presetName
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
+1
-2
@@ -13,8 +13,7 @@
|
||||
<script type="text/javascript" src="client/lib/base64.js"></script>
|
||||
<script type="text/javascript" src="client/lib/jquery-ui.min.js"></script>
|
||||
<script type="text/javascript" src="client/lib/moment.min.js"></script>
|
||||
<script type="text/javascript" src="client/lib/moment-timezone.min.js"></script>
|
||||
<script type="text/javascript" src="client/lib/moment-timezone-data.js"></script>
|
||||
<script type="text/javascript" src="client/lib/moment-timezone-with-data.min.js"></script>
|
||||
<script type="text/javascript" src="client/lib/jquery.timepicker.min.js"></script>
|
||||
<script type="text/javascript" src="client/lib/jquery.autocomplete.js"></script>
|
||||
<script type="text/javascript" src="client/lib/bootstrap.min.js"></script>
|
||||
|
||||
@@ -189,7 +189,7 @@ ul.dropdown-menu > li.checkbox {
|
||||
}
|
||||
|
||||
.field .link-container {
|
||||
margin-bottom: 0px;
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
|
||||
.field .link-container .list-group-item > div {
|
||||
@@ -211,11 +211,11 @@ ul.dropdown-menu > li.checkbox {
|
||||
}
|
||||
|
||||
.filter .field .link-container .list-group-item a {
|
||||
margin-top: 2px;
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.field .link-container .list-group-item a {
|
||||
margin-top: 2px;
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.field .link-container .list-group-item.link-with-role a {
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "espocrm",
|
||||
"version": "2.0.0",
|
||||
"version": "2.1.0",
|
||||
"description": "",
|
||||
"main": "index.php",
|
||||
"repository": "",
|
||||
|
||||
Reference in New Issue
Block a user