Compare commits

...

290 Commits
5.6.0 ... 5.6.8

Author SHA1 Message Date
yuri
741a6d5dab wysiwyg fixes 2019-07-25 16:25:18 +03:00
yuri
0c0a602330 list w categoties expand when text search 2019-07-25 13:06:30 +03:00
yuri
7d13018eba fix category expanding storing 2019-07-25 12:38:20 +03:00
yuri
44c65c0117 kb min body height in portal 2019-07-25 11:59:00 +03:00
yuri
e9e758c4b3 v 2019-07-25 11:26:02 +03:00
yuri
7e7acb8d28 fix template comments stripped 2019-07-25 11:25:48 +03:00
yuri
1ab897db28 fix email address field 2019-07-24 16:17:22 +03:00
yuri
1a990850ca add MMK currency 2019-07-24 16:05:37 +03:00
yuri
36e360e167 v 2019-07-24 15:49:55 +03:00
yuri
9ffdb1a1f0 title for some fields in list mode 2019-07-24 13:00:08 +03:00
yuri
64b15f9282 stream dont show create post for portal 2019-07-24 12:54:37 +03:00
yuri
e8ebe51f1c note create acl 2019-07-24 12:53:03 +03:00
yuri
ec2a7d2f48 fix no join name null 2019-07-24 12:25:24 +03:00
yuri
6782e7c15c dashboard fixes 2019-07-24 12:20:02 +03:00
yuri
3c73e3e8cf more string escaping 2019-07-24 12:04:24 +03:00
yuri
4ab7d19776 xss fixes 2019-07-23 17:50:02 +03:00
yuri
34e33bd13a searchPanelInPortalDisabled 2019-07-23 13:29:01 +03:00
yuri
49fa22fa3d v 2019-07-23 11:37:07 +03:00
yuri
10fcd79155 portal home tab 2019-07-23 11:36:39 +03:00
yuri
6cdc8f2823 mass update for portal users 2019-07-22 15:07:40 +03:00
yuri
d611ebfc86 cleanup try catch 2019-07-22 14:04:21 +03:00
yuri
599a7c6080 cleanup record service method 2019-07-22 13:53:02 +03:00
yuri
4c21f1192d email send: dont fail if attachment deleted 2019-07-22 13:00:25 +03:00
yuri
63e78baf21 fix orm join conditions 2019-07-22 12:46:14 +03:00
yuri
fa2c689a34 email skip index when search by link 2019-07-19 17:37:05 +03:00
yuri
a53b440b8b mass update hide not accessible fields 2019-07-19 17:13:30 +03:00
yuri
8189832af2 style option color 2019-07-19 17:07:58 +03:00
yuri
135f869e1a dynamic logix support currency 2019-07-19 13:12:18 +03:00
yuri
2d56525a25 fix autocomplete empty name 2019-07-19 12:57:48 +03:00
yuri
03773dd929 fix xss document file 2019-07-18 17:10:20 +03:00
yuri
5b8dba68f3 fix orm getAllAttributesFromComplexExpression 2019-07-18 16:55:55 +03:00
yuri
f63c75e18d v 2019-07-18 16:37:09 +03:00
yuri
6dd0bd8b90 sanitize wysiwyg 2019-07-18 16:36:00 +03:00
yuri
94e86f875a lead capture changes 2019-07-18 13:43:35 +03:00
yuri
b582b20003 array field improvement 2019-07-17 13:02:24 +03:00
yuri
e58e82eea1 entry point dont log stop error 2019-07-17 12:14:11 +03:00
yuri
dddc4feda8 image silent error not found 2019-07-17 12:13:52 +03:00
yuri
a65421a268 pt br lang 2019-07-17 12:01:45 +03:00
yuri
b6696dcc26 ru lang 2019-07-17 11:59:35 +03:00
yuri
4e3d8fb98f pl lang 2019-07-17 11:58:53 +03:00
yuri
e0aaff932e nl lang 2019-07-17 11:58:08 +03:00
yuri
17ea760851 layout fix 2019-07-16 17:55:57 +03:00
yuri
f3d11aede3 disable inlide edit for email accounts 2019-07-16 14:23:56 +03:00
yuri
8053d65f33 dynamic logic in options 2019-07-16 11:00:48 +03:00
yuri
01809d2cc3 confirm focus 2019-07-16 10:47:21 +03:00
yuri
ac8e3d1a69 fix portal base path 2019-07-16 10:41:47 +03:00
yuri
b669ccf733 fix portal isCustom undefined 2019-07-16 10:22:26 +03:00
yuri
8c42c6bf0b fix bool opted out search 2019-07-16 10:17:06 +03:00
yuri
97bdd22795 sanitize complex text 2019-07-15 12:37:23 +03:00
yuri
0f3cd7913c link skip acl param 2019-07-11 16:54:49 +03:00
yuri
502cfc8d76 fix foreign enum 2019-07-11 10:40:52 +03:00
yuri
d31e826305 fix websocket 2019-07-08 16:18:22 +03:00
yuri
28a62a2581 auth method list from metadata 2019-07-05 11:31:29 +03:00
yuri
deff6bad36 login page changes 2019-07-04 13:30:36 +03:00
yuri
f2bc80b7d9 auth settings changes 2019-07-04 12:15:29 +03:00
yuri
b5b5cac0ac calendar mode buttons improvement 2019-07-03 13:25:54 +03:00
yuri
308f30510b calendar css fixes 2019-07-03 11:51:35 +03:00
yuri
574513a9c8 fix navbar more scroll 2019-07-03 11:27:52 +03:00
yuri
f37bc7d46b external account token renewal fix 2019-07-02 16:09:25 +03:00
yuri
9f5db434df fix attachment multiple 2019-07-02 15:54:41 +03:00
yuri
1c8abd6e36 mutliple attachment multiple fields 2019-07-02 15:46:10 +03:00
yuri
c95fcd6fbd action configCheck 2019-07-01 16:31:02 +03:00
yuri
e6009366e8 version 2019-07-01 12:47:27 +03:00
yuri
286502d872 cssList fix 2019-07-01 12:47:07 +03:00
yuri
0749bdc006 cs fix 2019-07-01 12:45:54 +03:00
yuri
72ef137dbe fix app js logout request url 2019-07-01 12:45:50 +03:00
yuri
f624bdef5c settings global params 2019-07-01 12:30:44 +03:00
yuri
9730680e41 fix htmlizer 2019-07-01 10:30:23 +03:00
yuri
c59fa40615 log exception message line and file 2019-06-27 11:51:34 +03:00
yuri
4dc08dd37c custom login view 2019-06-27 11:34:35 +03:00
yuri
e76c7564dc custom unsubscribe template 2019-06-27 11:05:44 +03:00
yuri
802bac82f0 fix create activity from panel 2019-06-26 16:37:58 +03:00
yuri
c64cc13e4c link additional select params 2019-06-26 12:06:30 +03:00
yuri
30be62eabe fix google maps 2019-06-26 10:37:16 +03:00
yuri
1c1703b349 fix pdf 2019-06-25 13:35:50 +03:00
yuri
6916f3242c external account after connect hook 2019-06-25 11:07:32 +03:00
yuri
d2630a5c3f fix deleted user view 2019-06-24 14:25:52 +03:00
yuri
3e02776fcd email fixes 2019-06-24 13:29:45 +03:00
yuri
376bfe63e0 fix meeting set held 2019-06-20 16:33:09 +03:00
yuri
950a3b4703 calendar fix 2019-06-20 13:35:02 +03:00
yuri
2f5ffb7421 external acount acl 2019-06-18 13:32:18 +03:00
yuri
043ef8ef3e oauth client support header string 2019-06-13 16:30:10 +03:00
yuri
a959a2deaf view followers all users 2019-06-12 15:33:08 +03:00
yuri
65a3f4e5f3 external account return uri path 2019-06-12 13:36:34 +03:00
yuri
97355ad024 fix redirect uri 2019-06-12 11:26:50 +03:00
yuri
99300d2d61 integration redirect uri custom 2019-06-12 11:24:11 +03:00
yuri
e070d9d9f1 oauth-callback file 2019-06-12 11:11:43 +03:00
yuri
8f9bb79978 button handler init 2019-06-07 16:37:02 +03:00
yuri
405a5c29df layout manager css fix 2019-06-07 14:24:20 +03:00
yuri
4f2a2b290b fix calendar custom event 2019-06-07 11:32:30 +03:00
yuri
fd5218c10b btn-text color 2019-06-06 16:05:22 +03:00
yuri
73ab4aa42e meeting modal acceptance right 2019-06-06 13:16:48 +03:00
yuri
66a60b1257 timeline tpl change 2019-06-06 12:53:39 +03:00
yuri
c975954944 stream page btn-text 2019-06-06 12:42:47 +03:00
yuri
86799ed742 btn-text normal weight 2019-06-06 12:41:34 +03:00
yuri
1da6eca3be update npm packages 2019-06-06 11:45:31 +03:00
yuri
a462158582 category folder style fix 2019-06-05 16:22:35 +03:00
yuri
152b8991d3 email apply filters on sending 2019-06-05 14:15:36 +03:00
yuri
5adcf013d7 fix client manager 2019-06-05 12:56:02 +03:00
yuri
9d6401cbd3 fix iframe css 2019-06-05 12:49:49 +03:00
yuri
9f178bb467 next/prev fix on email remove to trash 2019-06-05 12:45:59 +03:00
yuri
f3b2cc9bcb cleanup 2019-06-05 12:19:10 +03:00
yuri
b18a8c1b2b fix 2019-06-05 12:15:42 +03:00
yuri
30dd7e560b search buttons narrow on small screen 2019-06-05 11:51:57 +03:00
yuri
ca35a4df75 fix dashboard tpl 2019-06-05 11:46:01 +03:00
yuri
fe0a6d1a8b map none label 2019-06-05 11:43:46 +03:00
yuri
f9223087df xlsx export array 2019-06-05 11:26:46 +03:00
yuri
f3980ef9a1 fix theme manager default css 2019-06-05 11:09:50 +03:00
yuri
93553d18fc fix job text filter 2019-06-05 11:05:05 +03:00
yuri
e35561093d css fix 2019-05-20 16:37:43 +03:00
yuri
b0742aa0c6 edit dashboard fix 2019-05-20 15:46:03 +03:00
yuri
efad2b142a font fixes 2019-05-20 15:40:50 +03:00
yuri
362f895e72 client link crossorigin 2019-05-20 15:22:26 +03:00
yuri
224b73eb89 font fix 2019-05-20 15:00:48 +03:00
yuri
c4cf45e7f8 fix preload font 2019-05-20 14:46:28 +03:00
yuri
cb45e57a99 client link noTimestamp 2019-05-20 14:00:50 +03:00
yuri
e7f62f79ef font swap 2019-05-20 13:56:21 +03:00
yuri
6fb21ccd77 client links 2019-05-20 13:51:05 +03:00
yuri
f55d814cbb font display block 2019-05-20 13:50:58 +03:00
yuri
8fa22a6c7d css fix 2019-05-20 13:18:03 +03:00
yuri
b86ca3a4ec stream popover fix 2019-05-20 12:19:54 +03:00
yuri
ab51e70ff4 add dashlet style fix 2019-05-20 12:02:38 +03:00
yuri
0bde83c0e7 css fix 2019-05-20 11:49:04 +03:00
yuri
589754851c font update 2019-05-20 11:47:27 +03:00
yuri
253930ba6d btn-text fix 2019-05-20 10:40:46 +03:00
yuri
09c19bbba5 remove cancel buttons from 2 modals 2019-05-17 16:38:41 +03:00
yuri
ac96ebffa5 btn-text 2019-05-17 16:10:26 +03:00
yuri
f01715452f last viewed date short 2019-05-17 13:18:19 +03:00
yuri
875b01dcd6 lastViewed metadata scopes param 2019-05-17 13:16:07 +03:00
yuri
61def7e332 pdf currency symbol 2019-05-17 12:50:36 +03:00
yuri
86b24ff618 orm field type attribute role 2019-05-17 12:50:27 +03:00
yuri
1d29d74204 cs fix 2019-05-17 12:46:39 +03:00
yuri
2cf820e181 user activities fix and optimization 2019-05-16 16:53:09 +03:00
yuri
bf8ab4dec2 account shipping address copy button hidden 2019-05-16 16:18:41 +03:00
yuri
7b772bde3c cs fix 2019-05-16 15:24:12 +03:00
yuri
dfbad82ba8 version 2019-05-15 14:34:11 +03:00
yuri
84938d88fc fix cleanup deleted records 2019-05-15 14:24:44 +03:00
yuri
ed5f66729d date time getSystemNowString 2019-05-14 16:49:47 +03:00
yuri
ee5218c46d orm entity id is changed fix 2019-05-14 15:48:27 +03:00
yuri
6758d71e2e lead capture skip email address is opted out field 2019-05-13 15:11:58 +03:00
yuri
1eec3d88cf integration test auth method 2019-05-13 12:31:49 +03:00
yuri
34f1c196f1 fix command upgrade msg 2019-05-13 10:43:59 +03:00
yuri
a87e2fb5db css fix 2019-05-13 10:25:57 +03:00
yuri
cecafa5e28 diff only one version from 2019-05-10 16:25:54 +03:00
yuri
a0e5f2d8ec cleanup 2019-05-10 16:06:58 +03:00
yuri
47080f9b78 search view changes 2019-05-10 16:06:41 +03:00
yuri
dd15767263 email is replied for expanded list 2019-05-10 13:37:28 +03:00
yuri
a698085196 list expanded changes 2019-05-10 12:48:06 +03:00
yuri
2d4e1a94a4 add dashlet modal change 2019-05-10 12:02:28 +03:00
yuri
dad49b9ac8 create button icon 2019-05-10 11:57:54 +03:00
yuri
c5c1abaeff Merge branch 'hotfix/5.6.2' of ssh://172.20.0.1/var/git/espo/backend into hotfix/5.6.2 2019-05-10 11:35:12 +03:00
yuri
eb4f933753 css list margin on wide screen 2019-05-10 11:34:49 +03:00
yuri
931f27cb70 css fix 2019-05-10 11:10:22 +03:00
Taras Machyshyn
e6e00d4c7f Bug fixes 2019-05-10 11:05:57 +03:00
yuri
3cf48129c6 css layout centered 2019-05-10 11:04:45 +03:00
yuri
b50bf02e90 updgrate info text 2019-05-09 17:36:02 +03:00
yuri
431e3428b3 fix wysiwyg stripping event handlers 2019-05-09 16:01:25 +03:00
yuri
6d2e056b10 email import not fetched text 2019-05-09 15:24:56 +03:00
yuri
1cbfacc864 Merge branch 'hotfix/5.6.2' of ssh://172.20.0.1/var/git/espo/backend into hotfix/5.6.2 2019-05-09 12:53:59 +03:00
Taras Machyshyn
f2b51d321f Fix 2019-05-09 12:53:32 +03:00
yuri
0fe0b8906c Merge branch 'hotfix/5.6.2' of ssh://172.20.0.1/var/git/espo/backend into hotfix/5.6.2 2019-05-09 11:41:26 +03:00
Taras Machyshyn
0876f03fe4 Merge branch 'hotfix/5.6.2' of ssh://172.20.0.1/var/git/espo/backend into hotfix/5.6.2 2019-05-09 11:41:08 +03:00
yuri
dd239af810 orm metadata index 2019-05-09 11:40:01 +03:00
Taras Machyshyn
5f2fae940c Added index key for ORM metadata 2019-05-09 10:52:55 +03:00
Taras Machyshyn
02f94875b1 Added defined index names 2019-05-09 10:27:39 +03:00
yuri
52536f9803 record service fix 2019-05-08 15:15:50 +03:00
yuri
7f963bd9f3 pass model to view after create 2019-05-08 12:21:28 +03:00
yuri
fcfbd478ca cleanup 2019-05-08 12:15:10 +03:00
yuri
d7164dd588 duplicateDisabled param 2019-05-08 11:51:29 +03:00
yuri
d20b2768e1 service create load additional fields 2019-05-08 11:46:59 +03:00
yuri
68301c52c1 logger cleanup 2019-05-08 11:31:35 +03:00
yuri
57aa9fa817 Merge branch 'hotfix/5.6.2' of ssh://172.20.0.1/var/git/espo/backend into hotfix/5.6.2 2019-05-08 11:13:59 +03:00
Taras Machyshyn
910291796b Merge branch 'hotfix/5.6.2' of ssh://172.20.0.1/var/git/espo/backend into hotfix/5.6.2 2019-05-08 11:13:01 +03:00
Taras Machyshyn
e4a661721f Added setLevel() for logger 2019-05-08 11:08:48 +03:00
yuri
4068a20b8e cleanup 2019-05-08 11:08:38 +03:00
yuri
c3be6db4f3 log changes 2019-05-07 18:28:57 +03:00
yuri
80678c97f0 field removal reload metadata 2019-05-07 16:07:57 +03:00
yuri
711d20f874 typo fixes 2019-05-07 14:53:08 +03:00
yuri
7f299e1aa2 email created by id index 2019-05-07 11:58:59 +03:00
yuri
ab612a3f32 event entity duration on list view fix 2019-05-07 10:57:56 +03:00
yuri
c0c244adc6 email select manager fix 2019-05-06 11:46:57 +03:00
yuri
46a985076e email from name caching 2019-05-03 16:41:09 +03:00
yuri
7f82fab69b email abort xht on select folder 2019-05-03 16:09:20 +03:00
yuri
12ed1b3383 email reset collection on folder change 2019-05-03 16:03:29 +03:00
yuri
eab279dd12 email draft optimization 2019-05-03 15:46:59 +03:00
yuri
e59a927faa email index skip 2019-05-03 15:25:42 +03:00
yuri
1c1cb13c4d fix select manager 2019-05-03 15:25:31 +03:00
yuri
45a37eabe2 orm use index optimization 2019-05-03 15:12:57 +03:00
yuri
bbe6d81aa2 Merge branch 'hotfix/5.6.2' of github.com:espocrm/espocrm into hotfix/5.6.2 2019-05-03 13:12:41 +03:00
Eymen Elkum
5d19b1bb72 fix additional css script (#1304) 2019-05-03 13:12:22 +03:00
yuri
866f05fea5 orm agragate wo joins 2019-05-03 12:55:34 +03:00
yuri
bd22554756 link parent display entity type 2019-05-03 12:20:38 +03:00
yuri
703e170773 cleanup orphan attachments change 2019-05-03 11:55:49 +03:00
yuri
a226242e69 update package.json 2019-05-03 11:22:31 +03:00
yuri
1438f8aa10 add created at fields 2019-05-03 11:09:12 +03:00
yuri
79564bab81 fix cleanup 2019-05-03 11:09:04 +03:00
yuri
5c7dd0b536 fix stream after remove 2019-05-03 11:08:30 +03:00
yuri
7e340b8b78 fix client field manager 2019-05-02 17:50:32 +03:00
yuri
a026dc577c diff fix 2019-05-02 16:14:42 +03:00
yuri
ed6c56629e css fix 2019-05-02 15:38:42 +03:00
yuri
97574707e5 wysiwyg strip all event handlers 2019-05-02 10:50:47 +03:00
yuri
942726eb74 diff fix 2019-05-01 12:17:11 +03:00
yuri
e1eb3f804e op admin stage probability 50 by default 2019-05-01 11:53:36 +03:00
yuri
2133a1390c bad request error msg 2019-05-01 11:48:23 +03:00
yuri
7fab959a2f Merge branch 'hotfix/5.6.2' of ssh://172.20.0.1/var/git/espo/backend into hotfix/5.6.2 2019-05-01 11:36:53 +03:00
yuri
88f64ab82f admin jobs link 2019-05-01 11:36:39 +03:00
Taras Machyshyn
1563d63051 Added params for an extension script 2019-05-01 11:33:04 +03:00
yuri
82026658e2 fix colors 2019-05-01 11:05:52 +03:00
yuri
26eec19a62 chart gray color 2019-05-01 11:01:11 +03:00
yuri
f548db2783 fix export 2019-05-01 10:43:18 +03:00
yuri
2da689e7e9 scope exportFormatList 2019-05-01 10:38:39 +03:00
yuri
8afb4804c3 orm sanitizeSelectAlias fix 2019-05-01 10:33:45 +03:00
yuri
56b8902969 orm fix week number 2019-05-01 10:31:01 +03:00
yuri
95cee6c845 email service get entity fix 2019-05-01 10:15:50 +03:00
yuri
dc58bcbd91 cleanup 2019-04-30 11:18:37 +03:00
yuri
8d3698e6a3 cleanup 2019-04-30 11:17:43 +03:00
yuri
c04526dc11 extension list layout fix 2019-04-30 11:17:05 +03:00
yuri
6a0d622344 event invitation smtp params fix 2019-04-30 11:13:35 +03:00
yuri
74797e0768 css fix 2019-04-30 11:09:41 +03:00
yuri
6fd0851dc9 fix dashboard 2019-04-30 11:00:26 +03:00
yuri
41e2773781 fix dashlet aftet adding 2019-04-30 10:58:50 +03:00
yuri
bf03412bf0 dashlet after adding 2019-04-30 10:49:44 +03:00
yuri
6b25aa1274 flotr fix 2019-04-26 13:50:03 +03:00
yuri
634acdd770 fix tasks meeting date filter 2019-04-25 15:26:07 +03:00
yuri
f2f01adfc9 phone number is opted out attribute 2019-04-25 12:26:07 +03:00
yuri
9cd2d1d21f import default phone number 2019-04-25 12:14:42 +03:00
yuri
fa144847e5 ability to import multiple email addresses 2019-04-25 11:55:03 +03:00
yuri
17138f715b controller before action usage 2019-04-24 15:36:49 +03:00
yuri
0babd05bff record service filter create update input methods 2019-04-24 13:35:47 +03:00
yuri
c5f14e55c5 record service validation order fix 2019-04-24 13:26:11 +03:00
yuri
03918c8ada charts si mul 2019-04-24 11:37:03 +03:00
yuri
80c7008a6c chart fixes 2019-04-24 11:02:02 +03:00
yuri
3d143cd3f0 fix notification badge overlapping 2019-04-23 17:00:32 +03:00
Pomazan Bogdan
365264b393 Правка перевода, логически не подходит (#1298) 2019-04-23 13:39:17 +03:00
yuri
3b4aea4966 email address rep change 2019-04-23 12:02:10 +03:00
yuri
cb77bc9085 list changes 2019-04-23 12:00:04 +03:00
yuri
691aff990b css fix 2019-04-22 15:35:45 +03:00
yuri
0f6ca667d9 vi lang 2019-04-22 13:57:59 +03:00
yuri
ba41229a83 tr lang 2019-04-22 13:57:19 +03:00
yuri
7c9d28098b es_mx lang 2019-04-22 13:56:44 +03:00
yuri
eccf9e4d85 nl lang 2019-04-22 13:55:53 +03:00
yuri
96dffc0b74 it lang 2019-04-22 13:55:06 +03:00
yuri
308d0bfc0c hr lang 2019-04-22 13:54:27 +03:00
yuri
f9dcf2db7d orm order by complex expression 2019-04-22 12:13:07 +03:00
yuri
88798f2b86 orm foreign select for foreign fields 2019-04-19 17:26:50 +03:00
yuri
28ecdc46b6 upgrade text 2019-04-19 13:48:39 +03:00
yuri
6a30031f49 css addition 2019-04-17 13:26:24 +03:00
yuri
d4f23e391f cleanup 2019-04-16 16:05:26 +03:00
yuri
79b7463886 additional css 2019-04-16 16:04:06 +03:00
Yurii Kondratskyi
eddae25d7f fix get acl level method (#1294) 2019-04-16 11:08:53 +03:00
Taras Machyshyn
5e9c85fc06 Merge branch 'hotfix/5.6.2' of ssh://172.20.0.1/var/git/espo/backend into hotfix/5.6.2 2019-04-12 14:34:29 +03:00
Taras Machyshyn
b44adb34ec Metadata changes 2019-04-12 14:34:09 +03:00
yuri
f0f6cac84a fix; php clear_cache.php 2019-04-12 13:25:08 +03:00
yuri
17e87f58c3 css fix 2019-04-11 16:30:41 +03:00
yuri
273698e2fc imap hander case insensitive 2019-04-11 11:46:16 +03:00
yuri
66f88e0e8e fix email address smtp apply case insesitive 2019-04-11 11:44:01 +03:00
yuri
de945008fb fix email send 2019-04-10 16:32:29 +03:00
yuri
5f91505b8e fix typo 2019-04-10 14:49:16 +03:00
yuri
6a25c283ec upgrade info msg 2019-04-10 13:45:02 +03:00
yuri
74fa9289a7 lead capture enhancements 2019-04-10 13:13:35 +03:00
yuri
ab4b975912 version 2019-04-10 11:59:38 +03:00
yuri
2260089301 upgrade message 2019-04-10 11:57:22 +03:00
yuri
9dd35f1e32 opp empty probability for stages 2019-04-10 11:45:47 +03:00
yuri
6b90cee851 fix opcache warning 2019-04-09 11:11:01 +03:00
yuri
6da132c034 fix date time between filter 2019-04-09 11:08:38 +03:00
yuri
bc966a8ea8 fix date time filter 2019-04-08 15:31:20 +03:00
yuri
6eab0c9b75 flotr fix 2019-04-08 15:01:46 +03:00
yuri
cf253fae8a fix 2019-04-08 13:54:57 +03:00
yuri
c0eaa8fb50 attachment multiple link disabled 2019-04-08 13:52:45 +03:00
yuri
a4889de80a image link disabled 2019-04-08 13:52:01 +03:00
yuri
acac599f6b file disabled link 2019-04-08 13:41:54 +03:00
yuri
1436ffa53f opp lead source translation param 2019-04-08 12:28:10 +03:00
yuri
c68a3dd0a6 fix stream panel 2019-04-05 17:03:27 +03:00
yuri
29c42022ef allow empty dashlet title 2019-04-05 16:52:49 +03:00
yuri
dd814c1b5e edit button default style 2019-04-05 16:33:29 +03:00
yuri
7fc2c71f58 fix account contact 2019-04-05 15:49:57 +03:00
yuri
e6a3d3c982 css fix 2019-04-05 15:02:09 +03:00
yuri
46380ee9ff css fix 2019-04-05 14:58:47 +03:00
yuri
1fa3908888 chart mouse track centered 2019-04-05 14:49:51 +03:00
yuri
914e6a1789 fix admin rebuild clear cache 2019-04-04 11:38:28 +03:00
yuri
9a1d073072 many-to-many same entity types 2019-04-04 11:28:28 +03:00
yuri
3712caf7b1 email inline attachments fix 2019-04-03 13:11:45 +03:00
yuri
a770e18bb2 Merge branch 'hotfix/5.6.1' of ssh://172.20.0.1/var/git/espo/backend into hotfix/5.6.1 2019-04-03 11:50:55 +03:00
Taras Machyshyn
5dc0cfb666 NamespaceLoader fix 2019-04-03 11:49:59 +03:00
yuri
e4ab02c77a fix ws 2019-04-02 12:25:47 +03:00
yuri
a0f729cd8d fix ws 2019-04-02 12:04:14 +03:00
yuri
d544b1c264 fix flotr2 2019-04-01 16:31:59 +03:00
yuri
d8549cbebe v 2019-04-01 16:29:30 +03:00
yuri
4ae4734dbf fix flotr2 2019-04-01 16:26:40 +03:00
yuri
cc72de7121 da_DK 2019-03-28 16:09:23 +02:00
711 changed files with 14839 additions and 7826 deletions

View File

@@ -46,6 +46,21 @@ class Note extends \Espo\Core\Acl\Base
return false;
}
public function checkEntityCreate(EntityUser $user, Entity $entity, $data)
{
if ($entity->get('parentId') && $entity->get('parentType')) {
$parent = $this->getEntityManager()->getEntity($entity->get('parentType'), $entity->get('parentId'));
if ($parent) {
if ($this->getAclManager()->checkEntity($user, $parent, 'stream')) {
return true;
}
}
return false;
}
return true;
}
public function checkEntityEdit(EntityUser $user, Entity $entity, $data)
{
if ($user->isAdmin()) {

View File

@@ -41,5 +41,26 @@ class Note extends \Espo\Core\AclPortal\Base
}
return false;
}
}
public function checkEntityCreate(EntityUser $user, Entity $entity, $data)
{
if ($entity->get('type') !== 'Post') return false;
if ($entity->get('type') === 'Post' && $entity->get('targetType')) {
return false;
}
if (!$entity->get('parentId') || !$entity->get('parentType')) {
return false;
}
$parent = $this->getEntityManager()->getEntity($entity->get('parentType'), $entity->get('parentId'));
if ($parent) {
if ($this->getAclManager()->checkEntity($user, $parent, 'stream')) {
return true;
}
}
return false;
}
}

View File

@@ -33,39 +33,38 @@ use \Espo\Core\Exceptions\Forbidden;
class ActionHistoryRecord extends \Espo\Core\Controllers\Record
{
public function actionUpdate($params, $data, $request)
public function beforeUpdate()
{
throw new Forbidden();
}
public function actionCreate($params, $data, $request)
public function beforeCreate()
{
throw new Forbidden();
}
public function actionListLinked($params, $data, $request)
public function beforeListLinked()
{
throw new Forbidden();
}
public function actionMassUpdate($params, $data, $request)
public function beforeMassUpdate()
{
throw new Forbidden();
}
public function actionCreateLink($params, $data, $request)
public function beforeCreateLink()
{
throw new Forbidden();
}
public function actionRemoveLink($params, $data, $request)
public function beforeRemoveLink()
{
throw new Forbidden();
}
public function actionMassDelete($params, $data, $request)
public function beforeMassDelete()
{
throw new Forbidden();
}
}

View File

@@ -40,27 +40,27 @@ class AuthLogRecord extends \Espo\Core\Controllers\Record
}
}
public function actionUpdate($params, $data, $request)
public function beforeUpdate()
{
throw new Forbidden();
}
public function actionMassUpdate($params, $data, $request)
public function beforeMassUpdate()
{
throw new Forbidden();
}
public function actionCreate($params, $data, $request)
public function beforeCreate()
{
throw new Forbidden();
}
public function actionCreateLink($params, $data, $request)
public function beforeCreateLink()
{
throw new Forbidden();
}
public function actionRemoveLink($params, $data, $request)
public function beforeRemoveLink()
{
throw new Forbidden();
}

View File

@@ -80,17 +80,17 @@ class AuthToken extends \Espo\Core\Controllers\Record
throw new Forbidden();
}
public function actionCreate($params, $data, $request)
public function beforeCreate()
{
throw new Forbidden();
}
public function actionCreateLink($params, $data, $request)
public function beforeCreateLink()
{
throw new Forbidden();
}
public function actionRemoveLink($params, $data, $request)
public function beforeRemoveLink()
{
throw new Forbidden();
}

View File

@@ -94,25 +94,6 @@ class Extension extends \Espo\Core\Controllers\Record
return true;
}
public function actionCreate($params, $data, $request)
{
throw new Forbidden();
}
public function actionUpdate($params, $data, $request)
{
throw new Forbidden();
}
public function actionPatch($params, $data, $request)
{
throw new Forbidden();
}
public function actionListLinked($params, $data, $request)
{
throw new Forbidden();
}
public function actionDelete($params, $data, $request)
{
@@ -129,22 +110,42 @@ class Extension extends \Espo\Core\Controllers\Record
return true;
}
public function actionMassUpdate($params, $data, $request)
public function beforeCreate()
{
throw new Forbidden();
}
public function actionMassDelete($params, $data, $request)
public function beforeUpdate()
{
throw new Forbidden();
}
public function actionCreateLink($params, $data, $request)
public function beforePatch()
{
throw new Forbidden();
}
public function actionRemoveLink($params, $data, $request)
public function beforeListLinked()
{
throw new Forbidden();
}
public function beforeMassUpdate()
{
throw new Forbidden();
}
public function beforeMassDelete()
{
throw new Forbidden();
}
public function beforeCreateLink()
{
throw new Forbidden();
}
public function beforeRemoveLink()
{
throw new Forbidden();
}

View File

@@ -47,17 +47,27 @@ class ExternalAccount extends \Espo\Core\Controllers\Record
public function actionList($params, $data, $request)
{
$integrations = $this->getEntityManager()->getRepository('Integration')->find();
$arr = array();
$list = [];
foreach ($integrations as $entity) {
if ($entity->get('enabled') && $this->getMetadata()->get('integrations.' . $entity->id .'.allowUserAccounts')) {
$arr[] = array(
$userAccountAclScope = $this->getMetadata()->get(['integrations', $entity->id, 'userAccountAclScope']);
if ($userAccountAclScope) {
if (!$this->getAcl()->checkScope($userAccountAclScope)) {
continue;
}
}
$list[] = [
'id' => $entity->id
);
];
}
}
return array(
'list' => $arr
);
return [
'list' => $list
];
}
public function actionGetOAuth2Info($params, $data, $request)

View File

@@ -43,27 +43,27 @@ class Import extends \Espo\Core\Controllers\Record
}
}
public function actionPatch($params, $data, $request)
public function beforePatch()
{
throw new BadRequest();
}
public function actionUpdate($params, $data, $request)
public function beforeUpdate()
{
throw new BadRequest();
}
public function actionMassUpdate($params, $data, $request)
public function beforeMassUpdate()
{
throw new BadRequest();
}
public function actionCreateLink($params, $data, $request)
public function beforeCreateLink()
{
throw new BadRequest();
}
public function actionRemoveLink($params, $data, $request)
public function beforeRemoveLink()
{
throw new BadRequest();
}

View File

@@ -41,37 +41,37 @@ class Job extends \Espo\Core\Controllers\Record
}
}
public function actionCreate($params, $data, $request)
public function beforeCreate()
{
throw new Forbidden();
}
public function actionUpdate($params, $data, $request)
public function beforeUpdate()
{
throw new Forbidden();
}
public function actionPatch($params, $data, $request)
public function beforePatch()
{
throw new Forbidden();
}
public function actionListLinked($params, $data, $request)
public function beforeListLinked()
{
throw new Forbidden();
}
public function actionMassUpdate($params, $data, $request)
public function beforeMassUpdate()
{
throw new Forbidden();
}
public function actionCreateLink($params, $data, $request)
public function beforeCreateLink()
{
throw new Forbidden();
}
public function actionRemoveLink($params, $data, $request)
public function beforeRemoveLink()
{
throw new Forbidden();
}

View File

@@ -35,7 +35,7 @@ class Notification extends \Espo\Core\Controllers\Record
{
public static $defaultAction = 'list';
public function actionList($params, $data, $request)
public function getActionList($params, $data, $request, $response)
{
$userId = $this->getUser()->id;
@@ -73,29 +73,28 @@ class Notification extends \Espo\Core\Controllers\Record
return $this->getService('Notification')->markAllRead($userId);
}
public function actionExport($params, $data, $request)
public function beforeExport()
{
throw new Error();
}
public function actionMassUpdate($params, $data, $request)
public function beforeMassUpdate()
{
throw new Error();
}
public function actionCreateLink($params, $data, $request)
public function beforeCreateLink()
{
throw new Error();
}
public function actionRemoveLink($params, $data, $request)
public function beforeRemoveLink()
{
throw new Error();
}
public function actionMerge($params, $data, $request)
public function beforeMerge()
{
throw new Error();
}
}

View File

@@ -40,7 +40,13 @@ class Settings extends \Espo\Core\Controllers\Base
{
$data = $this->getServiceFactory()->create('Settings')->getConfigData();
$data->jsLibs = $this->getMetadata()->get('app.jsLibs');
$data->jsLibs = $this->getMetadata()->get(['app', 'jsLibs']);
unset($data->loginView);
$loginView = $this->getMetadata()->get(['clientDefs', 'App', 'loginView']);
if ($loginView) {
$data->loginView = $loginView;
}
return $data;
}

View File

@@ -113,17 +113,13 @@ class User extends \Espo\Core\Controllers\Record
return $this->getRecordService()->generateNewApiKeyForEntity($data->id)->getValueMap();
}
public function actionCreateLink($params, $data, $request)
public function beforeCreateLink()
{
if (!$this->getUser()->isAdmin()) throw new Forbidden();
return parent::actionCreateLink($params, $data, $request);
}
public function actionRemoveLink($params, $data, $request)
public function beforeRemoveLink($params, $data, $request)
{
if (!$this->getUser()->isAdmin()) throw new Forbidden();
return parent::actionRemoveLink($params, $data, $request);
}
}

View File

@@ -133,7 +133,9 @@ class Application
$slim->run();
} catch (\Exception $e) {
$container->get('output')->processError($e->getMessage(), $e->getCode(), true, $e);
try {
$container->get('output')->processError($e->getMessage(), $e->getCode(), true, $e);
} catch (\Slim\Exception\Stop $e) {}
}
}

View File

@@ -48,7 +48,7 @@ class Upgrade extends Base
return;
}
fwrite(\STDOUT, "EspoCRM will be upgaded to version {$nextVersion} now. Type 'Y' to continue.\n");
fwrite(\STDOUT, "EspoCRM will be upgraded to version {$nextVersion} now. Enter [Y] to continue.\n");
if (!$this->confirm()) {
echo "Upgrade canceled.\n";

View File

@@ -115,7 +115,7 @@ class Container
$rotation = $config->get('logger.rotation', true);
$log = new \Espo\Core\Utils\Log('Espo');
$levelCode = $log->getLevelCode($config->get('logger.level', 'WARNING'));
$levelCode = $log::toMonologLevel($config->get('logger.level', 'WARNING'));
if ($rotation) {
$maxFileNumber = $config->get('logger.maxFileNumber', 30);
@@ -127,7 +127,7 @@ class Container
$errorHandler = new \Monolog\ErrorHandler($log);
$errorHandler->registerExceptionHandler(null, false);
$errorHandler->registerErrorHandler(array(), false);
$errorHandler->registerErrorHandler([], false);
return $log;
}

View File

@@ -119,6 +119,7 @@ class DataManager
$metadata->init(true);
$ormData = $this->getContainer()->get('ormMetadata')->getData(true);
$this->getContainer()->get('entityManager')->setMetadata($ormData);
$this->updateCacheTimestamp();

View File

@@ -0,0 +1,35 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2019 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: https://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\Exceptions;
class ForbiddenSilent extends Forbidden
{
public $logLevel = 'notice';
}

View File

@@ -0,0 +1,35 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2019 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: https://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\Exceptions;
class NotFoundSilent extends NotFound
{
public $logLevel = 'notice';
}

View File

@@ -491,7 +491,14 @@ class Xlsx extends \Espo\Core\Injectable
$sheet->setCellValue("$col$rowNumber", $value);
}
} else if ($type == 'multiEnum' || $type == 'array') {
if (!empty($row[$name])) {
$array = json_decode($row[$name]);
if (is_array($array)) {
$value = implode(', ', $array);
$sheet->setCellValue("$col$rowNumber", $value, \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING);
}
}
} else {
if (array_key_exists($name, $row)) {
$sheet->setCellValueExplicit("$col$rowNumber", $row[$name], \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING);

View File

@@ -69,7 +69,7 @@ class ClientManager
$externalAccountEntity = $this->clientMap[$hash]['externalAccountEntity'];
$externalAccountEntity->set('accessToken', $data['accessToken']);
$externalAccountEntity->set('tokenType', $data['tokenType']);
$this->getEntityManager()->saveEntity($externalAccountEntity);
$this->getEntityManager()->saveEntity($externalAccountEntity, ['isTokenRenewal' => true]);
}
}
@@ -87,7 +87,12 @@ class ClientManager
$className = $this->getMetadata()->get("integrations.{$integration}.clientClassName");
$redirectUri = $this->getConfig()->get('siteUrl') . '?entryPoint=oauthCallback'; // TODO move to client class
$redirectUri = $this->getConfig()->get('siteUrl') . '?entryPoint=oauthCallback';
$redirectUriPath = $this->getMetadata()->get(['integrations', $integration, 'params', 'redirectUriPath']);
if ($redirectUriPath) {
$redirectUri = rtrim($this->getConfig()->get('siteUrl'), '/') . '/' . $redirectUriPath;
}
if (!$externalAccountEntity) {
throw new Error("External Account {$integration} not found for {$userId}");

View File

@@ -187,7 +187,11 @@ class Client
$curlOptHttpHeader = array();
foreach ($httpHeaders as $key => $value) {
$curlOptHttpHeader[] = "{$key}: {$value}";
if (is_int($key) && !is_string($key)) {
$curlOptHttpHeader[] = $value;
continue;
}
$curlOptHttpHeader[] = "{$key}: {$value}";
}
$curlOptions[CURLOPT_HTTPHEADER] = $curlOptHttpHeader;

View File

@@ -147,6 +147,7 @@ class Htmlizer
}
$type = $entity->getAttributeType($attribute);
$fieldType = $entity->getAttributeParam($attribute, 'fieldType');
if ($type == Entity::DATETIME) {
if (!empty($data[$attribute])) {
@@ -194,6 +195,14 @@ class Htmlizer
unset($data[$attribute]);
}
if ($fieldType === 'currency' && $this->metadata) {
if ($entity->getAttributeParam($attribute, 'attributeRole') === 'currency') {
if ($currencyValue = $data[$attribute]) {
$data[$attribute . 'Symbol'] = $this->metadata->get(['app', 'currency', 'symbolMap', $currencyValue]);
}
}
}
if (array_key_exists($attribute, $data)) {
$keyRaw = $attribute . '_RAW';
$data[$keyRaw] = $data[$attribute];

View File

@@ -201,7 +201,7 @@ class Importer
return false;
}
} else {
$email->set('body', '(Not fetched)');
$email->set('body', 'Not fetched. The email size exceeds the limit.');
$email->set('isHtml', false);
}

View File

@@ -284,6 +284,7 @@ class Sender
$contents = $a->get('contents');
} else {
$fileName = $this->getEntityManager()->getRepository('Attachment')->getFilePath($a);
if (!is_file($fileName)) continue;
$contents = file_get_contents($fileName);
}
$attachment = new MimePart($contents);
@@ -303,6 +304,7 @@ class Sender
$contents = $a->get('contents');
} else {
$fileName = $this->getEntityManager()->getRepository('Attachment')->getFilePath($a);
if (!is_file($fileName)) continue;
$contents = file_get_contents($fileName);
}
$attachment = new MimePart($contents);

View File

@@ -50,9 +50,9 @@ class Application extends \Espo\Core\Application
$portal = $this->getContainer()->get('entityManager')->getEntity('Portal', $portalId);
if (!$portal) {
$portal = $this->getContainer()->get('entityManager')->getRepository('Portal')->where(array(
$portal = $this->getContainer()->get('entityManager')->getRepository('Portal')->where([
'customId' => $portalId
))->findOne();
])->findOne();
}
if (!$portal) {

View File

@@ -1038,7 +1038,8 @@ class Base
$where['type'] = 'between';
$dt = new \DateTime($value, new \DateTimeZone($timeZone));
$dtTo = clone $dt;
$dtTo->modify('+1 day -1 second');
if (strlen($value) <= 10)
$dtTo->modify('+1 day -1 second');
$dt->setTimezone(new \DateTimeZone('UTC'));
$dtTo->setTimezone(new \DateTimeZone('UTC'));
$from = $dt->format($format);
@@ -1054,7 +1055,9 @@ class Base
case 'after':
$where['type'] = 'after';
$dt = new \DateTime($value, new \DateTimeZone($timeZone));
$dt->modify('+1 day -1 second');
if (strlen($value) <= 10)
$dt->modify('+1 day -1 second');
$dt->setTimezone(new \DateTimeZone('UTC'));
$where['value'] = $dt->format($format);
break;
@@ -1068,7 +1071,8 @@ class Base
$dt = new \DateTime($value[1], new \DateTimeZone($timeZone));
$dt->setTimezone(new \DateTimeZone('UTC'));
$dt->modify('-1 second');
if (strlen($value[1]) <= 10)
$dt->modify('+1 day -1 second');
$to = $dt->format($format);
$where['value'] = [$from, $to];
@@ -1840,6 +1844,35 @@ class Base
return false;
}
public function hasLinkJoined($join, array &$result)
{
if (in_array($join, $result['joins'])) {
return true;
}
foreach ($result['joins'] as $item) {
if (is_array($item) && count($item) > 1) {
if ($item[0] == $join) {
return true;
}
}
}
if (in_array($join, $result['leftJoins'])) {
return true;
}
foreach ($result['leftJoins'] as $item) {
if (is_array($item) && count($item) > 1) {
if ($item[0] == $join) {
return true;
}
}
}
return false;
}
public function addJoin($join, array &$result)
{
if (empty($result['joins'])) {
@@ -1965,7 +1998,7 @@ class Base
if ($useFullTextSearch) {
foreach ($fieldList as $field) {
if (strpos($item, '.') !== false) {
if (strpos($field, '.') !== false) {
continue;
}

View File

@@ -48,4 +48,23 @@ class Event extends \Espo\Services\Record
$reminders = $this->getRepository()->getEntityReminderList($entity);
$entity->set('reminders', $reminders);
}
public function getSelectAttributeList($params)
{
$attributeList = parent::getSelectAttributeList($params);
if (is_array($attributeList)) {
if (array_key_exists('select', $params)) {
$passedAttributeList = $params['select'];
if (in_array('duration', $passedAttributeList)) {
if (!in_array('dateStart', $attributeList)) {
$attributeList[] = 'dateStart';
}
if (!in_array('dateEnd', $attributeList)) {
$attributeList[] = 'dateEnd';
}
}
}
}
return $attributeList;
}
}

View File

@@ -1,8 +1,7 @@
{
"fields": {
"billingAddress": "Betalingsadresse",
"shippingAddress": "Leveringsadresse",
"website": "Website"
"shippingAddress": "Leveringsadresse"
},
"links": {
"meetings": "Møder",

View File

@@ -4,8 +4,10 @@
"dateStart": "Startdato",
"dateEnd": "Slutdato",
"duration": "Varighed",
"status": "Status",
"reminders": "Underretninger"
"reminders": "Underretninger",
"dateStartDate": "Start dato (hele dagen)",
"dateEndDate": "Slut dato (hele dagen)",
"isAllDay": "Hele dagen"
},
"links": {
"parent": "Henviser til"
@@ -20,7 +22,6 @@
"labels": {
"Create {entityType}": "Opret {entityTypeTranslated}",
"Schedule {entityType}": "Planlæg {entityTypeTranslated}",
"Log {entityType}": "Log {entityTypeTranslated}",
"Set Held": "Marker som Gennemført",
"Set Not Held": "Marker som Ikke Gennemført"
},

View File

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

View File

@@ -1,5 +1,10 @@
{
"labels": {
"Create {entityType}": "Klant {entityTypeTranslated}"
}
}
"links": {
"meetings": "Vergaderingen",
"calls": "Gesprekken",
"tasks": "Taken"
},
"labels": {
"Create {entityType}": "Klant {entityTypeTranslated}"
}
}

View File

@@ -1,5 +1,14 @@
{
"labels": {
"Create {entityType}": "Klant {entityTypeTranslated}"
}
}
"fields": {
"billingAddress": "Facturatie adres",
"shippingAddress": "Verzending adres"
},
"links": {
"meetings": "vergaderingen",
"calls": "gesprekken",
"tasks": "taken"
},
"labels": {
"Create {entityType}": "Klant {entityTypeTranslated}"
}
}

View File

@@ -1,5 +1,37 @@
{
"labels": {
"Create {entityType}": "Klant {entityTypeTranslated}"
"fields": {
"parent": "Ouder",
"dateStart": "Datum Start",
"dateEnd": "Datum einde",
"duration": "Looptijd",
"status": "staat",
"reminders": "herinneringen",
"dateStartDate": "Startdatum (de hele dag)",
"dateEndDate": "Datum einde (hele dag)",
"isAllDay": "Is de hele dag"
},
"links": {
"parent": "Ouder"
},
"options": {
"status": {
"Planned": "Gepland",
"Held": "Vastgehouden",
"Not Held": "Niet vastgehouden"
}
}
},
"labels": {
"Create {entityType}": "Klant {entityTypeTranslated}",
"Schedule {entityType}": "Schema {entityTypeTranslated}",
"Set Held": "Held instellen",
"Set Not Held": "Set not Held"
},
"massActions": {
"setHeld": "Held instellen",
"setNotHeld": "Set not Held"
},
"presetFilters": {
"planned": "Gepland",
"todays": "Vandaag"
}
}

View File

@@ -1,5 +1,13 @@
{
"labels": {
"Create {entityType}": "Klant {entityTypeTranslated}"
}
}
"fields": {
"address": "Adres"
},
"links": {
"meetings": "Vergaderingen",
"calls": "Gesprekken",
"tasks": "Taken"
},
"labels": {
"Create {entityType}": "Klant {entityTypeTranslated}"
}
}

View File

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

View File

@@ -1,5 +1,9 @@
{
"labels": {
"Create {entityType}": "Criar {entityTypeTranslated}"
}
}
"links": {
"meetings": "Reuniões",
"tasks": "Tarefas"
},
"labels": {
"Create {entityType}": "Criar {entityTypeTranslated}"
}
}

View File

@@ -1,5 +1,13 @@
{
"labels": {
"Create {entityType}": "Criar {entityTypeTranslated}"
}
}
"fields": {
"billingAddress": "Endereço de Cobrança",
"shippingAddress": "Endereço de Entrega"
},
"links": {
"meetings": "Reuniões",
"tasks": "Tarefas"
},
"labels": {
"Create {entityType}": "Criar {entityTypeTranslated}"
}
}

View File

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

View File

@@ -1,5 +1,12 @@
{
"labels": {
"Create {entityType}": "Criar {entityTypeTranslated}"
}
}
"fields": {
"address": "Endereço"
},
"links": {
"meetings": "Reuniões",
"tasks": "Tarefas"
},
"labels": {
"Create {entityType}": "Criar {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1,8 @@
{
"fields": {
"status": "Tình trạng"
},
"presetFilters": {
"todays": "Hôm nay"
}
}

View File

@@ -0,0 +1,5 @@
{
"fields": {
"address": "Địa chỉ"
}
}

View File

@@ -61,6 +61,8 @@ abstract class Base
protected $packagePostfix = 'z';
protected $scriptParams = [];
/**
* Directory name of files in a package
*/
@@ -291,7 +293,7 @@ abstract class Base
$script = new $scriptName();
try {
$script->run($this->getContainer());
$script->run($this->getContainer(), $this->scriptParams);
} catch (\Exception $e) {
$this->throwErrorAndRemovePackage($e->getMessage());
}

View File

@@ -39,6 +39,8 @@ class Install extends \Espo\Core\Upgrades\Actions\Base\Install
{
$this->findExtension();
if (!$this->isNew()) {
$this->scriptParams['isUpgrade'] = true;
$this->compareVersion();
$this->uninstallExtension();
$this->deleteExtension();

View File

@@ -71,22 +71,62 @@ class Output
echo $data;
}
public function processError(string $message = 'Error', int $code = 500, bool $toPrint = false, $exception = null)
public function processError(string $message = 'Error', int $statusCode = 500, bool $toPrint = false, $exception = null)
{
$currentRoute = $this->getSlim()->router()->getCurrentRoute();
if (isset($currentRoute)) {
$inputData = $this->getSlim()->request()->getBody();
$inputData = $this->clearPasswords($inputData);
$GLOBALS['log']->error('API ['.$this->getSlim()->request()->getMethod().']:'.$currentRoute->getPattern().', Params:'.print_r($currentRoute->getParams(), true).', InputData: '.$inputData.' - '.$message);
$routePattern = $currentRoute->getPattern();
$routeParams = $currentRoute->getParams();
$method = $this->getSlim()->request()->getMethod();
$logMessage = "API ($statusCode) ";
$logMessageItemList = [];
if ($message) $logMessageItemList[] = $message;
$logMessageItemList[] .= "$method " . $_SERVER['REQUEST_URI'];
if ($inputData) $logMessageItemList[] = "Input data: " . $inputData;
if ($routePattern) $logMessageItemList[] = "Route pattern: ". $routePattern;
if (!empty($routeParams)) $logMessageItemList[] = "Route params: ". print_r($routeParams, true);
$logMessage .= implode("; ", $logMessageItemList);
$GLOBALS['log']->log('debug', $logMessage);
}
$this->displayError($message, $code, $toPrint, $exception);
$this->displayError($message, $statusCode, $toPrint, $exception);
}
public function displayError(string $text, int $statusCode = 500, bool $toPrint = false, $exception = null)
{
$GLOBALS['log']->error('Display Error: '.$text.', Code: '.$statusCode.' URL: '.$_SERVER['REQUEST_URI']);
$logLevel = 'error';
$messageLineFile = null;
if ($exception) {
$messageLineFile = 'line: ' . $exception->getLine() . ', file: ' . $exception->getFile();
}
if ($exception && !empty($exception->logLevel)) {
$logLevel = $exception->logLevel;
}
$logMessageItemList = [];
if ($text) $logMessageItemList[] = "{$text}";
if (!empty($this->slim)) {
$logMessageItemList[] = $this->getSlim()->request()->getMethod() . ' ' .$_SERVER['REQUEST_URI'];
}
if ($messageLineFile) {
$logMessageItemList[] = $messageLineFile;
}
$logMessage = "($statusCode) " . implode("; ", $logMessageItemList);
$GLOBALS['log']->log($logLevel, $logMessage);
ob_clean();

View File

@@ -195,6 +195,9 @@ class NamespaceLoader
if (file_exists($this->vendorNamespacesCacheFile) && $this->getConfig()->get('useCache')) {
$this->vendorNamespaces = $this->getFileManager()->getPhpContents($this->vendorNamespacesCacheFile);
if (!is_array($this->vendorNamespaces)) {
$this->vendorNamespaces = [];
}
}
}

View File

@@ -98,19 +98,45 @@ class ClientManager
if ($isDeveloperMode) {
$useCache = $this->getConfig()->get('useCacheInDeveloperMode');
$jsFileList = $this->getMetadata()->get(['app', 'client', 'developerModeScriptList']);
$jsFileList = $this->getMetadata()->get(['app', 'client', 'developerModeScriptList'], []);
$loaderCacheTimestamp = 'null';
} else {
$useCache = $this->getConfig()->get('useCache');
$jsFileList = $this->getMetadata()->get(['app', 'client', 'scriptList']);
$jsFileList = $this->getMetadata()->get(['app', 'client', 'scriptList'], []);
$loaderCacheTimestamp = $cacheTimestamp;
}
$cssFileList = $this->getMetadata()->get(['app', 'client', 'cssList'], []);
$linkList = $this->getMetadata()->get(['app', 'client', 'linkList'], []);
$scriptsHtml = '';
foreach ($jsFileList as $jsFile) {
$src = $this->basePath . $jsFile . '?r=' . $cacheTimestamp;
$scriptsHtml .= ' ' .
'<script type="text/javascript" src="'.$src.'" data-base-path="'.$this->basePath.'"></script>' . "\n";
$scriptsHtml .= "\n " .
"<script type=\"text/javascript\" src=\"{$src}\" data-base-path=\"{$this->basePath}\"></script>";
}
$additionalStyleSheetsHtml = '';
foreach ($cssFileList as $cssFile) {
$src = $this->basePath . $cssFile . '?r=' . $cacheTimestamp;
$additionalStyleSheetsHtml .= "\n <link rel=\"stylesheet\" href=\"{$src}\">";
}
$linksHtml = '';
foreach ($linkList as $item) {
$href = $this->basePath . $item['href'];
if (empty($item['noTimestamp'])) {
$href .= '?r=' . $cacheTimestamp;
}
$as = $item['as'] ?? '';
$rel = $item['rel'] ?? '';
$type = $item['type'] ?? '';
$additinalPlaceholder = '';
if (!empty($item['crossorigin'])) {
$additinalPlaceholder .= ' crossorigin';
}
$linksHtml .= "\n <link rel=\"{$rel}\" href=\"{$href}\" as=\"{$as}\" as=\"{$type}\"{$additinalPlaceholder}>";
}
$data = [
@@ -124,7 +150,9 @@ class ClientManager
'basePath' => $this->basePath,
'useCache' => $useCache ? 'true' : 'false',
'appClientClassName' => 'app',
'scriptsHtml' => $scriptsHtml
'scriptsHtml' => $scriptsHtml,
'additionalStyleSheetsHtml' => $additionalStyleSheetsHtml,
'linksHtml' => $linksHtml,
];
$html = file_get_contents($htmlFilePath);

View File

@@ -31,6 +31,7 @@ namespace Espo\Core\Utils\Database\Orm;
use Espo\Core\Utils\Util;
use Espo\ORM\Entity;
use Espo\Core\Utils\Database\Schema\Utils as SchemaUtils;
class Converter
{
@@ -90,7 +91,7 @@ class Converter
'select' => 'select',
'orderBy' => 'orderBy',
'where' => 'where',
'storeArrayValues' => 'storeArrayValues'
'storeArrayValues' => 'storeArrayValues',
);
protected $idParams = array(
@@ -188,10 +189,8 @@ class Converter
{
$ormMetadata = array();
$ormMetadata[$entityName] = array(
'fields' => array(
),
'relations' => array(
)
'fields' => [],
'relations' => []
);
foreach ($this->permittedEntityOptions as $optionName) {
@@ -208,6 +207,7 @@ class Converter
$ormMetadata = Util::merge($ormMetadata, $convertedLinks);
$this->applyFullTextSearch($ormMetadata, $entityName);
$this->applyIndexes($ormMetadata, $entityName);
if (!empty($entityMetadata['collection']) && is_array($entityMetadata['collection'])) {
$collectionDefs = $entityMetadata['collection'];
@@ -291,7 +291,7 @@ class Converter
*/
protected function convertFields($entityName, &$entityMetadata)
{
//List of unmerged fields with default field defenitions in $outputMeta
//List of unmerged fields with default field definitions in $outputMeta
$unmergedFields = array(
'name',
);
@@ -344,7 +344,7 @@ class Converter
}
/**
* Correct fields defenitions based on \Espo\Custom\Core\Utils\Database\Orm\Fields
* Correct fields definitions based on \Espo\Custom\Core\Utils\Database\Orm\Fields
*
* @param array $ormMetadata
*
@@ -506,6 +506,10 @@ class Converter
}
}
if (isset($fieldParams['type'])) {
$values['fieldType'] = $fieldParams['type'];
}
return $values;
}
@@ -552,4 +556,18 @@ class Converter
];
}
}
protected function applyIndexes(&$ormMetadata, $entityType)
{
if (!isset($ormMetadata[$entityType]['indexes'])) {
return;
}
foreach ($ormMetadata[$entityType]['indexes'] as $indexName => &$indexData) {
if (!isset($indexData['key'])) {
$indexType = SchemaUtils::getIndexTypeByIndexDefs($indexData);
$indexData['key'] = SchemaUtils::generateIndexName($indexName, $indexType);
}
}
}
}

View File

@@ -100,6 +100,8 @@ class Currency extends Base
'sql' => $converedFieldName . " {direction}",
'leftJoins' => $leftJoins,
],
'attributeRole' => 'valueConverted',
'fieldType' => 'currency',
];
$defs[$entityType]['fields'][$fieldName]['orderBy'] = [
@@ -108,6 +110,12 @@ class Currency extends Base
];
}
$defs[$entityType]['fields'][$fieldName]['attributeRole'] = 'value';
$defs[$entityType]['fields'][$fieldName]['fieldType'] = 'currency';
$defs[$entityType]['fields'][$fieldName . 'Currency']['attributeRole'] = 'currency';
$defs[$entityType]['fields'][$fieldName . 'Currency']['fieldType'] = 'currency';
return $defs;
}
}

View File

@@ -31,25 +31,47 @@ namespace Espo\Core\Utils\Database\Orm\Fields;
class Email extends Base
{
protected function load($fieldName, $entityName)
protected function load($fieldName, $entityType)
{
return array(
$entityName => array(
'fields' => array(
$fieldName => array(
$foreignJoinAlias = "{$fieldName}{$entityType}Foreign";
$foreignJoinMiddleAlias = "{$fieldName}{$entityType}ForeignMiddle";
return [
$entityType => [
'fields' => [
$fieldName => [
'select' => [
'sql' => 'emailAddresses.name',
'leftJoins' => [['emailAddresses', 'emailAddresses', ['primary' => 1]]],
],
'selectForeign' => [
'sql' => "{$foreignJoinAlias}.name",
'leftJoins' => [
[
'EntityEmailAddress',
$foreignJoinMiddleAlias,
[
"{$foreignJoinMiddleAlias}.entityId:" => "{alias}.id",
"{$foreignJoinMiddleAlias}.primary" => 1,
]
],
[
'EmailAddress',
$foreignJoinAlias,
[
"{$foreignJoinAlias}.id:" => "{$foreignJoinMiddleAlias}.emailAddressId",
]
]
],
],
'fieldType' => 'email',
'where' =>
array (
'LIKE' => \Espo\Core\Utils\Util::toUnderScore($entityName) . ".id IN (
'where' => [
'LIKE' => \Espo\Core\Utils\Util::toUnderScore($entityType) . ".id IN (
SELECT entity_id
FROM entity_email_address
JOIN email_address ON email_address.id = entity_email_address.email_address_id
WHERE
entity_email_address.deleted = 0 AND entity_email_address.entity_type = '{$entityName}' AND
entity_email_address.deleted = 0 AND entity_email_address.entity_type = '{$entityType}' AND
email_address.deleted = 0 AND email_address.lower LIKE {value}
)",
'=' => array(
@@ -82,31 +104,34 @@ class Email extends Base
'sql' => 'emailAddressesMultiple.lower IS NOT NULL',
'distinct' => true
)
),
],
'orderBy' => [
'sql' => 'emailAddresses.lower {direction}',
'leftJoins' => [['emailAddresses', 'emailAddresses', ['primary' => 1]]],
],
),
$fieldName .'Data' => array(
],
$fieldName .'Data' => [
'type' => 'text',
'notStorable' => true
),
$fieldName .'IsOptedOut' => array(
'notStorable' => true,
'notExportable' => true,
],
$fieldName .'IsOptedOut' => [
'type' => 'bool',
'notStorable' => true,
'select' => 'emailAddresses.opt_out',
'where' => [
'= TRUE' => [
'sql' => 'emailAddresses.opt_out = true AND emailAddresses.opt_out IS NOT NULL'
'sql' => 'emailAddresses.opt_out = true AND emailAddresses.opt_out IS NOT NULL',
'leftJoins' => [['emailAddresses', 'emailAddresses', ['primary' => 1]]],
],
'= FALSE' => [
'sql' => 'emailAddresses.opt_out = false OR emailAddresses.opt_out IS NULL'
'sql' => 'emailAddresses.opt_out = false OR emailAddresses.opt_out IS NULL',
'leftJoins' => [['emailAddresses', 'emailAddresses', ['primary' => 1]]],
]
],
'orderBy' => 'emailAddresses.opt_out {direction}'
)
),
]
],
'relations' => [
'emailAddresses' => [
'type' => 'manyMany',
@@ -117,7 +142,7 @@ class Email extends Base
'emailAddressId'
],
'conditions' => [
'entityType' => $entityName
'entityType' => $entityType
],
'additionalColumns' => [
'entityType' => [
@@ -131,7 +156,7 @@ class Email extends Base
]
]
]
)
);
]
];
}
}

View File

@@ -35,25 +35,30 @@ class Link extends Base
{
$fieldParams = $this->getFieldParams();
$data = array(
$entityName => array (
'fields' => array(
$fieldName.'Id' => array(
$data = [
$entityName => [
'fields' => [
$fieldName.'Id' => [
'type' => 'foreignId',
'index' => $fieldName
),
$fieldName.'Name' => array(
'index' => $fieldName,
'attributeRole' => 'id',
'fieldType' => 'link',
],
$fieldName.'Name' => [
'type' => 'varchar',
'notStorable' => true
)
)
),
'unset' => array(
$entityName => array(
'fields.'.$fieldName
)
)
);
'notStorable' => true,
'attributeRole' => 'name',
'fieldType' => 'link',
]
]
],
'unset' => [
$entityName => [
'fields.' . $fieldName
]
]
];
if (!empty($fieldParams['notStorable'])) {
$data[$entityName]['fields'][$fieldName.'Id']['notStorable'] = true;
}
@@ -64,4 +69,4 @@ class Link extends Base
return $data;
}
}
}

View File

@@ -41,12 +41,16 @@ class LinkMultiple extends Base
'notStorable' => true,
'isLinkMultipleIdList' => true,
'relation' => $fieldName,
'isUnordered' => true
'isUnordered' => true,
'attributeRole' => 'idList',
'fieldType' => 'linkMultiple',
],
$fieldName.'Names' => [
'type' => 'jsonObject',
'notStorable' => true,
'isLinkMultipleNameMap' => true
'isLinkMultipleNameMap' => true,
'attributeRole' => 'nameMap',
'fieldType' => 'linkMultiple',
]
]
],
@@ -71,7 +75,8 @@ class LinkMultiple extends Base
$data[$entityName]['fields'][$fieldName . 'Columns'] = [
'type' => 'jsonObject',
'notStorable' => true,
'columns' => $columns
'columns' => $columns,
'attributeRole' => 'columnsMap',
];
}

View File

@@ -33,33 +33,39 @@ class LinkParent extends Base
{
protected function load($fieldName, $entityName)
{
$data = array(
$entityName => array (
'fields' => array(
$fieldName.'Id' => array(
$data = [
$entityName => [
'fields' => [
$fieldName.'Id' => [
'type' => 'foreignId',
'index' => $fieldName
),
$fieldName.'Type' => array(
'index' => $fieldName,
'attributeRole' => 'id',
'fieldType' => 'linkParent',
],
$fieldName.'Type' => [
'type' => 'foreignType',
'notNull' => false,
'index' => $fieldName,
'len' => 100
),
$fieldName.'Name' => array(
'len' => 100,
'attributeRole' => 'type',
'fieldType' => 'linkParent',
],
$fieldName.'Name' => [
'type' => 'varchar',
'notStorable' => true,
'relation' => $fieldName,
'isParentName' => true
)
)
),
'unset' => array(
$entityName => array(
'isParentName' => true,
'attributeRole' => 'name',
'fieldType' => 'linkParent',
]
]
],
'unset' => [
$entityName => [
'fields.'.$fieldName
)
)
);
]
]
];
$fieldParams = $this->getFieldParams();
@@ -72,4 +78,4 @@ class LinkParent extends Base
return $data;
}
}
}

View File

@@ -31,25 +31,48 @@ namespace Espo\Core\Utils\Database\Orm\Fields;
class Phone extends Base
{
protected function load($fieldName, $entityName)
protected function load($fieldName, $entityType)
{
return array(
$entityName => array(
'fields' => array(
$foreignJoinAlias = "{$fieldName}{$entityType}Foreign";
$foreignJoinMiddleAlias = "{$fieldName}{$entityType}ForeignMiddle";
return [
$entityType => [
'fields' => [
$fieldName => array(
'select' => [
'sql' => 'phoneNumbers.name',
'leftJoins' => [['phoneNumbers', 'phoneNumbers', ['primary' => 1]]],
],
'selectForeign' => [
'sql' => "{$foreignJoinAlias}.name",
'leftJoins' => [
[
'EntityPhoneNumber',
$foreignJoinMiddleAlias,
[
"{$foreignJoinMiddleAlias}.entityId:" => "{alias}.id",
"{$foreignJoinMiddleAlias}.primary" => 1,
]
],
[
'PhoneNumber',
$foreignJoinAlias,
[
"{$foreignJoinAlias}.id:" => "{$foreignJoinMiddleAlias}.phoneNumberId",
]
]
],
],
'fieldType' => 'phone',
'where' =>
array (
'LIKE' => \Espo\Core\Utils\Util::toUnderScore($entityName) . ".id IN (
'LIKE' => \Espo\Core\Utils\Util::toUnderScore($entityType) . ".id IN (
SELECT entity_id
FROM entity_phone_number
JOIN phone_number ON phone_number.id = entity_phone_number.phone_number_id
WHERE
entity_phone_number.deleted = 0 AND entity_phone_number.entity_type = '{$entityName}' AND
entity_phone_number.deleted = 0 AND entity_phone_number.entity_type = '{$entityType}' AND
phone_number.deleted = 0 AND phone_number.name LIKE {value}
)",
'=' => array(
@@ -90,18 +113,36 @@ class Phone extends Base
),
$fieldName .'Data' => array(
'type' => 'text',
'notStorable' => true
'notStorable' => true,
'notExportable' => true,
),
$fieldName .'IsOptedOut' => [
'type' => 'bool',
'notStorable' => true,
'select' => 'phoneNumbers.opt_out',
'where' => [
'= TRUE' => [
'sql' => 'phoneNumbers.opt_out = true AND phoneNumbers.opt_out IS NOT NULL',
'leftJoins' => [['phoneNumbers', 'phoneNumbers', ['primary' => 1]]],
],
'= FALSE' => [
'sql' => 'phoneNumbers.opt_out = false OR phoneNumbers.opt_out IS NULL',
'leftJoins' => [['phoneNumbers', 'phoneNumbers', ['primary' => 1]]],
]
],
'orderBy' => 'phoneNumbers.opt_out {direction}'
],
$fieldName . 'Numeric' => [
'type' => 'varchar',
'notStorable' => true,
'notExportable' => true,
'where' => [
'LIKE' => \Espo\Core\Utils\Util::toUnderScore($entityName) . ".id IN (
'LIKE' => \Espo\Core\Utils\Util::toUnderScore($entityType) . ".id IN (
SELECT entity_id
FROM entity_phone_number
JOIN phone_number ON phone_number.id = entity_phone_number.phone_number_id
WHERE
entity_phone_number.deleted = 0 AND entity_phone_number.entity_type = '{$entityName}' AND
entity_phone_number.deleted = 0 AND entity_phone_number.entity_type = '{$entityType}' AND
phone_number.deleted = 0 AND phone_number.numeric LIKE {value}
)",
'=' => [
@@ -134,9 +175,9 @@ class Phone extends Base
'sql' => 'phoneNumbersNumericMultiple.numeric IS NOT NULL',
'distinct' => true
]
]
],
]
),
],
'relations' => [
'phoneNumbers' => [
'type' => 'manyMany',
@@ -147,7 +188,7 @@ class Phone extends Base
'phoneNumberId'
],
'conditions' => [
'entityType' => $entityName
'entityType' => $entityType
],
'additionalColumns' => [
'entityType' => [
@@ -161,7 +202,7 @@ class Phone extends Base
]
]
]
)
);
]
];
}
}

View File

@@ -35,20 +35,29 @@ class Attachments extends HasChildren
{
$parentRelation = parent::load($linkName, $entityName);
$relation = array(
$entityName => array (
'fields' => array(
$linkName.'Types' => array(
$relation = [
$entityName => [
'fields' => [
$linkName.'Types' => [
'type' => 'jsonObject',
'notStorable' => true,
),
),
),
);
],
],
'relations' => [
$linkName => [
'conditions' => [
'OR' => [
['field' => null],
['field' => $linkName],
],
],
],
],
],
];
$relation = \Espo\Core\Utils\Util::merge($parentRelation, $relation);
return $relation;
}
}

View File

@@ -33,7 +33,7 @@ use Espo\Core\Utils\Util;
class ManyMany extends Base
{
protected function load($linkName, $entityName)
protected function load($linkName, $entityType)
{
$foreignEntityName = $this->getForeignEntityName();
$foreignLinkName = $this->getForeignLinkName();
@@ -43,15 +43,28 @@ class ManyMany extends Base
if (!empty($linkParams['relationName'])) {
$relationName = $linkParams['relationName'];
} else {
$relationName = $this->getJoinTable($entityName, $foreignEntityName);
$relationName = $this->getJoinTable($entityType, $foreignEntityName);
}
$isStub = !$this->getMetadata()->get(['entityDefs', $entityName, 'fields', $linkName]);
$isStub = !$this->getMetadata()->get(['entityDefs', $entityType, 'fields', $linkName]);
$key1 = lcfirst($entityType) . 'Id';
$key2 = lcfirst($foreignEntityName) . 'Id';
if ($key1 === $key2) {
if (strcmp($linkName, $foreignLinkName)) {
$key1 = 'leftId';
$key2 = 'rightId';
} else {
$key1 = 'rightId';
$key2 = 'leftId';
}
}
return [
$entityName => [
$entityType => [
'fields' => [
$linkName.'Ids' => [
$linkName.'Ids' => [
'type' => 'jsonArray',
'notStorable' => true,
'isLinkStub' => $isStub,
@@ -67,13 +80,13 @@ class ManyMany extends Base
'type' => 'manyMany',
'entity' => $foreignEntityName,
'relationName' => $relationName,
'key' => 'id', //todo specify 'key'
'foreignKey' => 'id', //todo specify 'foreignKey'
'key' => 'id',
'foreignKey' => 'id',
'midKeys' => [
lcfirst($entityName).'Id',
lcfirst($foreignEntityName).'Id',
$key1,
$key2,
],
'foreign' => $foreignLinkName
'foreign' => $foreignLinkName,
],
],
],

View File

@@ -205,7 +205,6 @@ class Converter
}
$primaryColumns = array();
$uniqueColumns = array();
foreach ($entityParams['fields'] as $fieldName => $fieldParams) {
@@ -234,26 +233,24 @@ class Converter
if (!$tables[$entityName]->hasColumn($columnName)) {
$tables[$entityName]->addColumn($columnName, $fieldType, $this->getDbFieldParams($fieldParams));
}
//add unique
if ($fieldParams['type'] != 'id' && isset($fieldParams['unique'])) {
$uniqueColumns = $this->getKeyList($columnName, $fieldParams['unique'], $uniqueColumns);
} //END: add unique
}
$tables[$entityName]->setPrimaryKey($primaryColumns);
if (!empty($indexList[$entityName])) {
foreach($indexList[$entityName] as $indexName => $indexParams) {
$indexColumnList = $indexParams['columns'];
$indexFlagList = isset($indexParams['flags']) ? $indexParams['flags'] : array();
$tables[$entityName]->addIndex($indexColumnList, $indexName, $indexFlagList);
}
}
if (!empty($uniqueColumns)) {
foreach($uniqueColumns as $uniqueItem) {
$tables[$entityName]->addUniqueIndex($uniqueItem);
switch ($indexParams['type']) {
case 'index':
case 'fulltext':
$indexFlagList = isset($indexParams['flags']) ? $indexParams['flags'] : array();
$tables[$entityName]->addIndex($indexParams['columns'], $indexName, $indexFlagList);
break;
case 'unique':
$tables[$entityName]->addUniqueIndex($indexParams['columns'], $indexName);
break;
}
}
}
}
@@ -320,7 +317,7 @@ class Converter
'type' => 'foreignId',
'len' => $this->idParams['len'],
)));
$table->addIndex(array($columnName));
$table->addIndex(array($columnName), SchemaUtils::generateIndexName($columnName));
$uniqueIndex[] = $columnName;
}
@@ -349,7 +346,7 @@ class Converter
}
if (!empty($uniqueIndex)) {
$table->addUniqueIndex($uniqueIndex);
$table->addUniqueIndex($uniqueIndex, SchemaUtils::generateIndexName($columnName, 'unique'));
}
//END: add unique indexes
@@ -421,26 +418,7 @@ class Converter
}
/**
* Get key list (index, unique). Ex. index => true OR index => 'somename'
* @param string $columnName Column name (underscore field name)
* @param bool | string $keyValue
* @return array
*/
protected function getKeyList($columnName, $keyValue, array $keyList)
{
if ($keyValue === true) {
$tableIndexName = SchemaUtils::generateIndexName($columnName);
$keyList[$tableIndexName] = array($columnName);
} else if (is_string($keyValue)) {
$tableIndexName = SchemaUtils::generateIndexName($keyValue);
$keyList[$tableIndexName][] = $columnName;
}
return $keyList;
}
/**
* Get custom table defenition in "application/Espo/Core/Utils/Database/Schema/tables/" and in metadata 'additionalTables'
* Get custom table definition in "application/Espo/Core/Utils/Database/Schema/tables/" and in metadata 'additionalTables'
*
* @param array $ormMeta
*

View File

@@ -43,30 +43,35 @@ class Utils
continue;
}
if (isset($fieldParams['index'])) {
$keyValue = $fieldParams['index'];
$columnName = Util::toUnderScore($fieldName);
$indexType = static::getIndexTypeByFieldDefs($fieldParams);
if (!$indexType) {
continue;
}
if (!isset($indexList[$entityName])) {
$indexList[$entityName] = [];
}
if (!isset($indexList[$entityName])) {
$indexList[$entityName] = [];
}
if ($keyValue === true) {
$tableIndexName = static::generateIndexName($columnName);
$indexList[$entityName][$tableIndexName]['columns'] = array($columnName);
} else if (is_string($keyValue)) {
$tableIndexName = static::generateIndexName($keyValue);
$indexList[$entityName][$tableIndexName]['columns'][] = $columnName;
}
$keyValue = $fieldParams[$indexType];
$columnName = Util::toUnderScore($fieldName);
if ($keyValue === true) {
$tableIndexName = static::generateIndexName($columnName, $indexType);
$indexList[$entityName][$tableIndexName]['type'] = $indexType;
$indexList[$entityName][$tableIndexName]['columns'] = array($columnName);
} else if (is_string($keyValue)) {
$tableIndexName = static::generateIndexName($keyValue, $indexType);
$indexList[$entityName][$tableIndexName]['type'] = $indexType;
$indexList[$entityName][$tableIndexName]['columns'][] = $columnName;
}
}
if (isset($entityParams['indexes']) && is_array($entityParams['indexes'])) {
foreach ($entityParams['indexes'] as $indexName => $indexParams) {
$tableIndexName = static::generateIndexName($indexName);
$indexType = static::getIndexTypeByIndexDefs($indexParams);
$tableIndexName = static::generateIndexName($indexName, $indexType);
if (isset($indexParams['flags']) && is_array($indexParams['flags'])) {
$skipIndex = false;
foreach ($ignoreFlags as $ignoreFlag) {
if (($flagKey = array_search($ignoreFlag, $indexParams['flags'])) !== false) {
@@ -83,6 +88,7 @@ class Utils
}
if (is_array($indexParams['columns'])) {
$indexList[$entityName][$tableIndexName]['type'] = $indexType;
$indexList[$entityName][$tableIndexName]['columns'] = Util::toUnderScore($indexParams['columns']);
}
}
@@ -92,8 +98,42 @@ class Utils
return $indexList;
}
public static function generateIndexName($name, $prefix = 'IDX', $maxLength = 30)
public static function getIndexTypeByFieldDefs(array $fieldDefs)
{
if ($fieldDefs['type'] != 'id' && isset($fieldDefs['unique']) && $fieldDefs['unique']) {
return 'unique';
}
if (isset($fieldDefs['index']) && $fieldDefs['index']) {
return 'index';
}
}
public static function getIndexTypeByIndexDefs(array $indexDefs)
{
if (isset($indexDefs['unique']) && $indexDefs['unique']) {
return 'unique';
}
if (isset($indexDefs['flags']) && in_array('fulltext', $indexDefs['flags'])) {
return 'fulltext';
}
return 'index';
}
public static function generateIndexName($name, $type = 'index', $maxLength = 60)
{
switch ($type) {
case 'unique':
$prefix = 'UNIQ';
break;
default:
$prefix = 'IDX';
break;
}
$nameList = [];
$nameList[] = strtoupper($prefix);
$nameList[] = strtoupper( Util::toUnderScore($name) );

View File

@@ -251,4 +251,9 @@ class DateTime
return false;
}
public static function getSystemNowString()
{
return date(self::$systemDateTimeFormat);
}
}

View File

@@ -588,7 +588,7 @@ class FieldManager
}
/**
* Add all needed block for a field defenition
* Add all needed block for a field definition
*
* @param string $scope
* @param string $fieldName

View File

@@ -206,7 +206,7 @@ class Manager
$res = (file_put_contents($fullPath, $data, $flags) !== FALSE);
if ($res && function_exists('opcache_invalidate')) {
opcache_invalidate($fullPath);
@opcache_invalidate($fullPath);
}
return $res;
@@ -467,7 +467,7 @@ class Manager
if (file_exists($sourceFile) && is_file($sourceFile)) {
$res &= copy($sourceFile, $destFile);
if (function_exists('opcache_invalidate')) {
opcache_invalidate($destFile);
@opcache_invalidate($destFile);
}
}
}
@@ -562,7 +562,7 @@ class Manager
if (file_exists($filePath) && is_file($filePath)) {
if (function_exists('opcache_invalidate')) {
opcache_invalidate($filePath, true);
@opcache_invalidate($filePath, true);
}
$result &= unlink($filePath);
}

View File

@@ -28,28 +28,21 @@
************************************************************************/
namespace Espo\Core\Utils\Log\Monolog;
class Logger extends \Monolog\Logger
{
protected $defaultLevelName = 'DEBUG';
/**
* Get Level Code
* @param string $level Ex. DEBUG, ...
* @return int
*/
public function getLevelCode($levelName)
public function setLevel($levelName)
{
$levelName = strtoupper($levelName);
$level = static::toMonologLevel($levelName);
$levels = $this->getLevels();
if (isset($levels[$levelName])) {
return $levels[$levelName];
$handlers = $this->getHandlers();
foreach ($handlers as $handler) {
if ($handler->getLevel() > $level) {
$className = get_class($handler);
$handler->setLevel($level);
}
}
return $levels[$this->defaultLevelName];
}
}
}

View File

@@ -41,11 +41,16 @@ class Helper
*/
protected $copiedDefParams = array(
'readOnly',
'disabled',
'notStorable',
'layoutListDisabled',
'layoutDetailDisabled',
'layoutMassUpdateDisabled',
'layoutFiltersDisabled',
'directAccessDisabled',
'customizationDisabled',
'importDisabled',
'exportDisabled',
);
public function __construct(\Espo\Core\Utils\Metadata $metadata)
@@ -59,9 +64,9 @@ class Helper
}
/**
* Get field defenition by type in metadata, "fields" key
* Get field definition by type in metadata, "fields" key
*
* @param array | string $fieldDef - It can be a string or field defenition from entityDefs
* @param array | string $fieldDef - It can be a string or field definition from entityDefs
* @return array | null
*/
public function getFieldDefsByType($fieldDef)

View File

@@ -52,8 +52,6 @@ class ThemeManager
public function getStylesheet()
{
return $this->metadata->get('themes.' . $this->getName() . '.stylesheet', 'client/css/espo.css');
return $this->metadata->get(['themes', $this->getName(), 'stylesheet'], 'client/css/espo/espo.css');
}
}

View File

@@ -80,9 +80,10 @@ class Pusher implements WampServerInterface
if ($checkCommand) {
$checkResult = shell_exec($checkCommand);
if ($checkResult !== 'true') {
if ($this->isDebugMode) $this->log("check access failed for topic {$topicId} for user {$userId}");
if ($this->isDebugMode) $this->log("{$connectionId}: check access failed for topic {$topicId} for user {$userId}");
return;
}
if ($this->isDebugMode) $this->log("{$connectionId}: check access succeed for topic {$topicId} for user {$userId}");
}
if (!in_array($topicId, $this->connectionIdTopicIdListMap[$connectionId])) {
@@ -110,20 +111,37 @@ class Pusher implements WampServerInterface
if ($index !== false) {
if ($this->isDebugMode) $this->log("{$connectionId}: remove topic {$topicId} for user {$userId}");
unset($this->connectionIdTopicIdListMap[$connectionId][$index]);
$this->connectionIdTopicIdListMap[$connectionId][$index] = array_values($this->connectionIdTopicIdListMap[$connectionId][$index]);
$this->connectionIdTopicIdListMap[$connectionId] = array_values($this->connectionIdTopicIdListMap[$connectionId]);
}
}
}
protected function getCategoryData(string $topicId) : array
{
$arr = explode('.', $topicId);
$category = $arr[0];
if (array_key_exists($category, $this->categoriesData)) {
$data = $this->categoriesData[$category];
} else if (array_key_exists($topicId, $this->categoriesData)) {
$data = $this->categoriesData[$topicId];
} else {
$data = [];
}
return $data;
}
protected function getParamsFromTopicId(string $topicId) : array
{
$arr = explode('.', $topicId);
$category = $arr[0];
$data = $this->getCategoryData($topicId);
$params = [];
if (array_key_exists('paramList', $this->categoriesData[$category])) {
foreach ($this->categoriesData[$category]['paramList'] as $i => $item) {
if (array_key_exists('paramList', $data)) {
foreach ($data['paramList'] as $i => $item) {
if (isset($arr[$i + 1])) {
$params[$item] = $arr[$i + 1];
} else {
@@ -146,10 +164,12 @@ class Pusher implements WampServerInterface
return null;
}
$category = $this->getTopicCategory($topic);
if (!array_key_exists('accessCheckCommand', $this->categoriesData[$category])) return null;
$data = $this->getCategoryData($topic->getId());
if (!array_key_exists('accessCheckCommand', $data)) return null;
$command = $this->phpExecutablePath . " command.php " . $data['accessCheckCommand'];
$command = $this->phpExecutablePath . " command.php " . $this->categoriesData[$category]['accessCheckCommand'];
foreach ($params as $key => $value) {
$command = str_replace(':' . $key, $value, $command);
}
@@ -165,8 +185,9 @@ class Pusher implements WampServerInterface
protected function isTopicAllowed($topicId)
{
list($category) = explode('.', $topicId);
return in_array($category, $this->categoryList);
list($category) = explode( '.', $topicId);
return in_array($topicId, $this->categoryList) || in_array($category, $this->categoryList);
}
protected function getConnectionIdListByUserId($userId)

View File

@@ -101,6 +101,7 @@ return [
'portalUserLimit',
'stylesheet',
'userItems',
'globalItems',
'internalSmtpServer',
'internalSmtpPort',
'internalSmtpAuth',
@@ -193,6 +194,11 @@ return [
'adminNotificationsNewExtensionVersion',
'leadCaptureAllowOrigin',
'cronDisabled',
'defaultPortalId',
'cleanupDeletedRecords',
'authTokenPreventConcurrent',
'emailParser',
'latestVersion',
],
'superAdminItems' => [
'jobMaxPortion',
@@ -215,10 +221,30 @@ return [
'superAdminSystemItems' => [
],
'userItems' => [
'outboundEmailFromAddress',
'outboundEmailFromName',
'outboundEmailBccAddress',
'integrations',
],
'globalItems' => [
'cacheTimestamp',
'language',
'isDeveloperMode',
'dateFormat',
'timeFormat',
'timeZone',
'decimalMark',
'weekStart',
'thousandSeparator',
'companyLogoId',
'applicationName',
'jsLibs',
'maintenanceMode',
'siteUrl',
'useCache',
'useCacheInDeveloperMode',
'isDeveloperMode',
'version',
'useWebSocket',
'webSocketUrl',
'aclAllowDeleteCreated',
],
'isInstalled' => false,
'ldapUserNameAttribute' => 'sAMAccountName',

View File

@@ -185,7 +185,7 @@ class Email extends \Espo\Core\ORM\Entity
if (!empty($body)) {
$attachmentList = $this->getInlineAttachments();
foreach ($attachmentList as $attachment) {
$body = str_replace("?entryPoint=attachment&amp;id={$attachment->id}", "cid:{$attachment->id}", $body);
$body = str_replace("\"?entryPoint=attachment&amp;id={$attachment->id}\"", "\"cid:{$attachment->id}\"", $body);
}
}
@@ -196,12 +196,15 @@ class Email extends \Espo\Core\ORM\Entity
public function getInlineAttachments()
{
$attachmentList = array();
$attachmentList = [];
$idList = [];
$body = $this->get('body');
if (!empty($body)) {
if (preg_match_all("/\?entryPoint=attachment&amp;id=([^&=\"']+)/", $body, $matches)) {
if (!empty($matches[1]) && is_array($matches[1])) {
foreach($matches[1] as $id) {
foreach ($matches[1] as $id) {
if (in_array($id, $idList)) continue;
$idList[] = $id;
$attachment = $this->entityManager->getEntity('Attachment', $id);
if ($attachment) {
$attachmentList[] = $attachment;

View File

@@ -30,6 +30,7 @@
namespace Espo\EntryPoints;
use \Espo\Core\Exceptions\NotFound;
use \Espo\Core\Exceptions\NotFoundSilent;
use \Espo\Core\Exceptions\Forbidden;
use \Espo\Core\Exceptions\BadRequest;
use \Espo\Core\Exceptions\Error;
@@ -84,7 +85,7 @@ class Image extends \Espo\Core\EntryPoints\Base
$attachment = $this->getEntityManager()->getEntity('Attachment', $id);
if (!$attachment) {
throw new NotFound();
throw new NotFoundSilent();
}
if (!$disableAccessCheck && !$this->getAcl()->checkEntity($attachment)) {
@@ -98,7 +99,7 @@ class Image extends \Espo\Core\EntryPoints\Base
$fileType = $attachment->get('type');
if (!file_exists($filePath)) {
throw new NotFound();
throw new NotFoundSilent();
}
if (!in_array($fileType, $this->allowedFileTypes)) {

View File

@@ -87,6 +87,17 @@ class Stream extends \Espo\Core\Hooks\Base
if ($this->checkHasStream($entity)) {
$this->getStreamService()->unfollowAllUsersFromEntity($entity);
}
$query = $this->getEntityManager()->getQuery();
$sql = "
UPDATE `note`
SET `deleted` = 1, `modified_at` = '".date('Y-m-d H:i:s')."'
WHERE
(
(related_id = ".$query->quote($entity->id)." AND related_type = ".$query->quote($entity->getEntityType()) .")
)
";
$this->getEntityManager()->getPDO()->query($sql);
}
protected function handleCreateRelated(Entity $entity)

View File

@@ -205,25 +205,25 @@ class Cleanup extends \Espo\Core\Jobs\Base
}
if ($this->getConfig()->get('cleanupOrphanAttachments')) {
$collection = $this->getEntityManager()->getRepository('Attachment')->where(array(
array(
'role' => 'Attachment'
),
'OR' => array(
array(
$collection = $this->getEntityManager()->getRepository('Attachment')->where([
[
'role' => 'Attachment',
],
'OR' => [
[
'parentId' => null,
'parentType!=' => null,
'relatedType=' => null
),
array(
'relatedType=' => null,
],
[
'parentType' => null,
'relatedId' => null,
'relatedType!=' => null
)
),
'relatedType!=' => null,
]
],
'createdAt<' => $datetime->format('Y-m-d H:i:s'),
'createdAt>' => '2017-05-10 00:00:00'
))->limit(0, 5000)->find();
'createdAt>' => '2018-01-01 00:00:00',
])->limit(0, 5000)->find();
foreach ($collection as $e) {
$this->getEntityManager()->removeEntity($e);
@@ -428,13 +428,14 @@ class Cleanup extends \Espo\Core\Jobs\Base
{
if (!$this->getConfig()->get('cleanupDeletedRecords')) return;
$period = '-' . $this->getConfig()->get('cleanupDeletedRecordsPeriod', $this->cleanupDeletedRecordsPeriod);
$datetime = new \DateTime('-' . $period);
$datetime = new \DateTime($period);
$serviceFactory = $this->getServiceFactory();
$scopeList = array_keys($this->getMetadata()->get(['scopes']));
foreach ($scopeList as $scope) {
if (!$this->getMetadata()->get(['scopes', $scope, 'entity'])) continue;
if ($scope === 'Attachment') continue;
if (!$this->getMetadata()->get(['entityDefs', $scope, 'fields', 'modifiedAt'])) continue;
if (!$this->getEntityManager()->hasRepository($scope)) continue;
$repository = $this->getEntityManager()->getRepository($scope);
@@ -444,11 +445,34 @@ class Cleanup extends \Espo\Core\Jobs\Base
if (!method_exists($repository, 'select')) continue;
if (!method_exists($repository, 'deleteFromDb')) continue;
$deletedEntityList = $repository->select(['id', 'deleted'])->where([
$hasCleanupMethod = false;
$service = null;
if ($serviceFactory->checkExists($scope)) {
$service = $serviceFactory->create($scope);
if (method_exists($service, 'cleanup')) {
$hasCleanupMethod = true;
}
}
$whereClause = [
'deleted' => 1,
'modifiedAt<' => $datetime->format('Y-m-d H:i:s')
])->find(['withDeleted' => true]);
];
if ($this->getMetadata()->get(['entityDefs', $scope, 'fields', 'modifiedAt'])) {
$whereClause['modifiedAt<'] = $datetime->format('Y-m-d H:i:s');
} else if ($this->getMetadata()->get(['entityDefs', $scope, 'fields', 'createdAt'])) {
$whereClause['createdAt<'] = $datetime->format('Y-m-d H:i:s');
}
$deletedEntityList = $repository->select(['id', 'deleted'])->where($whereClause)->find(['withDeleted' => true]);
foreach ($deletedEntityList as $e) {
if ($hasCleanupMethod) {
try {
$service->cleanup($e->id);
} catch (\Throwable $e) {
$GLOBALS['log']->error("Cleanup job: Cleanup scope {$scope}: " . $e->getMessage());
}
}
$this->cleanupDeletedEntity($e);
}
}

View File

@@ -88,12 +88,12 @@ class SubscribeAgain extends \Espo\Core\EntryPoints\Base
}
$link = null;
$m = array(
$m = [
'Account' => 'accounts',
'Contact' => 'contacts',
'Lead' => 'leads',
'User' => 'users'
);
];
if (!empty($m[$target->getEntityType()])) {
$link = $m[$target->getEntityType()];
}
@@ -115,7 +115,11 @@ class SubscribeAgain extends \Espo\Core\EntryPoints\Base
}
$data = [
'queueItemId' => $queueItemId
'actionData' => [
'queueItemId' => $queueItemId,
],
'view' => $this->getMetadata()->get(['clientDefs', 'Campaign', 'subscribeView']),
'template' => $this->getMetadata()->get(['clientDefs', 'Campaign', 'subscribeTemplate']),
];
$runScript = "

View File

@@ -88,12 +88,12 @@ class Unsubscribe extends \Espo\Core\EntryPoints\Base
}
$link = null;
$m = array(
$m = [
'Account' => 'accounts',
'Contact' => 'contacts',
'Lead' => 'leads',
'User' => 'users'
);
];
if (!empty($m[$target->getEntityType()])) {
$link = $m[$target->getEntityType()];
}
@@ -115,7 +115,11 @@ class Unsubscribe extends \Espo\Core\EntryPoints\Base
}
$data = [
'queueItemId' => $queueItemId
'actionData' => [
'queueItemId' => $queueItemId,
],
'view' => $this->getMetadata()->get(['clientDefs', 'Campaign', 'unsubscribeView']),
'template' => $this->getMetadata()->get(['clientDefs', 'Campaign', 'unsubscribeTemplate']),
];
$runScript = "
@@ -137,4 +141,3 @@ class Unsubscribe extends \Espo\Core\EntryPoints\Base
}
}
}

View File

@@ -38,7 +38,9 @@ class Opportunity extends \Espo\Core\ORM\Repositories\RDB
if ($entity->isNew()) {
if (!$entity->has('probability') && $entity->get('stage')) {
$probability = $this->getMetadata()->get('entityDefs.Opportunity.fields.stage.probabilityMap.' . $entity->get('stage'), 0);
$entity->set('probability', $probability);
if (!is_null($probability)) {
$entity->set('probability', $probability);
}
}
}

View File

@@ -1,20 +1,18 @@
{
"fields": {
"name": "Navn",
"emailAddress": "Email",
"website": "Website",
"phoneNumber": "Telefon",
"billingAddress": "Betalingsadresse",
"shippingAddress": "Leveringsadresse",
"description": "Beskrivelse",
"sicCode": "Sic Kode",
"industry": "Industri",
"type": "Type",
"contactRole": "Titel",
"campaign": "Kampagne",
"targetLists": "Kontaktlister",
"targetList": "Kontaktliste",
"originalLead": "Oprindelig Lead"
"originalLead": "Oprindelig Lead",
"contactIsInactive": "inaktiv"
},
"links": {
"contacts": "Kontakter",
@@ -34,8 +32,6 @@
"options": {
"type": {
"Customer": "Kunde",
"Investor": "Investor",
"Partner": "Partner",
"Reseller": "Forhandler"
},
"industry": {
@@ -61,9 +57,7 @@
"Manufacturing": "Produktion",
"Publishing": "Medien",
"Real Estate": "Ejendom",
"Service": "Service",
"Sports": "Sport",
"Software": "Software",
"Technology": "Teknologi",
"Telecommunications": "Telekommunikation",
"Television": "TV",
@@ -75,25 +69,23 @@
"Defense": "Forsvar",
"Creative": "Kreativ",
"Culture": "Kultur",
"Consulting": "Consulting",
"Electric Power": "Elektroenergi",
"Hospitality": "Hotel og Restaurant",
"Mass Media": "Massemedier",
"Mining": "Mineindustri",
"Music": "Musik",
"Marketing": "Marketing",
"Petroleum": "Olieindustri",
"Retail": "Retail",
"Shipping": "Shipping",
"Support": "Service",
"Testing, Inspection & Certification": "Testning, Inspektion og Certificering",
"Wholesale": "Engros",
"Water": "Vand"
"Water": "Vand",
"Travel": "Rejse"
}
},
"labels": {
"Create Account": "Opret Konto",
"Copy Billing": "Kopier Betalingsadresse"
"Copy Billing": "Kopier Betalingsadresse",
"Set Primary": "Sæt primær"
},
"presetFilters": {
"customers": "Kunder",

View File

@@ -1,6 +1,10 @@
{
"layouts": {
"detailConvert": "Konverter Lead",
"listForAccount": "Liste (for Konto)"
"listForAccount": "Liste (for Konto)",
"listForContact": "Liste (for kontakter)"
},
"templates": {
"reminder": "Påmindelse"
}
}

View File

@@ -15,6 +15,8 @@
"current": "Aktuel",
"time": "Tid",
"User List": "Brugerliste",
"Manage Users": "Administrer Brugere"
"Manage Users": "Administrer Brugere",
"View Calendar": "Se kalender",
"Create Shared View": "Opret delt visning"
}
}

View File

@@ -2,7 +2,6 @@
"fields": {
"name": "Navn",
"parent": "Henviser til",
"status": "Status",
"dateStart": "Startdato",
"dateEnd": "Slutdato",
"direction": "Retning",
@@ -10,7 +9,6 @@
"description": "Beskrivelse",
"users": "Brugere",
"contacts": "Kontakter",
"leads": "Leads",
"reminders": "Underretninger",
"account": "Konto",
"acceptanceStatus": "Acceptstatus"

View File

@@ -2,8 +2,6 @@
"fields": {
"name": "Navn",
"description": "Beskrivelse",
"status": "Status",
"type": "Type",
"startDate": "Startdato",
"endDate": "Slutdato",
"targetLists": "Kontaktliste",
@@ -18,28 +16,31 @@
"leadCreatedCount": "Oprettede Leads",
"revenue": "Indtægt",
"revenueConverted": "Indtægt (konverteret)",
"budget": "Budget",
"budgetConverted": "Budget (konverteret)"
"budgetConverted": "Budget (konverteret)",
"contactsTemplate": "Kontakt skabelon",
"leadsTemplate": "Leads skabeloner",
"accountsTemplate": "Konto skabelon",
"usersTemplate": "Brugere skabelon",
"mailMergeOnlyWithAddress": "Spring over poster uden fyldt adresse",
"optedInCount": "Tilmeldt",
"budgetCurrency": "Budget Valuta"
},
"links": {
"targetLists": "Kontaktliste",
"excludingTargetLists": "Udelukker Kontaktlister",
"accounts": "Konti",
"contacts": "Kontakter",
"leads": "Leads",
"opportunities": "Muligheder",
"campaignLogRecords": "Log",
"massEmails": "Masse-Email",
"trackingUrls": "Tracking URLs"
"contactsTemplate": "Kontakt skabelon",
"leadsTemplate": "Leads skabeloner",
"accountsTemplate": "Konto skabelon",
"usersTemplate": "Brugere skabelon"
},
"options": {
"type": {
"Email": "Email",
"Web": "Web",
"Television": "TV",
"Radio": "Radio",
"Newsletter": "Nyhedsbrev",
"Mail": "Mail"
"Newsletter": "Nyhedsbrev"
},
"status": {
"Planning": "Planlægning",
@@ -59,7 +60,9 @@
"Email Templates": "Emailskabeloner",
"Unsubscribe again": "Frameld igen",
"Subscribe again": "Tilmeld igen",
"Create Target List": "Opret Kontaktliste"
"Create Target List": "Opret Kontaktliste",
"Mail Merge": "Mailfletning",
"Generate Mail Merge PDF": "Generer mailfletning i PDF"
},
"presetFilters": {
"active": "Aktiv"

View File

@@ -2,19 +2,20 @@
"fields": {
"action": "Handling",
"actionDate": "Dato",
"data": "Data",
"campaign": "Kampagne",
"parent": "Mål",
"object": "Objekt",
"application": "Applikation",
"queueItem": "Sæt i Kø",
"stringData": "Streng Data",
"stringAdditionalData": "Streng Ekstra Data"
"stringAdditionalData": "Streng Ekstra Data",
"isTest": "Er test"
},
"links": {
"queueItem": "Sæt i Kø",
"parent": "Henviser til",
"object": "Objekt"
"object": "Objekt",
"campaign": "Kampagne"
},
"options": {
"action": {
@@ -23,7 +24,8 @@
"Opted Out": "Sat til Opt-Out",
"Bounced": "Kunne ikke leveres",
"Clicked": "Klikket",
"Lead Created": "Lead Oprettet"
"Lead Created": "Lead Oprettet",
"Opted In": "Tilmeldt"
}
},
"labels": {
@@ -35,6 +37,7 @@
"optedOut": "Sat til Opt-Out",
"bounced": "Kunne ikke leveres",
"clicked": "Klikket",
"leadCreated": "Lead Oprettet"
"leadCreated": "Lead Oprettet",
"optedIn": "Tilmeldt"
}
}

View File

@@ -1,6 +1,5 @@
{
"fields": {
"url": "URL",
"urlToUse": "Kode til indsættelse i stedet for URL",
"campaign": "Kampagne"
},

View File

@@ -2,27 +2,24 @@
"fields": {
"name": "Navn",
"number": "Nummer",
"status": "Status",
"account": "Konto",
"contact": "Kontakt",
"contacts": "Kontakter",
"priority": "Prioritet",
"type": "Type",
"description": "Beskrivelse",
"inboundEmail": "Ingående Email",
"lead": "Lead"
"attachments": "Vedhæftede filer",
"inboundEmail": "Gruppe email konto"
},
"links": {
"inboundEmail": "Indgående Email",
"account": "Konto",
"contact": "Kontakt (Primær)",
"Contacts": "Kontakter",
"meetings": "Møder",
"calls": "Opkald",
"tasks": "Opgaver",
"emails": "Emails",
"articles": "Vidensbase Artikler",
"lead": "Lead"
"attachments": "Vedhæftede filer",
"inboundEmail": "Gruppe email konto"
},
"options": {
"status": {
@@ -35,14 +32,12 @@
},
"priority": {
"Low": "Lav",
"Normal": "Normal",
"High": "Høj",
"Urgent": "Vigtig"
},
"type": {
"Question": "Spørgsmål",
"Incident": "Hændelse",
"Problem": "Problem"
"Incident": "Hændelse"
}
},
"labels": {

View File

@@ -1,8 +1,6 @@
{
"fields": {
"name": "Navn",
"emailAddress": "Email",
"title": "Titel",
"accountRole": "Titel",
"account": "Konto",
"accounts": "Konti",
@@ -17,7 +15,10 @@
"targetList": "Kontaktliste",
"portalUser": "Portalbruger",
"originalLead": "Oprindelig Lead",
"acceptanceStatus": "Acceptance Status"
"accountIsInactive": "Konto inaktiv",
"acceptanceStatusMeetings": "Accept status (Møder)",
"acceptanceStatusCalls": "Accpet status (Opkald)",
"title": "Primær brugertitel"
},
"links": {
"opportunities": "Muligheder",
@@ -30,21 +31,21 @@
"casesPrimary": "Sager (Primære)",
"portalUser": "Portalbruger",
"originalLead": "Oprindelig Lead",
"documents": "Dokumenter"
"documents": "Dokumenter",
"tasksPrimary": "Opgaver (udvidede)"
},
"labels": {
"Create Contact": "Opret Kontakt"
},
"options": {
"opportunityRole": {
"": "--Ingen--",
"Decision Maker": "Beslutningstager",
"Evaluator": "Evaluator",
"Influencer": "Har Indflydelse"
}
},
"presetFilters": {
"portalUsers": "Portalbrugere",
"notPortalUsers": "Ikke Portalbrugere"
"notPortalUsers": "Ikke Portalbrugere",
"accountActive": "Aktiv"
}
}

View File

@@ -0,0 +1,6 @@
{
"fields": {
"futureDays": "Næste X dage",
"useLastStage": "Gruppe med sidste opnået stadie"
}
}

View File

@@ -5,9 +5,7 @@
},
"fields": {
"name": "Navn",
"status": "Status",
"file": "Fil",
"type": "Type",
"publishDate": "Offentliggørelsesdato",
"expirationDate": "Afslutningsdato",
"description": "Beskrivelse",
@@ -18,7 +16,6 @@
"accounts": "Konti",
"opportunities": "Muligheder",
"folder": "Mappe",
"leads": "Leads",
"contacts": "Kontakter"
},
"options": {
@@ -31,8 +28,6 @@
"type": {
"": "ingen",
"Contract": "Kontrakt",
"NDA": "NDA",
"EULA": "EULA",
"License Agreement": "Licensaftale"
}
},

View File

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

View File

@@ -1,7 +1,6 @@
{
"fields": {
"name": "Navn",
"status": "Status",
"target": "Mål",
"sentAt": "Dato Sendt",
"attemptCount": "Forsøg",

View File

@@ -3,11 +3,9 @@
"parent": "Overordnet",
"contacts": "Kontakter",
"opportunities": "Muligheder",
"leads": "Leads",
"meetings": "Møder",
"calls": "Opkald",
"tasks": "Opgaver",
"emails": "Emails",
"accounts": "Konti",
"cases": "Sager",
"documents": "Dokumenter",
@@ -18,7 +16,6 @@
"scopeNames": {
"Account": "Konto",
"Contact": "Kontakt",
"Lead": "Lead",
"Target": "Mål",
"Opportunity": "Mulighed",
"Meeting": "Møde",
@@ -32,7 +29,6 @@
"TargetList": "Kontaktliste",
"MassEmail": "Masse-Email",
"EmailQueueItem": "Emne i Email-Kø",
"CampaignTrackingUrl": "Tracking URL",
"Activities": "Aktiviteter",
"KnowledgeBaseArticle": "Vidensbase Artikel",
"KnowledgeBaseCategory": "Vidensbase Kategori",
@@ -41,7 +37,6 @@
"scopeNamesPlural": {
"Account": "Konti",
"Contact": "Kontakter",
"Lead": "Leads",
"Target": "Mål",
"Opportunity": "Muligheder",
"Meeting": "Møder",
@@ -108,11 +103,5 @@
"shippingAddressState": "Stat (Levering)",
"shippingAddressPostalCode": "Postnummer (Levering)",
"shippingAddressMap": "Kort (Levering)"
},
"options": {
"reminderTypes": {
"Popup": "Popup",
"Email": "Email"
}
}
}

View File

@@ -10,8 +10,6 @@
},
"fields": {
"name": "Navn",
"status": "Status",
"type": "Type",
"attachments": "Vedhæftede",
"publishDate": "Startdato",
"expirationDate": "Slutdato",
@@ -40,8 +38,5 @@
},
"presetFilters": {
"published": "Publiseret"
},
"tooltips": {
"portals": "Article will be available only in specified portals."
}
}

View File

@@ -2,19 +2,15 @@
"labels": {
"Converted To": "Konverteret Til",
"Create Lead": "Opret Lead",
"Convert": "Konverter",
"convert": "convert"
"Convert": "Konverter"
},
"fields": {
"name": " Navn",
"emailAddress": "Email",
"title": "Titel",
"website": "Website",
"phoneNumber": "Telefon",
"accountName": "Kontonavn",
"doNotCall": "Ring Ikke!",
"address": "Adresse",
"status": "Status",
"source": "Kilde",
"opportunityAmount": "Beløb for Mulighed",
"opportunityAmountConverted": "Beløb for Mulighed (Konverteret)",
@@ -26,8 +22,8 @@
"targetLists": "Kontaktlister",
"targetList": "Kontaktliste",
"industry": "Industri",
"acceptanceStatus": "Acceptance Status",
"opportunityAmountCurrency": "Opportunity Amount Currency"
"acceptanceStatusMeetings": "Accept status (Møder)",
"acceptanceStatusCalls": "Accept status (Opkald)"
},
"links": {
"targetLists": "Kontaktlister",
@@ -51,9 +47,7 @@
"source": {
"": "Ingen",
"Call": "Samtale",
"Email": "Email",
"Existing Customer": "Eksisterende Kunde",
"Partner": "Partner",
"Public Relations": "PR",
"Web Site": "Webside",
"Campaign": "Kampagne",

View File

@@ -1,7 +1,6 @@
{
"fields": {
"name": "Navn",
"status": "Status",
"storeSentEmails": "Lager Sendte Emails",
"startAt": "Startdato",
"fromAddress": "Fra Adresse",
@@ -13,7 +12,8 @@
"inboundEmail": "Emailkonto",
"targetLists": "Kontaktlister",
"excludingTargetLists": "Ekskluderer Kontaktlister",
"optOutEntirely": "Fuldstændig Opt-Out"
"optOutEntirely": "Fuldstændig Opt-Out",
"smtpAccount": "SMTP konto"
},
"links": {
"targetLists": "Kontaktlister",
@@ -35,7 +35,7 @@
},
"labels": {
"Create MassEmail": "Opret Masse-Email",
"Send Test": "Send Test"
"group": "gruppe"
},
"messages": {
"selectAtLeastOneTarget": "Vælg mindst et Mål",
@@ -44,7 +44,8 @@
"tooltips": {
"optOutEntirely": "Emailadresser tilhørende modtagere, som er afmeldt, bliver markeret som \"afmeldt\", og de vil ikke modtage masse-emails fremover.",
"targetLists": "Kontakter der skal modtage meddelelser",
"excludingTargetLists": "Kontakter der ikke skal modtage meddelelser"
"excludingTargetLists": "Kontakter der ikke skal modtage meddelelser",
"storeSentEmails": "Emails vil blive gemt i CRM"
},
"presetFilters": {
"actual": "Aktuel",

View File

@@ -2,7 +2,6 @@
"fields": {
"name": "Navn",
"parent": "Henviser til",
"status": "Status",
"dateStart": "Startdato",
"dateEnd": "Slutdato",
"duration": "Varighed",
@@ -12,7 +11,11 @@
"leads": "leads",
"reminders": "Påmindelser",
"account": "Konto",
"acceptanceStatus": "Acceptstatus"
"acceptanceStatus": "Acceptstatus",
"dateStartDate": "Start dato (hele dagen)",
"dateEndDate": "Slut dato (hele dagen)",
"isAllDay": "Hele dagen",
"Acceptance": "Accept"
},
"options": {
"status": {
@@ -37,7 +40,8 @@
"Set Not Held": "Marker som Ikke Gennemført",
"Send Invitations": "Send Invitationer",
"on time": "Til Tiden",
"before": "Før"
"before": "Før",
"All-Day": "Hele dagen"
},
"presetFilters": {
"planned": "Planlagt",

View File

@@ -14,7 +14,8 @@
"amountWeightedConverted": "Beløb Vægtet",
"campaign": "Kampagne",
"originalLead": "Oprindelig Lead",
"amountCurrency": "Amount Currency"
"contactRole": "Kontakt roller",
"lastStage": "Sidste etape"
},
"links": {
"contacts": "Kontakter",
@@ -26,8 +27,6 @@
"stage": {
"Prospecting": "Undersøges",
"Qualification": "Kvalifikation",
"Proposal": "Tilbud",
"Negotiation": "Forhandler",
"Needs Analysis": "Behovsanalyse",
"Value Proposition": "Vejledende Tilbud",
"Id. Decision Makers": "Identifikation af Beslutningstagere",
@@ -35,7 +34,9 @@
"Proposal/Price Quote": "Tilbud/Pristilbud",
"Negotiation/Review": "Forhandler/Gennemgår",
"Closed Won": "Vundet og Lukket",
"Closed Lost": "Tabt og Lukket"
"Closed Lost": "Tabt og Lukket",
"Proposal": "Forslag",
"Negotiation": "Forhandling"
}
},
"labels": {

View File

@@ -7,22 +7,25 @@
"endDate": "Slutdato",
"targetLists": "Kontaktliste",
"includingActionList": "Inkluder",
"excludingActionList": "Ekskluder"
"excludingActionList": "Ekskluder",
"optedOutCount": "Antal fravalgte",
"targetStatus": "Mål status",
"isOptedOut": "er fravalgte"
},
"links": {
"accounts": "Konti",
"contacts": "Kontakter",
"leads": "Leads",
"campaigns": "Kampagner",
"massEmails": "Masse-Emails"
},
"options": {
"type": {
"Email": "Email",
"Web": "Web",
"Television": "TV",
"Radio": "Radio",
"Newsletter": "Nyhedsbrev"
},
"targetStatus": {
"Opted Out": "Fravalgt",
"Listed": "Listet"
}
},
"labels": {

View File

@@ -2,7 +2,6 @@
"fields": {
"name": "Navn",
"parent": "Henviser til",
"status": "Status",
"dateStart": "Startdato",
"dateEnd": "Tidsfrist",
"dateStartDate": "Startdato (Hele dagen)",
@@ -13,10 +12,13 @@
"account": "Konto",
"dateCompleted": "Afsluttet Dato",
"attachments": "Vedhæftede",
"reminders": "Underretninger"
"reminders": "Underretninger",
"contact": "Kontakt"
},
"links": {
"attachments": "Vedhæftede"
"attachments": "Vedhæftede",
"account": "Konto",
"contact": "Kontakt"
},
"options": {
"status": {
@@ -28,7 +30,6 @@
},
"priority": {
"Low": "Lav",
"Normal": "Normal",
"High": "Høj",
"Urgent": "Vigtig"
}

View File

@@ -3,6 +3,7 @@
"targetLists": "Kontaktlister"
},
"fields": {
"acceptanceStatus": "Acceptance Status"
"acceptanceStatusMeetings": "Accept satus (Møde)",
"acceptanceStatusCalls": "Accept status (Opkald)"
}
}

View File

@@ -3,5 +3,9 @@
"detailConvert": "Convertir Referencia",
"listForAccount": "Listado (por Cuentas)",
"listForContact": "Lista (para Contactos)"
},
"templates": {
"invitation": "Invitación",
"reminder": "Recordatorio"
}
}

View File

@@ -24,7 +24,8 @@
"leadsTemplate": "Formato de Referencias",
"accountsTemplate": "Formato de Cuentas",
"usersTemplate": "Formato de Usuarios",
"mailMergeOnlyWithAddress": "Saltar registros sin dirección capturada"
"mailMergeOnlyWithAddress": "Saltar registros sin dirección capturada",
"optedInCount": "Opt-In aceptado"
},
"links": {
"targetLists": "Listas de Intereses",

View File

@@ -25,7 +25,8 @@
"Opted Out": "Rechazado",
"Bounced": "Rebotados",
"Clicked": "Leído",
"Lead Created": "Referencias Creadas"
"Lead Created": "Referencias Creadas",
"Opted In": "Opt-In aceptado"
}
},
"labels": {
@@ -37,6 +38,7 @@
"optedOut": "Rechazado",
"bounced": "Rebotados",
"clicked": "Leído",
"leadCreated": "Referencia Creada"
"leadCreated": "Referencia Creada",
"optedIn": "Opt-In aceptado"
}
}

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