Compare commits

...

373 Commits
4.2.2 ... 4.3.0

Author SHA1 Message Date
yuri
4d5f3bcbfe v 2016-12-08 11:41:55 +02:00
yuri
339e1dc897 defaultSidePanelFieldList 2016-12-07 17:32:43 +02:00
yuri
c4e617f18e css 2016-12-07 15:48:35 +02:00
yuri
89bc329ea6 css 2016-12-07 13:21:47 +02:00
yuri
eee49b215e portal user field show up 2016-12-07 11:32:22 +02:00
yuri
d9a8b83268 remove isToSelf not used field 2016-12-07 10:56:32 +02:00
yuri
def7abe3ba fix mention notification 2016-12-07 10:55:33 +02:00
yuri
75e64b3836 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2016-12-06 15:52:56 +02:00
yuri
efb106c597 fix translated options 2016-12-06 15:50:48 +02:00
Taras Machyshyn
5e1087c6b4 Cache in language 2016-12-06 13:18:06 +02:00
yuri
5f8800defa fix css 2016-12-06 13:08:06 +02:00
yuri
445c0b3834 integration check 2016-12-06 12:24:07 +02:00
yuri
61c1e5ab6c fix mass action list 2016-12-06 11:37:40 +02:00
yuri
2502cad0c1 settings activityEntityList no emails 2016-12-05 14:20:50 +02:00
yuri
76032493b9 fix 2016-12-01 12:33:34 +02:00
yuri
0a3a6b3773 load users config params after login 2016-12-01 12:29:26 +02:00
yuri
014d47d017 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2016-12-01 11:45:00 +02:00
Taras Machyshyn
8bd7734ae3 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2016-12-01 11:22:48 +02:00
Taras Machyshyn
7aa2ba917f Added 'userItems' 2016-12-01 11:21:20 +02:00
yuri
96f0ce2381 fix template 2016-11-30 17:41:30 +02:00
yuri
f1eb5b9862 email notification about email received 2016-11-30 15:54:23 +02:00
yuri
45cb5a6e38 Merge branch 'master' of https://github.com/espocrm/espocrm 2016-11-30 11:13:43 +02:00
ayman-alkom
4131ea54ae remove decleration duplicate (#319) 2016-11-30 11:12:56 +02:00
yuri
3806affe1b add industry 2016-11-30 10:58:17 +02:00
yuri
6ea3a03de3 readme change 2016-11-29 17:55:59 +02:00
yuri
5b25e8b7e6 fix address validate error 2016-11-29 17:04:27 +02:00
yuri
60c96ba5f7 import status colors 2016-11-29 15:29:09 +02:00
yuri
9732852891 job search fix 2016-11-29 15:27:28 +02:00
yuri
326b46a6e6 fix setting max records per page and int max length 2016-11-29 15:18:31 +02:00
yuri
29924ea3b1 css fix 2016-11-29 12:37:47 +02:00
yuri
434506699b fix list width 2016-11-29 12:00:13 +02:00
yuri
eeaaaf7af1 deferred filter 2016-11-29 11:19:32 +02:00
yuri
3f5dab0680 fix task date end field 2016-11-29 11:03:51 +02:00
yuri
dd69658f2a css change 2016-11-29 10:37:23 +02:00
yuri
3c2d998a30 added industries 2016-11-28 16:54:09 +02:00
yuri
a0ead9d774 fix css 2016-11-25 16:24:39 +02:00
yuri
4460a105a8 dynamic logic ID field and mass email duplicate fix 2016-11-25 15:52:05 +02:00
yuri
c6b369587d fix css 2016-11-25 12:46:13 +02:00
yuri
8c19ad5f7f fix warning 2016-11-25 12:22:48 +02:00
yuri
1824e0d0a6 version 2016-11-22 14:29:35 +02:00
yuri
404bb0c3cd layout panel colors 2 2016-11-22 12:27:03 +02:00
yuri
7fd6b16cf3 fix color panels 2016-11-22 10:39:28 +02:00
yuri
47abd1ab52 middle panel styles 2016-11-21 15:32:10 +02:00
yuri
5c8d728ac5 cleanup 2016-11-21 15:09:55 +02:00
yuri
2706bc461e fix error 2016-11-21 15:09:36 +02:00
yuri
91f52e4f42 panel color and sidePanels Small layout 2016-11-21 14:58:08 +02:00
yuri
5538dd45d3 preferences: reload page after theme or language changed 2016-11-21 11:00:06 +02:00
yuri
105205a9aa change in record base 2016-11-21 10:59:50 +02:00
yuri
e3547717a0 change varchar filter type order 2016-11-21 10:35:09 +02:00
yuri
1714e0e9a0 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2016-11-21 10:30:50 +02:00
yuri
afdd15ac44 text field: contains filter by default 2016-11-21 10:28:54 +02:00
yuri
09097dba4a cleanup 2016-11-21 10:28:23 +02:00
yuri
01a8fbd5b1 fix label 2016-11-21 10:27:21 +02:00
yuri
f2d5eed7e3 company entity: check for duplicates 2016-11-21 10:26:27 +02:00
Taras Machyshyn
cfae3cde1d Label correction 2016-11-18 18:18:01 +02:00
Taras Machyshyn
87a0c1b293 Installer bug fixes 2016-11-18 18:17:37 +02:00
Taras Machyshyn
486c8c8f37 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2016-11-18 17:33:45 +02:00
Taras Machyshyn
ea99ca3d38 FileManager: bug fixes 2016-11-18 17:33:29 +02:00
yuri
2069a25f30 b2c mode in settings 2016-11-18 11:31:25 +02:00
yuri
baf3493044 show up date time on mouse over shortened dates 2016-11-18 11:23:12 +02:00
yuri
543a12a475 note post textarea height 2016-11-18 11:11:00 +02:00
yuri
a73f97744a fix email address add 2016-11-18 10:58:59 +02:00
yuri
ae135ab678 fix mention text 2016-11-17 17:25:08 +02:00
yuri
96366548a3 fix avatar for deleted user and campaign tracker 1px 2016-11-17 10:49:00 +02:00
yuri
54cdd5180d industry 2016-11-17 10:25:56 +02:00
yuri
0a5ce2a4d4 industries 2016-11-16 17:09:50 +02:00
yuri
a4e1ccc4bb fix label 2016-11-16 15:28:30 +02:00
Taras Machyshyn
55c632979e Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2016-11-16 15:25:01 +02:00
Taras Machyshyn
b1208a449e Integration tests: added tests for upgrade, extension installation 2016-11-16 15:24:48 +02:00
Taras Machyshyn
9e2b329beb Integration tests improvements 2016-11-16 15:23:49 +02:00
Taras Machyshyn
10d329094c Added 'deleteBeforeCopy' for upgrade/extensions 2016-11-16 15:21:39 +02:00
yuri
38f44b3e5f cases layout change 2016-11-16 13:06:42 +02:00
yuri
c99a1261d5 lead industry field 2016-11-16 11:54:17 +02:00
yuri
4e2e344892 industry field additions 2016-11-16 11:45:31 +02:00
yuri
e0128bf54f contact-document relation 2016-11-16 11:17:46 +02:00
yuri
a683e6bdf8 improve export 2016-11-15 12:15:01 +02:00
yuri
bc64effb14 cleanup 2016-11-14 13:23:22 +02:00
yuri
6b0a280e2c fix layout swaping 2016-11-14 12:44:36 +02:00
yuri
ddf25e02e3 email: has attachment filter 2016-11-14 10:39:18 +02:00
yuri
967d5a6387 addition to about page 2016-11-11 16:46:11 +02:00
yuri
a5137b85af downgrade zendservicemanager 2016-11-11 16:34:38 +02:00
yuri
1ed79fedf0 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2016-11-10 14:44:40 +02:00
yuri
8a8cb08b99 order export 2016-11-10 14:41:39 +02:00
yuri
b49aaa69cc fix audited 2016-11-10 14:14:46 +02:00
yuri
015357d551 industries 2016-11-10 13:19:34 +02:00
yuri
5419d76080 link field empty name and audited note change 2016-11-10 12:58:54 +02:00
yuri
21e1422994 filed manager hook change 2016-11-09 11:43:30 +02:00
yuri
dafb076659 field manager inficate that field is readOnly 2016-11-09 10:56:19 +02:00
Taras Machyshyn
491f3d27ff Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2016-11-08 12:13:35 +02:00
Taras Machyshyn
3be58d5d09 Integration test improvements 2016-11-08 12:13:25 +02:00
yuri
8aeb23a985 version 2016-11-08 11:46:19 +02:00
yuri
fa99040232 downgrade zend 2016-11-08 11:45:32 +02:00
yuri
2e928f2442 clenup 2016-11-08 11:33:51 +02:00
yuri
8ebb35c0d5 my activities dashlet entity list 2016-11-07 17:18:04 +02:00
yuri
7d17bee201 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2016-11-07 16:06:12 +02:00
yuri
5c345fb4d9 import: idle mode 2016-11-07 16:01:52 +02:00
yuri
d2cd098ab0 fix record view readOnly 2016-11-07 15:09:15 +02:00
yuri
f4a88df067 import: skip search for duplicates 2016-11-07 12:54:14 +02:00
yuri
8f4917eb24 delete duplicates before import 2016-11-07 11:46:09 +02:00
yuri
a9310b108a fix warning 2016-11-07 10:45:52 +02:00
Taras Machyshyn
8c638dffd4 commented out sendRequest tests 2016-11-04 18:56:07 +02:00
yuri
0151273a75 cleanup 2016-11-04 17:39:00 +02:00
yuri
b10dc8128c conrollerManager in container 2016-11-04 17:32:58 +02:00
yuri
476d36c9c5 comment sendRequest tests 2016-11-04 17:22:55 +02:00
yuri
64087defd0 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2016-11-04 17:21:18 +02:00
yuri
807b7bc349 tooltip 2016-11-04 16:57:18 +02:00
yuri
3018c345fa entity manager tooltips 2016-11-04 16:34:12 +02:00
yuri
8f35c59943 ckeanup 2016-11-04 15:34:49 +02:00
yuri
348332f7ca entity manager: fix loading after creation 2016-11-04 12:52:24 +02:00
yuri
9ebd992222 default status styles 2016-11-04 12:43:49 +02:00
yuri
9597ea0dbd stream messages improvements 2016-11-04 12:29:39 +02:00
yuri
23fb6c6e1f status field in entity manager 2016-11-04 11:36:28 +02:00
Taras Machyshyn
ddb5033de8 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2016-11-03 18:00:15 +02:00
Taras Machyshyn
43a49e3c31 Integration tests: added 'createUser' method, portal support 2016-11-03 18:00:03 +02:00
yuri
5a06ee50d8 fix notice 2016-11-03 16:19:43 +02:00
yuri
c02f65ef81 audited relations 2016-11-03 16:14:19 +02:00
yuri
dbb9b20b7f fix case compose email 2016-11-03 11:37:39 +02:00
yuri
3bfd540f53 kb: move to top move to bottom 2016-11-03 11:11:03 +02:00
yuri
a971f7357f fix dynamic logic 2016-11-02 16:31:04 +02:00
yuri
4918f8f985 task reminders 2016-11-02 16:30:56 +02:00
Taras Machyshyn
988388ed16 Added 'sendRequest' possibility 2016-11-02 15:52:38 +02:00
yuri
f1de9deb80 event entity: reminders 2016-11-02 12:08:01 +02:00
yuri
2642a6ea6e fix notice 2016-11-02 11:52:42 +02:00
yuri
74b82c92d8 fix default layout detailSmall 2016-11-02 11:15:38 +02:00
yuri
ded3d112b2 created and modified fields fixes 2016-11-02 11:11:51 +02:00
yuri
c9e7f02b23 cut text columns 2016-11-01 15:07:18 +02:00
yuri
c7fb864e04 email changes and test 2016-11-01 12:54:51 +02:00
yuri
16d300445c fix emails 2016-11-01 12:43:20 +02:00
yuri
edc24621a6 fix php mime mail parser 2016-10-31 15:52:38 +02:00
yuri
17b48c0e4c fix wysywyg issue with base tags 2016-10-31 15:52:29 +02:00
yuri
771d58f425 phpmimemailparser 2016-10-31 11:30:14 +02:00
Felix Kaechele
fd76459077 Fix de_DE translation (#269)
* Fix escape symbols in translations

Signed-off-by: Felix Kaechele <felix@kaechele.ca>

* Update German translation

Signed-off-by: Felix Kaechele <felix@kaechele.ca>
2016-10-28 15:21:27 +03:00
yuri
1955dff68e manual merge 2016-10-28 14:50:48 +03:00
yuri
730b5fb8a9 fix calendar dashlet 2016-10-28 14:46:37 +03:00
yuri
c1a115e2b0 reminders: skip if declined 2016-10-21 15:55:03 +03:00
yuri
7773e41ad4 convert lead changes 2016-10-21 14:48:58 +03:00
yuri
304bbb62ad fix naming 2016-10-21 12:05:36 +03:00
yuri
7b439ace04 cleanup 2016-10-21 11:32:10 +03:00
yuri
d9ef87fb90 fix naming 2016-10-21 11:30:21 +03:00
yuri
2333b7a1ef user permission = all by default 2016-10-21 11:21:03 +03:00
yuri
1a00932c63 Merge branch 'hotfix/4.2.7' 2016-10-20 15:48:49 +03:00
yuri
1f09015c86 fix ids 2016-10-20 15:48:33 +03:00
yuri
5d79d8520f email: show group email account if email address matches 2016-10-20 15:04:26 +03:00
yuri
dde044d8c5 imptove email/phone filters 2016-10-20 14:53:47 +03:00
yuri
aeeb84b626 fix text and varchar 2016-10-20 14:47:18 +03:00
yuri
ccb9022e16 fix email address search 2016-10-20 12:47:44 +03:00
yuri
a9e783b5f1 email autocomplete: users first 2016-10-20 11:36:52 +03:00
yuri
46190df6ff fix notice 2016-10-20 11:30:19 +03:00
yuri
1eb258cab7 email filtering fix 2016-10-20 11:26:48 +03:00
yuri
a3836b0091 email fix 2016-10-19 16:38:27 +03:00
yuri
7fbc9fbac8 email fix 2016-10-19 16:38:00 +03:00
yuri
d0b5e2188b add marketing industry 2016-10-19 16:13:01 +03:00
yuri
dfa2d6be89 email notifications related to parent 2016-10-19 16:04:43 +03:00
yuri
276941197f email importer refactoring 2016-10-19 15:46:07 +03:00
yuri
20569e4252 fix notices 2016-10-19 12:47:54 +03:00
yuri
3b7f99daa1 fix import 2016-10-19 12:14:21 +03:00
Taras Machyshyn
5ee30d9f3f Integration test improvements 2016-10-17 17:35:44 +03:00
yuri
3cde7fa5e3 company type fix 2016-10-11 15:11:36 +03:00
yuri
aecb9bbdf4 lang 2016-10-11 15:11:25 +03:00
yuri
1a95e14ee8 entity manager improvements 2016-10-11 12:32:28 +03:00
yuri
83aa07b918 entity manager remove layouts 2016-10-11 11:30:53 +03:00
yuri
4f8eea59fe removeDir method 2016-10-11 11:28:45 +03:00
yuri
248280e086 fix entity manager 2016-10-11 11:28:38 +03:00
yuri
02b465ab8c company entity type 2016-10-11 11:22:48 +03:00
yuri
c9d26dc1d8 base plus entity template 2016-10-10 17:32:03 +03:00
yuri
389b158134 look up email address in custom entities 2016-10-10 16:35:30 +03:00
yuri
312af9befb change person layouts 2016-10-10 16:19:34 +03:00
yuri
b7f22bf687 Merge branch 'stable' 2016-10-10 15:33:27 +03:00
yuri
f062ac255a version 2016-10-10 15:19:14 +03:00
yuri
dd8082244c activities and tasks for person type 2016-10-10 12:37:21 +03:00
yuri
25ff7e17c1 activities dashlet change 2016-10-07 15:27:17 +03:00
yuri
f4b3071a92 change readme 2016-10-07 14:50:34 +03:00
yuri
0c7b182215 Merge branch 'stable' 2016-10-07 14:36:32 +03:00
yuri
2223a1ecf4 getActivitiesQuery change 2016-10-07 12:54:10 +03:00
yuri
3a24784980 fix email filter 2016-10-07 12:46:46 +03:00
yuri
f86564a580 tests 2016-10-07 12:46:27 +03:00
yuri
58897949eb fix email filter 2016-10-07 12:46:19 +03:00
yuri
2dee859b2a fix link field 2016-10-07 12:10:57 +03:00
yuri
b1dbd173ad fix mail filters 2016-10-07 11:14:37 +03:00
yuri
a9fc290627 mail filter test 2016-10-07 11:14:15 +03:00
yuri
12794968fc fix mail filters 2016-10-07 11:14:02 +03:00
yuri
1b4cbb91ee activities refactor additional 2016-10-06 17:26:51 +03:00
yuri
62f1362cf6 activities big refactor 2016-10-06 16:46:52 +03:00
yuri
49031d0b33 fix orm 2016-10-06 11:49:57 +03:00
yuri
22fea07b0a fix orm 2016-10-05 17:54:00 +03:00
yuri
81692371ec search by hasOne link 2016-10-05 15:35:53 +03:00
yuri
a96314f0f0 Merge branch 'hotfix/4.2.6' 2016-10-05 12:37:45 +03:00
yuri
a92c294255 fix layout manager reset to default 2016-10-05 12:37:38 +03:00
yuri
7c554a605e originalLead field 2016-10-05 12:07:08 +03:00
yuri
57eeec63c1 remove opportunities link from lead 2016-10-05 11:44:07 +03:00
yuri
bd158008b2 update tcpdf 2016-10-04 12:58:24 +03:00
yuri
c0ae8d5cdd Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2016-10-03 12:17:33 +03:00
yuri
78347920aa fix settings entityDefs 2016-10-03 12:17:26 +03:00
yuri
c88e662a30 mass email: massEmailDisableMandatoryOptOutLink 2016-10-03 12:03:28 +03:00
yuri
5ae57ff5f6 fix campaign log percentage 2016-10-03 11:32:06 +03:00
Taras Machyshyn
7c74c85a66 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2016-09-29 17:48:00 +03:00
Taras Machyshyn
07fc2123e7 Added testData cache folder to gitignore 2016-09-29 17:47:46 +03:00
yuri
afdb47b601 fix test email 2016-09-29 17:35:57 +03:00
yuri
5ea437afbb Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2016-09-29 17:30:49 +03:00
yuri
c7041546c4 fix number test 2016-09-29 17:27:26 +03:00
yuri
1d2fd21a06 Merge branch 'hotfix/4.2.6' 2016-09-29 17:25:35 +03:00
yuri
27624ead8d number fix 2 2016-09-29 17:25:25 +03:00
Taras Machyshyn
fefc003984 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2016-09-29 17:21:23 +03:00
Taras Machyshyn
70d3bf9fff Integration tests: rename method to 'createApplication' 2016-09-29 17:21:05 +03:00
yuri
4a64c4b648 Merge branch 'hotfix/4.2.6' 2016-09-29 17:21:01 +03:00
yuri
15c980e57f rename number to number util 2016-09-29 17:20:52 +03:00
yuri
9d8fc5b8ea sort by address field 2016-09-28 17:46:14 +03:00
yuri
8184221d2b Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2016-09-28 16:15:31 +03:00
yuri
5786983d8b add team to roles 2016-09-28 16:13:53 +03:00
yuri
1e1468ab68 acl changes and user role 2016-09-28 15:36:41 +03:00
yuri
e3f0177759 role changes 2016-09-27 16:46:14 +03:00
yuri
f1fc6160fe fix typo 2016-09-27 16:30:59 +03:00
yuri
38d4a39014 campaign log to target list 2016-09-27 15:13:25 +03:00
yuri
566d0c1aa0 fix orm query in array 2016-09-27 12:35:40 +03:00
yuri
ce752b32fd naming fix 2016-09-27 11:15:59 +03:00
Taras Machyshyn
a61553d08b Improved tests 2016-09-26 16:36:47 +03:00
yuri
daacb026d8 panel actions changes 2016-09-26 16:16:40 +03:00
yuri
0ae637fba2 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2016-09-26 12:50:41 +03:00
yuri
b81fd66b85 remove note with parent remval 2016-09-26 12:48:57 +03:00
yuri
071e94ac97 Merge branch 'hotfix/4.2.6' 2016-09-26 12:04:32 +03:00
yuri
a98465f30e skip notification checking while upgrade 2016-09-26 12:04:10 +03:00
yuri
e949a0bae6 dashboard: ability to rename tabs 2016-09-26 11:31:29 +03:00
yuri
f7f9e2fda0 fix export 2016-09-23 11:37:21 +03:00
yuri
80a0571b0c export: skip attributes 2016-09-23 11:31:38 +03:00
yuri
d91897a922 fix export 2016-09-22 17:31:42 +03:00
Taras Machyshyn
05180ece92 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2016-09-22 17:25:40 +03:00
Taras Machyshyn
ee8e960ce4 Added integration tests 2016-09-22 17:25:26 +03:00
yuri
5b4a8666cd Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2016-09-22 17:17:21 +03:00
yuri
7240757a28 export field list 2016-09-22 17:15:27 +03:00
yuri
2a8baa2f34 export: use POST request 2016-09-22 14:54:35 +03:00
yuri
0c44e0d101 duplicate target list only not opted out 2016-09-22 12:45:23 +03:00
yuri
63ab5b77f3 relationship copy change 2016-09-22 12:32:53 +03:00
yuri
300198c9fa duplicate with links 2016-09-22 11:24:21 +03:00
yuri
340af18beb Merge branch 'hotfix/4.2.6' 2016-09-20 15:58:34 +03:00
yuri
8862fd279e fix language enum translation 2016-09-20 15:47:58 +03:00
Taras Machyshyn
83cff1bc42 Added favicon to installator 2016-09-20 11:54:51 +03:00
yuri
a75534e32a Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2016-09-20 11:52:07 +03:00
Taras Machyshyn
462eb8792c Added test cache to gitignore 2016-09-20 11:51:57 +03:00
yuri
19f29c0401 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2016-09-20 11:49:41 +03:00
Taras Machyshyn
27657260fc Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2016-09-20 11:47:40 +03:00
yuri
c17fb9ab1e Merge branch 'hotfix/4.2.6' 2016-09-16 17:46:31 +03:00
yuri
bd6eafd291 version 2016-09-16 17:44:26 +03:00
yuri
a63306603a fix email filter 2016-09-16 17:44:10 +03:00
yuri
d3b1114b1e filters refactor 2016-09-16 16:38:10 +03:00
yuri
87340e29e8 base field: search data 2016-09-16 15:38:21 +03:00
yuri
4f1f8d9301 lead: created fields filter populate 2016-09-16 14:51:20 +03:00
yuri
664b294471 bool notNull 2016-09-16 11:23:44 +03:00
yuri
7774c12ab2 lang 2016-09-16 11:02:14 +03:00
yuri
6592dfc4da mass retrive from trash 2016-09-16 11:00:19 +03:00
yuri
e823ecc178 user: create portal user fix 2016-09-15 12:59:06 +03:00
yuri
74d5a737db int/float: isEmpty, isNotEmpty filters 2016-09-15 12:07:26 +03:00
yuri
bf9799af66 fix markdown link syntax 2016-09-14 17:09:10 +03:00
yuri
fc9b67672f date: isEmpty filter 2016-09-14 16:14:06 +03:00
yuri
6e8342511f lang 2016-09-14 16:01:29 +03:00
yuri
da2cebd765 fix orm 3 2016-09-14 14:03:30 +03:00
yuri
ef19dc3330 fix orm 2 2016-09-14 13:53:11 +03:00
Taras Machyshyn
2f73a2d19b Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2016-09-14 13:25:07 +03:00
yuri
fab257eb97 fix orm 2016-09-14 13:07:32 +03:00
yuri
850c3fd696 orm: get parent relation 2016-09-14 13:00:22 +03:00
yuri
a619738652 fix dashboard javascript error 2016-09-13 16:53:06 +03:00
Taras Machyshyn
395c575f78 Moved tests into 'unit' folder 2016-09-13 16:20:42 +03:00
yuri
ecf8256b14 fix textFilterFields list 2016-09-13 15:26:51 +03:00
yuri
2d6d6a2b19 link parent: search by parent type 2016-09-13 15:17:49 +03:00
yuri
ee404fa400 parentType = null if empty 2016-09-13 15:05:38 +03:00
yuri
10fc546369 Merge branch 'hotfix/4.2.5' 2016-09-13 13:13:00 +03:00
yuri
0ea0c09007 fix acl 2016-09-13 13:12:39 +03:00
yuri
85b2dbac11 Merge branch 'hotfix/4.2.5' 2016-09-13 10:28:39 +03:00
yuri
d69f994f4c fix insert template issue with signature 2016-09-13 10:28:24 +03:00
yuri
12a368054a dynamic logic options 2016-09-12 17:27:39 +03:00
yuri
7a2a9de174 Merge branch 'hotfix/4.2.5' of ssh://172.20.0.1/var/git/espo/backend into hotfix/4.2.5 2016-09-12 11:17:15 +03:00
yuri
0e14f2b987 fix dynamic logic save 2016-09-12 11:16:36 +03:00
yuri
a50d97adf5 cleanup 2016-09-09 16:36:57 +03:00
yuri
62e9138cf1 minor core changes, metadata delete fix 2016-09-09 16:33:43 +03:00
yuri
657e7f0e37 email sender: define sender header 2016-09-08 15:05:58 +03:00
yuri
66d648c7d4 hack zend messageId bug 2016-09-08 12:42:53 +03:00
yuri
08dd77a6b6 metadata refactoring 2016-09-08 11:59:33 +03:00
yuri
037f0d4db1 dynamicLogic edit 3 2016-09-07 17:18:40 +03:00
yuri
19ff591105 fix date field default null 2016-09-07 15:17:00 +03:00
Taras Machyshyn
7d2655f757 Fixed warnings on PHP 7 2016-09-07 15:04:06 +03:00
yuri
7b1a1be366 dynamicLogic edit 2 2016-09-06 16:59:50 +03:00
yuri
1fc3ae72b5 dynamicLogic edit 1 2016-09-05 17:01:41 +03:00
yuri
991b864881 tabs fix 2016-09-05 11:35:04 +03:00
yuri
f95fa96ae1 fix package.json 2016-09-05 11:13:23 +03:00
yuri
e33cdf268a dynamicLogic change 2016-09-05 11:12:36 +03:00
yuri
dcae13f4dc dynamicLogic string dev 2016-09-02 17:17:31 +03:00
yuri
96ac275970 client: fix naming 2016-09-02 16:17:09 +03:00
yuri
e58a4c4bbf fix stream status update message 2016-09-02 12:43:26 +03:00
yuri
c35985e658 Merge branch 'master' of https://github.com/espocrm/espocrm 2016-09-02 12:40:19 +03:00
yuri
4bc5a89ac2 fix stream status update message 2016-09-02 12:40:09 +03:00
yuri
b7e14d60cc dynamicLogic string change 2016-09-02 12:31:45 +03:00
Mauricio Panuncio
7164a71d88 es_ES - Complete translations to today (#219)
* Translate es_ES: Globals. Admin.

* Tickets.

* Emails.

* New translations (missing).

* New translations (missing) II.

* Various.

* Opportunities: Improvement. Self-explanatory names.

* Some small fixes.

* Undo Tickets. Redo Casos.
2016-09-02 12:30:13 +03:00
yuri
6bedd86e08 dynamic logic development 2016-09-01 17:38:47 +03:00
yuri
d39e99cbdd dynamic logic changes 2016-09-01 11:24:13 +03:00
yuri
ab3134f44a dynamicLogic additional compare types 2016-08-31 17:23:00 +03:00
yuri
5aaca9921c dynamicLogic core 2 2016-08-31 16:28:25 +03:00
yuri
9223e43b7c htmlizer: format number fix 2016-08-31 14:55:12 +03:00
yuri
3766dd16e7 dynamicLogic core 1 2016-08-31 14:48:51 +03:00
yuri
e53aff0031 fill primary account on selecting from account 2016-08-30 15:30:06 +03:00
yuri
4a40f1fba7 trim smtp credentials 2016-08-30 14:51:08 +03:00
yuri
8432e9dcc8 fix endWidth search 2016-08-30 13:05:41 +03:00
yuri
04e9bd9de3 fix endWidth search 2016-08-30 13:04:52 +03:00
yuri
ed8ed591f7 user: search by position 2016-08-30 13:00:37 +03:00
yuri
34ff0218b9 email notifications about posts w/o parent 2016-08-29 17:12:42 +03:00
yuri
2f6c928077 fix notification parent not null 2016-08-29 16:40:24 +03:00
yuri
eff0152f44 layout manager: Side Panels 2016-08-29 15:51:17 +03:00
yuri
a3188ce694 preferences: doNotFillAssignedUserIfNotRequired 2016-08-29 12:36:21 +03:00
yuri
4bf81be56f fix naming 2016-08-29 12:22:37 +03:00
yuri
7c22960f42 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2016-08-26 17:44:40 +03:00
yuri
3d6f90ad93 fix field manager 2016-08-26 17:44:04 +03:00
yuri
a02aebeed2 tooltips editable 2016-08-26 15:01:41 +03:00
Taras Machyshyn
d06afd4def Deleted unnecessary file 2016-08-26 14:25:30 +03:00
Taras Machyshyn
88e148fca8 Possibility to redefine hooks 2016-08-26 14:23:41 +03:00
Taras Machyshyn
2d00ef314f Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2016-08-26 14:22:35 +03:00
yuri
0b6a29a035 field manager: reset to detault 2016-08-26 12:19:50 +03:00
yuri
5feb0eb7c7 fix FieldManager controller 2016-08-26 11:21:09 +03:00
yuri
48cb718fbe naming change 2016-08-26 10:51:09 +03:00
yuri
13ad6ec0d0 field manager: change tpl 2016-08-26 10:48:36 +03:00
yuri
9cfdb3a00b Merge branch 'master' of https://github.com/espocrm/espocrm 2016-08-25 17:24:16 +03:00
rodrigoscoelho
b560254df2 Adding Home Page APP meta info for Android and IOS (#145)
This will allow the program to start as a separated program on Android and IOS. As a native program.
2016-08-25 17:23:44 +03:00
yuri
101ed19073 shortcut icon 2016-08-25 17:23:03 +03:00
yuri
65868fd26a fix unsubscribe 2016-08-25 17:07:30 +03:00
yuri
d2e5b26765 update zend 2016-08-25 17:00:34 +03:00
yuri
2cac1ea608 unsubscribe changes 2016-08-25 16:54:30 +03:00
yuri
9d37e68e24 sync contact name on portal user update 2016-08-25 13:01:08 +03:00
yuri
290a1c5b31 php 5.4 drop 2 2016-08-25 13:00:50 +03:00
yuri
ff924f666f drop php 5.4 support 2016-08-25 12:25:30 +03:00
yuri
8c5eb9c913 update zend 2016-08-25 12:19:06 +03:00
yuri
25e72fa6a5 Merge branch 'hotfix/4.2.5' 2016-08-25 11:58:24 +03:00
yuri
76602aede1 email import: fix for emails w/o subject header 2016-08-25 11:45:56 +03:00
Ayman Alkom
77411702a7 remove repeated key (#205) 2016-08-25 10:44:29 +03:00
Ayman Alkom
1aeee24d97 remove repeated key (#205) 2016-08-25 10:43:53 +03:00
yuri
d8cd0d31db email template foreign fields and acl 2016-08-25 10:43:06 +03:00
yuri
e72557bb59 Merge branch 'hotfix/4.2.5' 2016-08-24 11:09:41 +03:00
yuri
84e587fcaf mass email: automatically fill name 2016-08-24 10:49:53 +03:00
yuri
f8e75dccdf version 2016-08-23 15:41:32 +03:00
yuri
b6eca1db17 fix email entityDefs 2016-08-23 15:39:00 +03:00
yuri
6ff414ffcd fix users teams field 2016-08-23 15:26:00 +03:00
yuri
b54c9a797d Merge branch 'hotfix/4.2.5' 2016-08-23 12:05:51 +03:00
yuri
7acbf3084d template: related attributes 2016-08-23 12:05:29 +03:00
yuri
87847446a8 css cleanup 2016-08-23 11:18:20 +03:00
yuri
59f250b386 allow template entity 2016-08-23 11:17:39 +03:00
yuri
f93ce594d0 add mobile phone type to account 2016-08-23 11:13:58 +03:00
yuri
c41f4e51e5 fix filter fields css 2016-08-23 11:01:17 +03:00
yuri
6efd355864 remove tabs 2016-08-23 10:11:37 +03:00
Ayman Alkom
7ee2ee9d35 small syntax fixes (#197)
* fix variable name

* init $number variable

* another fix
2016-08-23 10:09:15 +03:00
Ayman Alkom
737215c599 small syntax fixes (#197)
* fix variable name

* init $number variable

* another fix
2016-08-23 10:08:17 +03:00
Yuri Kuznetsov
ced0c25a08 Merge pull request #194 from grzegorzgie/patch-3
Fix pl_PL
2016-08-22 19:51:22 +03:00
Yuri Kuznetsov
5e810d2aa3 Merge pull request #193 from grzegorzgie/patch-2
Fix pl_PL
2016-08-22 19:51:09 +03:00
Yuri Kuznetsov
c9e27dd190 Merge pull request #192 from grzegorzgie/patch-2
Fix pl_PL
2016-08-22 19:50:12 +03:00
yuri
1f8a14aca0 fix warning 2016-08-22 15:50:21 +03:00
yuri
f518ae59c0 activities small refactoring 2016-08-22 12:27:18 +03:00
Taras Machyshyn
c5866f3d86 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2016-08-22 12:21:16 +03:00
yuri
4d4bde46f8 version 2016-08-22 11:02:41 +03:00
yuri
cb89ad59af cleanup 2016-08-22 11:00:03 +03:00
yuri
092b5024fe it_IT language 2016-08-22 10:36:17 +03:00
grzegorzgie
8221f7182b Fix pl_PL 2016-08-21 13:21:43 +02:00
grzegorzgie
ecee739b1a Fix pl_PL 2016-08-21 13:16:03 +02:00
grzegorzgie
702b47296c Fix pl_PL
Repair linguistic errors
2016-08-21 13:02:22 +02:00
yuri
517edf2ced undo comment 2016-08-19 17:52:09 +03:00
yuri
219c28313c fix mass select 2016-08-19 17:48:14 +03:00
Taras Machyshyn
656bba1d3f Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2016-08-19 17:34:57 +03:00
yuri
311d92202b fix assignment notification if user is removed 2016-08-19 14:43:44 +03:00
yuri
05cb2ee272 email: fix move to trash update counts 2016-08-18 17:07:16 +03:00
yuri
f896a2d71a fix calendar range issue 2016-08-18 11:13:24 +03:00
yuri
dd6704ace5 fix moving to trash 2016-08-18 10:40:16 +03:00
yuri
1dc4d44a65 es_ES lang fix 2016-08-17 17:52:22 +03:00
yuri
bfb28ea178 Merge branch 'hotfix/4.2.3' of ssh://172.20.0.1/var/git/espo/backend into hotfix/4.2.3 2016-08-17 17:43:43 +03:00
yuri
ec6f3a22f2 version 2016-08-17 17:30:34 +03:00
yuri
5a0c7c330c fix email imported error in php4 2016-08-17 17:28:17 +03:00
Taras Machyshyn
f64df5af87 LDAP fixed admin login 2016-08-17 11:54:59 +03:00
Taras Machyshyn
d4dc7a4051 LDAP fixes 2016-08-16 16:53:40 +03:00
Taras Machyshyn
d676f85c8f LDAP fixes 2016-08-16 16:01:32 +03:00
Taras Machyshyn
ab382f2387 LDAP: changed ldapUserObjectClass attribute. 2016-08-15 16:46:55 +03:00
790 changed files with 20314 additions and 6626 deletions

4
.gitignore vendored
View File

@@ -14,7 +14,9 @@
/client/css/sakura-vertical.css
/client/css/violet.css
/client/css/violet-vertical.css
/tests/testData/cache/*
/tests/unit/testData/cache/*
!/tests/unit/testData/cache/.data
/tests/integration/config.php
composer.phar
vendor/
/custom/Espo/Custom/*

View File

@@ -8,14 +8,14 @@ Download the latest release from our [website](http://www.espocrm.com).
### Requirements
* PHP 5.4 or above (with pdo, json, gd, mcrypt extensions);
* PHP 5.5 or above (with pdo, json, gd, mcrypt extensions);
* MySQL 5.1 or above.
For more information about server configuration see [this article](http://blog.espocrm.com/administration/server-configuration-for-espocrm/).
### How to report bug
Create an issue [here](https://github.com/espocrm/espocrm/issues) or post on our [forum](http://forum.espocrm.com/bug-reports?routestring=forum/bug-reports).
Create an issue [here](https://github.com/espocrm/espocrm/issues) or post on our [forum](http://forum.espocrm.com/forum/bug-reports).
### How to get started (for developers)

View File

@@ -0,0 +1,41 @@
<?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/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Acl;
use \Espo\ORM\Entity;
class Team extends \Espo\Core\Acl\Base
{
public function checkInTeam(\Espo\Entities\User $user, Entity $entity)
{
$userTeamIdList = $user->getLinkMultipleIdList('teams');
return in_array($entity->id, $userTeamIdList);
}
}

View File

@@ -60,11 +60,17 @@ class App extends \Espo\Core\Controllers\Base
}
$userData['emailAddressList'] = $emailAddressList;
$settings = (object)[];
foreach ($this->getConfig()->get('userItems') as $item) {
$settings->$item = $this->getConfig()->get($item);
}
return array(
'user' => $userData,
'acl' => $this->getAcl()->getMap(),
'preferences' => $preferences,
'token' => $this->getUser()->get('token')
'token' => $this->getUser()->get('token'),
'settings' => $settings
);
}

View File

@@ -44,8 +44,13 @@ class Attachment extends \Espo\Core\Controllers\Record
throw new Forbidden();
}
list($prefix, $contents) = explode(',', $data);
$contents = base64_decode($contents);
$arr = explode(',', $data);
if (count($arr) > 1) {
list($prefix, $contents) = $arr;
$contents = base64_decode($contents);
} else {
$contents = '';
}
$attachment = $this->getEntityManager()->getEntity('Attachment');
$this->getEntityManager()->saveEntity($attachment);

View File

@@ -40,32 +40,32 @@ class AuthToken extends \Espo\Core\Controllers\Record
}
}
public function actionUpdate($params, $data)
public function actionUpdate($params, $data, $request)
{
throw new Forbidden();
}
public function actionCreate($params, $data)
public function actionCreate($params, $data, $request)
{
throw new Forbidden();
}
public function actionListLinked($params, $data)
public function actionListLinked($params, $data, $request)
{
throw new Forbidden();
}
public function actionMassUpdate($params, $data)
public function actionMassUpdate($params, $data, $request)
{
throw new Forbidden();
}
public function actionCreateLink($params, $data)
public function actionCreateLink($params, $data, $request)
{
throw new Forbidden();
}
public function actionRemoveLink($params, $data)
public function actionRemoveLink($params, $data, $request)
{
throw new Forbidden();
}

View File

@@ -198,6 +198,13 @@ class EntityManager extends \Espo\Core\Controllers\Base
$params['linkMultipleFieldForeign'] = $data['linkMultipleFieldForeign'];
}
if (array_key_exists('audited', $data)) {
$params['audited'] = $data['audited'];
}
if (array_key_exists('auditedForeign', $data)) {
$params['auditedForeign'] = $data['auditedForeign'];
}
$result = $this->getContainer()->get('entityManagerUtil')->createLink($params);
if ($result) {
@@ -242,6 +249,13 @@ class EntityManager extends \Espo\Core\Controllers\Base
$params['linkMultipleFieldForeign'] = $data['linkMultipleFieldForeign'];
}
if (array_key_exists('audited', $data)) {
$params['audited'] = $data['audited'];
}
if (array_key_exists('auditedForeign', $data)) {
$params['auditedForeign'] = $data['auditedForeign'];
}
$result = $this->getContainer()->get('entityManagerUtil')->updateLink($params);
if ($result) {

View File

@@ -94,17 +94,17 @@ class Extension extends \Espo\Core\Controllers\Record
return true;
}
public function actionCreate($params, $data)
public function actionCreate($params, $data, $request)
{
throw new Forbidden();
}
public function actionUpdate($params, $data)
public function actionUpdate($params, $data, $request)
{
throw new Forbidden();
}
public function actionPatch($params, $data)
public function actionPatch($params, $data, $request)
{
throw new Forbidden();
}
@@ -139,12 +139,12 @@ class Extension extends \Espo\Core\Controllers\Record
throw new Forbidden();
}
public function actionCreateLink($params, $data)
public function actionCreateLink($params, $data, $request)
{
throw new Forbidden();
}
public function actionRemoveLink($params, $data)
public function actionRemoveLink($params, $data, $request)
{
throw new Forbidden();
}

View File

@@ -31,7 +31,8 @@ namespace Espo\Controllers;
use \Espo\Core\Exceptions\Error,
\Espo\Core\Exceptions\Forbidden,
\Espo\Core\Exceptions\NotFound;
\Espo\Core\Exceptions\NotFound,
\Espo\Core\Exceptions\BadRequest;
class FieldManager extends \Espo\Core\Controllers\Base
{
@@ -44,19 +45,23 @@ class FieldManager extends \Espo\Core\Controllers\Base
public function actionRead($params, $data)
{
if (empty($params['scope']) || empty($params['name'])) {
throw new BadRequest();
}
$data = $this->getContainer()->get('fieldManager')->read($params['name'], $params['scope']);
if (!isset($data)) {
throw new NotFound();
throw new BadRequest();
}
return $data;
}
public function actionCreate($params, $data)
public function postActionCreate($params, $data)
{
if (empty($data['name'])) {
throw new Error("Field 'name' cannnot be empty");
if (empty($params['scope']) || empty($data['name'])) {
throw new BadRequest();
}
$fieldManager = $this->getContainer()->get('fieldManager');
@@ -72,8 +77,12 @@ class FieldManager extends \Espo\Core\Controllers\Base
return $fieldManager->read($data['name'], $params['scope']);
}
public function actionUpdate($params, $data)
public function putActionUpdate($params, $data)
{
if (empty($params['scope']) || empty($params['name'])) {
throw new BadRequest();
}
$fieldManager = $this->getContainer()->get('fieldManager');
$fieldManager->update($params['name'], $data, $params['scope']);
@@ -86,8 +95,12 @@ class FieldManager extends \Espo\Core\Controllers\Base
return $fieldManager->read($params['name'], $params['scope']);
}
public function actionDelete($params, $data)
public function deleteActionDelete($params, $data)
{
if (empty($params['scope']) || empty($params['name'])) {
throw new BadRequest();
}
$res = $this->getContainer()->get('fieldManager')->delete($params['name'], $params['scope']);
$this->getContainer()->get('dataManager')->rebuildMetadata();
@@ -95,5 +108,17 @@ class FieldManager extends \Espo\Core\Controllers\Base
return $res;
}
public function postActionResetToDefault($params, $data)
{
if (empty($data['scope']) || empty($data['name'])) {
throw new BadRequest();
}
$this->getContainer()->get('fieldManager')->resetToDefault($data['name'], $data['scope']);
$this->getContainer()->get('dataManager')->rebuildMetadata();
return true;
}
}

View File

@@ -127,8 +127,48 @@ class Import extends \Espo\Core\Controllers\Record
throw new BadRequest();
}
if (!isset($data['fieldDelimiter'])) {
throw new BadRequest();
}
if (!isset($data['textQualifier'])) {
throw new BadRequest();
}
if (!isset($data['dateFormat'])) {
throw new BadRequest();
}
if (!isset($data['timeFormat'])) {
throw new BadRequest();
}
if (!isset($data['personNameFormat'])) {
throw new BadRequest();
}
if (!isset($data['decimalMark'])) {
throw new BadRequest();
}
if (!isset($data['defaultValues'])) {
throw new BadRequest();
}
if (!isset($data['action'])) {
throw new BadRequest();
}
if (!isset($data['attachmentId'])) {
throw new BadRequest();
}
if (!isset($data['entityType'])) {
throw new BadRequest();
}
$importParams = array(
'headerRow' => $data['headerRow'],
'headerRow' => !empty($data['headerRow']),
'fieldDelimiter' => $data['fieldDelimiter'],
'textQualifier' => $data['textQualifier'],
'dateFormat' => $data['dateFormat'],
@@ -138,6 +178,8 @@ class Import extends \Espo\Core\Controllers\Record
'currency' => $data['currency'],
'defaultValues' => $data['defaultValues'],
'action' => $data['action'],
'skipDuplicateChecking' => !empty($data['skipDuplicateChecking']),
'idleMode' => !empty($data['idleMode'])
);
if (array_key_exists('updateBy', $data)) {

View File

@@ -67,6 +67,19 @@ class Integration extends \Espo\Core\Controllers\Record
$entity->set($data);
$this->getEntityManager()->saveEntity($entity);
$integrationsConfigData = $this->getConfig()->get('integrations');
if (!$integrationsConfigData || !($integrationsConfigData instanceof \StdClass)) {
$integrationsConfigData = (object)[];
}
$integrationName = $params['id'];
$integrationsConfigData->$integrationName = $entity->get('enabled');
$this->getConfig()->set('integrations', $integrationsConfigData);
$this->getConfig()->save();
return $entity->toArray();
}
}

View File

@@ -37,7 +37,11 @@ class Settings extends \Espo\Core\Controllers\Base
{
protected function getConfigData()
{
$data = $this->getConfig()->getData($this->getUser()->isAdmin());
if ($this->getUser()->id == 'system') {
$data = $this->getConfig()->getData();
} else {
$data = $this->getConfig()->getData($this->getUser()->isAdmin());
}
$fieldDefs = $this->getMetadata()->get('entityDefs.Settings.fields');

View File

@@ -155,7 +155,7 @@ class Base implements Injectable
}
if (!isset($data->$action)) {
return true;
return false;
}
$value = $data->$action;

View File

@@ -177,7 +177,7 @@ class Table
return $this->data->table->$scope->$action;
}
}
return false;
return 'no';
}
private function load()
@@ -459,6 +459,17 @@ class Table
$defaultValue = (object) $defaultValue;
}
$table->$scope = $defaultValue;
if (is_object($table->$scope)) {
$actionList = $this->getMetadata()->get(['scopes', $scope, $this->type . 'ActionList']);
if ($actionList) {
foreach (get_object_vars($table->$scope) as $action => $level) {
if (!in_array($action, $actionList)) {
unset($table->$scope->$action);
}
}
}
}
}
}
}
@@ -606,7 +617,9 @@ class Table
if (!is_object($row)) continue;
foreach ($this->actionList as $i => $action) {
$actionList = $this->getMetadata()->get(['scopes', $scope, $this->type . 'ActionList'], $this->actionList);
foreach ($actionList as $i => $action) {
if (isset($row->$action)) {
$level = $row->$action;
if (!isset($data->$scope->$action)) {
@@ -623,7 +636,7 @@ class Table
if (in_array($action, $this->booleanActionList)) {
$data->$scope->$action = 'yes';
} else {
if (isset($data->$scope->$previousAction)) {
if ($action === 'stream' && isset($data->$scope->$previousAction)) {
$data->$scope->$action = $data->$scope->$previousAction;
}
}
@@ -684,11 +697,12 @@ class Table
private function buildCache()
{
$contents = '<' . '?'. 'php return ' . $this->varExport($this->data) . ';';
$this->fileManager->putContents($this->cacheFilePath, $contents);
$this->fileManager->putPhpContents($this->cacheFilePath, $this->data, true);
/*$contents = '<' . '?'. 'php return ' . $this->varExport($this->data) . ';';
$this->fileManager->putContents($this->cacheFilePath, $contents);*/
}
private function varExport($variable)
/*private function varExport($variable)
{
if ($variable instanceof \StdClass) {
$result = '(object) ' . $this->varExport(get_object_vars($variable), true);
@@ -703,6 +717,6 @@ class Table
}
return $result;
}
}*/
}

View File

@@ -71,7 +71,7 @@ class Base extends \Espo\Core\Acl\Base
}
if (!isset($data->$action)) {
return true;
return false;
}
$value = $data->$action;

View File

@@ -222,7 +222,7 @@ class Application
}
try {
$controllerManager = new \Espo\Core\ControllerManager($container);
$controllerManager = $this->getContainer()->get('controllerManager');
$result = $controllerManager->process($controllerName, $actionName, $params, $data, $slim->request());
$container->get('output')->render($result);
} catch (\Exception $e) {

View File

@@ -135,6 +135,13 @@ class Container
);
}
protected function loadControllerManager()
{
return new \Espo\Core\ControllerManager(
$this
);
}
protected function loadPreferences()
{
return $this->get('entityManager')->getEntity('Preferences', $this->get('user')->id);
@@ -181,7 +188,7 @@ class Container
protected function loadNumber()
{
return new \Espo\Core\Utils\Number(
return new \Espo\Core\Utils\NumberUtil(
$this->get('config')->get('decimalMark'),
$this->get('config')->get('thousandSeparator')
);
@@ -209,8 +216,8 @@ class Container
protected function loadMetadata()
{
return new \Espo\Core\Utils\Metadata(
$this->get('config'),
$this->get('fileManager')
$this->get('fileManager'),
$this->get('config')->get('useCache')
);
}
@@ -247,7 +254,17 @@ class Container
$this->get('metadata'),
$this->get('fileManager'),
$this->get('entityManager'),
$this->get('classParser')
$this->get('classParser'),
$this->get('ormMetadata')
);
}
protected function loadOrmMetadata()
{
return new \Espo\Core\Utils\Metadata\OrmMetadata(
$this->get('metadata'),
$this->get('fileManager'),
$this->get('config')->get('useCache')
);
}
@@ -263,10 +280,20 @@ class Container
protected function loadLanguage()
{
return new \Espo\Core\Utils\Language(
\Espo\Core\Utils\Language::detectLanguage($this->get('config'), $this->get('preferences')),
$this->get('fileManager'),
$this->get('config'),
$this->get('metadata'),
$this->get('preferences')
$this->get('config')->get('useCache')
);
}
protected function loadDefaultLanguage()
{
return new \Espo\Core\Utils\Language(
null,
$this->get('fileManager'),
$this->get('metadata'),
$this->get('useCache')
);
}

View File

@@ -226,6 +226,10 @@ class Record extends Base
public function actionExport($params, $data, $request)
{
if (!$request->isPost()) {
throw new BadRequest();
}
if ($this->getConfig()->get('exportDisabled') && !$this->getUser()->isAdmin()) {
throw new Forbidden();
}
@@ -234,17 +238,23 @@ class Record extends Base
throw new Forbidden();
}
$ids = $request->get('ids');
$where = $request->get('where');
$byWhere = $request->get('byWhere');
$ids = isset($data['ids']) ? $data['ids'] : null;
$where = isset($data['where']) ? json_decode(json_encode($data['where']), true) : null;
$byWhere = isset($data['byWhere']) ? $data['byWhere'] : false;
$selectData = isset($data['selectData']) ? json_decode(json_encode($data['selectData']), true) : null;
$params = array();
if ($byWhere) {
$params['selectData'] = $selectData;
$params['where'] = $where;
} else {
$params['ids'] = $ids;
}
if (isset($data['attributeList'])) {
$params['attributeList'] = $data['attributeList'];
}
return array(
'id' => $this->getRecordService()->export($params)
);
@@ -266,6 +276,9 @@ class Record extends Base
$params = array();
if (array_key_exists('where', $data) && !empty($data['byWhere'])) {
$params['where'] = json_decode(json_encode($data['where']), true);
if (array_key_exists('selectData', $data)) {
$params['selectData'] = json_decode(json_encode($data['selectData']), true);
}
} else if (array_key_exists('ids', $data)) {
$params['ids'] = $data['ids'];
}
@@ -290,6 +303,9 @@ class Record extends Base
if (array_key_exists('where', $data) && !empty($data['byWhere'])) {
$where = json_decode(json_encode($data['where']), true);
$params['where'] = $where;
if (array_key_exists('selectData', $data)) {
$params['selectData'] = json_decode(json_encode($data['selectData']), true);
}
}
if (array_key_exists('ids', $data)) {
$params['ids'] = $data['ids'];
@@ -318,7 +334,13 @@ class Record extends Base
throw new BadRequest();
}
$where = json_decode(json_encode($data['where']), true);
return $this->getRecordService()->linkEntityMass($id, $link, $where);
$selectData = null;
if (isset($data['selectData']) && is_array($data['selectData'])) {
$selectData = json_decode(json_encode($data['selectData']), true);
}
return $this->getRecordService()->linkEntityMass($id, $link, $where, $selectData);
} else {
$foreignIdList = array();
if (isset($data['id'])) {

View File

@@ -28,6 +28,7 @@
************************************************************************/
namespace Espo\Core;
class DataManager
{
private $container;
@@ -113,11 +114,11 @@ class DataManager
$metadata->init(true);
$ormMeta = $metadata->getOrmMetadata(true);
$ormData = $this->getContainer()->get('ormMetadata')->getData(true);
$this->updateCacheTimestamp();
return empty($ormMeta) ? false : true;
return empty($ormData) ? false : true;
}
/**

View File

@@ -51,6 +51,8 @@ class HookManager
'afterSave',
'beforeRemove',
'afterRemove',
'afterRelate',
'afterUnrelate'
);
protected $paths = array(
@@ -63,7 +65,6 @@ class HookManager
public function __construct(Container $container)
{
$this->container = $container;
$this->loadHooks();
}
protected function getConfig()
@@ -85,24 +86,30 @@ class HookManager
$metadata = $this->container->get('metadata');
$this->data = $this->getHookData($this->paths['corePath']);
$data = $this->getHookData($this->paths['customPath']);
foreach ($metadata->getModuleList() as $moduleName) {
$modulePath = str_replace('{*}', $moduleName, $this->paths['modulePath']);
$this->data = array_merge_recursive($this->data, $this->getHookData($modulePath));
$data = $this->getHookData($modulePath, $data);
}
$this->data = array_merge_recursive($this->data, $this->getHookData($this->paths['customPath']));
$data = $this->getHookData($this->paths['corePath'], $data);
$this->data = $this->sortHooks($data);
if ($this->getConfig()->get('useCache')) {
$this->getFileManager()->putPhpContents($this->cacheFile, $this->data);
}
}
public function process($scope, $hookName, $injection = null, array $options = array())
public function process($scope, $hookName, $injection = null, array $options = array(), array $hookData = array())
{
if (!isset($this->data)) {
$this->loadHooks();
}
if ($scope != 'Common') {
$this->process('Common', $hookName, $injection, $options);
$this->process('Common', $hookName, $injection, $options, $hookData);
}
if (!empty($this->data[$scope])) {
@@ -115,7 +122,7 @@ class HookManager
}
}
$hook = $this->hooks[$className];
$hook->$hookName($injection, $options);
$hook->$hookName($injection, $options, $hookData);
}
}
}
@@ -131,6 +138,7 @@ class HookManager
}
return $hook;
}
$GLOBALS['log']->error("Hook class '{$className}' does not exist.");
}
@@ -141,14 +149,12 @@ class HookManager
*
* @return array
*/
protected function getHookData($hookDirs)
protected function getHookData($hookDirs, array $hookData = array())
{
if (is_string($hookDirs)) {
$hookDirs = (array) $hookDirs;
}
$hooks = array();
foreach ($hookDirs as $hookDir) {
if (file_exists($hookDir)) {
@@ -157,6 +163,7 @@ class HookManager
foreach ($fileList as $scopeName => $hookFiles) {
$hookScopeDirPath = Util::concatPath($hookDir, $scopeName);
$normalizedScopeName = Util::normilizeScopeName($scopeName);
$scopeHooks = array();
foreach($hookFiles as $hookFile) {
@@ -164,32 +171,70 @@ class HookManager
$className = Util::getClassName($hookFilePath);
foreach($this->hookList as $hookName) {
if (method_exists($className, $hookName)) {
$scopeHooks[$hookName][$className::$order][] = $className;
$entityHookData = isset($hookData[$scopeName][$hookName]) ? $hookData[$scopeName][$hookName] : array();
if (method_exists($className, $hookName) && !$this->isHookExists($className, $entityHookData)) {
$hookData[$normalizedScopeName][$hookName][$className::$order][] = $className;
}
}
}
//sort hooks by order
foreach ($scopeHooks as $hookName => $hookList) {
ksort($hookList);
$sortedHookList = array();
foreach($hookList as $hookDetails) {
$sortedHookList = array_merge($sortedHookList, $hookDetails);
}
$normalizedScopeName = Util::normilizeScopeName($scopeName);
$hooks[$normalizedScopeName][$hookName] = isset($hooks[$normalizedScopeName][$hookName]) ? array_merge($hooks[$normalizedScopeName][$hookName], $sortedHookList) : $sortedHookList;
}
}
}
}
return $hookData;
}
/**
* Sort hooks by an order
*
* @param array $scopeHooks
*
* @return array
*/
protected function sortHooks(array $unsortedHooks)
{
$hooks = array();
foreach ($unsortedHooks as $scopeName => $scopeHooks) {
foreach ($scopeHooks as $hookName => $hookList) {
ksort($hookList);
$sortedHookList = array();
foreach($hookList as $hookDetails) {
$sortedHookList = array_merge($sortedHookList, $hookDetails);
}
$normalizedScopeName = Util::normilizeScopeName($scopeName);
$hooks[$normalizedScopeName][$hookName] = isset($hooks[$normalizedScopeName][$hookName]) ? array_merge($hooks[$normalizedScopeName][$hookName], $sortedHookList) : $sortedHookList;
}
}
return $hooks;
}
/**
* Check if hook exists in the list
*
* @param string $className
* @param array $hookData
*
* @return boolean
*/
protected function isHookExists($className, array $hookData)
{
$class = preg_replace('/^.*\\\(.*)$/', '$1', $className);
foreach ($hookData as $key => $hookList) {
foreach ($hookList as $rowHookName) {
if (preg_match('/\\'.$class.'$/', $rowHookName)) {
return true;
}
}
}
return false;
}
}

View File

@@ -34,7 +34,7 @@ use Espo\Core\Exceptions\Error;
use Espo\Core\Utils\File\Manager as FileManager;
use Espo\Core\Utils\DateTime;
use Espo\Core\Utils\Number;
use Espo\Core\Utils\NumberUtil;
require('vendor/zordius/lightncandy/src/lightncandy.php');
@@ -48,7 +48,7 @@ class Htmlizer
protected $acl;
public function __construct(FileManager $fileManager, DateTime $dateTime, Number $number, $acl = null)
public function __construct(FileManager $fileManager, DateTime $dateTime, NumberUtil $number, $acl = null)
{
$this->fileManager = $fileManager;
$this->dateTime = $dateTime;
@@ -61,15 +61,12 @@ class Htmlizer
return $this->acl;
}
protected function formatNumber($value)
{
return $this->number->format($value);
}
protected function format($value)
{
if (is_float($value) || is_int($value)) {
$value = $this->formatNumber($value);
if (is_float($value)) {
$value = $this->number->format($value, 2);
} else if (is_int($value)) {
$value = $this->number->format($value);
} else if (is_string($value)) {
$value = nl2br($value);
}

View File

@@ -42,7 +42,7 @@ class EntityManager extends Base
'user' => $config->get('database.user'),
'charset' => $config->get('database.charset', 'utf8'),
'password' => $config->get('database.password'),
'metadata' => $this->getContainer()->get('metadata')->getOrmMetadata(),
'metadata' => $this->getContainer()->get('ormMetadata')->getData(),
'repositoryFactoryClassName' => '\\Espo\\Core\\ORM\\RepositoryFactory',
'driver' => $config->get('database.driver'),
'platform' => $config->get('database.platform')

View File

@@ -36,7 +36,9 @@ class EntityManagerUtil extends Base
$entityManager = new \Espo\Core\Utils\EntityManager(
$this->getContainer()->get('metadata'),
$this->getContainer()->get('language'),
$this->getContainer()->get('fileManager')
$this->getContainer()->get('fileManager'),
$this->getContainer()->get('config'),
$this->getContainer()
);
return $entityManager;

View File

@@ -0,0 +1,44 @@
<?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/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Loaders;
class TemplateFileManager extends Base
{
public function load()
{
$templateFileManager = new \Espo\Core\Utils\TemplateFileManager(
$this->getContainer()->get('config'),
$this->getContainer()->get('metadata')
);
return $templateFileManager;
}
}

View File

@@ -39,6 +39,18 @@ class FiltersMatcher
}
protected function matchTo(Email $email, $filter)
{
if ($email->get('to')) {
$toArr = explode(';', $email->get('to'));
foreach ($toArr as $to) {
if ($this->matchString(strtolower($filter->get('to')), strtolower($to))) {
return true;
}
}
}
}
public function match(Email $email, $subject, $skipBody = false)
{
if (is_array($subject) || $subject instanceof \Traversable) {
@@ -48,30 +60,42 @@ class FiltersMatcher
}
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;
}
}
}
$filterCount = 0;
if (!$skipBody) {
if ($this->matchBody($email, $filterList)) {
if ($filter->get('from')) {
$filterCount++;
if (!$this->matchString(strtolower($filter->get('from')), strtolower($email->get('from')))) {
continue;
}
}
if ($filter->get('to')) {
$filterCount++;
if (!$this->matchTo($email, $filter)) {
continue;
}
}
if ($filter->get('subject')) {
$filterCount++;
if (!$this->matchString($filter->get('subject'), $email->get('name'))) {
continue;
}
}
$wordList = $filter->get('bodyContains');
if (!empty($wordList)) {
$filterCount++;
if ($skipBody) {
continue;
}
if (!$this->matchBody($email, $filter)) {
continue;
}
}
if ($filterCount) {
return true;
}
}
@@ -79,30 +103,19 @@ class FiltersMatcher
return false;
}
public function matchBody(Email $email, $subject)
protected function matchBody(Email $email, $filter)
{
if (is_array($subject) || $subject instanceof \Traversable) {
$filterList = $subject;
} else {
$filterList = [$subject];
}
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;
}
}
$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)

View File

@@ -64,235 +64,235 @@ class Importer
return $this->filtersMatcher;
}
public function importMessage($message, $assignedUserId = null, $teamsIdList = [], $userIdList = [], $filterList = [], $fetchOnlyHeader = false, $folderData = null)
public function importMessage($parserType = 'ZendMail', $message, $assignedUserId = null, $teamsIdList = [], $userIdList = [], $filterList = [], $fetchOnlyHeader = false, $folderData = null)
{
try {
$email = $this->getEntityManager()->getEntity('Email');
$parser = $message->getParser();
$parserClassName = '\\Espo\\Core\\Mail\\Parsers\\' . $parserType;
$email->set('isBeingImported', true);
if (!$parser || get_class($parser) !== $parserClassName) {
$parser = new $parserClassName($this->getEntityManager());
}
$subject = $message->subject;
if ($subject !== '0' && empty(trim($subject))) {
$subject = '(No Subject)';
$email = $this->getEntityManager()->getEntity('Email');
$email->set('isBeingImported', true);
$subject = '';
if ($parser->checkMessageAttribute($message, 'subject')) {
$subject = $parser->getMessageAttribute($message, 'subject');
}
if (!empty($subject) && is_string($subject)) {
$subject = trim($subject);
}
if ($subject !== '0' && empty($subject)) {
$subject = '(No Subject)';
}
$email->set('isHtml', false);
$email->set('name', $subject);
$email->set('status', 'Archived');
$email->set('attachmentsIds', []);
if ($assignedUserId) {
$email->set('assignedUserId', $assignedUserId);
$email->addLinkMultipleId('assignedUsers', $assignedUserId);
}
$email->set('teamsIds', $teamsIdList);
if (!empty($userIdList)) {
foreach ($userIdList as $uId) {
$email->addLinkMultipleId('users', $uId);
}
}
$email->set('isHtml', false);
$email->set('name', $subject);
$email->set('status', 'Archived');
$email->set('attachmentsIds', []);
if ($parser->checkMessageAttribute($message, 'from')) {
$email->set('fromString', $parser->getMessageAttribute($message, 'from'));
}
if ($parser->checkMessageAttribute($message, 'reply-To')) {
$email->set('replyToString', $parser->getMessageAttribute($message, 'reply-To'));
}
$fromArr = $parser->getAddressListFromMessage($message, 'from');
$toArr = $parser->getAddressListFromMessage($message, 'to');
$ccArr = $parser->getAddressListFromMessage($message, 'cc');
$replyToArr = $parser->getAddressListFromMessage($message, 'reply-To');
if (count($fromArr)) {
$email->set('from', $fromArr[0]);
}
$email->set('to', implode(';', $toArr));
$email->set('cc', implode(';', $ccArr));
$email->set('replyTo', implode(';', $replyToArr));
if ($folderData) {
foreach ($folderData as $uId => $folderId) {
$email->setLinkMultipleColumn('users', 'folderId', $uId, $folderId);
}
}
if ($this->getFiltersMatcher()->match($email, $filterList, true)) {
return false;
}
if ($parser->checkMessageAttribute($message, 'message-Id') && $parser->getMessageAttribute($message, 'message-Id')) {
$messageId = $parser->getMessageMessageId($message);
$email->set('messageId', $messageId);
if ($parser->checkMessageAttribute($message, 'delivered-To')) {
$email->set('messageIdInternal', $messageId . '-' . $parser->getMessageAttribute($message, 'delivered-To'));
}
if (stripos($messageId, '@espo-system') !== false) {
return;
}
}
if ($duplicate = $this->findDuplicate($email)) {
if ($assignedUserId) {
$email->set('assignedUserId', $assignedUserId);
$email->addLinkMultipleId('assignedUsers', $assignedUserId);
$duplicate->addLinkMultipleId('users', $assignedUserId);
$duplicate->addLinkMultipleId('assignedUsers', $assignedUserId);
}
$email->set('teamsIds', $teamsIdList);
if (!empty($userIdList)) {
foreach ($userIdList as $uId) {
$email->addLinkMultipleId('users', $uId);
$duplicate->addLinkMultipleId('users', $uId);
}
}
$fromArr = $this->getAddressListFromMessage($message, 'from');
if (isset($message->from)) {
$email->set('fromString', $message->from);
}
if (isset($message->replyTo)) {
$email->set('replyToString', $message->replyTo);
}
$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 ($folderData) {
foreach ($folderData as $uId => $folderId) {
$email->setLinkMultipleColumn('users', 'folderId', $uId, $folderId);
}
}
if ($this->getFiltersMatcher()->match($email, $filterList, true)) {
return false;
}
$duplicate->set('isBeingImported', true);
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;
$this->getEntityManager()->saveEntity($duplicate);
if (!empty($teamsIdList)) {
foreach ($teamsIdList as $teamId) {
$this->getEntityManager()->getRepository('Email')->relate($duplicate, 'teams', $teamId);
}
}
return $duplicate;
}
if ($duplicate = $this->findDuplicate($email)) {
if ($assignedUserId) {
$duplicate->addLinkMultipleId('users', $assignedUserId);
$duplicate->addLinkMultipleId('assignedUsers', $assignedUserId);
}
if (!empty($userIdList)) {
foreach ($userIdList as $uId) {
$duplicate->addLinkMultipleId('users', $uId);
}
}
if ($folderData) {
foreach ($folderData as $uId => $folderId) {
$email->setLinkMultipleColumn('users', 'folderId', $uId, $folderId);
}
}
$duplicate->set('isBeingImported', true);
$this->getEntityManager()->saveEntity($duplicate);
if (!empty($teamsIdList)) {
foreach ($teamsIdList as $teamId) {
$this->getEntityManager()->getRepository('Email')->relate($duplicate, 'teams', $teamId);
}
}
return $duplicate;
}
if (isset($message->date)) {
$dt = new \DateTime($message->date);
if ($parser->checkMessageAttribute($message, 'date')) {
try {
$dt = new \DateTime($parser->getMessageAttribute($message, 'date'));
if ($dt) {
$dateSent = $dt->setTimezone(new \DateTimeZone('UTC'))->format('Y-m-d H:i:s');
$email->set('dateSent', $dateSent);
}
} else {
$email->set('dateSent', date('Y-m-d H:i:s'));
}
if (isset($message->deliveryDate)) {
$dt = new \DateTime($message->deliveryDate);
} catch (\Exception $e) {}
} else {
$email->set('dateSent', date('Y-m-d H:i:s'));
}
if ($parser->checkMessageAttribute($message, 'delivery-Date')) {
try {
$dt = new \DateTime($parser->getMessageAttribute($message, 'delivery-Date'));
if ($dt) {
$deliveryDate = $dt->setTimezone(new \DateTimeZone('UTC'))->format('Y-m-d H:i:s');
$email->set('deliveryDate', $deliveryDate);
$email->set('delivery-Date', $deliveryDate);
}
} catch (\Exception $e) {}
}
if (!$fetchOnlyHeader) {
$parser->fetchContentParts($email, $message);
if ($this->getFiltersMatcher()->match($email, $filterList)) {
return false;
}
} else {
$email->set('body', '(Not fetched)');
$email->set('isHtml', false);
}
$inlineIds = array();
$parentFound = false;
if (!$fetchOnlyHeader) {
if ($message->isMultipart()) {
foreach (new \RecursiveIteratorIterator($message) as $part) {
$this->importPartDataToEmail($email, $part, $inlineIds);
}
} else {
$this->importPartDataToEmail($email, $message, $inlineIds, 'text/plain');
}
$replied = null;
if (!$email->get('body') && $email->get('bodyPlain')) {
$email->set('body', $email->get('bodyPlain'));
}
$body = $email->get('body');
if (!empty($body)) {
foreach ($inlineIds as $cid => $attachmentId) {
if (strpos($body, 'cid:' . $cid) !== false) {
$body = str_replace('cid:' . $cid, '?entryPoint=attachment&amp;id=' . $attachmentId, $body);
} else {
$email->addLinkMultipleId('attachments', $attachmentId);
}
}
$email->set('body', $body);
}
if ($this->getFiltersMatcher()->matchBody($email, $filterList)) {
return false;
}
} else {
$email->set('body', '(Not fetched)');
$email->set('isHtml', false);
if ($parser->checkMessageAttribute($message, 'in-Reply-To') && $parser->getMessageAttribute($message, 'in-Reply-To')) {
$arr = explode(' ', $parser->getMessageAttribute($message, 'in-Reply-To'));
$inReplyTo = $arr[0];
$replied = $this->getEntityManager()->getRepository('Email')->where(array(
'messageId' => $inReplyTo
))->findOne();
if ($replied) {
$email->set('repliedId', $replied->id);
}
}
$parentFound = false;
$replied = null;
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 ($parser->checkMessageAttribute($message, 'references') && $parser->getMessageAttribute($message, 'references')) {
$arr = explode(' ', $parser->getMessageAttribute($message, 'references'));
$reference = $arr[0];
$reference = str_replace(array('/', '@'), " ", trim($reference, '<>'));
$parentType = $parentId = null;
$emailSent = PHP_INT_MAX;
$number = null;
$n = sscanf($reference, '%s %s %d %d espo', $parentType, $parentId, $emailSent, $number);
if ($n != 4) {
$n = sscanf($reference, '%s %s %d %d espo-system', $parentType, $parentId, $emailSent, $number);
}
if (isset($message->references) && !empty($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;
}
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;
}
$email->set('parentType', $parentType);
$email->set('parentId', $parentId);
$parentFound = true;
}
}
}
if (!$parentFound) {
if ($replied && $replied->get('parentId') && $replied->get('parentType')) {
$parentFound = $this->getEntityManager()->getEntity($replied->get('parentType'), $replied->get('parentId'));
if ($parentFound) {
$email->set('parentType', $replied->get('parentType'));
$email->set('parentId', $replied->get('parentId'));
}
if (!$parentFound) {
if ($replied && $replied->get('parentId') && $replied->get('parentType')) {
$parentFound = $this->getEntityManager()->getEntity($replied->get('parentType'), $replied->get('parentId'));
if ($parentFound) {
$email->set('parentType', $replied->get('parentType'));
$email->set('parentId', $replied->get('parentId'));
}
}
if (!$parentFound) {
$from = $email->get('from');
if ($from) {
$parentFound = $this->findParent($email, $from);
}
}
if (!$parentFound) {
$from = $email->get('from');
if ($from) {
$parentFound = $this->findParent($email, $from);
}
if (!$parentFound) {
if (!empty($replyToArr)) {
$parentFound = $this->findParent($email, $replyToArr[0]);
}
}
if (!$parentFound) {
if (!empty($replyToArr)) {
$parentFound = $this->findParent($email, $replyToArr[0]);
}
if (!$parentFound) {
if (!empty($toArr)) {
$parentFound = $this->findParent($email, $toArr[0]);
}
}
if (!$parentFound) {
if (!empty($toArr)) {
$parentFound = $this->findParent($email, $toArr[0]);
}
}
$this->getEntityManager()->saveEntity($email);
$this->getEntityManager()->saveEntity($email);
return $email;
} catch (\Exception $e) {}
return $email;
}
protected function findParent(Entity $email, $emailAddress)
@@ -321,15 +321,15 @@ class Importer
$email->set('parentId', $account->id);
return true;
} else {
$lead = $this->getEntityManager()->getRepository('Lead')->where(array(
'emailAddress' => $emailAddress
))->findOne();
if ($lead) {
$email->set('parentType', 'Lead');
$email->set('parentId', $lead->id);
return true;
}
}
$lead = $this->getEntityManager()->getRepository('Lead')->where(array(
'emailAddress' => $emailAddress
))->findOne();
if ($lead) {
$email->set('parentType', 'Lead');
$email->set('parentId', $lead->id);
return true;
}
}
}
}
@@ -344,243 +344,4 @@ 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 = $this->normilizeHeader($message->getHeader($type))->getAddressList();
foreach ($list as $address) {
$addressList[] = $address->getEmail();
}
}
return $addressList;
}
protected function importPartDataToEmail(\Espo\Entities\Email $email, $part, &$inlineIds = array(), $defaultContentType = null)
{
try {
$type = null;
if ($part->getHeaders() && isset($part->contentType)) {
$type = strtok($part->contentType, ';');
}
$contentDisposition = false;
if (isset($part->ContentDisposition)) {
if (strpos(strtolower($part->ContentDisposition), 'attachment') === 0) {
$contentDisposition = 'attachment';
} else if (strpos(strtolower($part->ContentDisposition), 'inline') === 0) {
$contentDisposition = 'inline';
}
} else if (isset($part->contentID)) {
$contentDisposition = 'inline';
}
if (empty($type)) {
if (!empty($defaultContentType)) {
$type = $defaultContentType;
} else {
return;
}
}
$encoding = null;
$isAttachment = true;
if ($type == 'text/plain' || $type == 'text/html') {
if ($contentDisposition !== 'attachment') {
$isAttachment = false;
$content = $this->getContentFromPart($part);
if ($type == 'text/plain') {
$bodyPlain = '';
if ($email->get('bodyPlain')) {
$bodyPlain .= $email->get('bodyPlain') . "\n";
}
$bodyPlain .= $content;
$email->set('bodyPlain', $bodyPlain);
} else if ($type == 'text/html') {
$body = '';
if ($email->get('body')) {
$body .= $email->get('body') . "<br>";
}
$body .= $content;
$email->set('isHtml', true);
$email->set('body', $body);
}
}
}
if ($isAttachment) {
$content = $part->getContent();
$disposition = null;
$fileName = null;
$contentId = null;
if ($contentDisposition) {
if ($contentDisposition === 'attachment') {
$fileName = $this->fetchFileNameFromContentDisposition($part->ContentDisposition);
if ($fileName) {
$disposition = 'attachment';
}
} else if ($contentDisposition === 'inline') {
if (isset($part->contentID)) {
$contentId = trim($part->contentID, '<>');
$fileName = $contentId;
$disposition = 'inline';
} else {
// for iOS attachments
if (empty($fileName)) {
$fileName = $this->fetchFileNameFromContentDisposition($part->ContentDisposition);
if ($fileName) {
$disposition = 'attachment';
}
}
}
}
}
if (isset($part->contentTransferEncoding)) {
$encoding = strtolower($this->normilizeHeader($part->getHeader('Content-Transfer-Encoding'))->getTransferEncoding());
}
$attachment = $this->getEntityManager()->getEntity('Attachment');
$attachment->set('name', $fileName);
$attachment->set('type', $type);
if ($disposition == 'inline') {
$attachment->set('role', 'Inline Attachment');
} else {
$attachment->set('role', 'Attachment');
}
if ($encoding == 'base64') {
$content = base64_decode($content);
}
$attachment->set('contents', $content);
$this->getEntityManager()->saveEntity($attachment);
if ($disposition == 'attachment') {
$attachmentsIds = $email->get('attachmentsIds');
$attachmentsIds[] = $attachment->id;
$email->set('attachmentsIds', $attachmentsIds);
} else if ($disposition == 'inline') {
$inlineIds[$contentId] = $attachment->id;
}
}
} catch (\Exception $e) {}
}
protected function decodeAttachmentFileName($fileName)
{
if ($fileName && stripos($fileName, "''") !== false) {
list($encoding, $fileName) = explode("''", $fileName);
$fileName = rawurldecode($fileName);
if (strtoupper($encoding) !== 'UTF-8') {
if ($encoding) {
$fileName = mb_convert_encoding($fileName, 'UTF-8', $encoding);
}
}
}
return $fileName;
}
protected function fetchFileNameFromContentDisposition($contentDisposition)
{
$contentDisposition = preg_replace('/\\\\"/', "{{_!Q!U!O!T!E!_}}", $contentDisposition);
$fileName = false;
$m = array();
if (preg_match('/filename="([^"]+)";?/i', $contentDisposition, $m)) {
$fileName = $m[1];
} else if (preg_match('/filename=([^";]+);?/i', $contentDisposition, $m)) {
$fileName = $m[1];
} else if (preg_match('/filename\*="([^"]+)";?/i', $contentDisposition, $m)) {
$fileName = $m[1];
$fileName = $this->decodeAttachmentFileName($fileName);
} else if (preg_match('/filename\*=([^";]+);?/i', $contentDisposition, $m)) {
$fileName = $m[1];
$fileName = $this->decodeAttachmentFileName($fileName);
} else {
$fileName = '';
foreach (['0', '1'] as $i) {
if (preg_match('/filename\*'.$i.'[\*]?="([^"]+)";?/i', $contentDisposition, $m)) {
$part = $m[1];
$fileName .= $part;
} else if (preg_match('/filename\*'.$i.'[\*]?=([^";]+);?/i', $contentDisposition, $m)) {
$part = $m[1];
$fileName .= $part;
}
}
if ($fileName === '') {
$fileName = null;
} else {
$fileName = $this->decodeAttachmentFileName($fileName);
}
}
if ($fileName) {
$fileName = str_replace('{{_!Q!U!O!T!E!_}}', '"', $fileName);
}
return $fileName;
}
protected function getContentFromPart($part)
{
if ($part instanceof \Zend\Mime\Part) {
$content = $part->getRawContent();
if (strtolower($part->charset) != 'utf-8') {
$content = mb_convert_encoding($content, 'UTF-8', $part->charset);
}
} else {
$content = $part->getContent();
$encoding = null;
if (isset($part->contentTransferEncoding)) {
$cteHeader = $this->normilizeHeader($part->getHeader('Content-Transfer-Encoding'));
$encoding = strtolower($cteHeader->getTransferEncoding());
}
if ($encoding == 'base64') {
$content = base64_decode($content);
}
$charset = 'UTF-8';
if (isset($part->contentType)) {
$ctHeader = $this->normilizeHeader($part->getHeader('Content-Type'));
$charsetParamValue = $ctHeader->getParameter('charset');
if (!empty($charsetParamValue)) {
$charset = strtoupper($charsetParamValue);
}
}
if (isset($part->contentTransferEncoding)) {
$cteHeader = $this->normilizeHeader($part->getHeader('Content-Transfer-Encoding'));
if ($cteHeader->getTransferEncoding() == 'quoted-printable') {
$content = quoted_printable_decode($content);
}
}
if ($charset !== 'UTF-8') {
$content = mb_convert_encoding($content, 'UTF-8', $charset);
}
}
return $content;
}
}

View File

@@ -31,8 +31,6 @@ namespace Espo\Core\Mail\Mail\Storage;
class Imap extends \Zend\Mail\Storage\Imap
{
protected $messageClass = '\\Espo\\Core\\Mail\\Mail\\Storage\\Message';
public function getIdsFromUID($uid)
{
$uid = intval($uid) + 1;
@@ -44,5 +42,22 @@ class Imap extends \Zend\Mail\Storage\Imap
return $this->protocol->search(array('SINCE "' . $date . '"'));
}
public function getHeaderAndFlags($id, $part = null)
{
$data = $this->protocol->fetch(['FLAGS', 'RFC822.HEADER'], $id);
$header = $data['RFC822.HEADER'];
$flags = [];
foreach ($data['FLAGS'] as $flag) {
$flags[] = isset(static::$knownFlags[$flag]) ? static::$knownFlags[$flag] : $flag;
}
return array(
'flags' => $flags,
'header' => $header
);
}
}

View File

@@ -0,0 +1,144 @@
<?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/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Mail;
use \Espo\Entities\Email;
class MessageWrapper
{
private $storage;
private $id;
private $rawHeader = null;
private $rawContent = null;
private $zendMessage = null;
protected $zendMessageClass = '\Zend\Mail\Storage\Message';
protected $fullRawContent = null;
protected $flagList = null;
public function __construct($storage = null, $id = null, $parser = null)
{
if ($storage) {
$data = $storage->getHeaderAndFlags($id);
$this->rawHeader = $data['header'];
$this->flagList = $data['flags'];
}
$this->id = $id;
$this->storage = $storage;
$this->parser = $parser;
}
public function setFullRawContent($content)
{
$this->fullRawContent = $content;
}
public function getRawHeader()
{
return $this->rawHeader;
}
public function getParser()
{
return $this->parser;
}
public function checkAttribute($attribute)
{
return $this->getParser()->checkMessageAttribute($this, $attribute);
}
public function getAttribute($attribute)
{
return $this->getParser()->getMessageAttribute($this, $attribute);
}
public function getRawContent()
{
if (is_null($this->rawContent)) {
$this->rawContent = $this->storage->getRawContent($this->id);
}
return $this->rawContent;
}
public function getFullRawContent()
{
if ($this->fullRawContent) {
return $this->fullRawContent;
}
return $this->getRawHeader() . "\n" . $this->getRawContent();
}
public function getZendMessage()
{
if (!$this->zendMessage) {
$data = array();
if ($this->storage) {
$data['handler'] = $this->storage;
}
if ($this->flagList) {
$data['flags'] = $this->flagList;
}
if ($this->fullRawContent) {
$data['raw'] = $this->fullRawContent;
} else {
if ($this->rawHeader) {
$data['headers'] = $this->rawHeader;
}
}
if ($this->id) {
$data['id'] = $this->id;
}
$this->zendMessage = new $this->zendMessageClass($data);
}
return $this->zendMessage;
}
public function getFlags()
{
return $this->flagList;
}
public function isFetched()
{
return !!$this->rawHeader;
}
}

View File

@@ -0,0 +1,167 @@
<?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/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Mail\Parsers;
class PhpMimeMailParser
{
private $entityManager;
private $parserHash = array();
public function __construct($entityManager)
{
$this->entityManager = $entityManager;
}
protected function getEntityManager()
{
return $this->entityManager;
}
protected function getParser($message)
{
$key = spl_object_hash($message);
if (!array_key_exists($key, $this->parserHash)) {
$this->parserHash[$key] = new PhpMimeMailParser\Parser();
$raw = $message->getRawHeader();
if (!$raw) {
$raw = $message->getFullRawContent();
}
$this->parserHash[$key]->setText($raw);
}
return $this->parserHash[$key];
}
protected function loadContent($message)
{
$this->getParser($message);
$raw = $message->getFullRawContent();
$this->getParser($message)->setText($raw);
}
public function checkMessageAttribute($message, $attribute)
{
return $this->getParser($message)->getHeader($attribute) !== false;
}
public function getMessageAttribute($message, $attribute)
{
if (!$this->checkMessageAttribute($message, $attribute)) return null;
return $this->getParser($message)->getHeader($attribute);
}
public function getMessageMessageId($message)
{
return $this->getMessageAttribute($message, 'Message-ID');
}
public function getAddressListFromMessage($message, $type)
{
$addressList = [];
if ($this->checkMessageAttribute($message, $type)) {
$list = $this->getParser($message)->getAddresses($type);
foreach ($list as $address) {
$addressList[] = $address['address'];
}
}
return $addressList;
}
public function fetchContentParts(\Espo\Entities\Email $email, $message)
{
$this->loadContent($message);
$bodyPlain = $this->getParser($message)->getMessageBody('text');
$bodyHtml = $this->getParser($message)->getMessageBody('html');
if ($bodyHtml) {
$email->set('isHtml', true);
$email->set('body', $bodyHtml);
$email->set('bodyPlain', $bodyPlain);
} else {
$email->set('isHtml', false);
$email->set('body', $bodyPlain);
}
if (!$email->get('body') && $email->get('bodyPlain')) {
$email->set('body', $email->get('bodyPlain'));
}
$attachmentObjList = $this->getParser($message)->getAttachments();
$inlineIds = array();
foreach ($attachmentObjList as $attachmentObj) {
$attachment = $this->getEntityManager()->getEntity('Attachment');
$content = $attachmentObj->getContent();
$disposition = $attachmentObj->getContentDisposition();
$attachment = $this->getEntityManager()->getEntity('Attachment');
$attachment->set('name', $attachmentObj->getFileName());
$attachment->set('type', $attachmentObj->getContentType());
if ($disposition == 'inline') {
$attachment->set('role', 'Inline Attachment');
$contentId = $attachmentObj->getContentID();
} else {
$attachment->set('role', 'Attachment');
}
$attachment->set('contents', $content);
$this->getEntityManager()->saveEntity($attachment);
if ($disposition == 'attachment') {
$attachmentsIds = $email->get('attachmentsIds');
$attachmentsIds[] = $attachment->id;
$email->set('attachmentsIds', $attachmentsIds);
} else if ($disposition == 'inline') {
$inlineIds[$contentId] = $attachment->id;
}
}
$body = $email->get('body');
if (!empty($body)) {
foreach ($inlineIds as $cid => $attachmentId) {
if (strpos($body, 'cid:' . $cid) !== false) {
$body = str_replace('cid:' . $cid, '?entryPoint=attachment&amp;id=' . $attachmentId, $body);
} else {
$email->addLinkMultipleId('attachments', $attachmentId);
}
}
$email->set('body', $body);
}
}
}

View File

@@ -0,0 +1,89 @@
<?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/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Mail\Parsers\PhpMimeMailParser;
use \PhpMimeMailParser\Attachment;
class Parser extends \PhpMimeMailParser\Parser
{
public function getAttachments()
{
$attachments = [];
$dispositions = ['attachment', 'inline'];
$non_attachment_types = ['text/plain', 'text/html'];
$nonameIter = 0;
foreach ($this->parts as $part) {
$disposition = $this->getPart('content-disposition', $part);
$filename = 'noname';
if (isset($part['disposition-filename'])) {
$filename = $this->decodeHeader($part['disposition-filename']);
} elseif (isset($part['content-name'])) {
// if we have no disposition but we have a content-name, it's a valid attachment.
// we simulate the presence of an attachment disposition with a disposition filename
$filename = $this->decodeHeader($part['content-name']);
if (!$disposition) {
$disposition = 'attachment';
}
} elseif (!in_array($part['content-type'], $non_attachment_types, true)
&& substr($part['content-type'], 0, 10) !== 'multipart/'
) {
// if we cannot get it by getMessageBody(), we assume it is an attachment
$disposition = 'attachment';
}
if (in_array($disposition, $dispositions) === true && isset($filename) === true) {
if ($filename == 'noname') {
$nonameIter++;
$filename = 'noname'.$nonameIter;
}
$headersAttachments = $this->getPart('headers', $part);
$contentidAttachments = $this->getPart('content-id', $part);
$mimePartStr = $this->getPartComplete($part);
$attachments[] = new Attachment(
$filename,
$this->getPart('content-type', $part),
$this->getAttachmentStream($part),
$disposition,
$contentidAttachments,
$headersAttachments,
$mimePartStr
);
}
}
return $attachments;
}
}

View File

@@ -0,0 +1,349 @@
<?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/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Mail\Parsers;
class ZendMail
{
private $entityManager;
public function __construct($entityManager)
{
$this->entityManager = $entityManager;
}
protected function getEntityManager()
{
return $this->entityManager;
}
public function checkMessageAttribute($message, $attribute)
{
$zendMessage = $message->getZendMessage();
return isset($zendMessage->$attribute);
}
public function getMessageAttribute($message, $attribute)
{
$zendMessage = $message->getZendMessage();
if (!isset($zendMessage->$attribute)) return null;
return $zendMessage->$attribute;
}
public function getMessageMessageId($message)
{
$zendMessage = $message->getZendMessage();
if (!isset($zendMessage->messageId)) return null;
$messageId = $zendMessage->messageId;
$messageId = str_replace('<<', '<', $messageId);
$messageId = str_replace('>>', '>', $messageId);
return $messageId;
}
public function getAddressListFromMessage($message, $type)
{
$zendMessage = $message->getZendMessage();
$addressList = array();
if (isset($zendMessage->$type)) {
$list = $this->normilizeHeader($zendMessage->getHeader($type))->getAddressList();
foreach ($list as $address) {
$addressList[] = $address->getEmail();
}
}
return $addressList;
}
public function fetchContentParts(\Espo\Entities\Email $email, $message)
{
$zendMessage = $message->getZendMessage();
$inlineIds = array();
if ($zendMessage->isMultipart()) {
foreach (new \RecursiveIteratorIterator($zendMessage) as $part) {
$this->importPartDataToEmail($email, $part, $inlineIds);
}
} else {
$this->importPartDataToEmail($email, $zendMessage, $inlineIds, 'text/plain');
}
if (!$email->get('body') && $email->get('bodyPlain')) {
$email->set('body', $email->get('bodyPlain'));
}
$body = $email->get('body');
if (!empty($body)) {
foreach ($inlineIds as $cid => $attachmentId) {
if (strpos($body, 'cid:' . $cid) !== false) {
$body = str_replace('cid:' . $cid, '?entryPoint=attachment&amp;id=' . $attachmentId, $body);
} else {
$email->addLinkMultipleId('attachments', $attachmentId);
}
}
$email->set('body', $body);
}
}
protected function importPartDataToEmail(\Espo\Entities\Email $email, $part, &$inlineIds = array(), $defaultContentType = null)
{
try {
$type = null;
if ($part->getHeaders() && isset($part->contentType)) {
$type = strtok($part->contentType, ';');
}
$contentDisposition = false;
if (isset($part->ContentDisposition)) {
if (strpos(strtolower($part->ContentDisposition), 'attachment') === 0) {
$contentDisposition = 'attachment';
} else if (strpos(strtolower($part->ContentDisposition), 'inline') === 0) {
$contentDisposition = 'inline';
}
} else if (isset($part->contentID)) {
$contentDisposition = 'inline';
}
if (empty($type)) {
if (!empty($defaultContentType)) {
$type = $defaultContentType;
} else {
return;
}
}
$encoding = null;
$isAttachment = true;
if ($type == 'text/plain' || $type == 'text/html') {
if ($contentDisposition !== 'attachment') {
$isAttachment = false;
$content = $this->getContentFromPart($part);
if ($type == 'text/plain') {
$bodyPlain = '';
if ($email->get('bodyPlain')) {
$bodyPlain .= $email->get('bodyPlain') . "\n";
}
$bodyPlain .= $content;
$email->set('bodyPlain', $bodyPlain);
} else if ($type == 'text/html') {
$body = '';
if ($email->get('body')) {
$body .= $email->get('body') . "<br>";
}
$body .= $content;
$email->set('isHtml', true);
$email->set('body', $body);
}
}
}
if ($isAttachment) {
$content = $part->getContent();
$disposition = null;
$fileName = null;
$contentId = null;
if ($contentDisposition) {
if ($contentDisposition === 'attachment') {
$fileName = $this->fetchFileNameFromContentDisposition($part->ContentDisposition);
if ($fileName) {
$disposition = 'attachment';
}
} else if ($contentDisposition === 'inline') {
if (isset($part->contentID)) {
$contentId = trim($part->contentID, '<>');
$fileName = $contentId;
$disposition = 'inline';
} else {
// for iOS attachments
if (empty($fileName)) {
$fileName = $this->fetchFileNameFromContentDisposition($part->ContentDisposition);
if ($fileName) {
$disposition = 'attachment';
}
}
}
}
}
if (isset($part->contentTransferEncoding)) {
$encoding = strtolower($this->normilizeHeader($part->getHeader('Content-Transfer-Encoding'))->getTransferEncoding());
}
$attachment = $this->getEntityManager()->getEntity('Attachment');
$attachment->set('name', $fileName);
$attachment->set('type', $type);
if ($disposition == 'inline') {
$attachment->set('role', 'Inline Attachment');
} else {
$attachment->set('role', 'Attachment');
}
if ($encoding == 'base64') {
$content = base64_decode($content);
}
$attachment->set('contents', $content);
$this->getEntityManager()->saveEntity($attachment);
if ($disposition == 'attachment') {
$attachmentsIds = $email->get('attachmentsIds');
$attachmentsIds[] = $attachment->id;
$email->set('attachmentsIds', $attachmentsIds);
} else if ($disposition == 'inline') {
$inlineIds[$contentId] = $attachment->id;
}
}
} catch (\Exception $e) {}
}
protected function getContentFromPart($part)
{
if ($part instanceof \Zend\Mime\Part) {
$content = $part->getRawContent();
if (strtolower($part->charset) != 'utf-8') {
$content = mb_convert_encoding($content, 'UTF-8', $part->charset);
}
} else {
$content = $part->getContent();
$encoding = null;
if (isset($part->contentTransferEncoding)) {
$cteHeader = $this->normilizeHeader($part->getHeader('Content-Transfer-Encoding'));
$encoding = strtolower($cteHeader->getTransferEncoding());
}
if ($encoding == 'base64') {
$content = base64_decode($content);
}
$charset = 'UTF-8';
if (isset($part->contentType)) {
$ctHeader = $this->normilizeHeader($part->getHeader('Content-Type'));
$charsetParamValue = $ctHeader->getParameter('charset');
if (!empty($charsetParamValue)) {
$charset = strtoupper($charsetParamValue);
}
}
if (isset($part->contentTransferEncoding)) {
$cteHeader = $this->normilizeHeader($part->getHeader('Content-Transfer-Encoding'));
if ($cteHeader->getTransferEncoding() == 'quoted-printable') {
$content = quoted_printable_decode($content);
}
}
if ($charset !== 'UTF-8') {
$content = mb_convert_encoding($content, 'UTF-8', $charset);
}
}
return $content;
}
protected function normilizeHeader($header)
{
if (is_a($header, 'ArrayIterator')) {
return $header->current();
} else {
return $header;
}
}
protected function fetchFileNameFromContentDisposition($contentDisposition)
{
$contentDisposition = preg_replace('/\\\\"/', "{{_!Q!U!O!T!E!_}}", $contentDisposition);
$fileName = false;
$m = array();
if (preg_match('/filename="([^"]+)";?/i', $contentDisposition, $m)) {
$fileName = $m[1];
} else if (preg_match('/filename=([^";]+);?/i', $contentDisposition, $m)) {
$fileName = $m[1];
} else if (preg_match('/filename\*="([^"]+)";?/i', $contentDisposition, $m)) {
$fileName = $m[1];
$fileName = $this->decodeAttachmentFileName($fileName);
} else if (preg_match('/filename\*=([^";]+);?/i', $contentDisposition, $m)) {
$fileName = $m[1];
$fileName = $this->decodeAttachmentFileName($fileName);
} else {
$fileName = '';
foreach (['0', '1'] as $i) {
if (preg_match('/filename\*'.$i.'[\*]?="([^"]+)";?/i', $contentDisposition, $m)) {
$part = $m[1];
$fileName .= $part;
} else if (preg_match('/filename\*'.$i.'[\*]?=([^";]+);?/i', $contentDisposition, $m)) {
$part = $m[1];
$fileName .= $part;
}
}
if ($fileName === '') {
$fileName = null;
} else {
$fileName = $this->decodeAttachmentFileName($fileName);
}
}
if ($fileName) {
$fileName = str_replace('{{_!Q!U!O!T!E!_}}', '"', $fileName);
}
return $fileName;
}
protected function decodeAttachmentFileName($fileName)
{
if ($fileName && stripos($fileName, "''") !== false) {
list($encoding, $fileName) = explode("''", $fileName);
$fileName = rawurldecode($fileName);
if (strtoupper($encoding) !== 'UTF-8') {
if ($encoding) {
$fileName = mb_convert_encoding($fileName, 'UTF-8', $encoding);
}
}
}
return $fileName;
}
}

View File

@@ -151,7 +151,7 @@ class Sender
return $this;
}
public function send(Email $email, $params = array(), &$message = null, $attachmetList = [])
public function send(Email $email, $params = array(), &$message = null, $attachmentList = [])
{
if (!$message) {
$message = new Message();
@@ -190,6 +190,10 @@ class Sender
$email->set('from', $fromAddress);
}
$sender = new \Zend\Mail\Header\Sender();
$sender->setAddress($email->get('from'));
$message->getHeaders()->addHeader($sender);
if (!empty($params['replyToAddress'])) {
$replyToName = null;
if (!empty($params['replyToName'])) {
@@ -242,7 +246,7 @@ class Sender
$attachmentCollection = $email->get('attachments');
$attachmentInlineCollection = $email->getInlineAttachments();
foreach ($attachmetList as $attachment) {
foreach ($attachmentList as $attachment) {
$attachmentCollection[] = $attachment;
}

View File

@@ -31,6 +31,15 @@ namespace Espo\Core\ORM;
class Entity extends \Espo\ORM\Entity
{
public function hasLinkMultipleField($field)
{
return $this->hasAttribute($field . 'Ids');
}
public function hasLinkField($field)
{
return $this->hasAttribute($field . 'Id');
}
public function loadLinkMultipleField($field, $columns = null)
{

View File

@@ -198,6 +198,35 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
return $result;
}
protected function afterRelate(Entity $entity, $relationName, $foreign, $data = null, array $options = array())
{
parent::afterRelate($entity, $relationName, $foreign, $data, $options);
if ($foreign instanceof Entity) {
$foreignEntity = $foreign;
$hookData = array(
'relationName' => $relationName,
'relationData' => $data,
'foreignEntity' => $foreignEntity
);
$this->getEntityManager()->getHookManager()->process($this->entityType, 'afterRelate', $entity, $options, $hookData);
}
}
protected function afterUnrelate(Entity $entity, $relationName, $foreign, array $options = array())
{
parent::afterUnrelate($entity, $relationName, $foreign, $options);
if ($foreign instanceof Entity) {
$foreignEntity = $foreign;
$hookData = array(
'relationName' => $relationName,
'foreignEntity' => $foreignEntity
);
$this->getEntityManager()->getHookManager()->process($this->entityType, 'afterUnrelate', $entity, $options, $hookData);
}
}
protected function beforeSave(Entity $entity, array $options = array())
{
parent::beforeSave($entity, $options);
@@ -241,24 +270,29 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
}
if ($entity->hasAttribute('createdById')) {
if (empty($options['import']) || !$entity->has('createdById')) {
$entity->set('createdById', $this->entityManager->getUser()->id);
$entity->set('createdById', $this->getEntityManager()->getUser()->id);
}
}
if ($entity->has('modifiedById')) {
$restoreData['modifiedById'] = $entity->get('modifiedById');
}
if ($entity->has('modifiedByName')) {
$restoreData['modifiedByName'] = $entity->get('modifiedByName');
}
if ($entity->has('modifiedAt')) {
$restoreData['modifiedAt'] = $entity->get('modifiedAt');
}
$entity->clear('modifiedById');
$entity->clear('modifiedByName');
} else {
if (empty($options['silent'])) {
if ($entity->hasAttribute('modifiedAt')) {
$entity->set('modifiedAt', $nowString);
}
if ($entity->hasAttribute('modifiedById')) {
$entity->set('modifiedById', $this->entityManager->getUser()->id);
$entity->set('modifiedById', $this->getEntityManager()->getUser()->id);
$entity->set('modifiedByName', $this->getEntityManager()->getUser()->get('name'));
}
}

View File

@@ -29,7 +29,7 @@
namespace Espo\Core\Pdf;
require "vendor/tecnick.com/tcpdf/tcpdf.php";
require "vendor/tecnickcom/tcpdf/tcpdf.php";
class Tcpdf extends \TCPDF
{

View File

@@ -169,20 +169,22 @@ class AclManager extends \Espo\Core\AclManager
return parent::get($user, $permission);
}
public function checkReadOnlyTeam(User $user, $permission)
public function checkReadOnlyTeam(User $user, $scope)
{
if ($this->checkUserIsNotPortal($user)) {
return $this->getMainManager()->checkReadOnlyTeam($user, $permission);
$data = $this->getTable($user)->getScopeData($scope);
return $this->getMainManager()->checkReadOnlyTeam($user, $data);
}
return false;
return parent::checkReadOnlyTeam($user, $scope);
}
public function checkReadOnlyOwn(User $user, $permission)
public function checkReadOnlyOwn(User $user, $scope)
{
if ($this->checkUserIsNotPortal($user)) {
return $this->getMainManager()->checkReadOnlyOwn($user, $permission);
$data = $this->getTable($user)->getScopeData($scope);
return $this->getMainManager()->checkReadOnlyOwn($user, $data);
}
return false;
return parent::checkReadOnlyOwn($user, $scope);
}
public function check(User $user, $subject, $action = null)

View File

@@ -91,10 +91,10 @@ class Container extends \Espo\Core\Container
protected function loadLanguage()
{
$language = new \Espo\Core\Portal\Utils\Language(
\Espo\Core\Utils\Language::detectLanguage($this->get('config'), $this->get('preferences')),
$this->get('fileManager'),
$this->get('config'),
$this->get('metadata'),
$this->get('preferences')
$this->get('useCache')
);
$language->setPortal($this->get('portal'));
return $language;

View File

@@ -33,7 +33,6 @@ use \Espo\Entities\Portal;
class Language extends \Espo\Core\Utils\Language
{
public function setPortal($portal)
{
if ($portal->get('language') !== '' && $portal->get('language')) {

View File

@@ -0,0 +1,174 @@
<?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/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Repositories;
use Espo\ORM\Entity;
use Espo\Core\Utils\Util;
class Event extends \Espo\Core\ORM\Repositories\RDB
{
protected $reminderDateAttribute = 'dateStart';
protected function afterRemove(Entity $entity, array $options = array())
{
$pdo = $this->getEntityManager()->getPDO();
$sql = "
DELETE FROM `reminder`
WHERE
entity_id = ".$pdo->quote($entity->id)." AND
entity_type = ".$pdo->quote($entity->getEntityType())." AND
deleted = 0
";
$pdo->query($sql);
}
protected function afterSave(Entity $entity, array $options = array())
{
parent::afterSave($entity, $options);
if (
$entity->isNew() ||
$entity->isAttributeChanged('assignedUserId') ||
$entity->isAttributeChanged('usersIds') ||
$entity->isAttributeChanged($this->reminderDateAttribute) ||
$entity->has('reminders')
) {
$pdo = $this->getEntityManager()->getPDO();
$reminderTypeList = $this->getMetadata()->get('entityDefs.Reminder.fields.type.options');
if (!$entity->has('reminders')) {
$reminderList = $this->getEntityReminderList($entity);
} else {
$reminderList = $entity->get('reminders');
}
if (!$entity->isNew()) {
$sql = "
DELETE FROM `reminder`
WHERE
entity_id = ".$pdo->quote($entity->id)." AND
entity_type = ".$pdo->quote($entity->getEntityType())." AND
deleted = 0
";
$pdo->query($sql);
}
if (empty($reminderList) || !is_array($reminderList)) return;
$entityType = $entity->getEntityType();
$dateValue = $entity->get($this->reminderDateAttribute);
if (!$dateValue) {
$e = $this->get($entity->id);
if ($e) {
$dateValue = $e->get($this->reminderDateAttribute);
}
}
if ($entity->hasLinkMultipleField('users')) {
$userIdList = $entity->getLinkMultipleIdList('users');
} else {
$userIdList = [];
if ($entity->get('assignedUserId')) {
$userIdList[] = $entity->get('assignedUserId');
}
}
if (!$dateValue) return;
if (empty($userIdList)) return;
$dateValueObj = new \DateTime($dateValue);
if (!$dateValueObj) return;
foreach ($reminderList as $item) {
$remindAt = clone $dateValueObj;
$seconds = intval($item->seconds);
$type = $item->type;
if (!in_array($type , $reminderTypeList)) continue;
$remindAt->sub(new \DateInterval('PT' . $seconds . 'S'));
foreach ($userIdList as $userId) {
$id = Util::generateId();
$sql = "
INSERT
INTO `reminder`
(id, entity_id, entity_type, `type`, user_id, remind_at, start_at, `seconds`)
VALUES (
".$pdo->quote($id).",
".$pdo->quote($entity->id).",
".$pdo->quote($entityType).",
".$pdo->quote($type).",
".$pdo->quote($userId).",
".$pdo->quote($remindAt->format('Y-m-d H:i:s')).",
".$pdo->quote($dateValue).",
".$pdo->quote($seconds)."
)
";
$pdo->query($sql);
}
}
}
}
public function getEntityReminderList(Entity $entity)
{
$pdo = $this->getEntityManager()->getPDO();
$reminderList = [];
$sql = "
SELECT DISTINCT `seconds`, `type`
FROM `reminder`
WHERE
`entity_type` = ".$pdo->quote($entity->getEntityType())." AND
`entity_id` = ".$pdo->quote($entity->id)." AND
`deleted` = 0
ORDER BY `seconds` ASC
";
$sth = $pdo->prepare($sql);
$sth->execute();
$rows = $sth->fetchAll(\PDO::FETCH_ASSOC);
foreach ($rows as $row) {
$o = new \StdClass();
$o->seconds = intval($row['seconds']);
$o->type = $row['type'];
$reminderList[] = $o;
}
return $reminderList;
}
}

View File

@@ -125,12 +125,21 @@ class Base
protected function order($sortBy, $desc = false, &$result)
{
if (!empty($sortBy)) {
$result['orderBy'] = $sortBy;
$type = $this->getMetadata()->get(['entityDefs', $this->getEntityType(), 'fields', $sortBy, 'type']);
if ($type === 'link') {
$result['orderBy'] .= 'Name';
} else if ($type === 'linkParent') {
$result['orderBy'] .= 'Type';
} else if ($type === 'address') {
if (!$desc) {
$orderPart = 'ASC';
} else {
$orderPart = 'DESC';
}
$result['orderBy'] = [[$sortBy . 'Country', $orderPart], [$sortBy . 'City', $orderPart], [$sortBy . 'Street', $orderPart]];
return;
} else if ($type === 'enum') {
$list = $this->getMetadata()->get(['entityDefs', $this->getEntityType(), 'fields', $sortBy, 'options']);
if ($list && is_array($list) && count($list)) {
@@ -210,8 +219,17 @@ class Base
if (!empty($item['value'])) {
$methodName = 'apply' . ucfirst($type);
if (method_exists($this, $methodName)) {;
$this->$methodName($item['field'], $item['value'], $result);
if (method_exists($this, $methodName)) {
$attribute = null;
if (isset($item['field'])) {
$attribute = $item['field'];
}
if (isset($item['attribute'])) {
$attribute = $item['attribute'];
}
if ($attribute) {
$this->$methodName($attribute, $item['value'], $result);
}
}
}
}
@@ -239,18 +257,21 @@ class Base
$defs = $relDefs[$link];
if ($relationType == 'manyMany') {
$this->addJoin($link, $result);
$this->addJoin([$link, $link . 'Filter'], $result);
$midKeys = $seed->getRelationParam($link, 'midKeys');
if (!empty($midKeys)) {
$key = $midKeys[1];
$part[$link . 'Middle.' . $key] = $idsValue;
$part[$link . 'Filter' . 'Middle.' . $key] = $idsValue;
}
} else if ($relationType== 'belongsTo') {
} else if ($relationType == 'belongsTo') {
$key = $seed->getRelationParam($link, 'key');
if (!empty($key)) {
$part[$key] = $idsValue;
}
} else if ($relationType == 'hasOne') {
$this->addJoin([$link, $link . 'Filter'], $result);
$part[$link . 'Filter' . '.id'] = $idsValue;
} else {
return;
}
@@ -689,13 +710,20 @@ class Base
protected function checkWhere($where)
{
foreach ($where as $w) {
$attribute = null;
if (isset($w['field'])) {
$attribute = $w['field'];
}
if (isset($w['attribute'])) {
$attribute = $w['attribute'];
}
if ($attribute) {
if (isset($w['type']) && $w['type'] === 'linkedWith') {
if (in_array($w['field'], $this->getAcl()->getScopeForbiddenFieldList($this->getEntityType()))) {
if (in_array($attribute, $this->getAcl()->getScopeForbiddenFieldList($this->getEntityType()))) {
throw new Forbidden();
}
} else {
if (in_array($w['field'], $this->getAcl()->getScopeForbiddenAttributeList($this->getEntityType()))) {
if (in_array($attribute, $this->getAcl()->getScopeForbiddenAttributeList($this->getEntityType()))) {
throw new Forbidden();
}
}
@@ -728,7 +756,15 @@ class Base
$value = null;
$timeZone = 'UTC';
if (empty($item['field'])) {
$attribute = null;
if (isset($item['field'])) {
$attribute = $item['field'];
}
if (isset($item['attribute'])) {
$attribute = $item['attribute'];
}
if (!$attribute) {
return null;
}
if (empty($item['type'])) {
@@ -741,14 +777,13 @@ class Base
$timeZone = $item['timeZone'];
}
$type = $item['type'];
$field = $item['field'];
if (empty($value) && in_array($type, array('on', 'before', 'after'))) {
return null;
}
$where = array();
$where['field'] = $field;
$where['attribute'] = $attribute;
$dt = new \DateTime('now', new \DateTimeZone($timeZone));
@@ -875,8 +910,16 @@ class Base
{
$part = array();
if (!empty($item['field']) && !empty($item['type'])) {
$methodName = 'getWherePart' . ucfirst($item['field']) . ucfirst($item['type']);
$attribute = null;
if (!empty($item['field'])) { // for backward compatibility
$attribute = $item['field'];
}
if (!empty($item['attribute'])) {
$attribute = $item['attribute'];
}
if (!empty($attribute) && !empty($item['type'])) {
$methodName = 'getWherePart' . ucfirst($attribute) . ucfirst($item['type']);
if (method_exists($this, $methodName)) {
$value = null;
if (!empty($item['value'])) {
@@ -909,74 +952,74 @@ class Base
}
break;
case 'like':
$part[$item['field'] . '*'] = $item['value'];
$part[$attribute . '*'] = $item['value'];
break;
case 'equals':
case 'on':
$part[$item['field'] . '='] = $item['value'];
$part[$attribute . '='] = $item['value'];
break;
case 'startsWith':
$part[$item['field'] . '*'] = $item['value'] . '%';
$part[$attribute . '*'] = $item['value'] . '%';
break;
case 'endsWith':
$part[$item['field'] . '*'] = $item['value'] . '%';
$part[$attribute . '*'] = '%' . $item['value'];
break;
case 'contains':
$part[$item['field'] . '*'] = '%' . $item['value'] . '%';
$part[$attribute . '*'] = '%' . $item['value'] . '%';
break;
case 'notEquals':
case 'notOn':
$part[$item['field'] . '!='] = $item['value'];
$part[$attribute . '!='] = $item['value'];
break;
case 'greaterThan':
case 'after':
$part[$item['field'] . '>'] = $item['value'];
$part[$attribute . '>'] = $item['value'];
break;
case 'lessThan':
case 'before':
$part[$item['field'] . '<'] = $item['value'];
$part[$attribute . '<'] = $item['value'];
break;
case 'greaterThanOrEquals':
$part[$item['field'] . '>='] = $item['value'];
$part[$attribute . '>='] = $item['value'];
break;
case 'lessThanOrEquals':
$part[$item['field'] . '<'] = $item['value'];
$part[$attribute . '<'] = $item['value'];
break;
case 'in':
$part[$item['field'] . '='] = $item['value'];
$part[$attribute . '='] = $item['value'];
break;
case 'notIn':
$part[$item['field'] . '!='] = $item['value'];
$part[$attribute . '!='] = $item['value'];
break;
case 'isNull':
$part[$item['field'] . '='] = null;
$part[$attribute . '='] = null;
break;
case 'isNotNull':
case 'ever':
$part[$item['field'] . '!='] = null;
$part[$attribute . '!='] = null;
break;
case 'isTrue':
$part[$item['field'] . '='] = true;
$part[$attribute . '='] = true;
break;
case 'isFalse':
$part[$item['field'] . '='] = false;
$part[$attribute . '='] = false;
break;
case 'today':
$part[$item['field'] . '='] = date('Y-m-d');
$part[$attribute . '='] = date('Y-m-d');
break;
case 'past':
$part[$item['field'] . '<'] = date('Y-m-d');
$part[$attribute . '<'] = date('Y-m-d');
break;
case 'future':
$part[$item['field'] . '>='] = date('Y-m-d');
$part[$attribute . '>='] = date('Y-m-d');
break;
case 'lastSevenDays':
$dt1 = new \DateTime();
$dt2 = clone $dt1;
$dt2->modify('-7 days');
$part['AND'] = array(
$item['field'] . '>=' => $dt2->format('Y-m-d'),
$item['field'] . '<=' => $dt1->format('Y-m-d'),
$attribute . '>=' => $dt2->format('Y-m-d'),
$attribute . '<=' => $dt1->format('Y-m-d'),
);
break;
case 'lastXDays':
@@ -986,8 +1029,8 @@ class Base
$dt2->modify('-'.$number.' days');
$part['AND'] = array(
$item['field'] . '>=' => $dt2->format('Y-m-d'),
$item['field'] . '<=' => $dt1->format('Y-m-d'),
$attribute . '>=' => $dt2->format('Y-m-d'),
$attribute . '<=' => $dt1->format('Y-m-d'),
);
break;
case 'nextXDays':
@@ -996,22 +1039,22 @@ class Base
$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'),
$attribute . '>=' => $dt1->format('Y-m-d'),
$attribute . '<=' => $dt2->format('Y-m-d'),
);
break;
case 'currentMonth':
$dt = new \DateTime();
$part['AND'] = array(
$item['field'] . '>=' => $dt->modify('first day of this month')->format('Y-m-d'),
$item['field'] . '<' => $dt->add(new \DateInterval('P1M'))->format('Y-m-d'),
$attribute . '>=' => $dt->modify('first day of this month')->format('Y-m-d'),
$attribute . '<' => $dt->add(new \DateInterval('P1M'))->format('Y-m-d'),
);
break;
case 'lastMonth':
$dt = new \DateTime();
$part['AND'] = array(
$item['field'] . '>=' => $dt->modify('first day of last month')->format('Y-m-d'),
$item['field'] . '<' => $dt->add(new \DateInterval('P1M'))->format('Y-m-d'),
$attribute . '>=' => $dt->modify('first day of last month')->format('Y-m-d'),
$attribute . '<' => $dt->add(new \DateInterval('P1M'))->format('Y-m-d'),
);
break;
case 'currentQuarter':
@@ -1019,8 +1062,8 @@ class Base
$quarter = ceil($dt->format('m') / 3);
$dt->modify('first day of January this year');
$part['AND'] = array(
$item['field'] . '>=' => $dt->add(new \DateInterval('P'.(($quarter - 1) * 3).'M'))->format('Y-m-d'),
$item['field'] . '<' => $dt->add(new \DateInterval('P3M'))->format('Y-m-d'),
$attribute . '>=' => $dt->add(new \DateInterval('P'.(($quarter - 1) * 3).'M'))->format('Y-m-d'),
$attribute . '<' => $dt->add(new \DateInterval('P3M'))->format('Y-m-d'),
);
break;
case 'lastQuarter':
@@ -1033,29 +1076,29 @@ class Base
$dt->sub('P1Y');
}
$part['AND'] = array(
$item['field'] . '>=' => $dt->add(new \DateInterval('P'.(($quarter - 1) * 3).'M'))->format('Y-m-d'),
$item['field'] . '<' => $dt->add(new \DateInterval('P3M'))->format('Y-m-d'),
$attribute . '>=' => $dt->add(new \DateInterval('P'.(($quarter - 1) * 3).'M'))->format('Y-m-d'),
$attribute . '<' => $dt->add(new \DateInterval('P3M'))->format('Y-m-d'),
);
break;
case 'currentYear':
$dt = new \DateTime();
$part['AND'] = array(
$item['field'] . '>=' => $dt->modify('first day of January this year')->format('Y-m-d'),
$item['field'] . '<' => $dt->add(new \DateInterval('P1Y'))->format('Y-m-d'),
$attribute . '>=' => $dt->modify('first day of January this year')->format('Y-m-d'),
$attribute . '<' => $dt->add(new \DateInterval('P1Y'))->format('Y-m-d'),
);
break;
case 'lastYear':
$dt = new \DateTime();
$part['AND'] = array(
$item['field'] . '>=' => $dt->modify('first day of January last year')->format('Y-m-d'),
$item['field'] . '<' => $dt->add(new \DateInterval('P1Y'))->format('Y-m-d'),
$attribute . '>=' => $dt->modify('first day of January last year')->format('Y-m-d'),
$attribute . '<' => $dt->add(new \DateInterval('P1Y'))->format('Y-m-d'),
);
break;
case 'between':
if (is_array($item['value'])) {
$part['AND'] = array(
$item['field'] . '>=' => $item['value'][0],
$item['field'] . '<=' => $item['value'][1],
$attribute . '>=' => $item['value'][0],
$attribute . '<=' => $item['value'][1],
);
}
break;

View File

@@ -0,0 +1,37 @@
<?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/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Templates\Controllers;
class BasePlus extends \Espo\Core\Controllers\Record
{
}

View File

@@ -0,0 +1,35 @@
<?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/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Templates\Controllers;
class Company extends \Espo\Core\Controllers\Record
{
}

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/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Templates\Entities;
class BasePlus extends \Espo\Core\ORM\Entity
{
}

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/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Templates\Entities;
class Company extends \Espo\Core\ORM\Entity
{
}

View File

@@ -0,0 +1,37 @@
[
{
"label": "Overview",
"rows": [
[
{
"name": "name"
},
{
"name": "website"
}
],
[
{
"name": "emailAddress"
},
{
"name": "phoneNumber"
}
],
[
{
"name": "billingAddress"
},
{
"name": "shippingAddress"
}
],
[
{
"name": "description",
"fullWidth": true
}
]
]
}
]

View File

@@ -0,0 +1,37 @@
[
{
"label": "",
"rows": [
[
{
"name": "name",
"fullWidth": true
}
],
[
{
"name": "website",
"fullWidth": true
}
],
[
{
"name": "emailAddress",
"fullWidth": true
}
],
[
{
"name": "phoneNumber",
"fullWidth": true
}
],
[
{
"name": "billingAddressCountry",
"fullWidth": true
}
]
]
}
]

View File

@@ -6,7 +6,9 @@
{
"name": "name"
},
false
{
"name": "parent"
}
],
[
{
@@ -26,7 +28,9 @@
{
"name": "duration"
},
false
{
"name": "reminders"
}
],
[
{

View File

@@ -32,6 +32,12 @@
"fullWidth": true
}
],
[
{
"name": "parent",
"fullWidth": true
}
],
[
{
"name": "description",

View File

@@ -0,0 +1,33 @@
[
{
"label": "Overview",
"rows": [
[
{
"name": "name"
},
false
],
[
{
"name": "emailAddress"
},
{
"name": "phoneNumber"
}
],
[
{
"name": "address"
},
false
],
[
{
"name": "description",
"fullWidth": true
}
]
]
}
]

View File

@@ -0,0 +1,25 @@
[
{
"label": "",
"rows": [
[
{
"name": "name",
"fullWidth": true
}
],
[
{
"name": "emailAddress",
"fullWidth": true
}
],
[
{
"name": "phoneNumber",
"fullWidth": true
}
]
]
}
]

View File

@@ -4,6 +4,7 @@
"tab": true,
"acl": true,
"aclPortal": true,
"aclPortalLevelList": ["all", "account", "own", "no"],
"customizable": true,
"importable": true,
"notifications": true

View File

@@ -0,0 +1,26 @@
{
"controller": "controllers/record",
"boolFilterList": ["onlyMy"],
"sidePanels": {
"detail": [
{
"name": "activities",
"label": "Activities",
"view": "crm:views/record/panels/activities",
"aclScope": "Activities"
},
{
"name": "history",
"label": "History",
"view": "crm:views/record/panels/history",
"aclScope": "Activities"
},
{
"name": "tasks",
"label": "Tasks",
"view": "crm:views/record/panels/tasks",
"aclScope": "Task"
}
]
}
}

View File

@@ -0,0 +1,89 @@
{
"fields": {
"name": {
"type": "varchar",
"required": true,
"trim": true
},
"description": {
"type": "text"
},
"createdAt": {
"type": "datetime",
"readOnly": true
},
"modifiedAt": {
"type": "datetime",
"readOnly": true
},
"createdBy": {
"type": "link",
"readOnly": true,
"view": "views/fields/user"
},
"modifiedBy": {
"type": "link",
"readOnly": true,
"view": "views/fields/user"
},
"assignedUser": {
"type": "link",
"required": true,
"view": "views/fields/assigned-user"
},
"teams": {
"type": "linkMultiple",
"view": "views/fields/teams"
}
},
"links": {
"createdBy": {
"type": "belongsTo",
"entity": "User"
},
"modifiedBy": {
"type": "belongsTo",
"entity": "User"
},
"assignedUser": {
"type": "belongsTo",
"entity": "User"
},
"teams": {
"type": "hasMany",
"entity": "Team",
"relationName": "EntityTeam",
"layoutRelationshipsDisabled": true
},
"meetings": {
"type": "hasMany",
"entity": "Meeting",
"foreign": "parent",
"layoutRelationshipsDisabled": true
},
"calls": {
"type": "hasMany",
"entity": "Call",
"foreign": "parent",
"layoutRelationshipsDisabled": true
},
"tasks": {
"type": "hasChildren",
"entity": "Task",
"foreign": "parent",
"layoutRelationshipsDisabled": true
}
},
"collection": {
"sortBy": "createdAt",
"asc": false
},
"indexes": {
"name": {
"columns": ["name", "deleted"]
},
"assignedUser": {
"columns": ["assignedUserId", "deleted"]
}
}
}

View File

@@ -0,0 +1,11 @@
{
"entity": true,
"layouts": true,
"tab": true,
"acl": true,
"aclPortal": true,
"aclPortalLevelList": ["all", "account", "own", "no"],
"customizable": true,
"importable": true,
"notifications": true
}

View File

@@ -0,0 +1,26 @@
{
"controller": "controllers/record",
"boolFilterList": ["onlyMy"],
"sidePanels": {
"detail": [
{
"name": "activities",
"label": "Activities",
"view": "crm:views/record/panels/activities",
"aclScope": "Activities"
},
{
"name": "history",
"label": "History",
"view": "crm:views/record/panels/history",
"aclScope": "Activities"
},
{
"name": "tasks",
"label": "Tasks",
"view": "crm:views/record/panels/tasks",
"aclScope": "Task"
}
]
}
}

View File

@@ -0,0 +1,141 @@
{
"fields": {
"name": {
"type": "varchar",
"required": true,
"trim": true
},
"description": {
"type": "text"
},
"website": {
"type": "url"
},
"emailAddress": {
"type": "email"
},
"phoneNumber": {
"type": "phone",
"typeList": ["Office", "Mobile", "Fax", "Other"],
"defaultType": "Office"
},
"billingAddress": {
"type": "address"
},
"billingAddressStreet": {
"type": "text",
"maxLength": 255,
"dbType": "varchar"
},
"billingAddressCity": {
"type": "varchar"
},
"billingAddressState": {
"type": "varchar"
},
"billingAddressCountry": {
"type": "varchar"
},
"billingAddressPostalCode": {
"type": "varchar"
},
"shippingAddress": {
"type": "address",
"view": "crm:views/account/fields/shipping-address"
},
"shippingAddressStreet": {
"type": "text",
"maxLength": 255,
"dbType": "varchar"
},
"shippingAddressCity": {
"type": "varchar"
},
"shippingAddressState": {
"type": "varchar"
},
"shippingAddressCountry": {
"type": "varchar"
},
"shippingAddressPostalCode": {
"type": "varchar"
},
"createdAt": {
"type": "datetime",
"readOnly": true
},
"modifiedAt": {
"type": "datetime",
"readOnly": true
},
"createdBy": {
"type": "link",
"readOnly": true,
"view": "views/fields/user"
},
"modifiedBy": {
"type": "link",
"readOnly": true,
"view": "views/fields/user"
},
"assignedUser": {
"type": "link",
"required": false,
"view": "views/fields/assigned-user"
},
"teams": {
"type": "linkMultiple",
"view": "views/fields/teams"
}
},
"links": {
"createdBy": {
"type": "belongsTo",
"entity": "User"
},
"modifiedBy": {
"type": "belongsTo",
"entity": "User"
},
"assignedUser": {
"type": "belongsTo",
"entity": "User"
},
"teams": {
"type": "hasMany",
"entity": "Team",
"relationName": "EntityTeam",
"layoutRelationshipsDisabled": true
},
"meetings": {
"type": "hasMany",
"entity": "Meeting",
"foreign": "parent",
"layoutRelationshipsDisabled": true
},
"calls": {
"type": "hasMany",
"entity": "Call",
"foreign": "parent",
"layoutRelationshipsDisabled": true
},
"tasks": {
"type": "hasChildren",
"entity": "Task",
"foreign": "parent",
"layoutRelationshipsDisabled": true
}
},
"collection": {
"sortBy": "createdAt",
"asc": false
},
"indexes": {
"name": {
"columns": ["name", "deleted"]
},
"assignedUser": {
"columns": ["assignedUserId", "deleted"]
}
}
}

View File

@@ -0,0 +1,11 @@
{
"entity": true,
"layouts": true,
"tab": true,
"acl": true,
"aclPortal": true,
"aclPortalLevelList": ["all", "account", "own", "no"],
"customizable": true,
"importable": true,
"notifications": true
}

View File

@@ -1,4 +1,20 @@
{
"controller": "controllers/record",
"boolFilterList": ["onlyMy"]
"controller": "controllers/record",
"boolFilterList": ["onlyMy"],
"activityDefs": {
"activitiesCreate": true,
"historyCreate": true
},
"filterList": [
{
"name":"planned"
},
{
"name":"held",
"style": "success"
},
{
"name":"todays"
}
]
}

View File

@@ -41,6 +41,11 @@
"description": {
"type": "text"
},
"reminders": {
"type": "jsonArray",
"notStorable": true,
"view": "crm:views/meeting/fields/reminders"
},
"createdAt": {
"type": "datetime",
"readOnly": true

View File

@@ -4,8 +4,13 @@
"tab": true,
"acl": true,
"aclPortal": true,
"aclPortalLevelList": ["all", "account", "contact", "own", "no"],
"customizable": true,
"importable": true,
"calendar": true,
"notifications": true
"activity": true,
"notifications": true,
"activityStatusList": ["Planned"],
"historyStatusList": ["Held", "Not Held"],
"statusField": "status"
}

View File

@@ -1,4 +1,26 @@
{
"controller": "controllers/record",
"boolFilterList": ["onlyMy"]
"controller": "controllers/record",
"boolFilterList": ["onlyMy"],
"sidePanels": {
"detail": [
{
"name": "activities",
"label": "Activities",
"view": "crm:views/record/panels/activities",
"aclScope": "Activities"
},
{
"name": "history",
"label": "History",
"view": "crm:views/record/panels/history",
"aclScope": "Activities"
},
{
"name": "tasks",
"label": "Tasks",
"view": "crm:views/record/panels/tasks",
"aclScope": "Task"
}
]
}
}

View File

@@ -95,6 +95,24 @@
"entity": "Team",
"relationName": "EntityTeam",
"layoutRelationshipsDisabled": true
},
"meetings": {
"type": "hasMany",
"entity": "Meeting",
"foreign": "parent",
"layoutRelationshipsDisabled": true
},
"calls": {
"type": "hasMany",
"entity": "Call",
"foreign": "parent",
"layoutRelationshipsDisabled": true
},
"tasks": {
"type": "hasChildren",
"entity": "Task",
"foreign": "parent",
"layoutRelationshipsDisabled": true
}
},
"collection": {

View File

@@ -4,6 +4,7 @@
"tab": true,
"acl": true,
"aclPortal": true,
"aclPortalLevelList": ["all", "account", "own", "no"],
"customizable": true,
"importable": true,
"notifications": true

View File

@@ -0,0 +1,37 @@
<?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/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Templates\Repositories;
class BasePlus extends \Espo\Core\ORM\Repositories\RDB
{
}

View File

@@ -0,0 +1,35 @@
<?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/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Templates\Repositories;
class Company extends \Espo\Core\ORM\Repositories\RDB
{
}

View File

@@ -29,8 +29,7 @@
namespace Espo\Core\Templates\Repositories;
class Event extends \Espo\Core\ORM\Repositories\RDB
class Event extends \Espo\Core\Repositories\Event
{
}

View File

@@ -0,0 +1,57 @@
<?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/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Templates\SelectManagers;
class Event extends \Espo\Core\SelectManagers\Base
{
protected function filterPlanned(&$result)
{
$result['whereClause'][] = array(
'status' => 'Planned'
);
}
protected function filterHeld(&$result)
{
$result['whereClause'][] = array(
'status' => 'Held'
);
}
protected function filterTodays(&$result)
{
$result['whereClause'][] = $this->convertDateTimeWhere(array(
'type' => 'today',
'attribute' => 'dateStart',
'timeZone' => $this->getUserTimeZone()
));
}
}

View File

@@ -0,0 +1,37 @@
<?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/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Templates\Services;
class BasePlus extends \Espo\Services\Record
{
}

View File

@@ -0,0 +1,42 @@
<?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/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Templates\Services;
use \Espo\ORM\Entity;
class Company extends \Espo\Services\Record
{
protected function getDuplicateWhereClause(Entity $entity, $data = array())
{
return array(
'name' => $entity->get('name')
);
}
}

View File

@@ -29,8 +29,19 @@
namespace Espo\Core\Templates\Services;
use \Espo\ORM\Entity;
class Event extends \Espo\Services\Record
{
public function loadAdditionalFields(Entity $entity)
{
parent::loadAdditionalFields($entity);
$this->loadRemindersField($entity);
}
protected function loadRemindersField(Entity $entity)
{
$reminders = $this->getRepository()->getEntityReminderList($entity);
$entity->set('reminders', $reminders);
}
}

View File

@@ -29,6 +29,7 @@
namespace Espo\Core\Templates\Services;
use \Espo\ORM\Entity;
class Person extends \Espo\Services\Record
{

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create {entityType}": "Vytvořit {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create {entityType}": "Vytvořit {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create {entityType}": "Vytvořit {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create {entityType}": "Vytvořit {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create {entityType}": "Vytvořit {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create {entityType}": "{entityTypeTranslated} erstellen"
}
}

View File

@@ -0,0 +1,10 @@
{
"links": {
"meetings": "Meetings",
"calls": "Anrufe",
"tasks": "Aufgaben"
},
"labels": {
"Create {entityType}": "{entityTypeTranslated} erstellen"
}
}

View File

@@ -0,0 +1,15 @@
{
"fields": {
"billingAddress": "Rechnungsadresse",
"shippingAddress": "Lieferadresse",
"website": "Webseite"
},
"links": {
"meetings": "Meetings",
"calls": "Anrufe",
"tasks": "Aufgaben"
},
"labels": {
"Create {entityType}": "{entityTypeTranslated} erstellen"
}
}

View File

@@ -0,0 +1,35 @@
{
"fields": {
"parent": "Bezieht sich auf",
"dateStart": "Startdatum",
"dateEnd": "Enddatum",
"duration": "Dauer",
"status": "Status"
},
"links": {
"parent": "Bezieht sich auf"
},
"options": {
"status": {
"Planned": "Geplant",
"Held": "Durchgeführt",
"Not Held": "Nicht durchgeführt"
}
},
"labels": {
"Create {entityType}": "{entityTypeTranslated} erstellen",
"Schedule {entityType}": "{entityTypeTranslated} planen",
"Log {entityType}": "Log {entityTypeTranslated}",
"Set Held": "Auf durchgeführt setzen",
"Set Not Held": "Auf nicht durchgeführt setzen"
},
"massActions": {
"setHeld": "Auf durchgeführt setzen",
"setNotHeld": "Auf nicht durchgeführt setzen"
},
"presetFilters": {
"planned": "Geplant",
"held": "Durchgeführt",
"todays": "Heutige"
}
}

View File

@@ -0,0 +1,13 @@
{
"fields": {
"address": "Adresse"
},
"links": {
"meetings": "Meetings",
"calls": "Anrufe",
"tasks": "Aufgaben"
},
"labels": {
"Create {entityType}": "{entityTypeTranslated} erstellen"
}
}

View File

@@ -0,0 +1,9 @@
{
"fields": {
},
"links": {
},
"labels": {
"Create {entityType}": "Create {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,12 @@
{
"fields": {
},
"links": {
"meetings": "Meetings",
"calls": "Calls",
"tasks": "Tasks"
},
"labels": {
"Create {entityType}": "Create {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,15 @@
{
"fields": {
"billingAddress": "Billing Address",
"shippingAddress": "Shipping Address",
"website": "Website"
},
"links": {
"meetings": "Meetings",
"calls": "Calls",
"tasks": "Tasks"
},
"labels": {
"Create {entityType}": "Create {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,36 @@
{
"fields": {
"parent": "Parent",
"dateStart": "Date Start",
"dateEnd": "Date End",
"duration": "Duration",
"status": "Status",
"reminders": "Reminders"
},
"links": {
"parent": "Parent"
},
"options": {
"status": {
"Planned": "Planned",
"Held": "Held",
"Not Held": "Not Held"
}
},
"labels": {
"Create {entityType}": "Create {entityTypeTranslated}",
"Schedule {entityType}": "Schedule {entityTypeTranslated}",
"Log {entityType}": "Log {entityTypeTranslated}",
"Set Held": "Set Held",
"Set Not Held": "Set Not Held"
},
"massActions": {
"setHeld": "Set Held",
"setNotHeld": "Set Not Held"
},
"presetFilters": {
"planned": "Planned",
"held": "Held",
"todays": "Today's"
}
}

View File

@@ -0,0 +1,13 @@
{
"fields": {
"address": "Address"
},
"links": {
"meetings": "Meetings",
"calls": "Calls",
"tasks": "Tasks"
},
"labels": {
"Create {entityType}": "Create {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create {entityType}": "Vytvořit {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create {entityType}": "Crear {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create {entityType}": "Crear {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create {entityType}": "Crear {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create {entityType}": "Crear {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create {entityType}": "Créer un {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create {entityType}": "Créer un {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create {entityType}": "Créer un {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create {entityType}": "Créer un {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create {entityType}": "Créer un {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create {entityType}": "Buat {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create {entityType}": "Buat {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create {entityType}": "Buat {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create {entityType}": "Buat {entityTypeTranslated}"
}
}

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