Compare commits

...

34 Commits

Author SHA1 Message Date
Yuri Kuznetsov 97c951484d fix add dashlet 2014-07-25 15:18:37 +03:00
Yuri Kuznetsov 2215f15aaf lang 2014-07-25 12:58:26 +03:00
Yuri Kuznetsov 6429823cc3 version 2014-07-25 11:28:50 +03:00
Yuri Kuznetsov 2a71bb2ea1 duplicate 2014-07-25 11:24:20 +03:00
Yuri Kuznetsov 1674c2221e disableExport 2014-07-24 18:48:03 +03:00
Yuri Kuznetsov e3b448f520 style preset 2014-07-24 17:48:49 +03:00
Yuri Kuznetsov a04c973817 css 2014-07-24 16:21:17 +03:00
Yuri Kuznetsov 01c9939215 fix css 2014-07-24 16:08:32 +03:00
Yuri Kuznetsov 2d57824cb0 fix css 2014-07-24 15:33:23 +03:00
Yuri Kuznetsov 878385f14b fix import UI 2014-07-24 15:27:27 +03:00
Yuri Kuznetsov 8b82058d06 fix import 2014-07-24 15:20:17 +03:00
Yuri Kuznetsov 68e05f0935 datetime edit more nice 2014-07-24 15:00:49 +03:00
Yuri Kuznetsov cb7492c042 email to case: dont copy email body 2014-07-24 14:41:05 +03:00
Yuri Kuznetsov beb811755f improve bool filters ui 2014-07-24 11:21:21 +03:00
Yuri Kuznetsov 7c7fc4ee0f impreove preset search 2014-07-24 11:01:47 +03:00
Yuri Kuznetsov aeef25f7ec fix preset ok 2014-07-23 17:19:35 +03:00
Yuri Kuznetsov 6856ff97b1 fix previous commit 2014-07-23 17:11:09 +03:00
Yuri Kuznetsov 10f6f7600c create notifications via cron 2014-07-23 17:04:03 +03:00
Yuri Kuznetsov a0b678a3d9 remove some files from grunt uglify 2014-07-23 16:17:30 +03:00
Yuri Kuznetsov f78e558a33 fix search 2014-07-23 15:56:06 +03:00
Yuri Kuznetsov e205ef3370 improve fields tpls 2014-07-23 15:38:38 +03:00
Yuri Kuznetsov 8b6e4d469e onlyMy for meetings and calls 2014-07-23 12:49:10 +03:00
Yuri Kuznetsov a63ec2710b fix filter 2014-07-23 12:20:16 +03:00
Yuri Kuznetsov 6895fac58c date filters 2014-07-23 11:57:48 +03:00
Yuri Kuznetsov ea3c8f2d35 fix preset 2014-07-23 09:56:42 +03:00
Yuri Kuznetsov 0f2c050537 lang 2014-07-22 18:22:38 +03:00
Yuri Kuznetsov 2cd8866c73 Merge branch 'stable' 2014-07-22 18:07:44 +03:00
Yuri Kuznetsov 86a5e19b55 version 2014-07-22 17:46:38 +03:00
Yuri Kuznetsov 61f435812d fix timezones 2014-07-22 17:42:55 +03:00
Yuri Kuznetsov 217d0f6540 preset filters 4 2014-07-22 17:37:21 +03:00
Yuri Kuznetsov f0e7a8f029 preset filters 2 2014-07-22 16:25:56 +03:00
Yuri Kuznetsov d2954483d9 preset filters 2014-07-21 16:45:18 +03:00
Yuri Kuznetsov 999b88cba1 Merge branch 'stable' 2014-07-18 17:04:59 +03:00
Yuri Kuznetsov 717df30063 Merge branch 'release/2.0' into stable 2014-07-18 17:04:35 +03:00
68 changed files with 1029 additions and 5555 deletions
+1 -5
View File
@@ -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',
+4 -2
View File
@@ -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();
+6 -1
View File
@@ -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(
+15 -1
View File
@@ -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"]
}
}
@@ -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)
);
}
}
@@ -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
}
}
}
+5 -3
View File
@@ -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();
+1 -1
View File
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
View File
@@ -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>&nbsp;{{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>
+9 -2
View File
@@ -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);
+47 -26
View File
@@ -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;
},
});
+1
View File
@@ -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
+1
View File
@@ -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});
},
+14 -1
View File
@@ -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});
},
});
});
+49 -32
View File
@@ -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;
+7 -31
View File
@@ -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 () {
+1 -1
View File
@@ -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 () {
+29 -22
View File
@@ -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 = [];
+271 -36
View File
@@ -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
View File
@@ -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>
+3 -3
View File
@@ -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
View File
@@ -1,6 +1,6 @@
{
"name": "espocrm",
"version": "2.0.0",
"version": "2.1.0",
"description": "",
"main": "index.php",
"repository": "",