Compare commits

...

249 Commits
4.6.0 ... 4.8.0

Author SHA1 Message Date
yuri
e557782c64 fix notice 2017-08-04 14:05:28 +03:00
yuri
7cc7f3fbcf week function 2017-08-03 12:45:53 +03:00
yuri
3f293a6d23 orm: week function 2017-08-03 12:07:06 +03:00
yuri
309b09f6ce Merge branch 'master' of github.com:espocrm/espocrm 2017-08-02 12:26:58 +03:00
yuri
0b7e9f3d62 disableHooks property 2017-08-02 11:51:36 +03:00
tanyalei
58c17ffd5c beforeSave for Event fix (#602) 2017-08-01 15:18:44 +03:00
yuri
7d3ba23a9f fix labal manager 2017-07-31 16:41:04 +03:00
yuri
ca2e4f5b37 fix duplicateIgnore 2017-07-31 16:17:47 +03:00
yuri
d82174fec5 fix excel export 2017-07-31 13:00:55 +03:00
yuri
033c65b79b acl strict mode 2017-07-31 12:15:01 +03:00
yuri
dbc8e5d9cc entity manager: common reserved word 2017-07-31 11:28:48 +03:00
yuri
42407935d5 duplicateIgnore 2017-07-31 11:23:52 +03:00
yuri
e7d558b0bf version 2017-07-31 11:14:48 +03:00
yuri
290b530b92 Merge branch 'master' of github.com:espocrm/espocrm 2017-07-18 15:23:24 +03:00
yuri
18726bdccd empty reminder if event is not actual 2017-07-18 15:23:09 +03:00
Dmitry Danilson
06b0498abb Fix #592: Replace undefined with (#593) 2017-07-14 13:57:40 +03:00
yuri
7a7627e931 update mail-mime-parser 2017-07-12 12:03:37 +03:00
yuri
c6038e9cee email account: unset recent flag 2017-07-12 11:32:10 +03:00
yuri
0e05596155 beforeSave repositoty order fix 2017-07-12 11:28:59 +03:00
yuri
0ca212f911 fix warning 2017-07-11 11:08:24 +03:00
yuri
94c2b9deda fix link search 2017-07-07 11:42:46 +03:00
yuri
2016a1bb0d relationship panel link in defs 2017-07-07 11:05:06 +03:00
yuri
6841ba9c85 side panel fieldList 2017-07-07 11:04:19 +03:00
yuri
d5988f2c04 fix after twice after remove hook 2017-07-05 15:38:11 +03:00
yuri
a56c9c3a46 fix complexText link 2017-07-04 17:28:51 +03:00
yuri
9d0bbdcd4d sr_RS lang 2017-07-04 15:47:57 +03:00
yuri
4bfaac3414 layout changes 2017-07-03 17:11:54 +03:00
yuri
d54c044fd9 Merge branch 'hotfix/4.7.3' 2017-07-03 11:27:08 +03:00
yuri
1def66bb77 fix team clear roles cache 2017-07-03 11:26:25 +03:00
yuri
75041db2e9 currency setting changes 2017-06-28 14:37:45 +03:00
yuri
356948cdf7 fix preferences edit 2017-06-28 14:04:33 +03:00
yuri
db92abf3ee Merge branch 'hotfix/4.7.3' 2017-06-28 12:45:20 +03:00
yuri
d5d59abf13 fix notice 2017-06-28 12:45:11 +03:00
yuri
69c1e3e8e0 email load bcc name 2017-06-28 12:22:08 +03:00
yuri
963c4e3e2b fix typo 2017-06-26 12:43:22 +03:00
yuri
cc4c735a27 entity manager: dont allow entity name if controller exists 2017-06-26 12:42:47 +03:00
yuri
175517a9dc list row menu links 2017-06-26 12:23:39 +03:00
yuri
9cb3548393 cleanup reminders 2017-06-26 12:22:36 +03:00
yuri
ac0dea317f reminder threshold fix 2017-06-26 11:42:44 +03:00
yuri
839dfb9709 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2017-06-23 15:47:24 +03:00
Taras Machyshyn
2c7a4098f3 Upgrade manager: added beforeUpgradeFiles and afterUpgradeFiles 2017-06-23 13:16:36 +03:00
yuri
0228396376 fix formula dayOfWeek 2017-06-23 13:11:00 +03:00
yuri
c5aa0eba48 fix formula dayOfWeek 2017-06-23 13:10:42 +03:00
Taras Machyshyn
ac4472975d Fixed scheduled job run date 2017-06-23 12:40:33 +03:00
yuri
379f5fbaaf field manager ui fix 2017-06-23 12:34:21 +03:00
yuri
af71408c52 cleanup 2017-06-23 12:19:22 +03:00
yuri
b7a4fa1e7c Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2017-06-23 12:15:39 +03:00
yuri
92b6e45ef2 default for link field 2017-06-23 12:15:31 +03:00
yuri
acf6f67627 fix warning 2017-06-23 12:15:12 +03:00
Taras Machyshyn
017c34dcde Orm converter: remove fields without type 2017-06-22 17:26:05 +03:00
yuri
f6e54918b2 fix orm 2017-06-22 12:43:11 +03:00
yuri
75b3c33526 tpl fix 2017-06-21 15:22:28 +03:00
yuri
f784760735 css fix 2017-06-21 14:22:30 +03:00
yuri
a739b3a96e fix link field empty name 2017-06-21 13:10:55 +03:00
yuri
0a8e0f00c2 defaultAttributes param 2017-06-21 12:37:01 +03:00
yuri
00c4cf4893 update mailmimeparser 2017-06-21 12:07:17 +03:00
yuri
e8e9ea9e2f fix search trim 2017-06-20 16:08:50 +03:00
yuri
c9cdb6767b update slim 2017-06-20 14:50:03 +03:00
yuri
24f9c1c762 es_MX lang 2017-06-20 11:08:15 +03:00
yuri
766a880799 lang 2017-06-20 11:04:23 +03:00
yuri
07de0dbeb0 fix search preset 2017-06-19 16:35:04 +03:00
yuri
d1752f2ff0 fix select related 2017-06-19 16:13:59 +03:00
yuri
ef27234ef8 enum column and varchar column field views 2017-06-19 15:58:20 +03:00
yuri
685862f1de fix enum int float 2017-06-19 11:04:12 +03:00
yuri
97621c45a8 opportunity role search 2017-06-16 12:50:35 +03:00
yuri
b96d433796 search by account fixes 2017-06-15 17:31:32 +03:00
yuri
03d784c284 label manager 2017-06-15 16:53:12 +03:00
yuri
02ef97724f inactive account 2017-06-15 16:50:37 +03:00
yuri
93e2c65d30 en_GB lang 2017-06-14 15:55:17 +03:00
yuri
d02bf65a55 version 2017-06-14 12:53:29 +03:00
yuri
fb2f566314 email importer contact parent if no account 2017-06-14 12:51:36 +03:00
yuri
2227870854 Merge branch 'master' of github.com:espocrm/espocrm 2017-06-14 12:34:16 +03:00
yuri
b195509843 Merge branch 'hotfix/4.7.3' 2017-06-14 12:34:06 +03:00
yuri
2100fa0606 fix email acl ui 2017-06-14 12:33:56 +03:00
Dmitry Danilson
3fe43bdfa3 #563 Replace Container::loadLog() with loader class (#565)
Looks good. Thanks.
2017-06-14 12:18:25 +03:00
yuri
26ad3d48af da_DK fix 2017-06-14 11:59:16 +03:00
yuri
9c224fefb7 label editor: confirm leave out 2017-06-12 14:51:51 +03:00
barwi
e4db873e43 fix typo (#560) 2017-06-12 14:29:50 +03:00
barwi
f06d53b3d2 fix typo (#560) 2017-06-12 14:28:58 +03:00
yuri
4ef706b716 Merge branch 'hotfix/4.7.2' 2017-06-08 16:52:16 +03:00
yuri
09e364625d fix formula parser 2017-06-08 16:52:03 +03:00
yuri
35f575ed7f fix array field 2017-06-07 12:06:58 +03:00
yuri
7b0d46b37f travel industry 2017-06-07 11:51:39 +03:00
yuri
b8be2c9e13 travel industry 2017-06-07 11:51:20 +03:00
yuri
f05c763119 label manager 2017-06-06 15:09:36 +03:00
yuri
262b3bb3b3 Merge branch 'hotfix/4.7.2' 2017-06-06 14:01:53 +03:00
yuri
f474137b91 fix mention 2017-06-06 14:01:33 +03:00
yuri
7a18d88c50 fix export 2 2017-06-06 12:20:46 +03:00
yuri
d3ea71c726 Merge branch 'hotfix/4.7.2' 2017-06-06 12:03:35 +03:00
yuri
8d1c9fe666 fix export 2017-06-06 12:01:19 +03:00
yuri
62ccfaa9c0 Merge branch 'hotfix/4.7.2' 2017-05-31 10:38:00 +03:00
yuri
e6566bccdc fix portal 2017-05-29 13:42:41 +03:00
yuri
14ed7282f3 cleanup 2017-05-29 11:47:50 +03:00
yuri
b13145adce version 2017-05-29 11:40:52 +03:00
yuri
c0b169f7dd Merge branch 'hotfix/4.7.2' 2017-05-29 11:40:18 +03:00
yuri
3cfca9845a improve dependencies 2017-05-29 11:40:06 +03:00
yuri
afa8d07bb4 fix notice 2017-05-29 11:35:00 +03:00
yuri
02434e0ca1 css fix 2017-05-26 11:48:34 +03:00
yuri
3d68c77ed4 Merge branch 'hotfix/4.7.1' 2017-05-26 11:19:11 +03:00
yuri
11f76c507b fix export sheet name 2017-05-25 14:51:11 +03:00
yuri
a2e241966e Merge branch 'hotfix/4.7.1' 2017-05-25 14:22:49 +03:00
yuri
c68f3eb44d template fixes 2017-05-25 14:22:39 +03:00
yuri
6a1d8dcdca version 2017-05-25 11:44:44 +03:00
yuri
ba358e5847 Merge branch 'hotfix/4.7.1' of ssh://172.20.0.1/var/git/espo/backend into hotfix/4.7.1 2017-05-25 11:40:18 +03:00
yuri
5e174fd5a9 excel export fix 2017-05-25 11:16:58 +03:00
yuri
7bf7944360 excel export fix 2017-05-25 11:16:23 +03:00
yuri
9f72b074d5 Merge branch 'hotfix/4.7.1' 2017-05-24 13:41:26 +03:00
yuri
9af2500292 xsl export fix 2017-05-24 13:41:15 +03:00
yuri
59b7934aa2 Merge branch 'hotfix/4.7.1' 2017-05-22 15:41:03 +03:00
yuri
8e9dea260e fix port theme 2017-05-22 15:40:31 +03:00
yunga91
29ed6cb3b0 Fix view name resolving for custom field types in filter action (#546)
* fix view name resolving for custom field types in merge action

* fix view name resolving for custom field types in filter action
2017-05-22 14:36:43 +03:00
yuri
a7a61a993c Merge branch 'master' of github.com:espocrm/espocrm 2017-05-22 14:36:09 +03:00
yunga91
68d1c0854b Fix view name resolving for custom field types in filter action (#546)
* fix view name resolving for custom field types in merge action

* fix view name resolving for custom field types in filter action
2017-05-22 14:35:31 +03:00
yuri
fdd6f01415 Merge branch 'hotfix/4.7.1' 2017-05-22 14:16:03 +03:00
yuri
4670bb49e8 excel export by default 2017-05-22 14:15:56 +03:00
yuri
54a045e25a Merge branch 'hotfix/4.7.1' 2017-05-19 17:37:25 +03:00
yuri
fcc18befc3 excel export fix currency 2017-05-19 17:37:18 +03:00
yuri
52ac9b1621 small fix 2017-05-19 17:14:58 +03:00
yuri
862b76f165 Merge branch 'hotfix/4.7.1' 2017-05-19 15:11:16 +03:00
yuri
9bca545f01 fix excel temp file name 2017-05-19 15:11:05 +03:00
Taras Machyshyn
80032f8ed7 Correct labels 2017-05-18 17:08:37 +03:00
yuri
a63a690307 field manager: forbid underscore name 2017-05-18 13:24:36 +03:00
yuri
5ed0b2ba6b field manager: forbid underscore name 2017-05-18 13:24:15 +03:00
yuri
24dc2716f7 fix excel export label 2017-05-18 12:44:30 +03:00
yuri
bbe26618ac fix excel export label 2017-05-18 12:44:10 +03:00
yuri
6f65d9e8d8 Merge branch 'hotfix/4.7.1' 2017-05-18 12:18:39 +03:00
yuri
3f40896374 export fileName 2017-05-18 12:18:31 +03:00
yuri
f1b0decbab Merge branch 'hotfix/4.7.1' 2017-05-17 16:08:06 +03:00
yuri
8d8778b397 lang 2017-05-17 16:07:36 +03:00
yuri
c2b60698bc Merge branch 'hotfix/4.7.1' 2017-05-17 15:56:37 +03:00
yuri
68d3b422cc lang 2017-05-17 15:56:28 +03:00
yuri
63ff4c1dca Merge branch 'hotfix/4.7.1' 2017-05-17 15:32:03 +03:00
yuri
145420aaaa fix export noJoin names 2017-05-17 15:31:54 +03:00
yuri
8987ec61fe Merge branch 'hotfix/4.7.1' 2017-05-16 13:45:29 +03:00
yuri
34ee17b7f5 fix notice 2017-05-16 13:45:12 +03:00
yuri
a93627d274 tooltips 2017-05-16 12:58:23 +03:00
yuri
90fe18b660 Merge branch 'hotfix/4.7.1' 2017-05-16 12:47:44 +03:00
yuri
2a0646d1dd fix warning 2017-05-16 12:47:37 +03:00
yuri
6182376d1f Merge branch 'hotfix/4.7.1' 2017-05-16 12:21:48 +03:00
yuri
52620e84cd skipCreatedBy 2017-05-16 12:21:35 +03:00
yuri
66235f7e77 hide 2017-05-16 11:36:46 +03:00
yuri
49b61dab53 Merge branch 'hotfix/4.7.1' 2017-05-16 11:34:33 +03:00
yuri
4747113742 lang 2017-05-16 11:34:22 +03:00
yuri
a10f2949a4 Merge branch 'hotfix/4.7.1' 2017-05-16 11:30:52 +03:00
yuri
f26d7c6a62 lang 2017-05-16 11:30:43 +03:00
yuri
2e0d8cfd43 hide 2017-05-16 11:12:35 +03:00
yuri
666d8ab5ad Merge branch 'hotfix/4.7.1' 2017-05-16 10:55:34 +03:00
yuri
db57c53652 BAM currency 2017-05-16 10:55:27 +03:00
yuri
bbcf9e00fa notification number 2017-05-15 16:54:34 +03:00
yuri
badf1aec6f fix side panel 2017-05-15 11:38:46 +03:00
yuri
19bb4514df lang 2017-05-12 14:31:32 +03:00
yuri
778be7fea8 Merge branch 'master' of github.com:espocrm/espocrm 2017-05-12 11:22:53 +03:00
yuri
326970bcd4 fix lang 2017-05-12 11:22:42 +03:00
yuri
9411bce8b0 fix sorting field types 2017-05-12 11:12:40 +03:00
yuri
244c1a426b fix model factory 2017-05-12 11:04:36 +03:00
Mauricio Panuncio
b3f9ca8721 Fix and improve translations ES (#538)
* 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.

* Fix: 'Lenguaje' to 'Idioma'.

* Fix some erros in the installation screens.

* Fix some erros in the installation screens II.

* Chamge mod

* Translate. Round 1.

* Translate ES. Stream: Actividades to Historia.

* Translate ES. Lead: Potencial to Posible Cliente.

* Translate ES. Log: Registros.

* Translate ES. Round 2.

* Translate ES. Round 3.

* Translate ES. Round 4.

* Translate ES. New translations.

* Translate ES. Fix bugs.

* Translate ES. Activity: Actividades to Actividades planeadas.

* Translate ES. Hostory: Historia to Historial de aactividades.

* Translate ES. Improvements.

* Fix.

* Traanslate ES.

* Traanslate ES.

* Traanslate ES.

* Fix permissions.

* New translations ES

* Fix and improve translations ES.
2017-05-11 17:55:55 +03:00
yuri
8c66a83054 html cache timestamp 2017-05-10 16:52:56 +03:00
yuri
df6a1f6613 defaults config addition 2017-05-10 14:59:59 +03:00
yuri
1033a926fc v 4.7.0 2017-05-10 14:49:41 +03:00
yuri
d339c04177 field manager: dont allow field name beginning with a number 2017-05-10 12:52:28 +03:00
yuri
edcf0aae84 allow only one attachment multiple field 2017-05-10 12:47:14 +03:00
yuri
20f97caab3 field manager: not allow name same as link 2017-05-10 12:36:50 +03:00
yuri
86be4e7352 Merge branch 'hotfix/4.6.1' 2017-05-10 12:31:50 +03:00
yuri
697e271e66 Merge branch 'hotfix/4.6.1' of ssh://172.20.0.1/var/git/espo/backend into hotfix/4.6.1 2017-05-10 12:31:34 +03:00
yuri
5f74504da8 cleanup changes 2017-05-10 12:06:11 +03:00
yuri
fb3d46d389 attachments fill parentType or relatedId w/o id if entity is not created 2017-05-10 11:35:23 +03:00
yuri
875971fb6e generate password preview 2017-05-10 10:59:04 +03:00
yuri
1939a58f17 stream post storing improvement 2017-05-09 15:57:03 +03:00
yuri
40b3374fdd stream post keep attachments 2017-05-09 15:07:03 +03:00
yuri
c564b1b75a stream: store post content on leave 2017-05-09 12:57:49 +03:00
yuri
c28e73ce3f formula fix 2017-05-08 16:06:47 +03:00
yuri
788a6bfdc3 formula ui fix 2017-05-08 13:32:16 +03:00
yuri
15d1f79115 formula change ui 2017-05-08 13:30:14 +03:00
yuri
bf699d88f7 system jobs 2017-05-08 12:34:07 +03:00
yuri
d7f8e4e050 fix notice 2017-05-08 12:32:49 +03:00
yuri
3872d7897a stream ignoreScopeList fix 2017-05-08 11:46:48 +03:00
yuri
17a1f6ce63 stream ignoreScopeList fix 2017-05-08 11:45:50 +03:00
Mauricio Panuncio
eaa155eff7 Translate es (#536)
* 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.

* Fix: 'Lenguaje' to 'Idioma'.

* Fix some erros in the installation screens.

* Fix some erros in the installation screens II.

* Chamge mod

* Translate. Round 1.

* Translate ES. Stream: Actividades to Historia.

* Translate ES. Lead: Potencial to Posible Cliente.

* Translate ES. Log: Registros.

* Translate ES. Round 2.

* Translate ES. Round 3.

* Translate ES. Round 4.

* Translate ES. New translations.

* Translate ES. Fix bugs.

* Translate ES. Activity: Actividades to Actividades planeadas.

* Translate ES. Hostory: Historia to Historial de aactividades.

* Translate ES. Improvements.

* Fix.

* Traanslate ES.

* Traanslate ES.

* Traanslate ES.

* Fix permissions.
2017-05-08 10:57:45 +03:00
yuri
13fba6d71c Merge branch 'master' of github.com:espocrm/espocrm 2017-05-08 10:56:50 +03:00
yuri
0d423aa349 Merge branch 'hotfix/4.6.1' 2017-05-08 10:56:09 +03:00
yuri
2fca224ba4 fix html 2017-05-08 10:55:47 +03:00
yuri
574f85df46 email template related 2017-05-08 10:42:25 +03:00
yuri
b7176983e2 email template related 2017-05-08 10:42:01 +03:00
Mauricio Panuncio
e8f19ff0dc Translate es (#536)
* 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.

* Fix: 'Lenguaje' to 'Idioma'.

* Fix some erros in the installation screens.

* Fix some erros in the installation screens II.

* Chamge mod

* Translate. Round 1.

* Translate ES. Stream: Actividades to Historia.

* Translate ES. Lead: Potencial to Posible Cliente.

* Translate ES. Log: Registros.

* Translate ES. Round 2.

* Translate ES. Round 3.

* Translate ES. Round 4.

* Translate ES. New translations.

* Translate ES. Fix bugs.

* Translate ES. Activity: Actividades to Actividades planeadas.

* Translate ES. Hostory: Historia to Historial de aactividades.

* Translate ES. Improvements.

* Fix.

* Traanslate ES.

* Traanslate ES.

* Traanslate ES.

* Fix permissions.
2017-05-07 08:13:47 +03:00
yuri
d3cb4607ea orm functions 2017-05-05 15:57:11 +03:00
yuri
8ae0696f07 formula: minute, hour 2017-05-05 13:45:37 +03:00
yuri
299c3c13fc orm: functionList 2017-05-05 13:33:33 +03:00
yuri
a79a8bcef6 orm dayofweek 2017-05-05 12:50:05 +03:00
yuri
2a0dd46b5e formula: date functions 2017-05-05 12:44:27 +03:00
yuri
6c287bc2e2 orm: DATE functions 2017-05-05 11:53:08 +03:00
yuri
7add59ffcb formula: list 2017-05-05 11:03:21 +03:00
yuri
9747c43988 duplicate link new tab 2017-05-04 15:40:39 +03:00
yuri
8b9a0f1437 excel export changes 2017-05-04 14:43:12 +03:00
yuri
1315ca007c fix 2017-05-03 16:37:59 +03:00
yuri
e753d767fc no-currency support 2017-05-03 16:34:25 +03:00
yuri
db893859c2 excel export changes 2017-05-03 16:25:02 +03:00
yuri
16af3d2ad8 currency format 2017-05-03 16:24:47 +03:00
yuri
f334080fe6 metadata currency symbols 2017-05-03 14:20:07 +03:00
yuri
b2acfbed98 email keep attachments on select template option 2017-05-03 12:00:21 +03:00
yuri
2aca6ca28a email keep attachments on select template option 2017-05-03 11:59:12 +03:00
yuri
c96040edec export fixes 2017-05-02 15:30:48 +03:00
yuri
e0ebb44e64 export xslx 2017-05-02 15:14:18 +03:00
yuri
ee1af88f8d fix select manager order file field 2017-05-02 11:27:46 +03:00
yuri
1659e583ea fix select manager order file field 2017-05-02 11:27:27 +03:00
yuri
6f0575d798 Merge branch 'hotfix/4.6.1' of ssh://172.20.0.1/var/git/espo/backend 2017-04-28 17:05:53 +03:00
Taras Machyshyn
ba05c45f47 EntityManager: fixed translated options 2017-04-28 17:02:16 +03:00
yuri
353caf29b3 fix export 2017-04-28 16:40:55 +03:00
yuri
7600d473bb export change 2017-04-28 16:39:26 +03:00
yuri
c9ff48245e export changes 2017-04-28 16:16:44 +03:00
yuri
4ed4a94209 export changes 2017-04-28 15:50:18 +03:00
yuri
c88e01fe75 export populate with list layout 2017-04-28 15:10:40 +03:00
yuri
9e46edce79 fix export 2017-04-28 14:46:39 +03:00
yuri
bae5c67839 exportCollection 2017-04-28 14:44:25 +03:00
yuri
635d5d9c6c tr_TR lang 2017-04-28 13:45:18 +03:00
yuri
65be8c385f formula array includes 2017-04-28 13:36:11 +03:00
yuri
c3d44e2472 orm not subquery prevent too deep 2017-04-28 12:42:08 +03:00
yuri
4a781ed5f4 Merge branch 'hotfix/4.6.1' 2017-04-28 12:34:13 +03:00
yuri
d9995512a1 fix required sign 2017-04-28 12:34:04 +03:00
Taras Machyshyn
6ed2466785 Fixed nginx routing rules 2017-04-28 12:15:01 +03:00
Anthony Andriano
724c74b488 en_US Updates (#525)
* fixed a typo

* updated a few en_US labels to be more natural english
2017-04-28 11:31:19 +03:00
Anthony Andriano
67887c5ba4 update diff to use util instead of sys (#524) 2017-04-28 11:31:05 +03:00
yuri
ea2a0068ed Merge branch 'master' of github.com:espocrm/espocrm 2017-04-28 11:25:25 +03:00
Anthony Andriano
0bfdea5976 en_US Updates (#525)
* fixed a typo

* updated a few en_US labels to be more natural english
2017-04-27 20:04:13 +03:00
Anthony Andriano
305ebc293f update diff to use util instead of sys (#524) 2017-04-27 19:19:58 +03:00
yuri
f17f74672e fix orm 2017-04-27 15:57:39 +03:00
yuri
fcaf184b13 dynamic logic and forumla add field focus 2017-04-27 15:41:51 +03:00
yuri
efe653f1f6 dynamic logic and forumla add field focus 2017-04-27 15:41:24 +03:00
yuri
c1022bf8f3 Merge branch 'hotfix/4.6.1' 2017-04-27 15:01:01 +03:00
yuri
0f4c3988f8 fix class name 2017-04-27 15:00:45 +03:00
yuri
498b7b5679 Merge branch 'hotfix/4.6.1' 2017-04-27 12:36:14 +03:00
yuri
ca445a3cb7 kb ui acl fix 2017-04-27 12:36:02 +03:00
yuri
8a293749e0 fix ORM not 2017-04-27 12:14:22 +03:00
yuri
5c034279e8 Merge branch 'hotfix/4.6.1' 2017-04-27 11:33:31 +03:00
yuri
9460943ad2 escaping fix 2017-04-27 11:33:22 +03:00
yuri
5eaeabe231 ORM: Subqueries IN and NOT 2017-04-26 16:32:24 +03:00
yuri
2202b00600 inlineEditDisabled in clientDefs 2017-04-26 11:35:07 +03:00
Sebastien DOIDO
68ba5a5c4a Improve french translation: Campaign.json + MassEmail.json (#517) 2017-04-24 17:39:35 +03:00
Sebastien DOIDO
e80b490152 Improve french translation: Campaign.json + MassEmail.json (#517) 2017-04-24 16:03:29 +03:00
yuri
380937b644 enum field filters 2017-04-24 15:44:43 +03:00
yuri
ac72305101 Merge branch 'hotfix/4.6.1' 2017-04-24 14:59:27 +03:00
yuri
a41779b0bb fr_FR fixes 2017-04-24 14:58:41 +03:00
yuri
86cdbf24b7 Merge branch 'hotfix/4.6.1' 2017-04-24 12:44:41 +03:00
yuri
5d7bc98088 user detail view: created at field 2017-04-24 12:43:39 +03:00
yuri
1359e8eb2f Merge branch 'hotfix/4.6.1' 2017-04-24 12:32:15 +03:00
yuri
f3a7003444 fr_FR lang 2017-04-24 12:32:05 +03:00
yuri
624f76b1a0 clear local cache improvements 2017-04-24 12:24:30 +03:00
yuri
484ffa071c label fix 2017-04-24 11:40:13 +03:00
Sebastien DOIDO
850fb3cb15 Fix typo to have correct translation (#516) 2017-04-24 11:32:04 +03:00
Sebastien DOIDO
40075ed2e3 Fix typo to have correct translation (#516) 2017-04-24 11:31:26 +03:00
719 changed files with 18936 additions and 2077 deletions

View File

@@ -63,7 +63,7 @@ class Admin extends \Espo\Core\Controllers\Base
{
$scheduledJob = $this->getContainer()->get('scheduledJob');
return $scheduledJob->getAllNamesOnly();
return $scheduledJob->getAvailableList();
}
public function postActionUploadUpgradePackage($params, $data)

View File

@@ -25,7 +25,7 @@
*
* 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\Controllers;
@@ -34,17 +34,19 @@ use \Espo\Core\Exceptions\Error;
class EmailTemplate extends \Espo\Core\Controllers\Record
{
public function actionParse($params, $data, $request)
{
{
$id = $request->get('id');
$emailAddress = $request->get('emailAddress');
if (empty($id)) {
throw new Error();
}
return $this->getRecordService()->parse($id, array(
'emailAddress' => $request->get('emailAddress'),
'parentType' => $request->get('parentType'),
'parentId' => $request->get('parentId'),
'relatedType' => $request->get('relatedType'),
'relatedId' => $request->get('relatedId')
), true);
}

View File

@@ -0,0 +1,78 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2017 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\Controllers;
use Espo\Core\Utils as Utils;
use \Espo\Core\Exceptions\NotFound;
use \Espo\Core\Exceptions\Error;
use \Espo\Core\Exceptions\Forbidden;
use \Espo\Core\Exceptions\BadRequest;
class LabelManager extends \Espo\Core\Controllers\Base
{
protected function checkControllerAccess()
{
if (!$this->getUser()->isAdmin()) {
throw new Forbidden();
}
}
public function postActionGetScopeList($params)
{
$labelManager = $this->getContainer()->get('injectableFactory')->createByClassName('\\Espo\\Core\\Utils\\LabelManager');
return $labelManager->getScopeList();
}
public function postActionGetScopeData($params, $data, $request)
{
if (empty($data['scope']) || empty($data['language'])) {
throw new BadRequest();
}
$labelManager = $this->getContainer()->get('injectableFactory')->createByClassName('\\Espo\\Core\\Utils\\LabelManager');
return $labelManager->getScopeData($data['language'], $data['scope']);
}
public function postActionSaveLabels($params, $data)
{
if (empty($data['scope']) || empty($data['language']) || !isset($data['labels'])) {
throw new BadRequest();
}
$labels = get_object_vars($data['labels']);
$labelManager = $this->getContainer()->get('injectableFactory')->createByClassName('\\Espo\\Core\\Utils\\LabelManager');
$returnData = $labelManager->saveLabels($data['language'], $data['scope'], $labels);
$this->getContainer()->get('dataManager')->clearCache();
return $returnData;
}
}

View File

@@ -76,7 +76,11 @@ class Settings extends \Espo\Core\Controllers\Base
throw new BadRequest();
}
if (isset($data['useCache']) && $data['useCache'] != $this->getConfig()->get('useCache')) {
if (
(isset($data['useCache']) && $data['useCache'] != $this->getConfig()->get('useCache'))
||
(isset($data['aclStrictMode']) && $data['aclStrictMode'] !== $this->getConfig()->get('aclStrictMode'))
) {
$this->getContainer()->get('dataManager')->clearCache();
}

View File

@@ -36,7 +36,7 @@ use \Espo\Entities\User;
use \Espo\Core\Utils\Config;
use \Espo\Core\Utils\Metadata;
use \Espo\Core\Utils\FieldManager;
use \Espo\Core\Utils\FieldManagerUtil;
use \Espo\Core\Utils\File\Manager as FileManager;
class Table
@@ -77,7 +77,9 @@ class Table
protected $forbiddenFieldsCache = array();
public function __construct(User $user, Config $config = null, FileManager $fileManager = null, Metadata $metadata = null, FieldManager $fieldManager = null)
protected $isStrictMode = false;
public function __construct(User $user, Config $config = null, FileManager $fileManager = null, Metadata $metadata = null, FieldManagerUtil $fieldManager = null)
{
$this->data = (object) [
'table' => (object) [],
@@ -85,6 +87,8 @@ class Table
'fieldTableQuickAccess' => (object) [],
];
$this->isStrictMode = $config->get('aclStrictMode', false);
$this->user = $user;
$this->metadata = $metadata;
@@ -135,11 +139,6 @@ class Table
return $this->fieldManager;
}
protected function getConfig()
{
return $this->config;
}
public function getMap()
{
return $this->data;
@@ -454,7 +453,11 @@ class Table
$aclType = $this->defaultAclType;
}
if (!empty($aclType)) {
$defaultValue = $this->metadata->get('app.'.$this->type.'.scopeLevelTypesDefaults.' . $aclType, $this->metadata->get('app.'.$this->type.'.scopeLevelTypesDefaults.record'));
$paramDefaultsName = 'scopeLevelTypesDefaults';
if ($this->isStrictMode) {
$paramDefaultsName = 'scopeLevelTypesStrictDefaults';
}
$defaultValue = $this->metadata->get(['app', $this->type, $paramDefaultsName, $aclType], $this->metadata->get(['app', $this->type, $paramDefaultsName, 'record']));
if (is_array($defaultValue)) {
$defaultValue = (object) $defaultValue;
}

View File

@@ -107,7 +107,7 @@ class AclManager
$config = $this->getContainer()->get('config');
$fileManager = $this->getContainer()->get('fileManager');
$metadata = $this->getContainer()->get('metadata');
$fieldManager = $this->getContainer()->get('fieldManager');
$fieldManager = $this->getContainer()->get('fieldManagerUtil');
$this->tableHashMap[$key] = new $this->tableClassName($user, $config, $fileManager, $metadata, $fieldManager);
}

View File

@@ -37,7 +37,7 @@ use \Espo\Entities\Portal;
use \Espo\Core\Utils\Config;
use \Espo\Core\Utils\Metadata;
use \Espo\Core\Utils\FieldManager;
use \Espo\Core\Utils\FieldManagerUtil;
use \Espo\Core\Utils\File\Manager as FileManager;
class Table extends \Espo\Core\Acl\Table
@@ -52,7 +52,7 @@ class Table extends \Espo\Core\Acl\Table
protected $valuePermissionList = [];
public function __construct(User $user, Portal $portal, Config $config = null, FileManager $fileManager = null, Metadata $metadata = null, FieldManager $fieldManager = null)
public function __construct(User $user, Portal $portal, Config $config = null, FileManager $fileManager = null, Metadata $metadata = null, FieldManagerUtil $fieldManager = null)
{
if (empty($portal)) {
throw new Error("No portal was passed to AclPortal\\Table constructor.");

View File

@@ -93,31 +93,6 @@ class Container
return $className;
}
protected function loadLog()
{
$config = $this->get('config');
$path = $config->get('logger.path', 'data/logs/espo.log');
$rotation = $config->get('logger.rotation', true);
$log = new \Espo\Core\Utils\Log('Espo');
$levelCode = $log->getLevelCode($config->get('logger.level', 'WARNING'));
if ($rotation) {
$maxFileNumber = $config->get('logger.maxFileNumber', 30);
$handler = new \Espo\Core\Utils\Log\Monolog\Handler\RotatingFileHandler($path, $maxFileNumber, $levelCode);
} else {
$handler = new \Espo\Core\Utils\Log\Monolog\Handler\StreamHandler($path, $levelCode);
}
$log->pushHandler($handler);
$errorHandler = new \Monolog\ErrorHandler($log);
$errorHandler->registerExceptionHandler(null, false);
$errorHandler->registerErrorHandler(array(), false);
return $log;
}
protected function loadContainer()
{
return $this;
@@ -335,6 +310,13 @@ class Container
);
}
protected function loadFieldManagerUtil()
{
return new \Espo\Core\Utils\FieldManagerUtil(
$this->get('metadata')
);
}
protected function loadThemeManager()
{
return new \Espo\Core\Utils\ThemeManager(

View File

@@ -60,9 +60,9 @@ class ControllerManager
public function process($controllerName, $actionName, $params, $data, $request)
{
$customeClassName = '\\Espo\\Custom\\Controllers\\' . Util::normilizeClassName($controllerName);
if (class_exists($customeClassName)) {
$controllerClassName = $customeClassName;
$customClassName = '\\Espo\\Custom\\Controllers\\' . Util::normilizeClassName($controllerName);
if (class_exists($customClassName)) {
$controllerClassName = $customClassName;
} else {
$moduleName = $this->metadata->getScopeModuleName($controllerName);
if ($moduleName) {

View File

@@ -258,6 +258,14 @@ class Record extends Base
$params['attributeList'] = $data['attributeList'];
}
if (isset($data['fieldList'])) {
$params['fieldList'] = $data['fieldList'];
}
if (isset($data['format'])) {
$params['format'] = $data['format'];
}
return array(
'id' => $this->getRecordService()->export($params)
);

View File

@@ -283,24 +283,20 @@ class CronManager
}
try {
$previousDate = $cronExpression->getPreviousRunDate()->format('Y-m-d H:i:s');
$nextDate = $cronExpression->getNextRunDate()->format('Y-m-d H:i:s');
} catch (\Exception $e) {
$GLOBALS['log']->error('CronManager (ScheduledJob ['.$scheduledJob['id'].']): Unsupported CRON expression ['.$scheduling.']');
continue;
}
if ($cronExpression->isDue()) {
$previousDate = date('Y-m-d H:i:s');
}
$existingJob = $this->getCronJob()->getJobByScheduledJob($scheduledJob['id'], $previousDate);
$existingJob = $this->getCronJob()->getJobByScheduledJob($scheduledJob['id'], $nextDate);
if ($existingJob) continue;
$className = $this->getScheduledJobUtil()->get($scheduledJob['job']);
if ($className) {
if (method_exists($className, 'prepare')) {
$implementation = new $className($this->container);
$implementation->prepare($scheduledJob, $previousDate);
$implementation->prepare($scheduledJob, $nextDate);
continue;
}
}
@@ -314,7 +310,7 @@ class CronManager
'name' => $scheduledJob['name'],
'status' => self::PENDING,
'scheduledJobId' => $scheduledJob['id'],
'executeTime' => $previousDate,
'executeTime' => $nextDate,
'method' => $scheduledJob['job']
));
$this->getEntityManager()->saveEntity($jobEntity);

View File

@@ -59,6 +59,8 @@ class DataManager
$result &= $this->rebuildDatabase($entityList);
$this->rebuildScheduledJobs();
return $result;
}
@@ -121,6 +123,40 @@ class DataManager
return empty($ormData) ? false : true;
}
public function rebuildScheduledJobs()
{
$metadata = $this->getContainer()->get('metadata');
$entityManager = $this->getContainer()->get('entityManager');
$jobs = $metadata->get(['entityDefs', 'ScheduledJob', 'jobs'], array());
foreach ($jobs as $jobName => $defs) {
if ($jobName && !empty($defs['isSystem']) && !empty($defs['scheduling'])) {
if (!$entityManager->getRepository('ScheduledJob')->where(array(
'job' => $jobName,
'status' => 'Active',
'scheduling' => $defs['scheduling']
))->findOne()) {
$job = $entityManager->getRepository('ScheduledJob')->where(array(
'job' => $jobName
))->findOne();
if ($job) {
$entityManager->removeEntity($job);
}
$job = $entityManager->getEntity('ScheduledJob');
$job->set(array(
'job' => $jobName,
'status' => 'Active',
'scheduling' => $defs['scheduling'],
'isInternal' => true,
'name' => $jobName
));
$entityManager->saveEntity($job);
}
}
}
}
/**
* Update cache timestamp
*

View File

@@ -0,0 +1,65 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2017 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\Export;
use \Espo\Core\Exceptions\Error;
class Csv extends \Espo\Core\Injectable
{
protected $dependencyList = [
'config',
'preferences'
];
public function process($entityType, $params, $dataList)
{
if (!is_array($params['attributeList'])) {
throw new Error();
}
$attributeList = $params['attributeList'];
$delimiter = $this->getInjection('preferences')->get('exportDelimiter');
if (empty($delimiter)) {
$delimiter = $this->getInjection('config')->get('exportDelimiter', ';');
}
$fp = fopen('php://temp', 'w');
fputcsv($fp, $attributeList, $delimiter);
foreach ($dataList as $row) {
fputcsv($fp, $row, $delimiter);
}
rewind($fp);
$csv = stream_get_contents($fp);
fclose($fp);
return $csv;
}
}

View File

@@ -0,0 +1,459 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2017 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\Export;
use \Espo\ORM\Entity;
use \Espo\Core\Exceptions\Error;
class Xlsx extends \Espo\Core\Injectable
{
protected $dependencyList = [
'language',
'metadata',
'config',
'dateTime',
'entityManager',
'fileStorageManager',
'fileManager'
];
protected function getConfig()
{
return $this->getInjection('config');
}
protected function getMetadata()
{
return $this->getInjection('metadata');
}
protected function getEntityManager()
{
return $this->getInjection('entityManager');
}
public function loadAdditionalFields(Entity $entity, $fieldList)
{
foreach ($entity->getRelationList() as $link) {
if ($entity->getRelationType($link) === 'belongsToParent') {
if (in_array($link, $fieldList)) {
$parent = $entity->get($link);
if ($parent instanceof Entity) {
$entity->set($link . 'Name', $parent->get('name'));
}
}
} else if ($entity->getRelationType($link) === 'belongsTo' && $entity->getRelationParam($link, 'noJoin') && $entity->hasField($link . 'Name')) {
if (in_array($link, $fieldList)) {
$related = $entity->get($link);
if ($related instanceof Entity) {
$entity->set($link . 'Name', $related->get('name'));
}
}
}
}
}
public function addAdditionalAttributes($entityType, &$attributeList, $fieldList)
{
$linkList = [];
if (!in_array('id', $attributeList)) {
$attributeList[] = 'id';
}
$linkDefs = $this->getMetadata()->get(['entityDefs', $entityType, 'links']);
if (is_array($linkDefs)) {
foreach ($linkDefs as $link => $defs) {
if (empty($defs['type'])) continue;
if ($defs['type'] === 'belongsToParent') {
$linkList[] = $link;
} else if ($defs['type'] === 'belongsTo' && !empty($defs['noJoin'])) {
if ($this->getMetadata()->get(['entityDefs', $entityType, 'fields', $link])) {
$linkList[] = $link;
}
}
}
}
foreach ($linkList as $item) {
if (in_array($item, $fieldList) && !in_array($item . 'Name', $attributeList)) {
$attributeList[] = $item . 'Name';
}
}
foreach ($fieldList as $field) {
$type = $this->getMetadata()->get(['entityDefs', $entityType, 'fields', $field, 'type']);
if ($type === 'currencyConverted') {
if (!in_array($field, $attributeList)) {
$attributeList[] = $field;
}
}
}
}
public function process($entityType, $params, $dataList)
{
if (!is_array($params['fieldList'])) {
throw new Error();
}
$phpExcel = new \PHPExcel();
$sheet = $phpExcel->setActiveSheetIndex(0);
if (isset($params['exportName'])) {
$exportName = $params['exportName'];
} else {
$exportName = $this->getInjection('language')->translate($entityType, 'scopeNamesPlural');
}
$sheetName = substr($exportName, 0, 30);
$badCharList = ['*', ':', '/', '\\', '?', '[', ']'];
foreach ($badCharList as $badChar) {
$sheetName = str_replace($badCharList, ' ', $sheetName);
}
$sheetName = str_replace('\'', '', $sheetName);
$sheet->setTitle($sheetName);
$fieldList = $params['fieldList'];
$titleStyle = array(
'font' => array(
'bold' => true,
'size' => 12
)
);
$dateStyle = array(
'font' => array(
'size' => 12
)
);
$sheet->setCellValue('A1', $exportName);
$sheet->setCellValue('B1', \PHPExcel_Shared_Date::PHPToExcel(strtotime(date('Y-m-d H:i:s'))));
$sheet->getStyle('A1')->applyFromArray($titleStyle);
$sheet->getStyle('B1')->applyFromArray($dateStyle);
$sheet->getStyle('B1')->getNumberFormat()
->setFormatCode($this->getInjection('dateTime')->getDateTimeFormat());
$azRange = range('A', 'Z');
$azRangeCopied = $azRange;
foreach ($azRangeCopied as $i => $char1) {
foreach ($azRangeCopied as $j => $char2) {
$azRange[] = $char1 . $char2;
if ($i * count($azRangeCopied) + $j === count($fieldList)) {
break 2;
}
}
}
$rowNumber = 3;
$linkColList = [];
$lastIndex = 0;
foreach ($fieldList as $i => $name) {
$col = $azRange[$i];
$defs = $this->getInjection('metadata')->get(['entityDefs', $entityType, 'fields', $name]);
if (!$defs) {
$defs['type'] = 'base';
}
$label = $name;
if (strpos($name, '_') !== false) {
list($linkName, $foreignField) = explode('_', $name);
$foreignScope = $this->getInjection('metadata')->get(['entityDefs', $entityType, 'links', $linkName, 'entity']);
if ($foreignScope) {
$label = $this->getInjection('language')->translate($linkName, 'links', $entityType) . '.' . $this->getInjection('language')->translate($foreignField, 'fields', $foreignScope);
}
} else {
$label = $this->getInjection('language')->translate($name, 'fields', $entityType);
}
$sheet->setCellValue($col . $rowNumber, $label);
$sheet->getColumnDimension($col)->setAutoSize(true);
if (in_array($defs['type'], ['phone', 'email', 'url', 'link', 'linkParent'])) {
$linkColList[] = $col;
} else if ($name == 'name') {
$linkColList[] = $col;
}
$lastIndex = $i;
}
$col = $azRange[$i];
$headerStyle = array(
'font' => array(
'bold' => true,
'size' => 12
)
);
$sheet->getStyle("A$rowNumber:$col$rowNumber")->applyFromArray($headerStyle);
$sheet->setAutoFilter("A$rowNumber:$col$rowNumber");
$typesCache = array();
$rowNumber++;
foreach ($dataList as $row) {
$i = 0;
foreach ($fieldList as $i => $name) {
$col = $azRange[$i];
$defs = $this->getInjection('metadata')->get(['entityDefs', $entityType, 'fields', $name]);
if (!$defs) {
$defs = array();
$defs['type'] = 'base';
}
$type = $defs['type'];
$foreignField = $name;
$linkName = null;
if (strpos($name, '_') !== false) {
list($linkName, $foreignField) = explode('_', $name);
$foreignScope = $this->getInjection('metadata')->get(['entityDefs', $entityType, 'links', $linkName, 'entity']);
if ($foreignScope) {
$type = $this->getInjection('metadata')->get(['entityDefs', $foreignScope, 'fields', $foreignField, 'type'], $type);
}
}
$typesCache[$name] = $type;
$link = null;
if ($type == 'link') {
if (array_key_exists($name.'Name', $row)) {
$sheet->setCellValue("$col$rowNumber", $row[$name.'Name']);
}
} else if ($type == 'linkParent') {
if (array_key_exists($name.'Name', $row)) {
$sheet->setCellValue("$col$rowNumber", $row[$name.'Name']);
}
} else if ($type == 'int') {
$sheet->setCellValue("$col$rowNumber", $row[$name] ?: 0);
} else if ($type == 'currency') {
if (array_key_exists($name.'Currency', $row) && array_key_exists($name, $row)) {
$sheet->setCellValue("$col$rowNumber", $row[$name] ? $row[$name] : '');
$currency = $row[$name . 'Currency'];
$currencySymbol = $this->getMetadata()->get(['app', 'currency', 'symbolMap', $currency], '');
$sheet->getStyle("$col$rowNumber")
->getNumberFormat()
->setFormatCode('[$'.$currencySymbol.'-409]#,##0.00;-[$'.$currencySymbol.'-409]#,##0.00');
}
} else if ($type == 'currencyConverted') {
if (array_key_exists($name, $row)) {
$currency = $this->getConfig()->get('baseCurrency');
$currencySymbol = $this->getMetadata()->get(['app', 'currency', 'symbolMap', $currency], '');
$sheet->getStyle("$col$rowNumber")
->getNumberFormat()
->setFormatCode('[$'.$currencySymbol.'-409]#,##0.00;-[$'.$currencySymbol.'-409]#,##0.00');
$sheet->setCellValue("$col$rowNumber", $row[$name] ? $row[$name] : '');
}
} else if ($type == 'personName') {
if (!empty($row['name'])) {
$sheet->setCellValue("$col$rowNumber", $row['name']);
} else {
$personName = '';
if (!empty($row['firstName'])) {
$personName .= $row['firstName'];
}
if (!empty($row['lastName'])) {
if (!empty($row['firstName'])) {
$personName .= ' ';
}
$personName .= $row['lastName'];
}
$sheet->setCellValue("$col$rowNumber", $personName);
}
} else if ($type == 'date') {
if (isset($row[$name])) {
$sheet->setCellValue("$col$rowNumber", \PHPExcel_Shared_Date::PHPToExcel(strtotime($row[$name])));
}
} else if ($type == 'datetime' || $type == 'datetimeOptional') {
if (isset($row[$name])) {
$sheet->setCellValue("$col$rowNumber", \PHPExcel_Shared_Date::PHPToExcel(strtotime($row[$name])));
}
} else if ($type == 'image') {
if (isset($row[$name . 'Id']) && $row[$name . 'Id']) {
$attachment = $this->getEntityManager()->getEntity('Attachment', $row[$name . 'Id']);
if ($attachment) {
$objDrawing = new \PHPExcel_Worksheet_Drawing();
$filePath = $this->getInjection('fileStorageManager')->getLocalFilePath($attachment);
if ($filePath && file_exists($filePath)) {
$objDrawing->setPath($filePath);
$objDrawing->setHeight(100);
$objDrawing->setCoordinates("$col$rowNumber");
$objDrawing->setWorksheet($sheet);
$sheet->getRowDimension($rowNumber)->setRowHeight(100);
}
}
}
} else if ($type == 'file') {
if (array_key_exists($name.'Name', $row)) {
$sheet->setCellValue("$col$rowNumber", $row[$name.'Name']);
}
} else if ($type == 'enum') {
if (array_key_exists($name, $row)) {
if ($linkName) {
$value = $this->getInjection('language')->translateOption($row[$name], $foreignField, $foreignScope);
} else {
$value = $this->getInjection('language')->translateOption($row[$name], $name, $entityType);
}
$sheet->setCellValue("$col$rowNumber", $value);
}
} else {
if (array_key_exists($name, $row)) {
$sheet->setCellValue("$col$rowNumber", $row[$name]);
}
}
$link = false;
if ($name == 'name') {
if (array_key_exists('id', $row)) {
$link = $this->getConfig()->getSiteUrl() . "/#".$entityType . "/view/" . $row['id'];
}
} else if ($type == 'url') {
if (array_key_exists($name, $row) && filter_var($row[$name], FILTER_VALIDATE_URL)) {
$link = $row[$name];
}
} else if ($type == 'link') {
if (array_key_exists($name.'Id', $row)) {
$foreignEntity = $this->getMetadata()->get(['entityDefs', $entityType, 'links', $name, 'entity']);
if ($foreignEntity) {
$link = $this->getConfig()->getSiteUrl() . "/#" . $foreignEntity. "/view/". $row[$name.'Id'];
}
}
} else if ($type == 'file') {
if (array_key_exists($name.'Id', $row)) {
$link = $this->getConfig()->getSiteUrl() . "/?entryPoint=download&id=" . $row[$name.'Id'];
}
} else if ($type == 'linkParent') {
if (array_key_exists($name.'Id', $row) && array_key_exists($name.'Type', $row)) {
$link = $this->getConfig()->getSiteUrl() . "/#".$row[$name.'Type']."/view/". $row[$name.'Id'];
}
} else if ($type == 'phone') {
if (array_key_exists($name, $row)) {
$link = "tel:".$row[$name];
}
} else if ($type == 'email' && array_key_exists($name, $row)) {
if (array_key_exists($name, $row)) {
$link = "mailto:".$row[$name];
}
}
if ($link) {
$sheet->getCell("$col$rowNumber")->getHyperlink()->setUrl($link);
$sheet->getCell("$col$rowNumber")->getHyperlink()->setTooltip($link);
}
}
$rowNumber++;
}
$startingRowNumber = 4;
foreach ($fieldList as $i => $name) {
$col = $azRange[$i];
$type = $typesCache[$name];
if ($col == 'A') {
$sheet->getStyle("A2:A$rowNumber")
->getNumberFormat()
->setFormatCode(\PHPExcel_Style_NumberFormat::FORMAT_TEXT);
} else {
switch($type) {
case 'currency':
case 'currencyConverted': {
} break;
case 'int': {
$sheet->getStyle($col.$startingRowNumber.':'.$col.$rowNumber)
->getNumberFormat()
->setFormatCode('0');
} break;
case 'date': {
$sheet->getStyle($col.$startingRowNumber.':'.$col.$rowNumber)
->getNumberFormat()
->setFormatCode($this->getInjection('dateTime')->getDateFormat());
} break;
case 'datetime': {
$sheet->getStyle($col.$startingRowNumber.':'.$col.$rowNumber)
->getNumberFormat()
->setFormatCode($this->getInjection('dateTime')->getDateTimeFormat());
} break;
case 'datetimeOptional': {
$sheet->getStyle($col.$startingRowNumber.':'.$col.$rowNumber)
->getNumberFormat()
->setFormatCode($this->getInjection('dateTime')->getDateTimeFormat());
} break;
default: {
$sheet->getStyle($col.$startingRowNumber.':'.$col.$rowNumber)
->getNumberFormat()
->setFormatCode('@');
} break;
}
}
}
$linkStyle = [
'font' => [
'color' => ['rgb' => '345b7c'],
'underline' => 'single'
]
];
foreach ($linkColList as $linkColumn) {
$sheet->getStyle($linkColumn.$startingRowNumber.':'.$linkColumn.$rowNumber)->applyFromArray($linkStyle);
}
$objWriter = \PHPExcel_IOFactory::createWriter($phpExcel, 'Excel2007');
if (!$this->getInjection('fileManager')->isDir('data/cache/')) {
$this->getInjection('fileManager')->mkdir('data/cache/');
}
$tempFileName = 'data/cache/' . 'export_' . substr(md5(rand()), 0, 7);
$objWriter->save($tempFileName);
$fp = fopen($tempFileName, 'r');
$xlsx = stream_get_contents($fp);
$this->getInjection('fileManager')->unlink($tempFileName);
return $xlsx;
}
}

View File

@@ -42,6 +42,11 @@ class ExtensionManager extends Upgrades\Base
'after' => 'AfterInstall',
'beforeUninstall' => 'BeforeUninstall',
'afterUninstall' => 'AfterUninstall',
),
'customDirNames' => array(
'before' => 'beforeInstallFiles',
'after' => 'afterInstallFiles',
)
);
}

View File

@@ -0,0 +1,53 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2017 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\Formula\Functions\ArrayGroup;
use \Espo\Core\Exceptions\Error;
class IncludesType extends \Espo\Core\Formula\Functions\Base
{
public function process(\StdClass $item)
{
if (!property_exists($item, 'value') || !is_array($item->value)) {
throw new Error('Value for \'Array\\Includses\' item is not array.');
}
if (count($item->value) < 2) {
throw new Error('Bad arguments passed to \'Array\\Includses\'.');
}
$list = $this->evaluate($item->value[0]);
$needle = $this->evaluate($item->value[1]);
if (!is_array($list)) {
return false;
}
return in_array($needle, $list);
}
}

View File

@@ -0,0 +1,67 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2017 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\Formula\Functions\DateTimeGroup;
use \Espo\Core\Exceptions\Error;
class DateType extends \Espo\Core\Formula\Functions\Base
{
protected function init()
{
$this->addDependency('dateTime');
}
public function process(\StdClass $item)
{
if (!property_exists($item, 'value')) {
return true;
}
if (!is_array($item->value)) {
throw new Error();
}
if (count($item->value) < 1) {
throw new Error();
}
$value = $this->evaluate($item->value[0]);
if (empty($value)) return 0;
if (strlen($value) > 11) {
$resultString = $this->getInjection('dateTime')->convertSystemDateTime($value, null, 'D');
} else {
$resultString = $this->getInjection('dateTime')->convertSystemDate($value, 'D');
}
return intval($resultString);
}
}

View File

@@ -0,0 +1,69 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2017 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\Formula\Functions\DateTimeGroup;
use \Espo\Core\Exceptions\Error;
class DayOfWeekType extends \Espo\Core\Formula\Functions\Base
{
protected function init()
{
$this->addDependency('dateTime');
}
public function process(\StdClass $item)
{
if (!property_exists($item, 'value')) {
return true;
}
if (!is_array($item->value)) {
throw new Error();
}
if (count($item->value) < 1) {
throw new Error();
}
$value = $this->evaluate($item->value[0]);
if (empty($value)) return -1;
if (strlen($value) > 11) {
$resultString = $this->getInjection('dateTime')->convertSystemDateTime($value, null, 'e');
} else {
$resultString = $this->getInjection('dateTime')->convertSystemDate($value, 'e');
}
$result = intval($resultString);
return $result;
}
}

View File

@@ -0,0 +1,67 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2017 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\Formula\Functions\DateTimeGroup;
use \Espo\Core\Exceptions\Error;
class HourType extends \Espo\Core\Formula\Functions\Base
{
protected function init()
{
$this->addDependency('dateTime');
}
public function process(\StdClass $item)
{
if (!property_exists($item, 'value')) {
return true;
}
if (!is_array($item->value)) {
throw new Error();
}
if (count($item->value) < 1) {
throw new Error();
}
$value = $this->evaluate($item->value[0]);
if (empty($value)) return -1;
if (strlen($value) > 11) {
$resultString = $this->getInjection('dateTime')->convertSystemDateTime($value, null, 'H');
} else {
return 0;
}
return intval($resultString);
}
}

View File

@@ -0,0 +1,67 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2017 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\Formula\Functions\DateTimeGroup;
use \Espo\Core\Exceptions\Error;
class MinuteType extends \Espo\Core\Formula\Functions\Base
{
protected function init()
{
$this->addDependency('dateTime');
}
public function process(\StdClass $item)
{
if (!property_exists($item, 'value')) {
return true;
}
if (!is_array($item->value)) {
throw new Error();
}
if (count($item->value) < 1) {
throw new Error();
}
$value = $this->evaluate($item->value[0]);
if (empty($value)) return -1;
if (strlen($value) > 11) {
$resultString = $this->getInjection('dateTime')->convertSystemDateTime($value, null, 'm');
} else {
return 0;
}
return intval($resultString);
}
}

View File

@@ -0,0 +1,67 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2017 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\Formula\Functions\DateTimeGroup;
use \Espo\Core\Exceptions\Error;
class MonthType extends \Espo\Core\Formula\Functions\Base
{
protected function init()
{
$this->addDependency('dateTime');
}
public function process(\StdClass $item)
{
if (!property_exists($item, 'value')) {
return true;
}
if (!is_array($item->value)) {
throw new Error();
}
if (count($item->value) < 1) {
throw new Error();
}
$value = $this->evaluate($item->value[0]);
if (empty($value)) return 0;
if (strlen($value) > 11) {
$resultString = $this->getInjection('dateTime')->convertSystemDateTime($value, null, 'M');
} else {
$resultString = $this->getInjection('dateTime')->convertSystemDate($value, 'M');
}
return intval($resultString);
}
}

View File

@@ -0,0 +1,67 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2017 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\Formula\Functions\DateTimeGroup;
use \Espo\Core\Exceptions\Error;
class YearType extends \Espo\Core\Formula\Functions\Base
{
protected function init()
{
$this->addDependency('dateTime');
}
public function process(\StdClass $item)
{
if (!property_exists($item, 'value')) {
return true;
}
if (!is_array($item->value)) {
throw new Error();
}
if (count($item->value) < 1) {
throw new Error();
}
$value = $this->evaluate($item->value[0]);
if (empty($value)) return 0;
if (strlen($value) > 11) {
$resultString = $this->getInjection('dateTime')->convertSystemDateTime($value, null, 'YYYY');
} else {
$resultString = $this->getInjection('dateTime')->convertSystemDate($value, 'YYYY');
}
return intval($resultString);
}
}

View File

@@ -0,0 +1,54 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2017 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\Formula\Functions;
use \Espo\ORM\Entity;
use \Espo\Core\Exceptions\Error;
class ListType extends Base
{
public function process(\StdClass $item)
{
if (!property_exists($item, 'value')) {
throw new Error();
}
if (!is_array($item->value)) {
throw new Error();
}
$result = [];
foreach ($item->value as $item) {
$result[] = $this->evaluate($item);
}
return $result;
}
}

View File

@@ -219,6 +219,11 @@ class Parser
$firstOperator = null;
$minIndex = null;
if ($expression === '') return (object) [
'type' => 'value',
'value' => null
];
foreach ($this->priorityList as $operationList) {
foreach ($operationList as $operator) {
$index = strpos($expression, $operator, 1);
@@ -235,6 +240,18 @@ class Parser
!empty($this->operatorMap[$possibleRightOperator])
) continue;
$possibleLeftOperator = null;
if (strlen($operator) === 1) {
if ($index > 0) {
$possibleLeftOperator = trim($expression[$index - 1] . $operator);
}
}
if (
$possibleLeftOperator &&
$possibleLeftOperator != $operator &&
!empty($this->operatorMap[$possibleLeftOperator])
) continue;
$firstPart = substr($expression, 0, $index);
$secondPart = substr($expression, $index + strlen($operator));

View File

@@ -117,6 +117,9 @@ class Htmlizer
if ($item instanceof \StdClass) {
$v = get_object_vars($v);
}
if (!is_array($v)) {
$v = [];
}
foreach ($v as $k => $w) {
$v[$k] = $this->format($v[$k]);
}

View File

@@ -49,6 +49,17 @@ abstract class Injectable implements \Espo\Core\Interfaces\Injectable
{
}
public function __call($methodName, $args)
{
if (strpos($methodName, 'get') === 0) {
$injectionName = lcfirst(substr($methodName, 3));
if (in_array($injectionName, $this->dependencyList)) {
return $this->getInjection($injectionName);
}
}
throw new \BadMethodCallException('Method ' . $methodName . ' does not exist');
}
protected function getInjection($name)
{
return $this->injections[$name];

View File

@@ -0,0 +1,59 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2017 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 Log extends Base
{
public function load()
{
$config = $this->getContainer()->get('config');
$path = $config->get('logger.path', 'data/logs/espo.log');
$rotation = $config->get('logger.rotation', true);
$log = new \Espo\Core\Utils\Log('Espo');
$levelCode = $log->getLevelCode($config->get('logger.level', 'WARNING'));
if ($rotation) {
$maxFileNumber = $config->get('logger.maxFileNumber', 30);
$handler = new \Espo\Core\Utils\Log\Monolog\Handler\RotatingFileHandler($path, $maxFileNumber, $levelCode);
} else {
$handler = new \Espo\Core\Utils\Log\Monolog\Handler\StreamHandler($path, $levelCode);
}
$log->pushHandler($handler);
$errorHandler = new \Monolog\ErrorHandler($log);
$errorHandler->registerExceptionHandler(null, false);
$errorHandler->registerErrorHandler(array(), false);
return $log;
}
}

View File

@@ -314,11 +314,10 @@ class Importer
$email->set('parentId', $contact->get('accountId'));
return true;
}
} else {
$email->set('parentType', 'Contact');
$email->set('parentId', $contact->id);
return true;
}
$email->set('parentType', 'Contact');
$email->set('parentId', $contact->id);
return true;
} else {
$account = $this->getEntityManager()->getRepository('Account')->where(array(
'emailAddress' => $emailAddress

View File

@@ -47,6 +47,8 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
private $restoreData = null;
protected $hooksDisabled = false;
protected function addDependency($name)
{
$this->dependencies[] = $name;
@@ -172,7 +174,9 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
protected function beforeRemove(Entity $entity, array $options = array())
{
parent::beforeRemove($entity, $options);
$this->getEntityManager()->getHookManager()->process($this->entityType, 'beforeRemove', $entity, $options);
if (!$this->hooksDisabled) {
$this->getEntityManager()->getHookManager()->process($this->entityType, 'beforeRemove', $entity, $options);
}
$nowString = date('Y-m-d H:i:s', time());
if ($entity->hasAttribute('modifiedAt')) {
@@ -186,25 +190,25 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
protected function afterRemove(Entity $entity, array $options = array())
{
parent::afterRemove($entity, $options);
$this->getEntityManager()->getHookManager()->process($this->entityType, 'afterRemove', $entity, $options);
if (!$this->hooksDisabled) {
$this->getEntityManager()->getHookManager()->process($this->entityType, 'afterRemove', $entity, $options);
}
}
protected function afterMassRelate(Entity $entity, $relationName, array $params = array(), array $options = array())
{
$hookData = array(
'relationName' => $relationName,
'relationParams' => $params
);
$this->getEntityManager()->getHookManager()->process($this->entityType, 'afterMassRelate', $entity, $options, $hookData);
if (!$this->hooksDisabled) {
$hookData = array(
'relationName' => $relationName,
'relationParams' => $params
);
$this->getEntityManager()->getHookManager()->process($this->entityType, 'afterMassRelate', $entity, $options, $hookData);
}
}
public function remove(Entity $entity, array $options = array())
{
$result = parent::remove($entity, $options);
if ($result) {
$this->getEntityManager()->getHookManager()->process($this->entityType, 'afterRemove', $entity, $options);
}
return $result;
}
@@ -214,12 +218,14 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
if ($foreign instanceof Entity) {
$foreignEntity = $foreign;
$hookData = array(
'relationName' => $relationName,
'relationData' => $data,
'foreignEntity' => $foreignEntity
);
$this->getEntityManager()->getHookManager()->process($this->entityType, 'afterRelate', $entity, $options, $hookData);
if (!$this->hooksDisabled) {
$hookData = array(
'relationName' => $relationName,
'relationData' => $data,
'foreignEntity' => $foreignEntity
);
$this->getEntityManager()->getHookManager()->process($this->entityType, 'afterRelate', $entity, $options, $hookData);
}
}
}
@@ -229,11 +235,13 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
if ($foreign instanceof Entity) {
$foreignEntity = $foreign;
$hookData = array(
'relationName' => $relationName,
'foreignEntity' => $foreignEntity
);
$this->getEntityManager()->getHookManager()->process($this->entityType, 'afterUnrelate', $entity, $options, $hookData);
if (!$this->hooksDisabled) {
$hookData = array(
'relationName' => $relationName,
'foreignEntity' => $foreignEntity
);
$this->getEntityManager()->getHookManager()->process($this->entityType, 'afterUnrelate', $entity, $options, $hookData);
}
}
}
@@ -241,7 +249,9 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
{
parent::beforeSave($entity, $options);
$this->getEntityManager()->getHookManager()->process($this->entityType, 'beforeSave', $entity, $options);
if (!$this->hooksDisabled) {
$this->getEntityManager()->getHookManager()->process($this->entityType, 'beforeSave', $entity, $options);
}
}
protected function afterSave(Entity $entity, array $options = array())
@@ -257,7 +267,9 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
$this->processSpecifiedRelationsSave($entity);
$this->processFileFieldsSave($entity);
$this->getEntityManager()->getHookManager()->process($this->entityType, 'afterSave', $entity, $options);
if (!$this->hooksDisabled) {
$this->getEntityManager()->getHookManager()->process($this->entityType, 'afterSave', $entity, $options);
}
}
public function save(Entity $entity, array $options = array())
@@ -279,18 +291,22 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
$entity->set('modifiedAt', $nowString);
}
if ($entity->hasAttribute('createdById')) {
if (empty($options['import']) || !$entity->has('createdById')) {
$entity->set('createdById', $this->getEntityManager()->getUser()->id);
if (empty($options['skipCreatedBy']) && (empty($options['import']) || !$entity->has('createdById'))) {
if ($this->getEntityManager()->getUser()) {
$entity->set('createdById', $this->getEntityManager()->getUser()->id);
}
}
}
} else {
if (empty($options['silent'])) {
if (empty($options['silent']) && empty($options['skipModifiedBy'])) {
if ($entity->hasAttribute('modifiedAt')) {
$entity->set('modifiedAt', $nowString);
}
if ($entity->hasAttribute('modifiedById')) {
$entity->set('modifiedById', $this->getEntityManager()->getUser()->id);
$entity->set('modifiedByName', $this->getEntityManager()->getUser()->get('name'));
if ($this->getEntityManager()->getUser()) {
$entity->set('modifiedById', $this->getEntityManager()->getUser()->id);
$entity->set('modifiedByName', $this->getEntityManager()->getUser()->get('name'));
}
}
}
}
@@ -401,8 +417,16 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
} else {
if (!empty($columns)) {
foreach ($columns as $columnName => $columnField) {
if (isset($columnData->$id)) {
if ($columnData->$id->$columnName !== $existingColumnsData->$id->$columnName) {
if (isset($columnData->$id) && is_object($columnData->$id)) {
if (
property_exists($columnData->$id, $columnName)
&&
(
!property_exists($existingColumnsData->$id, $columnName)
||
$columnData->$id->$columnName !== $existingColumnsData->$id->$columnName
)
) {
$toUpdateIds[] = $id;
}
}

View File

@@ -108,7 +108,7 @@ class AclManager extends \Espo\Core\AclManager
$config = $this->getContainer()->get('config');
$fileManager = $this->getContainer()->get('fileManager');
$metadata = $this->getContainer()->get('metadata');
$fieldManager = $this->getContainer()->get('fieldManager');
$fieldManager = $this->getContainer()->get('fieldManagerUtil');
$portal = $this->getPortal();
$this->tableHashMap[$key] = new $this->tableClassName($user, $portal, $config, $fileManager, $metadata, $fieldManager);

View File

@@ -47,7 +47,7 @@ class ThemeManager extends \Espo\Core\Utils\ThemeManager
{
$theme = $this->portal->get('theme');
if (!$theme) {
$theme = $this->defaultName;
$theme = $this->config->get('theme', $this->defaultName);
}
return $theme;
}

View File

@@ -36,8 +36,20 @@ class Event extends \Espo\Core\ORM\Repositories\RDB
{
protected $reminderDateAttribute = 'dateStart';
protected $reminderSkippingStatusList = ['Held', 'Not Held'];
protected function beforeSave(Entity $entity, array $options = array())
{
if ($entity->isAttributeChanged('status') && in_array($entity->get('status'), $this->reminderSkippingStatusList)) {
$entity->set('reminders', []);
}
parent::beforeSave($entity, $options);
}
protected function afterRemove(Entity $entity, array $options = array())
{
parent::afterRemove($entity, $options);
$pdo = $this->getEntityManager()->getPDO();
$sql = "
DELETE FROM `reminder`

View File

@@ -130,7 +130,7 @@ class Base
$result['orderBy'] = $sortBy;
$type = $this->getMetadata()->get(['entityDefs', $this->getEntityType(), 'fields', $sortBy, 'type']);
if ($type === 'link') {
if (in_array($type, ['link', 'file', 'image'])) {
$result['orderBy'] .= 'Name';
} else if ($type === 'linkParent') {
$result['orderBy'] .= 'Type';
@@ -188,6 +188,8 @@ class Base
$whereClause = array();
foreach ($where as $item) {
if (!isset($item['type'])) continue;
if ($item['type'] == 'bool' && !empty($item['value']) && is_array($item['value'])) {
foreach ($item['value'] as $filter) {
$p = $this->getBoolFilterWhere($filter);
@@ -210,6 +212,8 @@ class Base
$additionalFilters = array();
foreach ($where as $item) {
if (!isset($item['type'])) continue;
$type = $item['type'];
if (!in_array($type, $ignoreTypeList)) {
$part = $this->getWherePart($item, $result);
@@ -985,6 +989,7 @@ class Base
switch ($item['type']) {
case 'or':
case 'and':
case 'not':
if (is_array($item['value'])) {
$arr = array();
foreach ($item['value'] as $i) {
@@ -1030,7 +1035,7 @@ class Base
$part[$attribute . '>='] = $item['value'];
break;
case 'lessThanOrEquals':
$part[$attribute . '<'] = $item['value'];
$part[$attribute . '<='] = $item['value'];
break;
case 'in':
$part[$attribute . '='] = $item['value'];
@@ -1161,16 +1166,49 @@ class Base
);
}
break;
case 'columnLike':
case 'columnIn':
case 'columnIsNull':
case 'columnNotIn':
$link = $this->getMetadata()->get(['entityDefs', $this->entityType, 'fields', $attribute, 'link']);
$column = $this->getMetadata()->get(['entityDefs', $this->entityType, 'fields', $attribute, 'column']);
$alias = $link . 'Filter' . strval(rand(10000, 99999));
$this->setDistinct(true, $result);
$this->addLeftJoin([$link, $alias], $result);
$value = $item['value'];
$columnKey = $alias . 'Middle.' . $column;
if ($item['type'] === 'columnIn') {
$part[$columnKey] = $value;
} else if ($item['type'] === 'columnNotIn') {
$part[$columnKey . '!='] = $value;
} else if ($item['type'] === 'columnIsNull') {
$part[$columnKey] = null;
} else if ($item['type'] === 'columnIsNotNull') {
$part[$columnKey . '!='] = null;
} else if ($item['type'] === 'columnLike') {
$part[$columnKey . '*'] = $value;
} else if ($item['type'] === 'columnStartsWith') {
$part[$columnKey . '*'] = $value . '%';
} else if ($item['type'] === 'columnEndsWith') {
$part[$columnKey . '*'] = '%' . $value;
} else if ($item['type'] === 'columnContains') {
$part[$columnKey . '*'] = '%' . $value . '%';
} else if ($item['type'] === 'columnEquals') {
$part[$columnKey . '='] = $value;
} else if ($item['type'] === 'columnNotEquals') {
$part[$columnKey . '!='] = $value;
}
break;
case 'isNotLinked':
if (!$result) break;
$alias = $attribute . 'IsNotLinkedFilter' . strval(rand(10000, 99999));;
$alias = $attribute . 'IsNotLinkedFilter' . strval(rand(10000, 99999));
$part[$alias . '.id'] = null;
$this->setDistinct(true, $result);
$this->addLeftJoin([$attribute, $alias], $result);
break;
case 'isLinked':
if (!$result) break;
$alias = $attribute . 'IsLinkedFilter' . strval(rand(10000, 99999));;
$alias = $attribute . 'IsLinkedFilter' . strval(rand(10000, 99999));
$part[$alias . '.id!='] = null;
$this->setDistinct(true, $result);
$this->addLeftJoin([$attribute, $alias], $result);

View File

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

View File

@@ -0,0 +1,10 @@
{
"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}": "Crear {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,10 @@
{
"links": {
"meetings": "Juntas",
"calls": "Llamadas",
"tasks": "Tareas"
},
"labels": {
"Create {entityType}": "Crear {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,15 @@
{
"fields": {
"billingAddress": "Dirección de Facturación",
"shippingAddress": "Dirección de envio",
"website": "Sitio Web"
},
"links": {
"meetings": "reuniones",
"calls": "Llamadas",
"tasks": "Tareas"
},
"labels": {
"Create {entityType}": "Crear {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,36 @@
{
"fields": {
"parent": "Padre",
"dateStart": "Fecha de incio",
"dateEnd": "Fecha final",
"duration": "Duracion",
"status": "Estátus",
"reminders": "Recordatorios"
},
"links": {
"parent": "Padre"
},
"options": {
"status": {
"Planned": "Planeado",
"Held": "Retenidas",
"Not Held": "Pendientes"
}
},
"labels": {
"Create {entityType}": "Creat",
"Schedule {entityType}": "Agendar {entityTypeTranslated}",
"Log {entityType}": "Registrar {entityTypeTranslated}",
"Set Held": "Marcar como Retenida",
"Set Not Held": "Marcar como Pendiente"
},
"massActions": {
"setHeld": "Marcar como Retenida",
"setNotHeld": "Marcar como Pendiente"
},
"presetFilters": {
"planned": "Planeado",
"held": "Retenida",
"todays": "Hoy"
}
}

View File

@@ -0,0 +1,13 @@
{
"fields": {
"address": "Dirección"
},
"links": {
"meetings": "Reuniones",
"calls": "Llamadas",
"tasks": "Tareas"
},
"labels": {
"Create {entityType}": "Crear {entityTypeTranslated}"
}
}

View File

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

View File

@@ -1,5 +1,10 @@
{
"labels": {
"Create {entityType}": "Créer un {entityTypeTranslated}"
}
}
"links": {
"meetings": "Meetings",
"calls": "Calls",
"tasks": "Tasks"
},
"labels": {
"Create {entityType}": "Créer un {entityTypeTranslated}"
}
}

View File

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

View File

@@ -1,5 +1,36 @@
{
"labels": {
"Create {entityType}": "Créer un {entityTypeTranslated}"
"fields": {
"parent": "Parent",
"dateStart": "Date de début",
"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}": "Créer un {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

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

View File

@@ -1,5 +1,5 @@
{
"labels": {
"Create {entityType}": "Создать {entityTypeTranslated}"
}
}
"labels": {
"Create {entityType}": "Создать {entityTypeTranslated}"
}
}

View File

@@ -1,5 +1,10 @@
{
"labels": {
"Create {entityType}": "Создать {entityTypeTranslated}"
}
}
"links": {
"meetings": "Meetings",
"calls": "Calls",
"tasks": "Tasks"
},
"labels": {
"Create {entityType}": "Создать {entityTypeTranslated}"
}
}

View File

@@ -1,5 +1,15 @@
{
"labels": {
"Create {entityType}": "Создать {entityTypeTranslated}"
}
}
"fields": {
"billingAddress": "Платежный адрес",
"shippingAddress": "Адрес доставки",
"website": "Website"
},
"links": {
"meetings": "Meetings",
"calls": "Calls",
"tasks": "Tasks"
},
"labels": {
"Create {entityType}": "Создать {entityTypeTranslated}"
}
}

View File

@@ -1,5 +1,36 @@
{
"labels": {
"Create {entityType}": "Создать {entityTypeTranslated}"
"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}": "Создать {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": "Запланировано",
"held": "Held",
"todays": "Сегодняшние"
}
}

View File

@@ -1,5 +1,13 @@
{
"labels": {
"Create {entityType}": "Создать {entityTypeTranslated}"
}
}
"fields": {
"address": "Адрес"
},
"links": {
"meetings": "Meetings",
"calls": "Calls",
"tasks": "Tasks"
},
"labels": {
"Create {entityType}": "Создать {entityTypeTranslated}"
}
}

View File

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

View File

@@ -0,0 +1,10 @@
{
"links": {
"meetings": "Sastanci",
"calls": "Pozivi",
"tasks": "Zadaci"
},
"labels": {
"Create {entityType}": "Napravi {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,15 @@
{
"fields": {
"billingAddress": "Adresa za naplatu",
"shippingAddress": "Adresa za dostavu",
"website": "Sajt"
},
"links": {
"meetings": "Sastanci",
"calls": "Pozivi",
"tasks": "Zadaci"
},
"labels": {
"Create {entityType}": "Napravi {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,36 @@
{
"fields": {
"parent": "Roditelj",
"dateStart": "Početni datum",
"dateEnd": "Krajnji datum",
"duration": "Trajanje",
"status": "Status",
"reminders": "Podsetnici"
},
"links": {
"parent": "Roditelj"
},
"options": {
"status": {
"Planned": "Planiran",
"Held": "Održan",
"Not Held": "Nije održan"
}
},
"labels": {
"Create {entityType}": "Napravi {entityTypeTranslated}",
"Schedule {entityType}": "Zakaži {entityTypeTranslated}",
"Log {entityType}": "Zabeleži {entitiTipeTranslated}",
"Set Held": "Postavi kao održano",
"Set Not Held": "Postavi kao nije održano"
},
"massActions": {
"setHeld": "Postavi kao održano",
"setNotHeld": "Postavi kao nije održano"
},
"presetFilters": {
"planned": "Planiran",
"held": "Održan",
"todays": "Današnji"
}
}

View File

@@ -0,0 +1,13 @@
{
"fields": {
"address": "Adresa"
},
"links": {
"meetings": "Sastanci",
"calls": "Pozivi",
"tasks": "Zadaci"
},
"labels": {
"Create {entityType}": "Napravi {entityTypeTranslated}"
}
}

View File

@@ -1,6 +1,6 @@
{
"fields": {
"billingAddress": "Billing Address",
"billingAddress": "Адреса виставлення рахунку",
"shippingAddress": "Адреса доставки",
"website": "Вебсайт"
},

View File

@@ -1,6 +1,6 @@
{
"fields": {
"parent": "Джерело",
"parent": "Батько",
"dateStart": "Дата початку",
"dateEnd": "Дата завершення",
"duration": "Тривалість",
@@ -8,29 +8,29 @@
"reminders": "Нагадування"
},
"links": {
"parent": "Джерело"
"parent": "Батько"
},
"options": {
"status": {
"Planned": "Плановий",
"Held": "Вміщений",
"Held": "Виконано",
"Not Held": "Не вміщений"
}
},
"labels": {
"Create {entityType}": "Створити {entityTypeTranslated}",
"Schedule {entityType}": "Розклад {entityTypeTranslated} ",
"Log {entityType}": "Log {entityTypeTranslated}",
"Set Held": "Set Held",
"Set Not Held": "Set Not Held"
"Log {entityType}": "Лог {entityTypeTranslated}",
"Set Held": "Позначити виконаним",
"Set Not Held": "Позначити невиконаним"
},
"massActions": {
"setHeld": "Set Held",
"setNotHeld": "Set Not Held"
"setHeld": "Позначити виконаними",
"setNotHeld": "Позначити невиконаними"
},
"presetFilters": {
"planned": "Планований",
"held": "Вміщений ",
"held": "Виконані",
"todays": "Сьогоднішні"
}
}

View File

@@ -42,6 +42,11 @@ class UpgradeManager extends Upgrades\Base
'scriptNames' => array(
'before' => 'BeforeUpgrade',
'after' => 'AfterUpgrade',
),
'customDirNames' => array(
'before' => 'beforeUpgradeFiles',
'after' => 'afterUpgradeFiles',
)
);
}

View File

@@ -389,9 +389,7 @@ abstract class Base
{
if (!isset($this->data['fileList'])) {
$packagePath = $this->getPackagePath();
$filesPath = Util::concatPath($packagePath, self::FILES);
$this->data['fileList'] = $this->getFileManager()->getFileList($filesPath, true, '', true, true);
$this->data['fileList'] = $this->getFileList($packagePath);
}
return $this->data['fileList'];
@@ -401,14 +399,55 @@ abstract class Base
{
if (!isset($this->data['restoreFileList'])) {
$backupPath = $this->getPath('backupPath');
$backupFilePath = Util::concatPath($backupPath, self::FILES);
$this->data['restoreFileList'] = $this->getFileManager()->getFileList($backupFilePath, true, '', true, true);
$this->data['restoreFileList'] = $this->getFileList($backupPath);
}
return $this->data['restoreFileList'];
}
/**
* Get file directories (files, beforeInstallFiles, afterInstallFiles)
*
* @param sting $parentDirPath
*
* @return array
*/
protected function getFileDirs($parentDirPath = null)
{
$dirNames = $this->getParams('customDirNames');
$paths = array(self::FILES, $dirNames['before'], $dirNames['after']);
if (isset($parentDirPath)) {
foreach ($paths as &$path) {
$path = Util::concatPath($parentDirPath, $path);
}
}
return $paths;
}
/**
* Get file list from directories: files, beforeUpgradeFiles, afterUpgradeFiles
*
* @param string $dirPath
*
* @return array
*/
protected function getFileList($dirPath)
{
$fileList = array();
$paths = $this->getFileDirs($dirPath);
foreach ($paths as $filesPath) {
if (file_exists($filesPath)) {
$files = $this->getFileManager()->getFileList($filesPath, true, '', true, true);
$fileList = array_merge($fileList, $files);
}
}
return $fileList;
}
protected function copy($sourcePath, $destPath, $recursively = false, array $fileList = null, $copyOnlyFiles = false)
{
try {
@@ -423,15 +462,32 @@ abstract class Base
/**
* Copy files from upgrade/extension package
*
* @param string $processId
* @param string $type
*
* @return boolean
*/
protected function copyFiles()
protected function copyFiles($type = null)
{
$packagePath = $this->getPackagePath();
$filesPath = Util::concatPath($packagePath, self::FILES);
switch ($type) {
case 'before':
case 'after':
$dirNames = $this->getParams('customDirNames');
$dirPath = $dirNames[$type];
break;
return $this->copy($filesPath, '', true);
default:
$dirPath = self::FILES;
break;
}
$packagePath = $this->getPackagePath();
$filesPath = Util::concatPath($packagePath, $dirPath);
if (file_exists($filesPath)) {
return $this->copy($filesPath, '', true);
}
return true;
}
public function getManifest()

View File

@@ -67,6 +67,11 @@ class Install extends \Espo\Core\Upgrades\Actions\Base
$this->backupExistingFiles();
//beforeInstallFiles
if (!$this->copyFiles('before')) {
$this->throwErrorAndRemovePackage('Cannot copy beforeInstall files.');
}
/* run before install script */
$this->runScript('before');
@@ -85,6 +90,11 @@ class Install extends \Espo\Core\Upgrades\Actions\Base
$this->throwErrorAndRemovePackage('Error occurred while EspoCRM rebuild.');
}
//afterInstallFiles
if (!$this->copyFiles('after')) {
$this->throwErrorAndRemovePackage('Cannot copy afterInstall files.');
}
/* run before install script */
$this->runScript('after');
@@ -112,7 +122,9 @@ class Install extends \Espo\Core\Upgrades\Actions\Base
$deleteFileList = array_diff($copyFileList, $backupFileList);
$res = $this->copy($backupFilePath, '', true);
$res &= $this->getFileManager()->remove($deleteFileList, null, true);
if (!empty($deleteFileList)) {
$res &= $this->getFileManager()->remove($deleteFileList, null, true);
}
if ($res) {
$this->getFileManager()->removeInDir($backupPath, true);

View File

@@ -95,15 +95,20 @@ class Uninstall extends \Espo\Core\Upgrades\Actions\Base
protected function restoreFiles()
{
$packagePath = $this->getPath('packagePath');
$filesPath = Util::concatPath($packagePath, self::FILES);
if (!file_exists($filesPath)) {
$manifestPath = Util::concatPath($packagePath, $this->manifestName);
if (!file_exists($manifestPath)) {
$this->unzipArchive($packagePath);
}
$res = $this->copy($filesPath, '', true);
$fileDirs = $this->getFileDirs($packagePath);
foreach ($fileDirs as $filesPath) {
if (file_exists($filesPath)) {
$res = $this->copy($filesPath, '', true);
}
}
$manifestJson = $this->getFileManager()->getContents(array($packagePath, $this->manifestName));
$manifestJson = $this->getFileManager()->getContents($manifestPath);
$manifest = Json::decode($manifestJson, true);
if (!empty($manifest['delete'])) {
$res &= $this->getFileManager()->remove($manifest['delete'], null, true);
@@ -114,7 +119,7 @@ class Uninstall extends \Espo\Core\Upgrades\Actions\Base
return $res;
}
protected function copyFiles()
protected function copyFiles($type = null)
{
$backupPath = $this->getPath('backupPath');
$res = $this->copy(array($backupPath, self::FILES), '', true);

View File

@@ -63,9 +63,8 @@ class Install extends \Espo\Core\Upgrades\Actions\Base\Install
/** copy scripts files */
$packagePath = $this->getPackagePath();
$res &= $this->copy(array($packagePath, self::SCRIPTS), array($backupPath, self::SCRIPTS), true);
return $res;
return $this->copy(array($packagePath, self::SCRIPTS), array($backupPath, self::SCRIPTS), true);
}
protected function isNew()

View File

@@ -28,6 +28,7 @@
************************************************************************/
namespace Espo\Core\Utils\Database\Orm;
use Espo\Core\Utils\Util;
class Base

View File

@@ -190,8 +190,14 @@ class Converter
public function afterProcess(array $ormMeta)
{
foreach($ormMeta as $entityName => &$entityParams) {
foreach($entityParams['fields'] as $fieldName => &$fieldParams) {
foreach ($ormMeta as $entityName => &$entityParams) {
foreach ($entityParams['fields'] as $fieldName => &$fieldParams) {
/* remove fields without type */
if (!isset($fieldParams['type']) && (!isset($fieldParams['notStorable']) || $fieldParams['notStorable'] === false)) {
unset($entityParams['fields'][$fieldName]);
continue;
}
switch ($fieldParams['type']) {
case 'id':
@@ -240,16 +246,16 @@ class Converter
$outputMeta = array(
'id' => array(
'type' => Entity::ID,
'dbType' => 'varchar',
'dbType' => 'varchar'
),
'name' => array(
'type' => isset($entityMeta['fields']['name']['type']) ? $entityMeta['fields']['name']['type'] : Entity::VARCHAR,
'notStorable' => true,
'notStorable' => true
),
'deleted' => array(
'type' => Entity::BOOL,
'default' => false,
),
'default' => false
)
);
foreach($entityMeta['fields'] as $fieldName => $fieldParams) {
@@ -307,14 +313,27 @@ class Converter
if (class_exists($className) && method_exists($className, 'load')) {
$helperClass = new $className($this->metadata, $ormMeta, $entityDefs);
$fieldResult = $helperClass->process( $fieldName, $entityName );
$fieldResult = $helperClass->process($fieldName, $entityName);
if (isset($fieldResult['unset'])) {
$ormMeta = Util::unsetInArray($ormMeta, $fieldResult['unset']);
unset($fieldResult['unset']);
}
$ormMeta = Util::merge($ormMeta, $fieldResult);
}
$defaultAttributes = $this->metadata->get(['entityDefs', $entityName, 'fields', $fieldName, 'defaultAttributes']);
if ($defaultAttributes && array_key_exists($fieldName, $defaultAttributes)) {
$defaultMetadataPart = array(
$entityName => array(
'fields' => array(
$fieldName => array(
'default' => $defaultAttributes[$fieldName]
)
)
)
);
$ormMeta = Util::merge($ormMeta, $defaultMetadataPart);
}
}

View File

@@ -40,24 +40,27 @@ class Link extends Base
'fields' => array(
$fieldName.'Id' => array(
'type' => 'foreignId',
'index' => $fieldName,
'index' => $fieldName
),
$fieldName.'Name' => array(
'type' => 'varchar',
'notStorable' => true,
),
),
'notStorable' => true
)
)
),
'unset' => array(
$entityName => array(
'fields.'.$fieldName,
),
),
'fields.'.$fieldName
)
)
);
if (!empty($fieldParams['notStorable'])) {
$data[$entityName]['fields'][$fieldName.'Id']['notStorable'] = true;
}
if (!empty($fieldParams['defaultAttributes']) && array_key_exists($fieldName.'Id', $fieldParams['defaultAttributes'])) {
$data[$entityName]['fields'][$fieldName.'Id']['default'] = $fieldParams['defaultAttributes'][$fieldName.'Id'];
}
return $data;
}

View File

@@ -33,31 +33,42 @@ class LinkParent extends Base
{
protected function load($fieldName, $entityName)
{
return array(
$data = array(
$entityName => array (
'fields' => array(
$fieldName.'Id' => array(
'type' => 'foreignId',
'index' => $fieldName,
'index' => $fieldName
),
$fieldName.'Type' => array(
'type' => 'foreignType',
'notNull' => false,
'index' => $fieldName,
'index' => $fieldName
),
$fieldName.'Name' => array(
'type' => 'varchar',
'notStorable' => true,
'relation' => $fieldName,
'isParentName' => true
),
),
)
)
),
'unset' => array(
$entityName => array(
'fields.'.$fieldName,
),
),
'fields.'.$fieldName
)
)
);
$fieldParams = $this->getFieldParams();
if (!empty($fieldParams['defaultAttributes']) && array_key_exists($fieldName.'Id', $fieldParams['defaultAttributes'])) {
$data[$entityName]['fields'][$fieldName.'Id']['default'] = $fieldParams['defaultAttributes'][$fieldName.'Id'];
}
if (!empty($fieldParams['defaultAttributes']) && array_key_exists($fieldName.'Type', $fieldParams['defaultAttributes'])) {
$data[$entityName]['fields'][$fieldName.'Type']['default'] = $fieldParams['defaultAttributes'][$fieldName.'Type'];
}
return $data;
}
}

View File

@@ -440,13 +440,13 @@ class Converter
$dependentEntities[] = $entityName;
foreach ($ormMeta[$entityName]['relations'] as $relationName => $relationParams) {
if (isset($relationParams['entity'])) {
$relationEntity = $relationParams['entity'];
if (!in_array($relationEntity, $dependentEntities)) {
$dependentEntities = $this->getDependentEntities($relationEntity, $ormMeta, $dependentEntities);
if (array_key_exists('relations', $ormMeta[$entityName])) {
foreach ($ormMeta[$entityName]['relations'] as $relationName => $relationParams) {
if (isset($relationParams['entity'])) {
$relationEntity = $relationParams['entity'];
if (!in_array($relationEntity, $dependentEntities)) {
$dependentEntities = $this->getDependentEntities($relationEntity, $ormMeta, $dependentEntities);
}
}
}
}

View File

@@ -93,6 +93,16 @@ class DateTime
$this->timezone = new \DateTimeZone($timeZone);
}
public function getDateFormat()
{
return $this->dateFormat;
}
public function getDateTimeFormat()
{
return $this->dateFormat . ' ' . $this->timeFormat;
}
public function getInternalDateTimeFormat()
{
return $this->internalDateTimeFormat;

View File

@@ -49,7 +49,7 @@ class EntityManager
private $container;
private $reservedWordList = ['__halt_compiler', 'abstract', 'and', 'array', 'as', 'break', 'callable', 'case', 'catch', 'class', 'clone', 'const', 'continue', 'declare', 'default', 'die', 'do', 'echo', 'else', 'elseif', 'empty', 'enddeclare', 'endfor', 'endforeach', 'endif', 'endswitch', 'endwhile', 'eval', 'exit', 'extends', 'final', 'for', 'foreach', 'function', 'global', 'goto', 'if', 'implements', 'include', 'include_once', 'instanceof', 'insteadof', 'interface', 'isset', 'list', 'namespace', 'new', 'or', 'print', 'private', 'protected', 'public', 'require', 'require_once', 'return', 'static', 'switch', 'throw', 'trait', 'try', 'unset', 'use', 'var', 'while', 'xor'];
private $reservedWordList = ['__halt_compiler', 'abstract', 'and', 'array', 'as', 'break', 'callable', 'case', 'catch', 'class', 'clone', 'const', 'continue', 'declare', 'default', 'die', 'do', 'echo', 'else', 'elseif', 'empty', 'enddeclare', 'endfor', 'endforeach', 'endif', 'endswitch', 'endwhile', 'eval', 'exit', 'extends', 'final', 'for', 'foreach', 'function', 'global', 'goto', 'if', 'implements', 'include', 'include_once', 'instanceof', 'insteadof', 'interface', 'isset', 'list', 'namespace', 'new', 'or', 'print', 'private', 'protected', 'public', 'require', 'require_once', 'return', 'static', 'switch', 'throw', 'trait', 'try', 'unset', 'use', 'var', 'while', 'xor', 'common'];
public function __construct(Metadata $metadata, Language $language, File\Manager $fileManager, Config $config, Container $container = null)
{
@@ -102,6 +102,26 @@ class EntityManager
return $this->container->get('serviceFactory');
}
protected function checkControllerExists($name)
{
$controllerClassName = '\\Espo\\Custom\\Controllers\\' . Util::normilizeClassName($name);
if (class_exists($controllerClassName)) {
return true;
} else {
foreach ($this->getMetadata()->getModuleList() as $moduleName) {
$controllerClassName = '\\Espo\\Modules\\' . $moduleName . '\\Controllers\\' . Util::normilizeClassName($name);
if (class_exists($controllerClassName)) {
return true;
}
}
$controllerClassName = '\\Espo\\Controllers\\' . Util::normilizeClassName($name);
if (class_exists($controllerClassName)) {
return true;
}
}
return false;
}
public function create($name, $type, $params = array())
{
$name = ucfirst($name);
@@ -114,6 +134,10 @@ class EntityManager
throw new Error();
}
if ($this->checkControllerExists($name)) {
throw new Conflict('Entity name \''.$name.'\' is not allowed.');
}
$serviceFactory = $this->getServiceFactory();
if ($serviceFactory && $serviceFactory->checKExists($name)) {
throw new Conflict('Entity name \''.$name.'\' is not allowed.');

View File

@@ -99,10 +99,18 @@ class FieldManager
if (isset($existingField)) {
throw new Conflict('Field ['.$name.'] exists in '.$scope);
}
if ($this->getMetadata()->get(['entityDefs', $scope, 'links', $name])) {
throw new Conflict('Link with name ['.$name.'] exists in '.$scope);
}
if (in_array($name, $this->forbiddenFieldNameList)) {
throw new Conflict('Field ['.$name.'] is not allowed');
}
$firstLatter = $name[0];
if (is_numeric($firstLatter)) {
throw new Conflict('Field name should begin with a letter');
}
return $this->update($scope, $name, $fieldDefs, true);
}
@@ -127,7 +135,7 @@ class FieldManager
$type = isset($fieldDefs['type']) ? $fieldDefs['type'] : $type = $this->getMetadata()->get(['entityDefs', $scope, 'fields', $name, 'type']);
$this->processHook('beforeSave', $type, $scope, $name, $fieldDefs);
$this->processHook('beforeSave', $type, $scope, $name, $fieldDefs, array('isNew' => $isNew));
if ($this->getMetadata()->get(['fields', $type, 'translatedOptions'])) {
if (isset($fieldDefs['translatedOptions'])) {

View File

@@ -0,0 +1,48 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2017 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\Utils\FieldManager\Hooks;
use Espo\Core\Exceptions\Conflict;
class AttachmentMultipleType extends Base
{
public function beforeSave($scope, $name, $defs, $options)
{
if (!empty($options['isNew'])) {
$fieldDefs = $this->getMetadata()->get(['entityDefs', $scope, 'fields'], array());
foreach ($fieldDefs as $field => $defs) {
$type = $this->getMetadata()->get(['entityDefs', $scope, 'fields', $field, 'type']);
if ($type === 'attachmentMultiple') {
throw new Conflict("Attachment-Multiple field already exists in '{$scope}'. There can be only one Attachment-Multiple field per entity type.");
}
}
}
}
}

View File

@@ -0,0 +1,98 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2017 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\Utils;
class FieldManagerUtil
{
private $metadata;
public function __construct(Metadata $metadata)
{
$this->metadata = $metadata;
}
protected function getMetadata()
{
return $this->metadata;
}
private function getAttributeListByType($scope, $name, $type)
{
$fieldType = $this->getMetadata()->get('entityDefs.' . $scope . '.fields.' . $name . '.type');
if (!$fieldType) return [];
$defs = $this->getMetadata()->get('fields.' . $fieldType);
if (!$defs) return [];
if (is_object($defs)) {
$defs = get_object_vars($defs);
}
$fieldList = [];
if (isset($defs[$type . 'Fields'])) {
$list = $defs[$type . 'Fields'];
$naming = 'suffix';
if (isset($defs['naming'])) {
$naming = $defs['naming'];
}
if ($naming == 'prefix') {
foreach ($list as $f) {
$fieldList[] = $f . ucfirst($name);
}
} else {
foreach ($list as $f) {
$fieldList[] = $name . ucfirst($f);
}
}
} else {
if ($type == 'actual') {
$fieldList[] = $name;
}
}
return $fieldList;
}
public function getActualAttributeList($scope, $name)
{
return $this->getAttributeListByType($scope, $name, 'actual');
}
public function getNotActualAttributeList($scope, $name)
{
return $this->getAttributeListByType($scope, $name, 'notActual');
}
public function getAttributeList($scope, $name)
{
return array_merge($this->getActualAttributeList($scope, $name), $this->getNotActualAttributeList($scope, $name));
}
}

View File

@@ -0,0 +1,216 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2017 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\Utils;
use \Espo\Core\Utils\Util;
use \Espo\Core\Exceptions\NotFound;
use \Espo\Core\Exceptions\Error;
class LabelManager extends \Espo\Core\Injectable
{
protected $dependencyList = [
'config',
'fileManager',
'metadata',
'defaultLanguage'
];
protected $ignoreList = [
'Global.sets'
];
public function getScopeList()
{
$scopeList = [];
$languageObj = $this->getDefaultLanguage();
$data = $languageObj->getAll();
foreach (array_keys($data) as $scope) {
if (!in_array($scope, $scopeList)) {
$scopeList[] = $scope;
}
}
foreach ($this->getMetadata()->get('scopes') as $scope => $data) {
if (!in_array($scope, $scopeList)) {
$scopeList[] = $scope;
}
}
return $scopeList;
}
public function getScopeData($language, $scope)
{
$languageObj = new Language($language, $this->getFileManager(), $this->getMetadata());
$data = $languageObj->get($scope);
if (empty($data)) {
return (object) [];
}
if ($this->getMetadata()->get(['scopes', $scope, 'entity'])) {
if (empty($data['fields'])) {
$data['fields'] = array();
}
foreach ($this->getMetadata()->get(['entityDefs', $scope, 'fields']) as $field => $item) {
if (!array_key_exists($field, $data['fields'])) {
$data['fields'][$field] = $languageObj->get('Global.fields.' . $field);
if (is_null($data['fields'][$field])) {
$data['fields'][$field] = '';
}
}
}
if (empty($data['links'])) {
$data['links'] = array();
}
foreach ($this->getMetadata()->get(['entityDefs', $scope, 'links']) as $link => $item) {
if (!array_key_exists($link, $data['links'])) {
$data['links'][$link] = $languageObj->get('Global.links.' . $link);
if (is_null($data['links'][$link])) {
$data['links'][$link] = '';
}
}
}
if (empty($data['labels'])) {
$data['labels'] = array();
}
if (!array_key_exists('Create ' . $scope, $data['labels'])) {
$data['labels']['Create ' . $scope] = '';
}
}
if ($scope === 'Global') {
if (empty($data['scopeNames'])) {
$data['scopeNames'] = array();
}
if (empty($data['scopeNamesPlural'])) {
$data['scopeNamesPlural'] = array();
}
foreach ($this->getMetadata()->get(['scopes']) as $scopeKey => $item) {
if (!empty($item['entity'])) {
if (empty($data['scopeNamesPlural'][$scopeKey])) {
$data['scopeNamesPlural'][$scopeKey] = '';
}
}
if (empty($data['scopeNames'][$scopeKey])) {
$data['scopeNames'][$scopeKey] = '';
}
}
}
foreach ($data as $key => $value) {
if (empty($value)) {
unset($data[$key]);
}
}
$finalData = array();
foreach ($data as $category => $item) {
if (in_array($scope . '.' . $category, $this->ignoreList)) continue;
foreach ($item as $key => $categoryItem) {
if (is_array($categoryItem)) {
foreach ($categoryItem as $subKey => $subItem) {
$finalData[$category][$category .'[.]' . $key .'[.]' . $subKey] = $subItem;
}
} else {
$finalData[$category][$category .'[.]' . $key] = $categoryItem;
}
}
}
return $finalData;
}
public function saveLabels($language, $scope, $labels)
{
$languageObj = new Language($language, $this->getFileManager(), $this->getMetadata());
$languageOriginalObj = new Language($language, $this->getFileManager(), $this->getMetadata(), false, true);
$returnDataHash = array();
foreach ($labels as $key => $value) {
$arr = explode('[.]', $key);
$category = $arr[0];
$name = $arr[1];
$setPath = [$scope, $category, $name];
$setValue = null;
if (count($arr) == 2) {
if ($value !== '') {
$languageObj->set($scope, $category, $name, $value);
$setValue = $value;
} else {
$setValue = $languageOriginalObj->get(implode('.', [$scope, $category, $name]));
if (is_null($setValue) && $scope !== 'Global') {
$setValue = $languageOriginalObj->get(implode('.', ['Global', $category, $name]));
}
$languageObj->delete($scope, $category, $name);
}
} else if (count($arr) == 3) {
$name = $arr[1];
$attribute = $arr[2];
$data = $languageObj->get($scope . '.' . $category . '.' . $name);
$setPath[] = $attribute;
if (is_array($data)) {
if ($value !== '') {
$data[$attribute] = $value;
$setValue = $value;
} else {
$dataOriginal = $languageOriginalObj->get($scope . '.' . $category . '.' . $name);
if (is_array($dataOriginal) && isset($dataOriginal[$attribute])) {
$data[$attribute] = $dataOriginal[$attribute];
$setValue = $dataOriginal[$attribute];
}
}
$languageObj->set($scope, $category, $name, $data);
}
}
if (!is_null($setValue)) {
$frontKey = implode('[.]', $setPath);
$returnDataHash[$frontKey] = $setValue;
}
}
$languageObj->save();
return $returnDataHash;
}
}

View File

@@ -61,13 +61,15 @@ class Language
protected $useCache = false;
protected $noCustom = false;
private $paths = array(
'corePath' => 'application/Espo/Resources/i18n',
'modulePath' => 'application/Espo/Modules/{*}/Resources/i18n',
'customPath' => 'custom/Espo/Custom/Resources/i18n',
);
public function __construct($language = null, File\Manager $fileManager, Metadata $metadata, $useCache = false)
public function __construct($language = null, File\Manager $fileManager, Metadata $metadata, $useCache = false, $noCustom = false)
{
if ($language) {
$this->currentLanguage = $language;
@@ -79,6 +81,7 @@ class Language
$this->metadata = $metadata;
$this->useCache = $useCache;
$this->noCustom = $noCustom;
$this->unifier = new \Espo\Core\Utils\File\Unifier($this->fileManager, $this->metadata);
}
@@ -345,7 +348,13 @@ class Language
protected function init($reload = false)
{
if ($reload || !file_exists($this->getLangCacheFile()) || !$this->useCache) {
$fullData = $this->getUnifier()->unify($this->name, $this->paths, true);
$paths = $this->paths;
if ($this->noCustom) {
unset($paths['customPath']);
}
$fullData = $this->getUnifier()->unify($this->name, $paths, true);
$result = true;
foreach ($fullData as $i18nName => $i18nData) {

View File

@@ -111,6 +111,15 @@ class ScheduledJob
return $this->getClassName($name);
}
public function getAvailableList()
{
$data = $this->getAll();
$list = array_keys($data);
return $list;
}
/**
* Get list of all job names
*

View File

@@ -35,7 +35,7 @@ class ThemeManager
protected $metadata;
private $defaultName = 'Espo';
protected $defaultName = 'Espo';
private $defaultStylesheet = 'Espo';

View File

@@ -63,7 +63,9 @@ return array (
'smtpUsername' => '',
'smtpPassword' => '',
'languageList' => [
'en_GB',
'en_US',
'es_MX',
'cs_CZ',
'da_DK',
'de_DE',
@@ -74,6 +76,7 @@ return array (
'nb_NO',
'nl_NL',
'tr_TR',
'sr_RS',
'ro_RO',
'ru_RU',
'pl_PL',
@@ -160,6 +163,9 @@ return array (
'cleanupJobPeriod' => '1 month',
'cleanupActionHistoryPeriod' => '15 days',
'cleanupAuthTokenPeriod' => '1 month',
'currencyFormat' => 1,
'currencyDecimalPlaces' => null,
'aclStrictMode' => false,
'isInstalled' => false
);

View File

@@ -124,7 +124,7 @@ class Email extends \Espo\Core\ORM\Entity
public function getToList()
{
$value = $email->get('to');
$value = $this->get('to');
if ($value) {
$arr = explode(';', $value);
if (is_array($arr)) {
@@ -136,7 +136,7 @@ class Email extends \Espo\Core\ORM\Entity
public function getCcList()
{
$value = $email->get('cc');
$value = $this->get('cc');
if ($value) {
$arr = explode(';', $value);
if (is_array($arr)) {
@@ -148,7 +148,7 @@ class Email extends \Espo\Core\ORM\Entity
public function getBccList()
{
$value = $email->get('bcc');
$value = $this->get('bcc');
if ($value) {
$arr = explode(';', $value);
if (is_array($arr)) {
@@ -160,7 +160,7 @@ class Email extends \Espo\Core\ORM\Entity
public function getReplyToList()
{
$value = $email->get('replyTo');
$value = $this->get('replyTo');
if ($value) {
$arr = explode(';', $value);
if (is_array($arr)) {

View File

@@ -44,7 +44,9 @@ class Portal extends \Espo\Core\EntryPoints\Base
} else if (!empty($data['id'])) {
$id = $data['id'];
} else {
$id = explode('/', $_SERVER['REQUEST_URI'])[count(explode('/', $_SERVER['SCRIPT_NAME'])) - 1];
$url = !empty($_SERVER['REDIRECT_URL']) ? $_SERVER['REDIRECT_URL'] : $_SERVER['REQUEST_URI'];
$id = explode('/', $url)[count(explode('/', $_SERVER['SCRIPT_NAME'])) - 1];
if (!$id) {
$id = $this->getConfig()->get('defaultPortalId');
}

View File

@@ -61,12 +61,10 @@ class Mentions extends \Espo\Core\Hooks\Base
}
}
preg_match_all('/(@\w+)/', $post, $matches);
preg_match_all('/(@[\w@.-]+)/', $post, $matches);
$mentionCount = 0;
if (is_array($matches) && !empty($matches[0]) && is_array($matches[0])) {
$parent = null;
if ($entity->get('parentId') && $entity->get('parentType')) {

View File

@@ -113,7 +113,7 @@ class Notifications extends \Espo\Core\Hooks\Base
if (is_array($targetUserIdList)) {
foreach ($targetUserIdList as $userId) {
if ($userId === $this->getUser()->id) continue;
if (in_array($user->id, $userIdList)) continue;
if (in_array($userId, $userIdList)) continue;
$userIdList[] = $userId;
}
}

View File

@@ -39,6 +39,14 @@ class Cleanup extends \Espo\Core\Jobs\Base
protected $cleanupAuthTokenPeriod = '1 month';
protected $cleanupNotificationsPeriod = '2 months';
protected $cleanupRemovedNotesPeriod = '2 months';
protected $cleanupAttachmentsPeriod = '1 month';
protected $cleanupRemindersPeriod = '15 days';
public function run()
{
$this->cleanupJobs();
@@ -97,6 +105,19 @@ class Cleanup extends \Espo\Core\Jobs\Base
$sth->execute();
}
protected function cleanupReminders()
{
$period = '-' . $this->getConfig()->get('cleanupRemindersPeriod', $this->cleanupRemindersPeriod);
$datetime = new \DateTime();
$datetime->modify($period);
$query = "DELETE FROM `reminder` WHERE DATE(remind_at) < '" . $datetime->format('Y-m-d') . "'";
$pdo = $this->getEntityManager()->getPDO();
$sth = $pdo->prepare($query);
$sth->execute();
}
protected function cleanupAuthToken()
{
$period = '-' . $this->getConfig()->get('cleanupAuthTokenPeriod', $this->cleanupAuthTokenPeriod);
@@ -122,7 +143,9 @@ class Cleanup extends \Espo\Core\Jobs\Base
{
$pdo = $this->getEntityManager()->getPDO();
$dateBefore = date('Y-m-d H:i:s', time() - 3600 * 24);
$period = '-' . $this->getConfig()->get('cleanupAttachmentsPeriod', $this->cleanupAttachmentsPeriod);
$datetime = new \DateTime();
$datetime->modify($period);
$collection = $this->getEntityManager()->getRepository('Attachment')->where(array(
'OR' => array(
@@ -130,14 +153,40 @@ class Cleanup extends \Espo\Core\Jobs\Base
'role' => ['Export File']
)
),
'createdAt<' => $dateBefore
))->limit(0, 100)->find();
'createdAt<' => $datetime->format('Y-m-d H:i:s')
))->limit(0, 1000)->find();
foreach ($collection as $e) {
$this->getEntityManager()->removeEntity($e);
}
$sql = "DELETE FROM attachment WHERE deleted = 1 AND created_at < ".$pdo->quote($dateBefore);
if ($this->getConfig()->get('cleanupOrphanAttachments')) {
$collection = $this->getEntityManager()->getRepository('Attachment')->where(array(
array(
'role' => 'Attachment'
),
'OR' => array(
array(
'parentId' => null,
'parentType!=' => null,
'relatedType=' => null
),
array(
'parentType' => null,
'relatedId' => null,
'relatedType!=' => null
)
),
'createdAt<' => $datetime->format('Y-m-d H:i:s'),
'createdAt>' => '2017-05-10 00:00:00'
))->limit(0, 1000)->find();
foreach ($collection as $e) {
$this->getEntityManager()->removeEntity($e);
}
}
$sql = "DELETE FROM attachment WHERE deleted = 1 AND created_at < ".$pdo->quote($datetime->format('Y-m-d H:i:s'));
$sth = $pdo->query($sql);
}
@@ -170,9 +219,11 @@ class Cleanup extends \Espo\Core\Jobs\Base
{
$pdo = $this->getEntityManager()->getPDO();
$dateBefore = date('Y-m-d H:i:s', time() - 3600 * 24 * 20);
$period = '-' . $this->getConfig()->get('cleanupRemovedNotesPeriod', $this->cleanupRemovedNotesPeriod);
$datetime = new \DateTime();
$datetime->modify($period);
$sql = "SELECT * FROM `note` WHERE deleted = 1 AND created_at < ".$pdo->quote($dateBefore);
$sql = "SELECT * FROM `note` WHERE deleted = 1 AND DATE(created_at) < ".$pdo->quote($datetime->format('Y-m-d'));
$sth = $pdo->prepare($sql);
$sth->execute();
while ($row = $sth->fetch(\PDO::FETCH_ASSOC)) {
@@ -193,9 +244,12 @@ class Cleanup extends \Espo\Core\Jobs\Base
{
$pdo = $this->getEntityManager()->getPDO();
$dateBefore = date('Y-m-d H:i:s', time() - 3600 * 24 * 50);
$period = '-' . $this->getConfig()->get('cleanupNotificationsPeriod', $this->cleanupNotificationsPeriod);
$datetime = new \DateTime();
$datetime->modify($period);
$sql = "SELECT * FROM `notification` WHERE DATE(created_at) < ".$pdo->quote($datetime->format('Y-m-d'));
$sql = "SELECT * FROM `notification` WHERE created_at < ".$pdo->quote($dateBefore);
$sth = $pdo->prepare($sql);
$sth->execute();
while ($row = $sth->fetch(\PDO::FETCH_ASSOC)) {

View File

@@ -36,8 +36,6 @@ class Meeting extends \Espo\Core\Repositories\Event
{
protected function beforeSave(Entity $entity, array $options = array())
{
parent::beforeSave($entity, $options);
$parentId = $entity->get('parentId');
$parentType = $entity->get('parentType');
if (!empty($parentId) || !empty($parentType)) {
@@ -61,6 +59,27 @@ class Meeting extends \Espo\Core\Repositories\Event
}
}
if (!$entity->isNew()) {
if ($entity->isFieldChanged('dateStart') && $entity->isFieldChanged('dateStart') && !$entity->isFieldChanged('dateEnd')) {
$dateEndPrevious = $entity->getFetched('dateEnd');
$dateStartPrevious = $entity->getFetched('dateStart');
if ($dateStartPrevious && $dateEndPrevious) {
$dtStart = new \DateTime($dateStartPrevious);
$dtEnd = new \DateTime($dateEndPrevious);
$dt = new \DateTime($entity->get('dateStart'));
if ($dtStart && $dtEnd && $dt) {
$duration = ($dtEnd->getTimestamp() - $dtStart->getTimestamp());
$dt->modify('+' . $duration . ' seconds');
$dateEnd = $dt->format('Y-m-d H:i:s');
$entity->set('dateEnd', $dateEnd);
}
}
}
}
parent::beforeSave($entity, $options);
$assignedUserId = $entity->get('assignedUserId');
if ($assignedUserId) {
if ($entity->has('usersIds')) {
@@ -98,24 +117,5 @@ class Meeting extends \Espo\Core\Repositories\Event
}
}
}
if (!$entity->isNew()) {
if ($entity->isFieldChanged('dateStart') && $entity->isFieldChanged('dateStart') && !$entity->isFieldChanged('dateEnd')) {
$dateEndPrevious = $entity->getFetched('dateEnd');
$dateStartPrevious = $entity->getFetched('dateStart');
if ($dateStartPrevious && $dateEndPrevious) {
$dtStart = new \DateTime($dateStartPrevious);
$dtEnd = new \DateTime($dateEndPrevious);
$dt = new \DateTime($entity->get('dateStart'));
if ($dtStart && $dtEnd && $dt) {
$duration = ($dtEnd->getTimestamp() - $dtStart->getTimestamp());
$dt->modify('+' . $duration . ' seconds');
$dateEnd = $dt->format('Y-m-d H:i:s');
$entity->set('dateEnd', $dateEnd);
}
}
}
}
}
}

View File

@@ -35,14 +35,13 @@ class Opportunity extends \Espo\Core\ORM\Repositories\RDB
{
public function beforeSave(Entity $entity, array $options = array())
{
parent::beforeSave($entity, $options);
if ($entity->isNew()) {
if (!$entity->has('probability') && $entity->get('stage')) {
$probability = $this->getMetadata()->get('entityDefs.Opportunity.probabilityMap.' . $entity->get('stage'), 0);
$entity->set('probability', $probability);
}
}
parent::beforeSave($entity, $options);
}
}

View File

@@ -35,6 +35,8 @@ class Task extends \Espo\Core\Repositories\Event
{
protected $reminderDateAttribute = 'dateEnd';
protected $reminderSkippingStatusList = ['Completed', 'Canceled'];
protected function init()
{
parent::init();
@@ -69,8 +71,6 @@ class Task extends \Espo\Core\Repositories\Event
protected function beforeSave(Entity $entity, array $options = array())
{
parent::beforeSave($entity, $options);
if ($entity->isFieldChanged('status')) {
if ($entity->get('status') == 'Completed') {
$entity->set('dateCompleted', date('Y-m-d H:i:s'));
@@ -125,5 +125,7 @@ class Task extends \Espo\Core\Repositories\Event
}
}
}
parent::beforeSave($entity, $options);
}
}

View File

@@ -2,7 +2,8 @@
"labels": {
"Converted To": "Konvertováno do",
"Create Lead": "Vytvořit stopu",
"Convert": "Konvertovat"
"Convert": "Konvertovat",
"convert": "konvertovat"
},
"fields": {
"name": "Název",

View File

@@ -1,19 +0,0 @@
{
"fields": {
"name": "Název",
"emailAddress": "Email",
"title": "Pozice",
"website": "Web",
"accountName": "Jméno organizace",
"phoneNumber": "Telefon",
"doNotCall": "Nevolat",
"address": "Adresa",
"description": "Popis"
},
"links": {
},
"labels": {
"Create Target": "Vytvořit cíl",
"Convert to Lead": "Konvertovat na stopu"
}
}

View File

@@ -16,7 +16,8 @@
"targetLists": "Kontaktlister",
"targetList": "Kontaktliste",
"portalUser": "Portalbruger",
"originalLead": "Oprindelig Lead"
"originalLead": "Oprindelig Lead",
"acceptanceStatus": "Acceptance Status"
},
"links": {
"opportunities": "Muligheder",

View File

@@ -3,6 +3,8 @@
"Create Lead": "Opret Lead",
"Create Contact": "Opret Kontakt",
"Create Task": "Opret Opgave",
"Create Case": "Opret Sag"
"Create Case": "Opret Sag",
"Add to Contact": "Add to Contact",
"Add to Lead": "Add to Lead"
}
}

View File

@@ -78,12 +78,12 @@
"labels": {
"Create InboundEmail": "Opret Indkommende Email",
"Activities": "Aktiviteter",
"History": "Forløb",
"History": "Historik",
"Attendees": "Deltagere",
"Schedule Meeting": "Planlæg Møde",
"Schedule Call": "Planlæg Opkald",
"Compose Email": "Skriv Email",
"Log Meeting": "Logge Møde",
"Log Meeting": "Log møde",
"Log Call": "Logge Opkald",
"Archive Email": "Arkiver Email",
"Create Task": "Opret Opgave",

View File

@@ -38,9 +38,10 @@
"Article": "Artikel"
}
},
"tooltips": {
},
"presetFilters": {
"published": "Publiseret"
},
"tooltips": {
"portals": "Article will be available only in specified portals."
}
}

View File

@@ -2,7 +2,8 @@
"labels": {
"Converted To": "Konverteret Til",
"Create Lead": "Opret Lead",
"Convert": "Konverter"
"Convert": "Konverter",
"convert": "convert"
},
"fields": {
"name": " Navn",
@@ -24,7 +25,9 @@
"campaign": "Kampagne",
"targetLists": "Kontaktlister",
"targetList": "Kontaktliste",
"industry": "Industri"
"industry": "Industri",
"acceptanceStatus": "Acceptance Status",
"opportunityAmountCurrency": "Opportunity Amount Currency"
},
"links": {
"targetLists": "Kontaktlister",

View File

@@ -13,7 +13,8 @@
"amountConverted": "Beløb (konverteret)",
"amountWeightedConverted": "Beløb Vægtet",
"campaign": "Kampagne",
"originalLead": "Oprindelig Lead"
"originalLead": "Oprindelig Lead",
"amountCurrency": "Amount Currency"
},
"links": {
"contacts": "Kontakter",

View File

@@ -1,17 +0,0 @@
{
"fields": {
"name": "Navn",
"emailAddress": "Email",
"title": "Titel",
"website": "Webside",
"accountName": "Kontonavn",
"phoneNumber": "Telefon",
"doNotCall": "Ring Ikke!",
"address": "Adresse",
"description": "Beskrivelse"
},
"labels": {
"Create Target": "Opret Mål",
"Convert to Lead": "Konverter til Lead"
}
}

View File

@@ -1,5 +1,8 @@
{
"links": {
"targetLists": "Kontaktlister"
},
"fields": {
"acceptanceStatus": "Acceptance Status"
}
}

View File

@@ -2,7 +2,8 @@
"labels": {
"Converted To": "Umgewandelt zu",
"Create Lead": "Interessent erstellen",
"Convert": "Umwandeln"
"Convert": "Umwandeln",
"convert": "umwandeln"
},
"fields": {
"name": "Name",

View File

@@ -1,17 +0,0 @@
{
"fields": {
"name": "Name",
"emailAddress": "E-Mail",
"title": "Funktion",
"website": "Webseite",
"accountName": "Firmenname",
"phoneNumber": "Telefon",
"doNotCall": "Anrufe unerwünscht",
"address": "Adresse",
"description": "Beschreibung"
},
"labels": {
"Create Target": "Zielkontakt erstellen",
"Convert to Lead": "Zu Interessent umwandeln"
}
}

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