Compare commits

...

219 Commits
3.6.2 ... 3.7.4

Author SHA1 Message Date
yuri
0a0f8da2f4 fix auth for 5.5 2015-10-22 10:41:07 +03:00
yuri
bb5d5a0ccf text filter default search by email address 2015-10-21 17:14:57 +03:00
yuri
1bd5eac103 ver 2015-10-21 10:09:21 +03:00
yuri
61957e628b fix optOut 2015-10-21 10:07:15 +03:00
yuri
c5296bf906 typo 2015-10-20 17:52:26 +03:00
yuri
a16919f10b fix typo 2015-10-20 17:38:04 +03:00
yuri
7050757e4e fix campaign type 2015-10-20 17:22:52 +03:00
yuri
d27e9c91e7 import: dont import if no fields 2015-10-20 15:47:42 +03:00
yuri
5e1c7bce33 leads actual filter 2015-10-20 15:38:25 +03:00
yuri
a23ac54da0 email template: translate enum 2015-10-20 15:32:01 +03:00
yuri
260c050f36 fix email address 2 2015-10-20 13:01:14 +03:00
yuri
fc5c07927f fix email address case 2015-10-20 12:57:42 +03:00
yuri
d321f0d701 email invitees duplicates 2015-10-20 12:32:50 +03:00
yuri
95cbc138f7 meeting/call/task and converted leads 2015-10-20 12:27:05 +03:00
yuri
395063ea35 order global search 2015-10-20 11:42:16 +03:00
yuri
835f95eb52 email template list layout 2015-10-20 11:29:05 +03:00
yuri
c0f6c91a0e fix email draft 2015-10-20 11:26:09 +03:00
yuri
b410d0e66f todays task 2015-10-19 17:43:32 +03:00
yuri
d04f72cfd8 vers 2015-10-19 16:14:44 +03:00
yuri
a9203b8e22 fix global search 2015-10-19 16:13:52 +03:00
yuri
e89c7c9328 email filter improvement 2015-10-19 15:36:22 +03:00
yuri
7808865080 import csv label 2015-10-19 15:27:09 +03:00
yuri
404e2de3b6 fix email isRead 2015-10-16 17:19:51 +03:00
yuri
f02dabc540 industry list 2015-10-16 13:50:42 +03:00
yuri
c29da46a97 fix client side access denied 2015-10-16 13:07:37 +03:00
yuri
b7702d2418 order fields in entity manager 2015-10-16 12:40:15 +03:00
yuri
2bc56db4c6 fix edit view link to detail 2015-10-16 12:33:59 +03:00
yuri
dd0ef5455e fix 2015-10-16 10:40:45 +03:00
yuri
901bee6d3f fix campaign 2015-10-15 11:52:25 +03:00
yuri
72a0415207 fix orderBy by alias 2015-10-14 13:03:08 +03:00
yuri
de69652137 fix id in model.set 2015-10-14 11:35:21 +03:00
yuri
1cd2d6b35e target lists field not importable 2015-10-12 10:43:50 +03:00
yuri
9888ec8a6f fix global searct 2015-10-12 10:30:16 +03:00
yuri
ea5321577a fix target list import 2015-10-08 17:55:48 +03:00
yuri
23c772e029 fix config 2015-10-07 16:53:22 +03:00
yuri
e67387d9ea additionalSelectColumns 2015-10-07 15:00:03 +03:00
yuri
2156094407 cleanup 2015-10-07 12:21:59 +03:00
yuri
03d5bf1ef2 tree impr 2015-10-07 12:18:01 +03:00
yuri
42f57409c8 list tree imp 2015-10-07 12:08:11 +03:00
yuri
69d7ce408d fix preferences layout 2015-10-07 11:24:52 +03:00
yuri
15b3c0440c lead converted panel change 2015-10-07 10:56:56 +03:00
yuri
e1f0715c4d email import: relate to converted entity if lead is converted 2015-10-07 10:35:07 +03:00
yuri
15aec72e97 fix double quick view 2015-10-07 10:33:47 +03:00
yuri
66eade5db7 v 2015-10-07 10:20:37 +03:00
yuri
3d89e25349 label 2015-10-05 14:36:52 +03:00
yuri
7c890aa83a fix empty tablist 2015-10-02 17:23:02 +03:00
yuri
62a29c86ab fix record controller 2015-10-02 15:27:34 +03:00
yuri
bd57b6bb8e detailLayout param 2 2015-10-02 15:10:13 +03:00
yuri
9ab1d16ea0 detailLayout param 2015-10-02 15:09:39 +03:00
yuri
435aa28ca2 iconHtml for menu items 2015-10-01 13:11:05 +03:00
yuri
4bd375f154 fix notice 2015-09-30 17:22:15 +03:00
yuri
d6521f9176 de_DE 2015-09-30 15:07:20 +03:00
yuri
f8e0e4955a fix meeting onlymy 2015-09-30 15:06:34 +03:00
yuri
7957359c15 fix prev 2015-09-30 11:41:53 +03:00
yuri
bdc3731b9a fix select manager 2015-09-30 11:39:11 +03:00
yuri
6b8af1220b opp lost filter 2015-09-30 10:24:39 +03:00
yuri
58fffeaa84 fix typo 2015-09-29 17:56:35 +03:00
yuri
5806785286 fix email notification dups 2015-09-29 11:56:15 +03:00
yuri
2f2d9b5f6f fix roles 2015-09-29 10:27:02 +03:00
yuri
5af20c1ee0 merge targetLists 2015-09-28 16:41:28 +03:00
yuri
7620be3b40 ve 2015-09-28 14:26:20 +03:00
yuri
86f591b409 trim emails when search 2015-09-28 11:18:12 +03:00
yuri
533dfe24d0 default dashlet options 2015-09-28 11:08:13 +03:00
yuri
10c51dc46e target lists panels 2015-09-28 10:55:58 +03:00
yuri
a9d24a1e09 fix mass update 2015-09-28 10:45:30 +03:00
yuri
0cfe093701 fix german language 2015-09-28 10:29:50 +03:00
yuri
11ec99fe6a duplicate email template 2015-09-28 10:26:59 +03:00
yuri
b07e5a6da2 campaign list layout 2015-09-28 10:24:10 +03:00
yuri
151738f896 fix tracking img 2015-09-28 10:23:20 +03:00
yuri
075a370e7a fix log opened 2015-09-25 16:46:55 +03:00
yuri
e7aa147b90 fix import 2015-09-25 15:41:38 +03:00
yuri
2e988fab6d css fiux 2015-09-25 14:51:56 +03:00
yuri
b38e03a53a cleanup 2015-09-25 11:32:17 +03:00
yuri
68e01f9e6f fix backbone id 2015-09-25 11:25:24 +03:00
yuri
4d32955e28 wysywyg field 2015-09-24 17:30:39 +03:00
yuri
13a2aa7f03 decrease wysywyg height 2015-09-24 17:17:17 +03:00
yuri
ac91087b82 header menu improvements 2015-09-24 12:55:47 +03:00
yuri
57a4a30402 updade history panel on activities changes 2015-09-24 12:26:27 +03:00
yuri
dd3ee8d69a update backbone and improve list view listening 2015-09-24 12:05:22 +03:00
yuri
accbc9582e add email settings 2015-09-24 10:47:26 +03:00
yuri
4a2845d7a5 showNewRecords stream panel 2015-09-23 15:41:13 +03:00
yuri
9b2ae8992f stream fetch new 2015-09-23 15:26:20 +03:00
yuri
1bb0038be8 dont reset filters if primary filter changed 2015-09-23 11:32:38 +03:00
yuri
6c4e73f1c4 created by field in export 2015-09-23 09:53:02 +03:00
yuri
72f5dbea87 created fields in export 2015-09-23 09:52:10 +03:00
yuri
aeb8411695 de_DE 2015-09-23 09:49:47 +03:00
yuri
df6db9bd47 changes in mass email 2015-09-22 16:26:13 +03:00
yuri
ab17044da9 grunt 2015-09-22 15:41:38 +03:00
yuri
7356bd98ab fix filter 2015-09-22 13:23:44 +03:00
yuri
a631e52f63 fix usubscribe email link 2015-09-22 12:50:23 +03:00
yuri
b925100df7 fix importer 2015-09-22 12:49:47 +03:00
yuri
eeba65440c email template info 2015-09-22 11:46:02 +03:00
yuri
95f72933a4 fix header menu 2015-09-22 11:28:20 +03:00
yuri
4b03e74662 update zend 2015-09-22 10:27:20 +03:00
yuri
b316eeb3eb fix attachment download 2015-09-21 18:44:03 +03:00
yuri
53e6904ce1 fix campaign 2015-09-21 12:38:26 +03:00
yuri
e2b9647cf1 not render list after sync if modal is opened 2015-09-21 12:02:50 +03:00
yuri
1145406531 remove queue after mass email remove 2015-09-21 11:41:14 +03:00
yuri
9ce65c9a2b fix email account 2015-09-18 17:20:43 +03:00
yuri
2ec1db8379 refactor select manager 2 2015-09-18 17:19:23 +03:00
yuri
8074baef56 activities dashlet 2015-09-18 15:44:19 +03:00
yuri
4584211c04 select manager refactoring 2015-09-18 13:51:00 +03:00
yuri
290099641b dont show declined meetings/calls in only my 2015-09-18 12:00:49 +03:00
yuri
d0c3044e8e dont show declined in calendar 2015-09-18 11:58:44 +03:00
yuri
4e983ff440 user calendar 2015-09-17 17:38:48 +03:00
yuri
9aba8b0d8f format address in js 2015-09-17 15:26:08 +03:00
yuri
ee6ef6694f custom tab list 2015-09-17 15:01:11 +03:00
yuri
8330137c4e fixes with assignment in mass update and autopopulate 2015-09-17 13:18:05 +03:00
yuri
bd420ad963 clear roles cache on team change 2015-09-17 12:18:12 +03:00
yuri
e03bedf439 job improvement 2015-09-17 11:46:05 +03:00
yuri
5cba246bb2 improve link 2 2015-09-17 11:21:26 +03:00
yuri
3490c16285 link select improvements 2015-09-17 11:19:33 +03:00
yuri
957fda9e8e clear cache if user changed roles 2015-09-17 11:00:21 +03:00
yuri
b940adc341 clear roles cache after role change 2015-09-17 10:46:18 +03:00
yuri
f44a093ea9 acl for activities 2015-09-16 15:53:21 +03:00
yuri
0b6dc1ff5f acl attendees 2015-09-16 15:41:28 +03:00
yuri
907eb5feb5 opted out remove 2015-09-16 15:17:14 +03:00
yuri
17a339c318 target list no remove 2015-09-16 14:24:49 +03:00
yuri
3bea161618 tooltip 2015-09-16 12:50:57 +03:00
yuri
b39a4a3624 track opened 2015-09-16 12:46:28 +03:00
yuri
2530d13d25 mass email test send 2015-09-16 12:26:19 +03:00
yuri
67e152c923 mass email dev 2015-09-15 15:38:58 +03:00
yuri
4934d4ad95 cleanup 2015-09-15 15:17:34 +03:00
yuri
9a072949fd lead listSmall change 2015-09-15 15:15:55 +03:00
yuri
6e5f8676a1 campaign dev 2015-09-15 14:59:03 +03:00
yuri
5a52484295 panel filters 2015-09-15 12:41:30 +03:00
yuri
a3338b0db7 mass email dev 2015-09-15 11:40:27 +03:00
yuri
d212d4a193 dev 2015-09-11 17:45:49 +03:00
yuri
e3f9a0f607 dev 2015-09-11 17:02:01 +03:00
yuri
91b7635d26 dev 2015-09-11 16:47:50 +03:00
yuri
532581cb50 small change 2015-09-11 11:47:19 +03:00
yuri
9b4a3a8ca9 fix lang 2015-09-11 11:43:01 +03:00
yuri
564c397258 Merge branch 'hotfix/3.6.3' 2015-09-11 11:27:45 +03:00
yuri
22e81898c0 fix external account 2015-09-11 11:27:30 +03:00
yuri
ec863fe09a dev 2015-09-11 11:25:11 +03:00
yuri
cb9a1d31d8 email account readOnl 2015-09-11 11:05:00 +03:00
yuri
0e8d0495a9 dev 2015-09-10 17:58:03 +03:00
yuri
fa19e5ebce dev 2015-09-10 17:27:27 +03:00
yuri
b67e8960fa Merge branch 'hotfix/3.6.3' 2015-09-10 11:02:06 +03:00
yuri
6efd697a1e fix email import counter 2015-09-10 10:57:48 +03:00
yuri
a9379dcf0a Merge branch 'hotfix/3.6.3' 2015-09-10 10:41:44 +03:00
yuri
9798f399bd fix import time 2015-09-10 10:41:37 +03:00
yuri
a520510f9b Merge branch 'hotfix/3.6.3' 2015-09-10 10:36:21 +03:00
yuri
fbfe82870f fix import dates 2015-09-10 10:36:00 +03:00
yuri
3b668f4dbd dev 2015-09-10 10:22:19 +03:00
yuri
62eb54d853 dev 2015-09-09 17:03:37 +03:00
yuri
81335454fd fix insert template 2015-09-09 17:03:32 +03:00
yuri
1b33221fc1 autoincrement field change 2015-09-09 09:49:01 +03:00
yuri
152c96838f detailSmall layouts changes 2015-09-09 09:42:18 +03:00
yuri
f78af9b204 dev 2015-09-08 17:51:00 +03:00
yuri
302b9e759f campaign log acl 2015-09-08 17:29:00 +03:00
yuri
dab7e329df dev 2015-09-08 17:22:36 +03:00
yuri
6a93fe77ca dev 2015-09-08 15:56:00 +03:00
yuri
0e21a9bf91 dev 2015-09-08 15:18:44 +03:00
yuri
723f504a56 dev 2015-09-08 12:21:49 +03:00
yuri
121b324532 add activities and tasks actions 2015-09-07 11:21:56 +03:00
yuri
c54a4eee95 varchar trim and system emails 2015-09-04 17:17:41 +03:00
yuri
2a03797436 return list fix 2015-09-04 16:11:49 +03:00
yuri
55dc4d1e77 loader change 2015-09-04 15:14:39 +03:00
yuri
cb51feef48 fix email importer 2015-09-04 13:24:28 +03:00
yuri
e75c957aad Merge branch 'hotfix/3.6.3' 2015-09-04 12:42:27 +03:00
yuri
e08624fae3 fix preferences controler 2015-09-04 12:42:09 +03:00
yuri
e8ac486d4a default select filters 2015-09-04 12:34:30 +03:00
yuri
1ded65b85d fix selectRelatedFilters 2015-09-04 12:14:23 +03:00
yuri
35b2f0e57e fix removeButton 2015-09-04 11:57:58 +03:00
yuri
d6ef8e1b33 fix list view return 2015-09-04 11:36:19 +03:00
yuri
118025f646 fix currency 2015-09-03 16:31:41 +03:00
yuri
0888752e2a range fields 2015-09-03 15:59:13 +03:00
yuri
df5177a565 activities panel change 2015-09-03 12:15:45 +03:00
yuri
3f03770b6e refactor activities service 2015-09-03 11:59:19 +03:00
yuri
27ebac0722 app status styles 2015-09-03 11:25:09 +03:00
yuri
e5b46bc547 email replyTo 2015-09-03 11:05:20 +03:00
yuri
468a236ce1 fix edit layout column 2015-09-02 15:40:18 +03:00
yuri
6fda3dd147 mass update layout fix 2015-09-02 15:17:09 +03:00
yuri
9f0705c82d pass event obj to actions 2015-09-02 14:02:48 +03:00
yuri
0ab0de2371 fix linkMultipeCategoryTree 2015-09-02 13:18:47 +03:00
yuri
ba7d79ef39 Merge branch 'master' of https://github.com/espocrm/espocrm 2015-09-02 11:56:39 +03:00
yuri
c0e51d85b5 vertical theme width 2015-09-02 11:56:30 +03:00
yuri
80334d59ff back route and isReturn 2015-09-02 11:20:00 +03:00
yuri
759ad1a8d3 rename scheduled job 2015-09-02 10:05:44 +03:00
yuri
7c569d3880 fix global search padding 2015-09-02 10:03:14 +03:00
yuri
7b21b82a2e email to case fix 2015-09-02 10:00:15 +03:00
yuri
5e86c5da8c list view return imporovement 2015-09-02 09:40:17 +03:00
Yuri Kuznetsov
9998f82596 Update README.md 2015-09-01 21:42:29 +03:00
Yuri Kuznetsov
9cb3f5fb3a Update README.md 2015-09-01 21:40:40 +03:00
yuri
dcce3335cd returnDispatchParams 2015-09-01 16:30:41 +03:00
yuri
5f9b4d99f3 list view stored 2015-09-01 16:02:39 +03:00
yuri
a6f4111383 automatically accept meeting/call for current user 2015-09-01 12:58:37 +03:00
yuri
569180ed86 email important 2015-09-01 12:31:08 +03:00
yuri
f9cc21a06d import update 2015-08-31 15:19:09 +03:00
yuri
e5ad31a965 prevent loop 2015-08-31 12:24:46 +03:00
yuri
5252d63260 lastXDays and nextXDays filters 2015-08-31 12:10:44 +03:00
yuri
a9fea0b9f5 change default jobs 2015-08-31 11:27:45 +03:00
yuri
cee91eb6af email replied 2015-08-31 11:23:39 +03:00
yuri
c61765cc31 Merge branch 'hotfix/3.6.3' 2015-08-28 15:50:28 +03:00
yuri
03d3884bf4 follow fix 2015-08-28 15:50:09 +03:00
yuri
cb01caddd3 Merge branch 'hotfix/3.6.2' 2015-08-28 14:30:56 +03:00
yuri
45d2cec423 Merge branch 'hotfix/3.6.2' 2015-08-28 14:24:41 +03:00
yuri
6d86c65ea6 Merge branch 'hotfix/3.6.2' 2015-08-27 17:13:01 +03:00
yuri
2571f967b7 displayTotalCount option 2015-08-27 16:04:21 +03:00
yuri
a93d5a0678 change email filter descriptipon 2015-08-27 16:00:21 +03:00
yuri
be1647548a email filters final 2015-08-27 15:50:49 +03:00
yuri
91571fa47c fix readOnly and create 2015-08-27 15:43:05 +03:00
yuri
cf3220581f Merge branch 'hotfix/3.6.2' 2015-08-27 15:29:37 +03:00
yuri
99b4f7eb5c email filters dev 2015-08-27 15:28:36 +03:00
yuri
803111b12f Merge branch 'hotfix/3.6.2' 2015-08-27 12:17:35 +03:00
yuri
2ceb686198 Merge branch 'hotfix/3.6.2' 2015-08-27 10:29:33 +03:00
yuri
cefe304c2f filters 2 2015-08-26 16:30:45 +03:00
yuri
cbe4f0009b Merge branch 'hotfix/3.6.2' 2015-08-26 12:09:12 +03:00
yuri
2d1aa77ddc email filter 2 2015-08-25 16:16:12 +03:00
yuri
5357061d52 campaign lead created change 2015-08-25 10:30:52 +03:00
yuri
13b7db63d0 add createdBy filters 2015-08-25 09:40:22 +03:00
yuri
62b0020c67 email filters dev 2015-08-24 17:33:43 +03:00
yuri
4512dfd420 refactor acl and assigmnent permissions, email filter entity 2015-08-24 16:50:54 +03:00
yuri
b1303cb50e rename hasAttachments 2015-08-24 15:07:47 +03:00
yuri
3facbc782b Merge branch 'hotfix/3.6.2' 2015-08-24 14:50:05 +03:00
yuri
dae249e4b2 Merge branch 'hotfix/3.6.2' 2015-08-24 12:10:29 +03:00
yuri
2497ab6a5e hasAttachments icon 2015-08-24 12:03:40 +03:00
yuri
2d00b27fb6 translatedOptions param 2015-08-24 11:48:45 +03:00
403 changed files with 8419 additions and 1260 deletions

View File

@@ -40,20 +40,20 @@ You need to have nodejs and Grunt CLI installed.
The build will be created in the `build` directory.
### How to make translation
### How to make a translation
Build po file with command:
`node po.js en_EN`
(specify needed language instead of en_EN)
After that tranlate the generated po file.
After that translate the generated po file.
Build json files from the translated po file:
1. Put your po file espocrm-en_EN.po to the `build` directory
1. Put your po file espocrm-en_EN.po into `build` directory
2. Run `node lang.js en_EN`
The files will be created in build directory.
Json files will be created in build directory grouped by folders.
### License

View File

@@ -0,0 +1,46 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
namespace Espo\Acl;
use \Espo\Entities\User;
use \Espo\ORM\Entity;
class EmailFilter extends \Espo\Core\Acl\Base
{
public function checkIsOwner(User $user, Entity $entity)
{
if ($entity->has('parentId') && $entity->has('parentType')) {
$parentType = $entity->get('parentType');
$parentId = $entity->get('parentId');
if (!$parentType || !$parentId) return;
$parent = $this->getEntityManager()->getEntity($parentType, $parentId);
if ($parent && $parent->has('assignedUserId') && $parent->get('assignedUserId') === $user->id) {
return true;
}
}
return;
}
}

View File

@@ -63,25 +63,65 @@ class Email extends \Espo\Core\Controllers\Record
return $this->getRecordService()->sendTestEmail($data);
}
public function actionMarkAsRead($params, $data, $request)
public function postActionMarkAsRead($params, $data, $request)
{
if (!$request->isPost()) {
throw new BadRequest();
if (!empty($data['ids'])) {
$ids = $data['ids'];
} else {
if (!empty($data['id'])) {
$ids = [$data['id']];
} else {
throw new BadRequest();
}
}
if (empty($data['ids']) || !is_array($data['ids'])) {
throw new BadRequest();
}
$ids = $data['ids'];
return $this->getRecordService()->markAsReadByIds($ids);
}
public function actionMarkAllAsRead($params, $data, $request)
public function postActionMarkAsNotRead($params, $data, $request)
{
if (!$request->isPost()) {
throw new BadRequest();
if (!empty($data['ids'])) {
$ids = $data['ids'];
} else {
if (!empty($data['id'])) {
$ids = [$data['id']];
} else {
throw new BadRequest();
}
}
return $this->getRecordService()->markAsNotReadByIds($ids);
}
public function postActionMarkAllAsRead($params, $data, $request)
{
return $this->getRecordService()->markAllAsRead();
}
public function postActionMarkAsImportant($params, $data, $request)
{
if (!empty($data['ids'])) {
$ids = $data['ids'];
} else {
if (!empty($data['id'])) {
$ids = [$data['id']];
} else {
throw new BadRequest();
}
}
return $this->getRecordService()->markAsImportantByIds($ids);
}
public function postActionMarkAsNotImportant($params, $data, $request)
{
if (!empty($data['ids'])) {
$ids = $data['ids'];
} else {
if (!empty($data['id'])) {
$ids = [$data['id']];
} else {
throw new BadRequest();
}
}
return $this->getRecordService()->markAsNotImportantByIds($ids);
}
}

View File

@@ -0,0 +1,29 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
namespace Espo\Controllers;
class EmailFilter extends \Espo\Core\Controllers\Record
{
}

View File

@@ -85,7 +85,7 @@ class ExternalAccount extends \Espo\Core\Controllers\Record
public function actionPatch($params, $data, $request)
{
if (!$request->isPost() && !$request->isPatch()) {
if (!$request->isPut() && !$request->isPost() && !$request->isPatch()) {
throw new BadRequest();
}

View File

@@ -29,7 +29,7 @@ class GlobalSearch extends \Espo\Core\Controllers\Base
{
public function actionSearch($params, $data, $request)
{
$query = $params['query'];
$query = $request->get('q');
$offset = intval($request->get('offset'));
$maxSize = intval($request->get('maxSize'));

View File

@@ -133,6 +133,10 @@ class Import extends \Espo\Core\Controllers\Record
'action' => $data['action'],
);
if (array_key_exists('updateBy', $data)) {
$importParams['updateBy'] = $data['updateBy'];
}
$attachmentId = $data['attachmentId'];
if (!$this->getAcl()->check($data['entityType'], 'edit')) {

View File

@@ -77,7 +77,7 @@ class Preferences extends \Espo\Core\Controllers\Base
$userId = $params['id'];
$this->handleUserAccess($userId);
if (!$request->isPost() && !$request->isPatch()) {
if (!$request->isPost() && !$request->isPatch() && !$request->isPut()) {
throw new BadRequest();
}

View File

@@ -18,12 +18,10 @@
*
* 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\Controllers;
class Role extends \Espo\Core\Controllers\Record
{
}

View File

@@ -81,7 +81,7 @@ class Base implements Injectable
return $this->getInjection('aclManager');
}
public function checkReadOnlyTeam(User $user, $scope, $data)
public function checkReadOnlyTeam(User $user, $data)
{
if (empty($data) || !is_array($data) || !isset($data['read'])) {
return false;
@@ -89,7 +89,7 @@ class Base implements Injectable
return $data['read'] === 'team';
}
public function checkReadOnlyOwn(User $user, $scope, $data)
public function checkReadOnlyOwn(User $user, $data)
{
if (empty($data) || !is_array($data) || !isset($data['read'])) {
return false;
@@ -160,10 +160,11 @@ class Base implements Injectable
if ($user->id === $entity->get('assignedUserId')) {
return true;
}
}
if ($entity->has('createdById')) {
if ($user->id === $entity->get('createdById')) {
return true;
} else {
if ($entity->has('createdById')) {
if ($user->id === $entity->get('createdById')) {
return true;
}
}
}
return false;

View File

@@ -81,7 +81,12 @@ class Table
public function getScopeData($scope)
{
if (array_key_exists($scope, $this->data['table'])) {
return $this->data['table'][$scope];
$data = $this->data['table'][$scope];
if (is_string($data)) {
$data = $this->getScopeData($data);
return $data;
}
return $data;
}
return null;
}

View File

@@ -124,7 +124,7 @@ class AclManager
return false;
}
$data = $this->getTable($user)->getScopeData($scope);
return $this->getImplementation($scope)->checkReadOnlyTeam($user, $scope, $data);
return $this->getImplementation($scope)->checkReadOnlyTeam($user, $data);
}
public function checkReadOnlyOwn(User $user, $scope)
@@ -133,7 +133,7 @@ class AclManager
return false;
}
$data = $this->getTable($user)->getScopeData($scope);
return $this->getImplementation($scope)->checkReadOnlyOwn($user, $scope, $data);
return $this->getImplementation($scope)->checkReadOnlyOwn($user, $data);
}
public function check(User $user, $subject, $action = null, $isOwner = null, $inTeam = null)

View File

@@ -99,7 +99,7 @@ class ControllerManager
}
if (!method_exists($controller, $primaryActionMethodName)) {
throw new NotFound("Action '$actionName' (".$request->getMethod().") does not exist in controller '$controller'");
throw new NotFound("Action '$actionName' (".$request->getMethod().") does not exist in controller '$controllerName'");
}
if (method_exists($controller, $beforeMethodName)) {

View File

@@ -124,6 +124,9 @@ class Record extends Base
$asc = $request->get('asc') === 'true';
$sortBy = $request->get('sortBy');
$q = $request->get('q');
$primaryFilter = $request->get('primaryFilter');
$textFilter = $request->get('textFilter');
$boolFilterList = $request->get('boolFilterList');
if (empty($maxSize)) {
$maxSize = self::MAX_SIZE_LIMIT;
@@ -132,14 +135,23 @@ class Record extends Base
throw new Forbidden("Max should should not exceed " . self::MAX_SIZE_LIMIT . ". Use pagination (offset, limit).");
}
$result = $this->getRecordService()->findEntities(array(
$params = array(
'where' => $where,
'offset' => $offset,
'maxSize' => $maxSize,
'asc' => $asc,
'sortBy' => $sortBy,
'q' => $q,
));
'textFilter' => $textFilter
);
if ($request->get('primaryFilter')) {
$params['primaryFilter'] = $request->get('primaryFilter');
}
if ($request->get('boolFilterList')) {
$params['boolFilterList'] = $request->get('boolFilterList');
}
$result = $this->getRecordService()->findEntities($params);
return array(
'total' => $result['total'],
@@ -158,6 +170,7 @@ class Record extends Base
$asc = $request->get('asc') === 'true';
$sortBy = $request->get('sortBy');
$q = $request->get('q');
$textFilter = $request->get('textFilter');
if (empty($maxSize)) {
$maxSize = self::MAX_SIZE_LIMIT;
@@ -166,15 +179,23 @@ class Record extends Base
throw new Forbidden();
}
$result = $this->getRecordService()->findLinkedEntities($id, $link, array(
$params = array(
'where' => $where,
'offset' => $offset,
'maxSize' => $maxSize,
'asc' => $asc,
'sortBy' => $sortBy,
'q' => $q,
));
'textFilter' => $textFilter
);
if ($request->get('primaryFilter')) {
$params['primaryFilter'] = $request->get('primaryFilter');
}
if ($request->get('boolFilterList')) {
$params['boolFilterList'] = $request->get('boolFilterList');
}
$result = $this->getRecordService()->findLinkedEntities($id, $link, $params);
return array(
'total' => $result['total'],

View File

@@ -82,6 +82,11 @@ abstract class Base
return $this->getContainer()->get('fileManager');
}
protected function getLanguage()
{
return $this->getContainer()->get('language');
}
public function __construct(Container $container)
{
$this->container = $container;

View File

@@ -0,0 +1,94 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
namespace Espo\Core\Mail;
use \Espo\Entities\Email;
class FiltersMatcher
{
public function __construct()
{
}
public function match(Email $email, $filterList = [])
{
foreach ($filterList as $filter) {
if ($filter->get('from')) {
if ($this->matchString(strtolower($filter->get('from')), strtolower($email->get('from')))) {
return true;
}
}
if ($filter->get('to')) {
if ($email->get('to')) {
$toArr = explode(';', $email->get('to'));
foreach ($toArr as $to) {
if ($this->matchString(strtolower($filter->get('to')), strtolower($to))) {
return true;
}
}
}
}
if ($filter->get('subject')) {
if ($this->matchString($filter->get('subject'), $email->get('name'))) {
return true;
}
}
}
return false;
}
public function matchBody(Email $email, $filterList = [])
{
foreach ($filterList as $filter) {
if ($filter->get('bodyContains')) {
$phraseList = $filter->get('bodyContains');
$body = $email->get('body');
$bodyPlain = $email->get('bodyPlain');
foreach ($phraseList as $phrase) {
if (stripos($bodyPlain, $phrase) !== false) {
return true;
}
if (stripos($body, $phrase) !== false) {
return true;
}
}
}
}
return false;
}
protected function matchString($pattern, $value)
{
if ($pattern == $value) {
return true;
}
$pattern = preg_quote($pattern, '#');
$pattern = str_replace('\*', '.*', $pattern).'\z';
if (preg_match('#^'.$pattern.'#', $value)) {
return true;
}
return false;
}
}

View File

@@ -25,6 +25,7 @@ namespace Espo\Core\Mail;
use \Zend\Mime\Mime as Mime;
use \Espo\ORM\Entity;
use \Espo\ORM\Email;
class Importer
{
@@ -34,11 +35,14 @@ class Importer
private $config;
private $filtersMatcher;
public function __construct($entityManager, $fileManager, $config)
{
$this->entityManager = $entityManager;
$this->fileManager = $fileManager;
$this->config = $config;
$this->filtersMatcher = new FiltersMatcher();
}
protected function getEntityManager()
@@ -55,7 +59,12 @@ class Importer
return $this->fileManager;
}
public function importMessage($message, $userId, $teamsIds = array())
protected function getFiltersMatcher()
{
return $this->filtersMatcher;
}
public function importMessage($message, $userId, $teamsIds = [], $filterList = [])
{
try {
$email = $this->getEntityManager()->getEntity('Email');
@@ -82,16 +91,26 @@ class Importer
$toArr = $this->getAddressListFromMessage($message, 'to');
$ccArr = $this->getAddressListFromMessage($message, 'cc');
$replyToArr = $this->getAddressListFromMessage($message, 'replyTo');
$email->set('from', $fromArr[0]);
$email->set('to', implode(';', $toArr));
$email->set('cc', implode(';', $ccArr));
$email->set('replyTo', implode(';', $replyToArr));
if ($this->getFiltersMatcher()->match($email, $filterList)) {
return false;
}
if (isset($message->messageId) && !empty($message->messageId)) {
$email->set('messageId', $message->messageId);
if (isset($message->deliveredTo)) {
$email->set('messageIdInternal', $message->messageId . '-' . $message->deliveredTo);
}
if (stripos($message->messageId, '@espo-system') !== false) {
return;
}
}
if ($duplicate = $this->findDuplicate($email)) {
@@ -149,15 +168,54 @@ class Importer
$email->set('body', $body);
}
if ($this->getFiltersMatcher()->matchBody($email, $filterList)) {
return false;
}
$parentFound = false;
if (isset($message->inReplyTo) && !empty($message->inReplyTo)) {
$arr = explode(' ', $message->inReplyTo);
$inReplyTo = $arr[0];
$replied = $this->getEntityManager()->getRepository('Email')->where(array(
'messageId' => $inReplyTo
))->findOne();
if ($replied) {
$email->set('repliedId', $replied->id);
}
}
if (isset($message->references) && !empty($message->references)) {
$reference = str_replace(array('/', '@'), " ", trim($message->references, '<>'));
$arr = explode(' ', $message->references);
$reference = $arr[0];
$reference = str_replace(array('/', '@'), " ", trim($reference, '<>'));
$parentType = $parentId = null;
$emailSent = PHP_INT_MAX;
$n = sscanf($reference, '%s %s %d %d espo', $parentType, $parentId, $emailSent, $number);
if ($n == 4 && $emailSent < time()) {
if (!empty($parentType) && !empty($parentId)) {
if ($parentType == 'Lead') {
$parent = $this->getEntityManager()->getEntity('Lead', $parentId);
if ($parent && $parent->get('status') == 'Converted') {
if ($parent->get('createdAccountId')) {
$account = $this->getEntityManager()->getEntity('Account', $parent->get('createdAccountId'));
if ($account) {
$parentType = 'Account';
$parentId = $account->id;
}
} else {
if ($this->getConfig()->get('b2cMode')) {
if ($parent->get('createdContactId')) {
$contact = $this->getEntityManager()->getEntity('Contact', $parent->get('createdContactId'));
if ($contact) {
$parentType = 'Contact';
$parentId = $contact->id;
}
}
}
}
}
}
$email->set('parentType', $parentType);
$email->set('parentId', $parentId);
$parentFound = true;
@@ -170,10 +228,15 @@ class Importer
if ($from) {
$parentFound = $this->findParent($email, $from);
}
if (!$parentFound) {
if (!empty($toArr)) {
$parentFound = $this->findParent($email, $toArr[0]);
}
}
if (!$parentFound) {
if (!empty($replyToArr)) {
$parentFound = $this->findParent($email, $replyToArr[0]);
}
}
if (!$parentFound) {
if (!empty($toArr)) {
$parentFound = $this->findParent($email, $toArr[0]);
}
}
@@ -234,12 +297,20 @@ class Importer
}
}
protected function normilizeHeader($header)
{
if (is_a($header, 'ArrayIterator')) {
return $header->current();
} else {
return $header;
}
}
protected function getAddressListFromMessage($message, $type)
{
$addressList = array();
if (isset($message->$type)) {
$list = $message->getHeader($type)->getAddressList();
$list = $this->normilizeHeader($message->getHeader($type))->getAddressList();
foreach ($list as $address) {
$addressList[] = $address->getEmail();
}
@@ -276,7 +347,7 @@ class Importer
$encoding = null;
$isAttachment = true;
if ($type == 'text/plain' || $type == 'text/html') {
if ($contentDisposition !== 'inline' && $contentDisposition !== 'attachment') {
if ($contentDisposition !== 'attachment') {
$isAttachment = false;
$content = $this->getContentFromPart($part);
if ($type == 'text/plain') {
@@ -329,7 +400,7 @@ class Importer
}
if (isset($part->contentTransferEncoding)) {
$encoding = strtolower($part->getHeader('Content-Transfer-Encoding')->getTransferEncoding());
$encoding = strtolower($this->normilizeHeader($part->getHeader('Content-Transfer-Encoding'))->getTransferEncoding());
}
$attachment = $this->getEntityManager()->getEntity('Attachment');
@@ -377,7 +448,7 @@ class Importer
$encoding = null;
if (isset($part->contentTransferEncoding)) {
$cteHeader = $part->getHeader('Content-Transfer-Encoding');
$cteHeader = $this->normilizeHeader($part->getHeader('Content-Transfer-Encoding'));
$encoding = strtolower($cteHeader->getTransferEncoding());
}
@@ -388,7 +459,7 @@ class Importer
$charset = 'UTF-8';
if (isset($part->contentType)) {
$ctHeader = $part->getHeader('Content-Type');
$ctHeader = $this->normilizeHeader($part->getHeader('Content-Type'));
$charsetParamValue = $ctHeader->getParameter('charset');
if (!empty($charsetParamValue)) {
$charset = strtoupper($charsetParamValue);
@@ -396,7 +467,7 @@ class Importer
}
if (isset($part->contentTransferEncoding)) {
$cteHeader = $part->getHeader('Content-Transfer-Encoding');
$cteHeader = $this->normilizeHeader($part->getHeader('Content-Transfer-Encoding'));
if ($cteHeader->getTransferEncoding() == 'quoted-printable') {
$content = quoted_printable_decode($content);
}

View File

@@ -0,0 +1,69 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Espo\Core\Mail\Mail\Header;
use \Zend\Mail\Header;
use Zend\Mime\Mime;
class XQueueItemId implements Header\HeaderInterface
{
protected $fieldName = 'X-QueueItemId';
protected $id = null;
public static function fromString($headerLine)
{
list($name, $value) = Header\GenericHeader::splitHeaderLine($headerLine);
$value = Header\HeaderWrap::mimeDecodeValue($value);
if (strtolower($name) !== 'x-queue-item-id') {
throw new Header\Exception\InvalidArgumentException('Invalid header line for Message-ID string');
}
$header = new static();
$header->setId($value);
return $header;
}
public function getFieldName()
{
return $this->fieldName;
}
public function setFieldName($value)
{
}
public function setEncoding($encoding)
{
return $this;
}
public function setId($id)
{
$this->id = $id;
}
public function getEncoding()
{
return 'ASCII';
}
public function toString()
{
return $this->fieldName . ': ' . $this->getFieldValue();
}
public function getFieldValue($format = Header\HeaderInterface::FORMAT_RAW)
{
return $this->id;
}
}

View File

@@ -131,9 +131,11 @@ class Sender
return $this;
}
public function send(Email $email, $params = array(), &$message = null)
public function send(Email $email, $params = array(), &$message = null, $attachmetList = [])
{
$message = new Message();
if (!$message) {
$message = new Message();
}
$config = $this->config;
$params = $this->params + $params;
@@ -206,10 +208,24 @@ class Sender
}
}
$value = $email->get('replyTo');
if ($value) {
$arr = explode(';', $value);
if (is_array($arr)) {
foreach ($arr as $address) {
$message->addReplyTo(trim($address));
}
}
}
$attachmentPartList = array();
$attachmentCollection = $email->get('attachments');
$attachmentInlineCollection = $email->getInlineAttachments();
foreach ($attachmetList as $attachment) {
$attachmentCollection[] = $attachment;
}
if (!empty($attachmentCollection)) {
foreach ($attachmentCollection as $a) {
$fileName = 'data/upload/' . $a->id;
@@ -311,6 +327,9 @@ class Sender
} else {
$messageId = '' . md5($email->get('name')) . '/' . time() . '/' . $rand . '@espo';
}
if ($email->get('isSystem')) {
$messageId .= '-system';
}
$messageIdHeader = new \Zend\Mail\Header\MessageId();
$messageIdHeader->setId($messageId);

View File

@@ -65,6 +65,11 @@ class Base
return $this->user;
}
protected function getAcl()
{
return $this->acl;
}
public function setEntityType($entityType)
{
$this->entityType = $entityType;
@@ -75,20 +80,20 @@ class Base
return $this->entityType;
}
protected function limit($params, &$result)
protected function limit($offset = null, $maxSize = null, &$result)
{
if (isset($params['offset']) && !is_null($params['offset'])) {
$result['offset'] = $params['offset'];
if (!is_null($offset)) {
$result['offset'] = $offset;
}
if (isset($params['maxSize']) && !is_null($params['maxSize'])) {
$result['limit'] = $params['maxSize'];
if (!is_null($maxSize)) {
$result['limit'] = $maxSize;
}
}
protected function order($params, &$result)
protected function order($sortBy, $asc, &$result)
{
if (!empty($params['sortBy'])) {
$result['orderBy'] = $params['sortBy'];
if (!empty($sortBy)) {
$result['orderBy'] = $sortBy;
$type = $this->metadata->get("entityDefs.{$this->entityType}.fields." . $result['orderBy'] . ".type");
if ($type == 'link') {
$result['orderBy'] .= 'Name';
@@ -96,12 +101,10 @@ class Base
$result['orderBy'] .= 'Type';
}
}
if (isset($params['asc'])) {
if ($params['asc']) {
$result['order'] = 'ASC';
} else {
$result['order'] = 'DESC';
}
if ($asc) {
$result['order'] = 'ASC';
} else {
$result['order'] = 'DESC';
}
}
@@ -118,78 +121,61 @@ class Base
return $this->seed;
}
protected function textFilter($value, &$result)
public function applyWhere($where, &$result)
{
$fieldDefs = $this->getSeed()->getFields();
$fieldList = $this->getTextFilterFields();
$d = array();
foreach ($fieldList as $field) {
if (
strlen($item['value']) >= self::MIN_LENGTH_FOR_CONTENT_SEARCH
&&
!empty($fieldDefs[$field]['type']) && $fieldDefs[$field]['type'] == 'text'
) {
$d[$field . '*'] = '%' . $value . '%';
} else {
$d[$field . '*'] = $value . '%';
}
}
$result['whereClause'][] = array(
'OR' => $d
);
$this->prepareResult($result);
$this->where($where, $result);
}
protected function where($params, &$result)
protected function where($where, &$result)
{
if (!empty($params['where']) && is_array($params['where'])) {
$where = array();
$this->prepareResult($result);
foreach ($params['where'] as $item) {
if ($item['type'] == 'bool' && !empty($item['value']) && is_array($item['value'])) {
foreach ($item['value'] as $filter) {
$p = $this->getBoolFilterWhere($filter);
if (!empty($p)) {
$params['where'][] = $p;
}
$this->boolFilter($filter, $result);
$whereClause = array();
foreach ($where as $item) {
if ($item['type'] == 'bool' && !empty($item['value']) && is_array($item['value'])) {
foreach ($item['value'] as $filter) {
$p = $this->getBoolFilterWhere($filter);
if (!empty($p)) {
$where[] = $p;
}
} else if ($item['type'] == 'textFilter' && !empty($item['value'])) {
if (!empty($item['value'])) {
$this->textFilter($item['value'], $result);
}
} else if ($item['type'] == 'primary' && !empty($item['value'])) {
$this->primaryFilter($item['value'], $result);
$this->applyBoolFilter($filter, $result);
}
} else if ($item['type'] == 'textFilter' && !empty($item['value'])) {
if (!empty($item['value'])) {
$this->textFilter($item['value'], $result);
}
} else if ($item['type'] == 'primary' && !empty($item['value'])) {
$this->applyPrimaryFilter($item['value'], $result);
}
}
$linkedWith = array();
$inCategory = array();
$ignoreList = ['linkedWith', 'inCategory', 'bool', 'primary'];
foreach ($where as $item) {
if (!in_array($item['type'], $ignoreList)) {
$part = $this->getWherePart($item);
if (!empty($part)) {
$whereClause[] = $part;
}
} else {
if ($item['type'] == 'linkedWith' && !empty($item['value'])) {
$linkedWith[$item['field']] = $item['value'];
} else if ($item['type'] == 'inCategory' && !empty($item['value'])) {
$inCategory[$item['field']] = $item['value'];
}
}
}
$linkedWith = array();
$inCategory = array();
$result['whereClause'] = array_merge($result['whereClause'], $whereClause);
$ignoreList = ['linkedWith', 'inCategory', 'bool', 'primary'];
foreach ($params['where'] as $item) {
if (!in_array($item['type'], $ignoreList)) {
$part = $this->getWherePart($item);
if (!empty($part)) {
$where[] = $part;
}
} else {
if ($item['type'] == 'linkedWith' && !empty($item['value'])) {
$linkedWith[$item['field']] = $item['value'];
} else if ($item['type'] == 'inCategory' && !empty($item['value'])) {
$inCategory[$item['field']] = $item['value'];
}
}
}
$result['whereClause'] = array_merge($result['whereClause'], $where);
if (!empty($linkedWith)) {
$this->handleLinkedWith($linkedWith, $result);
}
if (!empty($inCategory)) {
$this->handleInCategory($inCategory, $result);
}
if (!empty($linkedWith)) {
$this->handleLinkedWith($linkedWith, $result);
}
if (!empty($inCategory)) {
$this->handleInCategory($inCategory, $result);
}
}
@@ -256,15 +242,15 @@ class Base
if ($defs['type'] == 'manyMany') {
if (!empty($defs['relationName']) && !empty($defs['midKeys'])) {
if (!empty($defs['midKeys'])) {
$result['distinct'] = true;
$result['joins'][] = $link;
$key = $defs['midKeys'][1];
$relationName = lcfirst($defs['relationName']);
$middleName = $link . 'Middle';
$result['customJoin'] .= "
JOIN " . $query->toDb($pathName) . " AS `{$pathName}` ON {$pathName}.descendor_id = ".$query->sanitize($relationName) . "." . $query->toDb($key) . "
JOIN " . $query->toDb($pathName) . " AS `{$pathName}` ON {$pathName}.descendor_id = ".$query->sanitize($middleName) . "." . $query->toDb($key) . "
";
$part[$pathName . '.ascendorId'] = $val;
}
@@ -290,51 +276,23 @@ class Base
protected function q($params, &$result)
{
if (!empty($params['q'])) {
$fieldDefs = $this->getSeed()->getFields();
$value = $params['q'];
$fieldList = $this->getTextFilterFields();
$d = array();
foreach ($fieldList as $field) {
if (
strlen($value) >= self::MIN_LENGTH_FOR_CONTENT_SEARCH
&&
!empty($fieldDefs[$field]['type']) && $fieldDefs[$field]['type'] == 'text'
) {
$d[$field . '*'] = '%' . $value . '%';
} else {
$d[$field . '*'] = $value . '%';
}
}
$result['whereClause'][] = array(
'OR' => $d
);
$this->textFilter($params['q'], $result);
}
}
public function manageAccess(&$result)
{
if (empty($result)) {
$result = array();
}
if (empty($result['joins'])) {
$result['joins'] = [];
}
if (empty($result['leftJoins'])) {
$result['leftJoins'] = [];
}
if (empty($result['whereClause'])) {
$result['whereClause'] = array();
}
if (empty($result['customJoin'])) {
$result['customJoin'] = [];
}
$this->access($result);
$this->prepareResult($result);
$this->applyAccess($result);
}
public function manageTextFilter($textFilter, &$result)
{
$this->prepareResult($result);
$this->q(array('q' => $textFilter), $result);
}
protected function prepareResult(&$result)
{
if (empty($result)) {
$result = array();
@@ -349,29 +307,36 @@ class Base
$result['whereClause'] = array();
}
if (empty($result['customJoin'])) {
$result['customJoin'] = [];
$result['customJoin'] = '';
}
$this->q(array('q' => $textFilter), $result);
}
protected function access(&$result)
{
if ($this->acl->checkReadOnlyOwn($this->entityType)) {
$this->accessOnlyOwn($result);
}
if (!$this->user->isAdmin() && $this->acl->checkReadOnlyTeam($this->entityType)) {
$this->accessOnlyTeam($result);
} else {
if (!$this->user->isAdmin() && $this->acl->checkReadOnlyTeam($this->entityType)) {
$this->accessOnlyTeam($result);
}
}
}
protected function accessOnlyOwn(&$result)
{
if (!$this->getSeed()->hasField('assignedUserId')) {
if ($this->getSeed()->hasField('assignedUserId')) {
$result['whereClause'][] = array(
'assignedUserId' => $this->getUser()->id
);
return;
}
if ($this->getSeed()->hasField('createdById')) {
$result['whereClause'][] = array(
'createdById' => $this->getUser()->id
);
return;
}
$result['whereClause'][] = array(
'assignedUserId' => $this->getUser()->id
);
}
protected function accessOnlyTeam(&$result)
@@ -394,22 +359,48 @@ class Base
public function getAclParams()
{
$result = array();
$this->access($result);
$this->applyAccess($result);
return $result;
}
public function getSelectParams(array $params, $withAcl = false)
{
$result = array(
'joins' => [],
'leftJoins' => [],
'whereClause' => [],
'customJoin' => ''
);
$result = array();
$this->prepareResult($result);
if (!empty($params['sortBy'])) {
if (!array_key_exists('asc', $params)) {
$params['asc'] = true;
}
$this->order($params['sortBy'], $params['asc'], $result);
}
if (!isset($params['offset'])) {
$params['offset'] = null;
}
if (!isset($params['maxSize'])) {
$params['maxSize'] = null;
}
$this->limit($params['offset'], $params['maxSize'], $result);
if (!empty($params['primaryFilter'])) {
$this->applyPrimaryFilter($params['primaryFilter'], $result);
}
if (!empty($params['boolFilterList']) && is_array($params['boolFilterList'])) {
foreach ($params['boolFilterList'] as $filterName) {
$this->applyBoolFilter($filterName, $result);
}
}
if (!empty($params['where']) && is_array($params['where'])) {
$this->where($params['where'], $result);
}
if (!empty($params['textFilter'])) {
$this->textFilter($params['textFilter'], $result);
}
$this->order($params, $result);
$this->limit($params, $result);
$this->where($params, $result);
$this->q($params, $result);
if ($withAcl) {
@@ -498,6 +489,42 @@ class Base
$where['value'] = [$from, $to];
break;
case 'lastXDays':
$where['type'] = 'between';
$dtFrom = clone $dt;
$dt->setTimezone(new \DateTimeZone('UTC'));
$to = $dt->format($format);
$number = strval(intval($item['value']));
$dtFrom->modify('-'.$number.' day');
$dtFrom->setTime(0, 0, 0);
$dtFrom->setTimezone(new \DateTimeZone('UTC'));
$from = $dtFrom->format($format);
$where['value'] = [$from, $to];
break;
case 'nextXDays':
$where['type'] = 'between';
$dtTo = clone $dt;
$dt->setTimezone(new \DateTimeZone('UTC'));
$from = $dt->format($format);
$number = strval(intval($item['value']));
$dtTo->modify('+'.$number.' day');
$dtTo->setTime(24, 59, 59);
$dtTo->setTimezone(new \DateTimeZone('UTC'));
$to = $dtTo->format($format);
$where['value'] = [$from, $to];
break;
case 'on':
$where['type'] = 'between';
@@ -636,6 +663,27 @@ class Base
$item['field'] . '<=' => $dt1->format('Y-m-d'),
);
break;
case 'lastXDays':
$dt1 = new \DateTime();
$dt2 = clone $dt1;
$number = strval(intval($item['value']));
$dt2->modify('-'.$number.' days');
$part['AND'] = array(
$item['field'] . '>=' => $dt2->format('Y-m-d'),
$item['field'] . '<=' => $dt1->format('Y-m-d'),
);
break;
case 'nextXDays':
$dt1 = new \DateTime();
$dt2 = clone $dt1;
$number = strval(intval($item['value']));
$dt2->modify('+'.$number.' days');
$part['AND'] = array(
$item['field'] . '>=' => $dt1->format('Y-m-d'),
$item['field'] . '<=' => $dt2->format('Y-m-d'),
);
break;
case 'currentMonth':
$dt = new \DateTime();
$part['AND'] = array(
@@ -701,19 +749,78 @@ class Base
return $part;
}
protected function boolFilter($filterName, &$result)
public function applyOrder($sortBy, $asc, &$result)
{
$this->prepareResult($result);
$this->order($sortBy, $asc, $result);
}
public function applyLimit($offset, $maxSize, &$result)
{
$this->prepareResult($result);
$this->limit($offset, $maxSize, $result);
}
public function applyPrimaryFilter($filterName, &$result)
{
$this->prepareResult($result);
$method = 'filter' . ucfirst($filterName);
if (method_exists($this, $method)) {
$this->$method($result);
}
}
public function applyBoolFilter($filterName, &$result)
{
$this->prepareResult($result);
$method = 'boolFilter' . ucfirst($filterName);
if (method_exists($this, $method)) {
$this->$method($result);
}
}
protected function primaryFilter($filterName, &$result)
public function applyTextFilter($textFilter, &$result)
{
$method = 'filter' . ucfirst($filterName);
if (method_exists($this, $method)) {
$this->$method($result);
$this->prepareResult($result);
$this->textFilter($textFilter, $result);
}
protected function textFilter($textFilter, &$result)
{
$fieldDefs = $this->getSeed()->getFields();
$fieldList = $this->getTextFilterFields();
$d = array();
foreach ($fieldList as $field) {
if (
strlen($textFilter) >= self::MIN_LENGTH_FOR_CONTENT_SEARCH
&&
!empty($fieldDefs[$field]['type']) && $fieldDefs[$field]['type'] == 'text'
) {
$d[$field . '*'] = '%' . $textFilter . '%';
} else {
$d[$field . '*'] = $textFilter . '%';
}
}
$result['whereClause'][] = array(
'OR' => $d
);
}
public function applyAccess(&$result)
{
$this->prepareResult($result);
$this->access($result);
}
protected function boolFilters($params, &$result)
{
if (!empty($params['boolFilterList']) && is_array($params['boolFilterList'])) {
foreach ($params['boolFilterList'] as $filterName) {
$this->applyBoolFilter($filterName, $result);
}
}
}

View File

@@ -55,7 +55,10 @@ class Auth extends \Slim\Middleware
}
$espoCgiAuth = $req->headers('HTTP_ESPO_CGI_AUTH');
if ( !isset($authUsername) && !isset($authPassword) && !empty($espoCgiAuth) ) {
if (empty($espoCgiAuth)) {
$espoCgiAuth = $req->headers('REDIRECT_HTTP_ESPO_CGI_AUTH');
}
if (!isset($authUsername) && !isset($authPassword) && !empty($espoCgiAuth)) {
list($authUsername, $authPassword) = explode(':' , base64_decode(substr($espoCgiAuth, 6)));
}

View File

@@ -150,7 +150,17 @@ class Config
$removeData = empty($this->removeData) ? null : $this->removeData;
$result = $this->getFileManager()->mergePhpContents($this->configPath, $values, $removeData);
$data = include($this->configPath);
foreach ($values as $key => $value) {
$data[$key] = $value;
}
foreach ($removeData as $key) {
unset($data[$key]);
}
$result = $this->getFileManager()->putPhpContents($this->configPath, $data);
if ($result) {
$this->changedData = array();
@@ -220,7 +230,7 @@ class Config
$restrictItems = $this->getRestrictItems($isAdmin);
$values = array();
foreach($data as $key => $item) {
foreach ($data as $key => $item) {
if (!in_array($key, $restrictItems)) {
$values[$key]= $item;
}

View File

@@ -103,6 +103,9 @@ return array (
'b2cMode' => false,
'restrictedMode' => false,
'theme' => 'Espo',
'massEmailMaxPerHourCount' => 100,
'personalEmailMaxPortionSize' => 10,
'inboundEmailMaxPortionSize' => 20,
'isInstalled' => false,
);

View File

@@ -116,7 +116,10 @@ return array (
'ldapTryUsernameSplit',
'ldapOptReferrals',
'ldapCreateEspoUser',
'maxEmailAccountCount'
'maxEmailAccountCount',
'massEmailMaxPerHourCount',
'personalEmailMaxPortionSize',
'inboundEmailMaxPortionSize'
),
'isInstalled' => false,
);

View File

@@ -99,5 +99,53 @@ class Email extends \Espo\Core\ORM\Entity
}
return $attachmentList;
}
public function getToList()
{
$value = $email->get('to');
if ($value) {
$arr = explode(';', $value);
if (is_array($arr)) {
return $arr;
}
}
return [];
}
public function getCcList()
{
$value = $email->get('cc');
if ($value) {
$arr = explode(';', $value);
if (is_array($arr)) {
return $arr;
}
}
return [];
}
public function getBccList()
{
$value = $email->get('bcc');
if ($value) {
$arr = explode(';', $value);
if (is_array($arr)) {
return $arr;
}
}
return [];
}
public function getReplyToList()
{
$value = $email->get('replyTo');
if ($value) {
$arr = explode(';', $value);
if (is_array($arr)) {
return $arr;
}
}
return [];
}
}

View File

@@ -18,7 +18,7 @@
*
* 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\Entities;
@@ -33,7 +33,6 @@ class EmailAddress extends \Espo\Core\ORM\Entity
throw new Error("Not valid email address '{$value}'");
}
$this->valuesContainer['name'] = $value;
$this->set('lower', strtolower($value));
}
$this->set('lower', strtolower($value));
}
}

View File

@@ -0,0 +1,29 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
namespace Espo\Entities;
class EmailFilter extends \Espo\Core\ORM\Entity
{
}

View File

@@ -63,6 +63,7 @@ class Download extends \Espo\Core\EntryPoints\Base
$fileName = "data/upload/{$attachment->id}";
if (!file_exists($fileName)) {
throw new NotFound();
}
@@ -78,7 +79,7 @@ class Download extends \Espo\Core\EntryPoints\Base
if ($type) {
header('Content-Type: ' . $type);
}
header('Content-Disposition: ' . $disposition . '; filename=' . $attachment->get('name'));
header("Content-Disposition: " . $disposition . ";filename=\"" . $attachment->get('name') . "\"");
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');

View File

@@ -0,0 +1,59 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
namespace Espo\Modules\Crm\Acl;
use \Espo\Entities\User;
use \Espo\ORM\Entity;
class CampaignLogRecord extends \Espo\Core\Acl\Base
{
public function checkIsOwner(User $user, Entity $entity)
{
if ($entity->has('campaignId')) {
$campaignId = $entity->get('campaignId');
if (!$campaignId) return;
$campaign = $this->getEntityManager()->getEntity('Campaign', $campaignId);
if ($campaign && $this->getAclManager()->getImplementation('Campaign')->checkIsOwner($user, $campaign)) {
return true;
}
}
return;
}
public function checkInTeam(User $user, Entity $entity)
{
if ($entity->has('campaignId')) {
$campaignId = $entity->get('campaignId');
if (!$campaignId) return;
$campaign = $this->getEntityManager()->getEntity('Campaign', $campaignId);
if ($campaign && $this->getAclManager()->getImplementation('Campaign')->checkInTeam($user, $campaign)) {
return true;
}
}
return;
}
}

View File

@@ -0,0 +1,59 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
namespace Espo\Modules\Crm\Acl;
use \Espo\Entities\User;
use \Espo\ORM\Entity;
class CampaignTrackingUrl extends \Espo\Core\Acl\Base
{
public function checkIsOwner(User $user, Entity $entity)
{
if ($entity->has('campaignId')) {
$campaignId = $entity->get('campaignId');
if (!$campaignId) return;
$campaign = $this->getEntityManager()->getEntity('Campaign', $campaignId);
if ($campaign && $this->getAclManager()->getImplementation('Campaign')->checkIsOwner($user, $campaign)) {
return true;
}
}
return;
}
public function checkInTeam(User $user, Entity $entity)
{
if ($entity->has('campaignId')) {
$campaignId = $entity->get('campaignId');
if (!$campaignId) return;
$campaign = $this->getEntityManager()->getEntity('Campaign', $campaignId);
if ($campaign && $this->getAclManager()->getImplementation('Campaign')->checkInTeam($user, $campaign)) {
return true;
}
}
return;
}
}

View File

@@ -0,0 +1,59 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
namespace Espo\Modules\Crm\Acl;
use \Espo\Entities\User;
use \Espo\ORM\Entity;
class MassEmail extends \Espo\Core\Acl\Base
{
public function checkIsOwner(User $user, Entity $entity)
{
if ($entity->has('campaignId')) {
$campaignId = $entity->get('campaignId');
if (!$campaignId) return;
$campaign = $this->getEntityManager()->getEntity('Campaign', $campaignId);
if ($campaign && $this->getAclManager()->getImplementation('Campaign')->checkIsOwner($user, $campaign)) {
return true;
}
}
return;
}
public function checkInTeam(User $user, Entity $entity)
{
if ($entity->has('campaignId')) {
$campaignId = $entity->get('campaignId');
if (!$campaignId) return;
$campaign = $this->getEntityManager()->getEntity('Campaign', $campaignId);
if ($campaign && $this->getAclManager()->getImplementation('Campaign')->checkInTeam($user, $campaign)) {
return true;
}
}
return;
}
}

View File

@@ -28,7 +28,6 @@ use \Espo\Core\Exceptions\Error,
class Activities extends \Espo\Core\Controllers\Base
{
public static $defaultAction = 'index';
public function actionListCalendarEvents($params, $data, $request)
{
@@ -43,9 +42,32 @@ class Activities extends \Espo\Core\Controllers\Base
throw new BadRequest();
}
$service = $this->getService('Activities');
return $service->getEvents($this->getUser()->id, $from, $to);
$userId = $request->get('userId');
if (!$userId) {
$userId = $this->getUser()->id;
}
return $service->getEvents($userId, $from, $to);
}
public function actionListUpcoming($params, $data, $request)
{
$service = $this->getService('Activities');
$userId = $request->get('userId');
if (!$userId) {
$userId = $this->getUser()->id;
}
$offset = intval($request->get('offset'));
$maxSize = intval($request->get('maxSize'));
return $service->getUpcomingActivities($userId, array(
'offset' => $offset,
'maxSize' => $maxSize
));
}
public function actionPopupNotifications()

View File

@@ -0,0 +1,28 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
namespace Espo\Modules\Crm\Controllers;
class CampaignLogRecord extends \Espo\Core\Controllers\Record
{
}

View File

@@ -0,0 +1,28 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
namespace Espo\Modules\Crm\Controllers;
class CampaignTrackingUrl extends \Espo\Core\Controllers\Record
{
}

View File

@@ -0,0 +1,33 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
namespace Espo\Modules\Crm\Controllers;
class EmailQueueItem extends \Espo\Core\Controllers\Record
{
protected function checkControllerAccess()
{
if (!$this->getUser()->isAdmin()) {
throw new Forbidden();
}
}
}

View File

@@ -0,0 +1,64 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
namespace Espo\Modules\Crm\Controllers;
use \Espo\Core\Exceptions\BadRequest;
use \Espo\Core\Exceptions\Forbidden;
use \Espo\Core\Exceptions\NotFound;
class MassEmail extends \Espo\Core\Controllers\Record
{
public function postActionSendTest($params, $data)
{
if (empty($data['id']) || empty($data['targetList']) || !is_array($data['targetList'])) {
throw new BadRequest();
}
$id = $data['id'];
$targetList = [];
foreach ($data['targetList'] as $item) {
if (empty($item->id) || empty($item->type)) continue;
$targetId = $item->id;
$targetType = $item->type;
$target = $this->getEntityManager()->getEntity($targetType, $targetId);
if (!$target) continue;
if (!$this->getAcl()->check($target, 'read')) {
continue;
}
$targetList[] = $target;
}
$massEmail = $this->getEntityManager()->getEntity('MassEmail', $id);
if (!$massEmail) {
throw new NotFound();
}
if (!$this->getAcl()->check($massEmail, 'read')) {
throw new Forbidden();
}
$this->getRecordService()->createQueue($massEmail, true, $targetList);
$this->getRecordService()->processSending($massEmail, true);
return true;
}
}

View File

@@ -45,4 +45,18 @@ class TargetList extends \Espo\Core\Controllers\Record
return $this->getRecordService()->unlinkAll($data['id'], $data['link']);
}
public function postActionCancelOptOut($params, $data)
{
if (empty($data['id'])) {
throw new BadRequest();
}
if (empty($data['targetType'])) {
throw new BadRequest();
}
if (empty($data['targetId'])) {
throw new BadRequest();
}
return $this->getRecordService()->cancelOptOut($data['id'], $data['targetType'], $data['targetId']);
}
}

View File

@@ -0,0 +1,36 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
namespace Espo\Modules\Crm\Entities;
class CampaignTrackingUrl extends \Espo\Core\ORM\Entity
{
protected function _getUrlToUse()
{
return '{trackingUrl:' . $this->id . '}';
}
protected function _hasUrlToUse()
{
return !$this->isNew();
}
}

View File

@@ -0,0 +1,28 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
namespace Espo\Modules\Crm\Entities;
class EmailQueueItem extends \Espo\Core\ORM\Entity
{
}

View File

@@ -0,0 +1,28 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
namespace Espo\Modules\Crm\Entities;
class MassEmail extends \Espo\Core\ORM\Entity
{
}

View File

@@ -0,0 +1,79 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.phpppph
************************************************************************/
namespace Espo\Modules\Crm\EntryPoints;
use \Espo\Core\Utils\Util;
use \Espo\Core\Exceptions\NotFound;
use \Espo\Core\Exceptions\Forbidden;
use \Espo\Core\Exceptions\BadRequest;
use \Espo\Core\Exceptions\Error;
class CampaignTrackOpened extends \Espo\Core\EntryPoints\Base
{
public static $authRequired = false;
public function run()
{
if (empty($_GET['id'])) {
throw new BadRequest();
}
$queueItemId = $_GET['id'];
$queueItem = $this->getEntityManager()->getEntity('EmailQueueItem', $queueItemId);
if (!$queueItem) {
throw new NotFound();
}
$target = null;
$campaign = null;
$targetType = $queueItem->get('targetType');
$targetId = $queueItem->get('targetId');
if ($targetType && $targetId) {
$target = $this->getEntityManager()->getEntity($targetType, $targetId);
}
$massEmailId = $queueItem->get('massEmailId');
if (!$massEmailId) return;
$massEmail = $this->getEntityManager()->getEntity('MassEmail', $massEmailId);
if (!$massEmail) return;
$campaignId = $massEmail->get('campaignId');
if (!$campaignId) return;
$campaign = $this->getEntityManager()->getEntity('Campaign', $campaignId);
if (!$campaign) return;
if (!$target) {
return;
}
$campaignService = $this->getServiceFactory()->create('Campaign');
$campaignService->logOpened($campaignId, $queueItemId, $target);
}
}

View File

@@ -0,0 +1,76 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.phpppph
************************************************************************/
namespace Espo\Modules\Crm\EntryPoints;
use \Espo\Core\Utils\Util;
use \Espo\Core\Exceptions\NotFound;
use \Espo\Core\Exceptions\Forbidden;
use \Espo\Core\Exceptions\BadRequest;
use \Espo\Core\Exceptions\Error;
class CampaignUrl extends \Espo\Core\EntryPoints\Base
{
public static $authRequired = false;
public function run()
{
if (empty($_GET['id']) || empty($_GET['queueItemId'])) {
throw new BadRequest();
}
$queueItemId = $_GET['queueItemId'];
$trackingUrlId = $_GET['id'];
$queueItem = $this->getEntityManager()->getEntity('EmailQueueItem', $queueItemId);
$trackingUrl = $this->getEntityManager()->getEntity('CampaignTrackingUrl', $trackingUrlId);
if (!$queueItem || !$trackingUrl) {
throw new NotFound();
}
$target = null;
$campaign = null;
$targetType = $queueItem->get('targetType');
$targetId = $queueItem->get('targetId');
if ($targetType && $targetId) {
$target = $this->getEntityManager()->getEntity($targetType, $targetId);
}
$campaignId = $trackingUrl->get('campaignId');
if ($campaignId) {
$campaign = $this->getEntityManager()->getEntity('Campaign', $campaignId);
}
if ($campaign && $target) {
$campaignService = $this->getServiceFactory()->create('Campaign');
$campaignService->logClicked($campaignId, $queueItemId, $target, $trackingUrl);
}
ob_clean();
header('Location: ' . $trackingUrl->get('url') . '');
die;
}
}

View File

@@ -0,0 +1,98 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.phpppph
************************************************************************/
namespace Espo\Modules\Crm\EntryPoints;
use \Espo\Core\Utils\Util;
use \Espo\Core\Exceptions\NotFound;
use \Espo\Core\Exceptions\Forbidden;
use \Espo\Core\Exceptions\BadRequest;
use \Espo\Core\Exceptions\Error;
class Unsubscribe extends \Espo\Core\EntryPoints\Base
{
public static $authRequired = false;
public function run()
{
if (empty($_GET['id'])) {
throw new BadRequest();
}
$queueItemId = $_GET['id'];
$queueItem = $this->getEntityManager()->getEntity('EmailQueueItem', $queueItemId);
if (!$queueItem) {
throw new NotFound();
}
$campaign = null;
$target = null;
$massEmailId = $queueItem->get('massEmailId');
if ($massEmailId) {
$massEmail = $this->getEntityManager()->getEntity('MassEmail', $massEmailId);
if ($massEmail) {
$campaignId = $massEmail->get('campaignId');
if ($campaignId) {
$campaign = $this->getEntityManager()->getEntity('Campaign', $campaignId);
}
$targetType = $queueItem->get('targetType');
$targetId = $queueItem->get('targetId');
if ($targetType && $targetId) {
$target = $this->getEntityManager()->getEntity($targetType, $targetId);
$link = null;
$m = array(
'Account' => 'accounts',
'Contact' => 'contacts',
'Lead' => 'leads',
'User' => 'users'
);
if (!empty($m[$target->getEntityType()])) {
$link = $m[$target->getEntityType()];
}
if ($link) {
$targetListList = $massEmail->get('targetLists');
foreach ($targetListList as $targetList) {
$this->getEntityManager()->getRepository('TargetList')->updateRelation($targetList, $link, $target->id, array(
'optedOut' => true
));
}
echo $this->getLanguage()->translate('unsubscribed', 'messages', 'Campaign');
}
}
}
}
if ($campaign && $target) {
$campaignService = $this->getServiceFactory()->create('Campaign');
$campaignService->logOptedOut($campaignId, $queueItemId, $target, $queueItem->get('emailAddress'));
}
}
}

View File

@@ -0,0 +1,59 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
namespace Espo\Modules\Crm\Jobs;
use \Espo\Core\Exceptions;
class ProcessMassEmail extends \Espo\Core\Jobs\Base
{
public function run()
{
$service = $this->getServiceFactory()->create('MassEmail');
$massEmailList = $this->getEntityManager()->getRepository('MassEmail')->where(array(
'status' => 'Pending',
'startAt<=' => date('Y-m-d H:i:s')
))->find();
foreach ($massEmailList as $massEmail) {
try {
$service->createQueue($massEmail);
} catch (\Exception $e) {
$GLOBALS['log']->error('Job ProcessMassEmail#createQueue '.$massEmail->id.': [' . $e->getCode() . '] ' .$e->getMessage());
}
}
$massEmailList = $this->getEntityManager()->getRepository('MassEmail')->where(array(
'status' => 'In Process'
))->find();
foreach ($massEmailList as $massEmail) {
try {
$service->processSending($massEmail);
} catch (\Exception $e) {
$GLOBALS['log']->error('Job ProcessMassEmail#processSending '.$massEmail->id.': [' . $e->getCode() . '] ' .$e->getMessage());
}
}
return true;
}
}

View File

@@ -45,7 +45,7 @@ class Contact extends \Espo\Core\ORM\Repositories\RDB
$result = parent::afterSave($entity, $options);
$this->handleAfterSaveAccounts($entity, $options);
if ($entity->has('targetListId') && $entity->isNew()) {
if ($entity->has('targetListId')) {
$this->relate($entity, 'targetLists', $entity->get('targetListId'));
}

View File

@@ -30,7 +30,7 @@ class Lead extends \Espo\Core\ORM\Repositories\RDB
{
parent::afterSave($entity, $options);
if ($entity->has('targetListId') && $entity->isNew()) {
if ($entity->has('targetListId')) {
$this->relate($entity, 'targetLists', $entity->get('targetListId'));
}
}

View File

@@ -35,10 +35,17 @@ class Meeting extends \Espo\Core\ORM\Repositories\RDB
if (!empty($parentId) || !empty($parentType)) {
$parent = $this->getEntityManager()->getEntity($parentType, $parentId);
if (!empty($parent)) {
$accountId = null;
if ($parent->getEntityType() == 'Account') {
$accountId = $parent->id;
} else if ($parent->has('accountId')) {
} else if ($parent->get('accountId')) {
$accountId = $parent->get('accountId');
} else if ($parent->getEntityType() == 'Lead') {
if ($parent->get('status') == 'Converted') {
if ($parent->get('createdAccountId')) {
$accountId = $parent->get('createdAccountId');
}
}
}
if (!empty($accountId)) {
$entity->set('accountId', $accountId);
@@ -56,11 +63,28 @@ class Meeting extends \Espo\Core\ORM\Repositories\RDB
$usersIds[] = $assignedUserId;
$entity->set('usersIds', $usersIds);
$hash = $entity->get('usersNames');
if ($hash instanceof \stdClass) {
if ($hash instanceof \StdClass) {
$hash->$assignedUserId = $entity->get('assignedUserName');
$entity->set('usersNames', $hash);
}
}
if ($entity->isNew()) {
$currentUserId = $this->getEntityManager()->getUser()->id;
if (in_array($currentUserId, $usersIds)) {
$usersColumns = $entity->get('usersColumns');
if (empty($usersColumns)) {
$usersColumns = new \StdClass();
}
if ($usersColumns instanceof \StdClass) {
if (!($usersColumns->$currentUserId instanceof \StdClass)) {
$usersColumns->$currentUserId = new \StdClass();
}
if (empty($usersColumns->$currentUserId->status)) {
$usersColumns->$currentUserId->status = 'Accepted';
}
}
}
}
}
}

View File

@@ -98,10 +98,17 @@ class Task extends \Espo\Core\ORM\Repositories\RDB
if (!empty($parentId) || !empty($parentType)) {
$parent = $this->getEntityManager()->getEntity($parentType, $parentId);
if (!empty($parent)) {
$accountId = null;
if ($parent->getEntityType() == 'Account') {
$accountId = $parent->id;
} else if ($parent->has('accountId')) {
} else if ($parent->get('accountId')) {
$accountId = $parent->get('accountId');
} else if ($parent->getEntityType() == 'Lead') {
if ($parent->get('status') == 'Converted') {
if ($parent->get('createdAccountId')) {
$accountId = $parent->get('createdAccountId');
}
}
}
if (!empty($accountId)) {
$entity->set('accountId', $accountId);

View File

@@ -25,7 +25,9 @@
"contacts": "Kontakte",
"leads": "Interessenten",
"opportunities": "Verkaufschancen",
"campaignLogRecords": "Protokoll"
"campaignLogRecords": "Protokoll",
"massEmails": "Massen E-Mails",
"trackingUrls": "Tracking URLs"
},
"options": {
"type": {
@@ -48,9 +50,13 @@
"Target Lists": "Kontaktlisten",
"Statistics": "Statistik",
"hard": "hart",
"soft": "weich"
"soft": "weich",
"Unsubscribe": "Abmelden"
},
"presetFilters": {
"active": "Aktiv"
},
"messages": {
"unsubscribed": "Sie wurden erfolgreich von unserem Verteiler entfernt"
}
}

View File

@@ -6,7 +6,8 @@
"campaign": "Kampagne",
"parent": "Zielkontakt",
"object": "Objekt",
"application": "Applikation"
"application": "Applikation",
"queueItem": "Warteschlangeneintrag"
},
"options": {
"action": {
@@ -20,5 +21,13 @@
},
"labels": {
"All": "Alle"
},
"presetFilters": {
"sent": "Gesendet",
"opened": "Geöffnet",
"optedOut": "Keine E-Mails",
"bounced": "Nicht zustellbar",
"clicked": "Geklickt",
"leadCreated": "Interessent erstellt"
}
}

View File

@@ -0,0 +1,13 @@
{
"fields": {
"url": "URL",
"urlToUse": "Code zu Einfügen anstelle einer URL",
"campaign": "Kampagne"
},
"links": {
"campaign": "Kampagne"
},
"labels": {
"Create CampaignTrackingUrl": "Tracking URL erstellen"
}
}

View File

@@ -43,7 +43,11 @@
}
},
"labels": {
"Create Case": "Fall erstellen"
"Create Case": "Fall erstellen",
"Close": "Schließen",
"Reject": "Ablehnen",
"Closed": "Abgeschlossen",
"Rejected": "Abgelehnt"
},
"presetFilters": {
"open": "Offen",

View File

@@ -0,0 +1,28 @@
{
"fields": {
"name": "Name",
"status": "Status",
"target": "Zielkontakt",
"sentAt": "Sendedatum",
"attemptCount": "Versuche",
"emailAddress": "E-Mail Adresse",
"massEmail": "Massen E-Mails",
"isTest": "Ist Test"
},
"links": {
"target": "Zielkontakt",
"massEmail": "Massen E-Mails"
},
"options": {
"status": {
"Pending": "Schwebend",
"Sent": "Gesendet",
"Failed": "Fehlgeschlagen"
}
},
"presetFilters": {
"pending": "Schwebend",
"sent": "Gesendet",
"failed": "Fehlgeschlagen"
}
}

View File

@@ -13,7 +13,10 @@
"Document": "Dokument",
"DocumentFolder": "Dokumente Ordner",
"Campaign": "Kampagne",
"TargetList": "Kontaktliste"
"TargetList": "Kontaktliste",
"MassEmail": "Massen E-Mails",
"EmailQueueItem": "E-Mail Warteschlangeneintrag",
"CampaignTrackingUrl": "Tracking URL"
},
"scopeNamesPlural": {
"Account": "Firmen",
@@ -29,7 +32,10 @@
"Document": "Dokumente",
"DocumentFolder": "Dokumente Ordner",
"Campaign": "Kampagnen",
"TargetList": "Kontaktlisten"
"TargetList": "Kontaktlisten",
"MassEmail": "Massen E-Mails",
"EmailQueueItem": "E-Mail Warteschlangeneinträge",
"CampaignTrackingUrl": "Tracking URLs"
},
"dashlets": {
"Leads": "Meine Interessenten",
@@ -42,7 +48,8 @@
"OpportunitiesByStage": "Verkaufschancen nach Verkaufsphase",
"OpportunitiesByLeadSource": "Verkaufschancen nach Quelle",
"SalesByMonth": "Umsätze nach Monat",
"SalesPipeline": "Verkaufspipeline"
"SalesPipeline": "Verkaufspipeline",
"Activities": "Meine Aktivitäten"
},
"labels": {
"Create InboundEmail": "Eingehende E-Mail erstellen",

View File

@@ -56,6 +56,7 @@
},
"presetFilters": {
"active": "Aktiv",
"actual": "Tatsächlich",
"converted": "Umgewandelt"
}
}

View File

@@ -0,0 +1,41 @@
{
"fields": {
"name": "Name",
"status": "Status",
"storeSentEmails": "Gesendete E-Mails speichern",
"startAt": "Startdatum",
"fromAddress": "Von Adresse",
"fromName": "Von Name",
"replyToAddress": "Antwort-an Adresse",
"replyToName": "Antwort-an Name",
"campaign": "Kampagne",
"emailTemplate": "E-Mail Vorlage",
"inboundEmail": "E-Mail Konto",
"targetLists": "Kontaktlisten"
},
"links": {
"targetLists": "Kontaktlisten",
"queueItems": "Warteschlangeneinträge",
"campaign": "Kampagne",
"emailTemplate": "E-Mail Vorlage",
"inboundEmail": "E-Mail Konto"
},
"options": {
"status": {
"Draft": "Entwurf",
"Pending": "Schwebend",
"In Process": "In Arbeit",
"Complete": "Fertig",
"Canceled": "Storniert",
"Failed": "Fehlgeschlagen"
}
},
"labels": {
"Create MassEmail": "Massen E-Mail erstellen",
"Send Test": "Test senden"
},
"messages": {
"selectAtLeastOneTarget": "Zumindest ein Ziel auswählen",
"testSent": "Test E-Mail(s) die gesendet werden sollen"
}
}

View File

@@ -38,7 +38,7 @@
"Set Not Held": "Auf nicht gehalten setzen",
"Send Invitations": "Einladungen versenden",
"on time": "Aktuelle Zeit",
"before": "Bevor"
"before": "bevor"
},
"presetFilters": {
"planned": "Geplant",

View File

@@ -38,6 +38,7 @@
},
"presetFilters": {
"open": "Offen",
"won": "Gewonnen"
"won": "Gewonnen",
"lost": "Lost"
}
}

View File

@@ -1,10 +1,7 @@
{
"options": {
"job": {
"CheckInboundEmails": "Eingehende E-Mail prüfen",
"CheckEmailAccounts": "Persönliche E-Mail Konten prüfen",
"SendEmailReminders": "E-Mail Erinnerungen senden"
"ProcessMassEmail": "Massen E-Mails senden"
}
}
}

View File

@@ -11,7 +11,8 @@
"accounts": "Firmen",
"contacts": "Kontakte",
"leads": "Interessenten",
"campaigns": "Kampagnen"
"campaigns": "Kampagnen",
"massEmails": "Massen E-Mails"
},
"options": {
"type": {
@@ -24,6 +25,7 @@
},
"labels": {
"Create TargetList": "Kontaktliste erstellen",
"Opted Out": "Keine E-Mails"
"Opted Out": "Keine E-Mails",
"Cancel Opt-Out": "Opt-Out zurücksetzen"
}
}

View File

@@ -40,6 +40,7 @@
"Automotive": "Automotive",
"Banking": "Banking",
"Biotechnology": "Biotechnology",
"Building Materials & Equipment": "Building Materials & Equipment",
"Chemical": "Chemical",
"Computer": "Computer",
"Education": "Education",

View File

@@ -25,7 +25,9 @@
"contacts": "Contacts",
"leads": "Leads",
"opportunities": "Opportunities",
"campaignLogRecords": "Log"
"campaignLogRecords": "Log",
"massEmails": "Mass Emails",
"trackingUrls": "Tracking URLs"
},
"options": {
"type": {
@@ -48,9 +50,13 @@
"Target Lists": "Target Lists",
"Statistics": "Statistics",
"hard": "hard",
"soft": "soft"
"soft": "soft",
"Unsubscribe": "Unsubscribe"
},
"presetFilters": {
"active": "Active"
},
"messages": {
"unsubscribed": "You have been unsubscribed from our mailing list."
}
}

View File

@@ -6,7 +6,8 @@
"campaign": "Campaign",
"parent": "Target",
"object": "Object",
"application": "Application"
"application": "Application",
"queueItem": "Queue Item"
},
"options": {
"action": {
@@ -20,5 +21,13 @@
},
"labels": {
"All": "All"
},
"presetFilters": {
"sent": "Sent",
"opened": "Opened",
"optedOut": "Opted Out",
"bounced": "Bounced",
"clicked": "Clicked",
"leadCreated": "Lead Created"
}
}

View File

@@ -0,0 +1,13 @@
{
"fields": {
"url": "URL",
"urlToUse": "Code to insert instead of URL",
"campaign": "Campaign"
},
"links": {
"campaign": "Campaign"
},
"labels": {
"Create CampaignTrackingUrl": "Create Tracking URL"
}
}

View File

@@ -43,7 +43,11 @@
}
},
"labels": {
"Create Case": "Create Case"
"Create Case": "Create Case",
"Close": "Close",
"Reject": "Reject",
"Closed": "Closed",
"Rejected": "Rejected"
},
"presetFilters": {
"open": "Open",

View File

@@ -0,0 +1,28 @@
{
"fields": {
"name": "Name",
"status": "Status",
"target": "Target",
"sentAt": "Date Sent",
"attemptCount": "Attempts",
"emailAddress": "Email Address",
"massEmail": "Mass Email",
"isTest": "Is Test"
},
"links": {
"target": "Target",
"massEmail": "Mass Email"
},
"options": {
"status": {
"Pending": "Pending",
"Sent": "Sent",
"Failed": "Failed"
}
},
"presetFilters": {
"pending": "Pending",
"sent": "Sent",
"failed": "Failed"
}
}

View File

@@ -13,7 +13,10 @@
"Document": "Document",
"DocumentFolder": "Document Folder",
"Campaign": "Campaign",
"TargetList": "Target List"
"TargetList": "Target List",
"MassEmail": "Mass Email",
"EmailQueueItem": "Email Queue Item",
"CampaignTrackingUrl": "Tracking URL"
},
"scopeNamesPlural": {
"Account": "Accounts",
@@ -29,7 +32,10 @@
"Document": "Documents",
"DocumentFolder": "Document Folders",
"Campaign": "Campaigns",
"TargetList": "Target Lists"
"TargetList": "Target Lists",
"MassEmail": "Mass Emails",
"EmailQueueItem": "Email Queue Items",
"CampaignTrackingUrl": "Tracking URLs"
},
"dashlets": {
"Leads": "My Leads",
@@ -42,7 +48,8 @@
"OpportunitiesByStage": "Opportunities by Stage",
"OpportunitiesByLeadSource": "Opportunities by Lead Source",
"SalesByMonth": "Sales by Month",
"SalesPipeline": "Sales Pipeline"
"SalesPipeline": "Sales Pipeline",
"Activities": "My Activities"
},
"labels": {
"Create InboundEmail": "Create Inbound Email",

View File

@@ -56,6 +56,7 @@
},
"presetFilters": {
"active": "Active",
"actual": "Actual",
"converted": "Converted"
}
}

View File

@@ -0,0 +1,41 @@
{
"fields": {
"name": "Name",
"status": "Status",
"storeSentEmails": "Store Sent Emails",
"startAt": "Date Start",
"fromAddress": "From Address",
"fromName": "From Name",
"replyToAddress": "Reply-to Address",
"replyToName": "Reply-to Name",
"campaign": "Campaign",
"emailTemplate": "Email Template",
"inboundEmail": "Email Account",
"targetLists": "Target Lists"
},
"links": {
"targetLists": "Target Lists",
"queueItems": "Queue Items",
"campaign": "Campaign",
"emailTemplate": "Email Template",
"inboundEmail": "Email Account"
},
"options": {
"status": {
"Draft": "Draft",
"Pending": "Pending",
"In Process": "In Process",
"Complete": "Complete",
"Canceled": "Canceled",
"Failed": "Failed"
}
},
"labels": {
"Create MassEmail": "Create Mass Email",
"Send Test": "Send Test"
},
"messages": {
"selectAtLeastOneTarget": "Select at least one target.",
"testSent": "Test email(s) supposed to be sent"
}
}

View File

@@ -38,6 +38,7 @@
},
"presetFilters": {
"open": "Open",
"won": "Won"
"won": "Won",
"lost": "Lost"
}
}

View File

@@ -1,10 +1,7 @@
{
"options": {
"job": {
"CheckInboundEmails": "Check Inbound Emails",
"CheckEmailAccounts": "Check Personal Email Accounts",
"SendEmailReminders": "Send Email Reminders"
"ProcessMassEmail": "Send Mass Emails"
}
}
}

View File

@@ -11,7 +11,8 @@
"accounts": "Accounts",
"contacts": "Contacts",
"leads": "Leads",
"campaigns": "Campaigns"
"campaigns": "Campaigns",
"massEmails": "Mass Emails"
},
"options": {
"type": {
@@ -24,6 +25,7 @@
},
"labels": {
"Create TargetList": "Create Target List",
"Opted Out": "Opted Out"
"Opted Out": "Opted Out",
"Cancel Opt-Out": "Cancel Opt-Out"
}
}

View File

@@ -50,6 +50,7 @@
}
},
"presetFilters": {
"active": "Actif"
"active": "Actif",
"actual": "En cours"
}
}

View File

@@ -45,6 +45,7 @@
}
},
"presetFilters": {
"actual": "Werkelijke",
"active": "Actief"
}
}

View File

@@ -48,6 +48,7 @@
}
},
"presetFilters": {
"actual": "Atual",
"active": "Ativo"
}
}

View File

@@ -45,6 +45,7 @@
}
},
"presetFilters": {
"active": "Активный"
"actual": "Актуальные",
"active": "Активные"
}
}

View File

@@ -30,8 +30,8 @@
"Complete": "Завершить"
},
"presetFilters": {
"actual": "Actual",
"completed": "Завершена",
"actual": "Актуальные",
"completed": "Завершенные",
"todays": "На сегодня",
"overdue": "Просроченные"
}

View File

@@ -2,12 +2,12 @@
{
"label":"",
"rows":[
[{"name":"name"}],
[{"name":"name", "fullWidth": true}],
[{"name":"status"}, {"name":"direction"}],
[{"name":"dateStart"}],
[{"name":"duration"}],
[{"name":"parent"}],
[{"name":"description"}]
[{"name":"dateStart", "fullWidth": true}],
[{"name":"duration", "fullWidth": true}],
[{"name":"parent", "fullWidth": true}],
[{"name":"description", "fullWidth": true}]
]
}
]

View File

@@ -2,6 +2,7 @@
"account",
"assignedUser",
"createdAt",
"createdBy",
"dateStart",
"direction",
"modifiedAt",

View File

@@ -2,5 +2,5 @@
{"name":"name","width":40,"link":true},
{"name":"type","width":20},
{"name":"status"},
{"name":"endDate"}
{"name":"createdAt"}
]

View File

@@ -0,0 +1,17 @@
[
{
"label": "",
"rows": [
[
{"name": "name"},
{"name": "campaign"}
],
[
{"name": "url", "fullWidth": true}
],
[
{"name": "urlToUse", "fullWidth": true}
]
]
}
]

View File

@@ -0,0 +1,16 @@
[
{
"label": "",
"rows": [
[
{"name": "name", "fullWidth": true}
],
[
{"name": "url", "fullWidth": true}
],
[
{"name": "urlToUse", "fullWidth": true}
]
]
}
]

View File

@@ -0,0 +1,20 @@
[
{
"name": "name",
"width": 25,
"link": true
},
{
"name": "url",
"notSortable": true,
"width": 30
},
{
"name": "campaign",
"notSortable": true
},
{
"name": "createdAt",
"width": 10
}
]

View File

@@ -0,0 +1,16 @@
[
{
"name": "name",
"width": 30,
"link": true
},
{
"name": "url",
"width": 30,
"notSortable": true
},
{
"name": "urlToUse",
"notSortable": true
}
]

View File

@@ -2,11 +2,11 @@
{
"label":"",
"rows":[
[{"name":"name"}],
[{"name":"name", "fullWidth": true}],
[{"name":"status"}, {"name":"priority"}],
[{"name":"type"}, {"name":"number"}],
[{"name":"account"}],
[{"name":"description"}]
[{"name":"account", "fullWidth": true}],
[{"name":"description", "fullWidth": true}]
]
}
]

View File

@@ -3,6 +3,7 @@
"assignedUser",
"contact",
"createdAt",
"createdBy",
"number",
"modifiedAt",
"status",

View File

@@ -1 +1 @@
["opportunities","cases"]
["opportunities", "cases", "targetLists"]

View File

@@ -1,6 +1,7 @@
[
"accounts",
"createdAt",
"createdBy",
"expirationDate",
"type",
"status",

View File

@@ -0,0 +1,6 @@
[
"massEmail",
"status",
"target",
"sentAt"
]

View File

@@ -0,0 +1,20 @@
[
{
"name": "massEmail"
},
{
"name": "status",
"width": 12
},
{
"name": "target"
},
{
"name": "sentAt",
"width": 12
},
{
"name": "attemptCount",
"width": 10
}
]

View File

@@ -0,0 +1,17 @@
[
{
"name": "status",
"width": 30
},
{
"name": "target"
},
{
"name": "sentAt",
"width": 20
},
{
"name": "attemptCount",
"width": 10
}
]

View File

@@ -0,0 +1,20 @@
[
{
"name": "massEmail"
},
{
"name": "status",
"width": 12
},
{
"name": "target"
},
{
"name": "sentAt",
"width": 12
},
{
"name": "attemptCount",
"width": 10
}
]

View File

@@ -3,22 +3,25 @@
"label":"",
"rows":[
[
{"name":"name"}
{"name":"name", "fullWidth": true}
],
[
{"name":"emailAddress"}
{"name":"emailAddress", "fullWidth": true}
],
[
{"name":"phoneNumber"}
{"name":"phoneNumber", "fullWidth": true}
],
[
{"name":"accountName"}
{"name":"accountName", "fullWidth": true}
],
[
{"name":"title"}, {"name":"website"}
],
[
{"name":"status"}, {"name":"source"}
],
[
{"name":"description"}
{"name":"description", "fullWidth": true}
]
]
}

View File

@@ -3,6 +3,7 @@
"address",
"campaign",
"createdAt",
"createdBy",
"emailAddress",
"status",
"source",

View File

@@ -1,5 +1,6 @@
[
{"name":"name", "width":35, "link":true},
{"name":"status", "width":35},
{"name":"emailAddress", "notSortable": true}
{"name":"name", "width":32, "link":true},
{"name":"status", "width":17},
{"name":"emailAddress", "notSortable": true},
{"name":"createdAt", "width": 18}
]

View File

@@ -0,0 +1,30 @@
[
{
"label": "",
"rows": [
[
{"name": "name"},
{"name": "status"}
],
[
{"name": "campaign"},
{"name": "startAt"}
],
[
{"name": "targetLists"},
false
],
[
{"name": "emailTemplate"}, {"name": "storeSentEmails"}
],
[
{"name": "fromAddress"},
{"name": "fromName"}
],
[
{"name": "replyToAddress"},
{"name": "replyToName"}
]
]
}
]

View File

@@ -0,0 +1,31 @@
[
{
"label": "",
"rows": [
[
{"name": "name"},
{"name": "status"}
],
[
{"name": "startAt", "fullWidth": true}
],
[
{"name": "targetLists", "fullWidth": true}
],
[
{"name": "emailTemplate", "fullWidth": true}
],
[
{"name": "storeSentEmails"}
],
[
{"name": "fromAddress"},
{"name": "fromName"}
],
[
{"name": "replyToAddress"},
{"name": "replyToName"}
]
]
}
]

View File

@@ -0,0 +1,7 @@
[
"campaign",
"createdAt",
"createdBy",
"startAt",
"status"
]

View File

@@ -0,0 +1,7 @@
[
{"name": "name", "link": true, "width": 30},
{"name": "campaign"},
{"name": "status", "width": 14},
{"name": "startAt", "width": 14},
{"name": "emailTemplate"}
]

Some files were not shown because too many files have changed in this diff Show More