Compare commits

...

216 Commits

Author SHA1 Message Date
Yuri Kuznetsov ef3ed38b97 Merge branch 'master' into stable 2014-08-08 16:58:08 +03:00
Yuri Kuznetsov a38b86b2c6 dashlet sorting 2014-08-08 14:13:24 +03:00
Yuri Kuznetsov a87044623c diff fix 2014-08-08 11:45:13 +03:00
Yuri Kuznetsov 44a96ddece isFieldChanged 2014-08-07 18:30:52 +03:00
Yuri Kuznetsov b0e911870b git fix lang 2014-08-07 16:19:55 +03:00
Yuri Kuznetsov 96bb421b0a Delete label 2014-08-07 15:24:32 +03:00
Yuri Kuznetsov c281d7b2d9 fix lang 2014-08-07 12:08:51 +03:00
Yuri Kuznetsov dc5766088f confirm messages 2014-08-07 12:03:54 +03:00
Yuri Kuznetsov fe69f97b1e fix acl issue and mappen IN issue 2014-08-07 11:28:52 +03:00
Yuri Kuznetsov b7bc1e3622 services changes 2014-08-06 16:07:04 +03:00
Yuri Kuznetsov b3c4123669 email notifications 2014-08-06 15:57:30 +03:00
Yuri Kuznetsov 0795f6f6f2 clear cache messag in role 2014-08-06 10:50:26 +03:00
Yuri Kuznetsov c3cd909063 version 2014-08-05 17:34:39 +03:00
Yuri Kuznetsov 0e37c26b9f fix issue with this 2014-08-05 17:12:15 +03:00
Yuri Kuznetsov 80937f8d09 convert lead fix 2014-08-05 15:57:37 +03:00
Yuri Kuznetsov 380cdeba35 use cache false 2014-08-05 15:27:47 +03:00
Yuri Kuznetsov 1eabce0d2e change record detail #build 2014-08-05 15:27:34 +03:00
Yuri Kuznetsov 3cd5f801a5 use cache false 2014-08-05 14:42:01 +03:00
Yuri Kuznetsov c1cfd75bd6 fix notice 2014-08-05 14:38:47 +03:00
Yuri Kuznetsov dd5fabdfa4 contacts select in meetings 2014-08-05 11:44:10 +03:00
Yuri Kuznetsov 3e9c7056b0 improve mark all read 2014-08-05 10:54:19 +03:00
Yuri Kuznetsov 26188879b6 fix client acl 2014-08-05 10:49:29 +03:00
Yuri Kuznetsov 488717c6a1 dashlet changes 2014-08-04 17:05:24 +03:00
Yuri Kuznetsov b8476d7335 fix import 2014-08-04 15:23:41 +03:00
Yuri Kuznetsov 1cfad615c0 not team restricion for admin 2014-08-04 13:35:35 +03:00
Yuri Kuznetsov e3a3547a54 prevent error if field name is empty in layout 2014-08-04 12:36:51 +03:00
Yuri Kuznetsov e6948aba2e mark all read 2014-08-04 12:19:09 +03:00
Yuri Kuznetsov d4943e4d1c Merge branch 'stable' 2014-08-01 11:06:01 +03:00
Yuri Kuznetsov af5e7d99b2 Merge branch 'release/2.2' into stable 2014-08-01 11:05:46 +03:00
Yuri Kuznetsov 667a46f141 text field search 2014-07-31 17:36:48 +03:00
Yuri Kuznetsov 67f65492de version 2014-07-31 12:21:58 +03:00
Yuri Kuznetsov b6a98e7121 change show more count 2014-07-30 17:33:59 +03:00
Yuri Kuznetsov 9669ff91bb footer fix 2014-07-30 12:20:16 +03:00
Yuri Kuznetsov acb6e245a2 show total 2014-07-30 11:43:14 +03:00
Yuri Kuznetsov b5e015e9c9 vi_VN to config 2014-07-30 10:49:36 +03:00
Yuri Kuznetsov 70d2e311e5 vi_VN lang 2014-07-30 10:46:36 +03:00
Yuri Kuznetsov 6c7e5c0d02 update ro lang 2014-07-30 10:45:12 +03:00
Yuri Kuznetsov d4558d73b3 lang 2014-07-30 10:35:59 +03:00
Yuri Kuznetsov e2257f6cfd footer 2014-07-29 18:06:08 +03:00
Yuri Kuznetsov 149fa41e2b textFilter 2014-07-29 17:19:00 +03:00
Yuri Kuznetsov 71e4c2fca0 Merge branch 'hotfix/2.1.1' 2014-07-29 16:37:15 +03:00
Yuri Kuznetsov 5a4613617c fix diff 2014-07-29 16:07:25 +03:00
Yuri Kuznetsov cbf9dd458e diff change 2014-07-29 13:06:15 +03:00
Yuri Kuznetsov 08553bdf9d diff changes 2014-07-28 15:37:46 +03:00
Yuri Kuznetsov d0b61b42e0 fix diff 2014-07-28 12:54:33 +03:00
Yuri Kuznetsov 3e3348c4a7 version 2014-07-28 12:51:17 +03:00
Yuri Kuznetsov c32b0f365b diff.js 2014-07-28 12:50:25 +03:00
Yuri Kuznetsov 8fd0554714 fix null filter issue 2014-07-28 11:17:21 +03:00
Yuri Kuznetsov 982f0e1f62 change filter defs 2014-07-28 10:54:01 +03:00
Yuri Kuznetsov c87f5dfab3 bool filter change 2014-07-28 10:34:39 +03:00
Yuri Kuznetsov b335462aa4 Merge branch 'release/2.1' into stable 2014-07-25 15:21:50 +03:00
Yuri Kuznetsov 97c951484d fix add dashlet 2014-07-25 15:18:37 +03:00
Yuri Kuznetsov 2215f15aaf lang 2014-07-25 12:58:26 +03:00
Yuri Kuznetsov 65f38a4906 Merge branch 'release/2.1' into stable 2014-07-25 12:27:30 +03:00
Yuri Kuznetsov 6429823cc3 version 2014-07-25 11:28:50 +03:00
Yuri Kuznetsov 2a71bb2ea1 duplicate 2014-07-25 11:24:20 +03:00
Yuri Kuznetsov 1674c2221e disableExport 2014-07-24 18:48:03 +03:00
Yuri Kuznetsov e3b448f520 style preset 2014-07-24 17:48:49 +03:00
Yuri Kuznetsov a04c973817 css 2014-07-24 16:21:17 +03:00
Yuri Kuznetsov 01c9939215 fix css 2014-07-24 16:08:32 +03:00
Yuri Kuznetsov 2d57824cb0 fix css 2014-07-24 15:33:23 +03:00
Yuri Kuznetsov 878385f14b fix import UI 2014-07-24 15:27:27 +03:00
Yuri Kuznetsov 8b82058d06 fix import 2014-07-24 15:20:17 +03:00
Yuri Kuznetsov 68e05f0935 datetime edit more nice 2014-07-24 15:00:49 +03:00
Yuri Kuznetsov cb7492c042 email to case: dont copy email body 2014-07-24 14:41:05 +03:00
Yuri Kuznetsov beb811755f improve bool filters ui 2014-07-24 11:21:21 +03:00
Yuri Kuznetsov 7c7fc4ee0f impreove preset search 2014-07-24 11:01:47 +03:00
Yuri Kuznetsov aeef25f7ec fix preset ok 2014-07-23 17:19:35 +03:00
Yuri Kuznetsov 6856ff97b1 fix previous commit 2014-07-23 17:11:09 +03:00
Yuri Kuznetsov 10f6f7600c create notifications via cron 2014-07-23 17:04:03 +03:00
Yuri Kuznetsov a0b678a3d9 remove some files from grunt uglify 2014-07-23 16:17:30 +03:00
Yuri Kuznetsov f78e558a33 fix search 2014-07-23 15:56:06 +03:00
Yuri Kuznetsov e205ef3370 improve fields tpls 2014-07-23 15:38:38 +03:00
Yuri Kuznetsov 8b6e4d469e onlyMy for meetings and calls 2014-07-23 12:49:10 +03:00
Yuri Kuznetsov a63ec2710b fix filter 2014-07-23 12:20:16 +03:00
Yuri Kuznetsov 6895fac58c date filters 2014-07-23 11:57:48 +03:00
Yuri Kuznetsov ea3c8f2d35 fix preset 2014-07-23 09:56:42 +03:00
Yuri Kuznetsov 0f2c050537 lang 2014-07-22 18:22:38 +03:00
Yuri Kuznetsov 2cd8866c73 Merge branch 'stable' 2014-07-22 18:07:44 +03:00
Yuri Kuznetsov 86a5e19b55 version 2014-07-22 17:46:38 +03:00
Yuri Kuznetsov 61f435812d fix timezones 2014-07-22 17:42:55 +03:00
Yuri Kuznetsov 217d0f6540 preset filters 4 2014-07-22 17:37:21 +03:00
Yuri Kuznetsov f0e7a8f029 preset filters 2 2014-07-22 16:25:56 +03:00
Yuri Kuznetsov d2954483d9 preset filters 2014-07-21 16:45:18 +03:00
Yuri Kuznetsov 999b88cba1 Merge branch 'stable' 2014-07-18 17:04:59 +03:00
Yuri Kuznetsov 717df30063 Merge branch 'release/2.0' into stable 2014-07-18 17:04:35 +03:00
Yuri Kuznetsov d889c78f0c fix query 2014-07-18 10:31:01 +03:00
Yuri Kuznetsov 4737c506f2 do not pass phone to new contact 2014-07-17 15:50:45 +03:00
Yuri Kuznetsov 78cb1a8052 label css 2014-07-15 14:46:38 +03:00
Yuri Kuznetsov dac8ca5a6d search by email and phone fix 2014-07-15 12:55:22 +03:00
Yuri Kuznetsov e04ac6778a fix email phone fields 2014-07-15 12:34:12 +03:00
Yuri Kuznetsov 5f34d4f2fd change duplication where clause 2014-07-15 12:24:30 +03:00
Yuri Kuznetsov ca7c0f07e9 fix fields 2014-07-15 12:20:26 +03:00
Yuri Kuznetsov af26f29bec status 2014-07-15 11:11:09 +03:00
Taras Machyshyn 0c44f78ea1 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2014-07-15 11:01:00 +03:00
Taras Machyshyn bed62e2e71 orm convertation chnages 2014-07-15 11:00:38 +03:00
Yuri Kuznetsov 6d61db86df fix url field 2014-07-14 14:59:40 +03:00
Yuri Kuznetsov 4feb055256 fr_FR lang 2014-07-14 11:29:31 +03:00
Yuri Kuznetsov 10a44239ed fix tpls 2014-07-14 11:18:17 +03:00
Yuri Kuznetsov 8a78eda520 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2014-07-11 14:36:48 +03:00
Yuri Kuznetsov 891b159b30 view parameter for fields 2014-07-11 14:36:34 +03:00
Taras Machyshyn 0d06770e80 LDAP changes 2014-07-11 13:57:03 +03:00
Yuri Kuznetsov f8675bd45c languages 2014-07-11 12:24:10 +03:00
Yuri Kuznetsov c35136b524 pt_BR lang 2014-07-11 12:21:31 +03:00
Yuri Kuznetsov a0384deb00 ro_RO lang 2014-07-11 12:19:37 +03:00
Yuri Kuznetsov e35fb15a31 de_DE lang 2014-07-11 12:13:43 +03:00
Yuri Kuznetsov fe86807091 fix email template 2014-07-10 18:08:14 +03:00
Yuri Kuznetsov e12a15ecdb fix notice 2014-07-10 17:53:40 +03:00
Yuri Kuznetsov 24dcd46d6f change currency field look 2014-07-10 17:22:51 +03:00
Yuri Kuznetsov 15196f2062 fix linkMultiple fields 2014-07-10 17:00:09 +03:00
Yuri Kuznetsov 1ae1b20111 phone and email fields change 2014-07-10 16:35:31 +03:00
Yuri Kuznetsov 84de477be0 ldap password 2014-07-10 14:49:42 +03:00
Yuri Kuznetsov 97407de17d remove SAML 2014-07-10 13:22:49 +03:00
Yuri Kuznetsov 84bc5beed2 preferences dynamic form 2014-07-10 13:20:22 +03:00
Yuri Kuznetsov 6974b1637d ldap form 2014-07-10 13:08:16 +03:00
Yuri Kuznetsov dc89aff7b1 dynamic forms 2014-07-10 13:08:10 +03:00
Yuri Kuznetsov 2bfea69fe3 calendar remove event 2014-07-09 14:52:30 +03:00
Yuri Kuznetsov 0e1e481546 calendar timezone 2014-07-09 14:42:16 +03:00
Yuri Kuznetsov 52e938888d Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2014-07-09 14:30:08 +03:00
Yuri Kuznetsov a2ef1656fb fix email template 2014-07-09 14:27:52 +03:00
Yuri Kuznetsov c168c3ff9b fix field-manager 2014-07-09 14:27:45 +03:00
Yuri Kuznetsov 56af0baea2 entityFactory changhe 2014-07-09 14:15:27 +03:00
Taras Machyshyn 35ecf2e144 update tests 2014-07-09 13:33:42 +03:00
Taras Machyshyn d25692b15e added manifest.json checking for upgrade process 2014-07-09 12:13:34 +03:00
Taras Machyshyn 59a66cb8fa LDAP authorization 2014-07-09 11:53:30 +03:00
Yuri Kuznetsov 75ce454b2a tr_TR language 2014-07-09 11:39:03 +03:00
Yuri Kuznetsov b94134a35a data-panel-name 2014-07-08 16:59:41 +03:00
Yuri Kuznetsov 4b09f055e4 snap duration 2014-07-08 15:40:04 +03:00
Yuri Kuznetsov d743eca6a3 calendar 2014-07-08 15:33:07 +03:00
Yuri Kuznetsov 0e3c872310 calendar 2 2014-07-08 15:30:17 +03:00
Yuri Kuznetsov f8b56cba24 get rid of form on edit view 2014-07-08 11:45:15 +03:00
Yuri Kuznetsov b506cd726c fix modal close 2014-07-08 11:31:47 +03:00
Yuri Kuznetsov 998aaec170 upgrade bootstrap to 3.2 2014-07-08 11:20:53 +03:00
Yuri Kuznetsov 4c30507a85 link role css 2014-07-08 11:05:57 +03:00
Yuri Kuznetsov a55f1aca58 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2014-07-07 17:49:57 +03:00
Yuri Kuznetsov 868cf4e22c fix inline edit 2014-07-07 17:47:45 +03:00
Taras Machyshyn 2decd9016e fixed rebuild.php issue 2014-07-07 17:45:31 +03:00
Yuri Kuznetsov 9cba465473 fix prev commit 2014-07-07 17:24:12 +03:00
Yuri Kuznetsov b9344afb27 linkMultipleWithRole css fix 2014-07-07 17:11:06 +03:00
Yuri Kuznetsov 7074991771 email and phone fields remove button 2014-07-07 16:30:36 +03:00
Yuri Kuznetsov b43d37ac79 change layout 2014-07-07 16:21:11 +03:00
Yuri Kuznetsov 18b393b286 activities new logic 2014-07-07 16:17:44 +03:00
Yuri Kuznetsov 728fce75d7 fix linkMultiple css 2014-07-07 14:09:52 +03:00
Yuri Kuznetsov 50d441aea0 account - contact - 5 2014-07-07 11:32:01 +03:00
Yuri Kuznetsov d75f9e5acd fix handleSelectParams 2014-07-04 18:26:52 +03:00
Yuri Kuznetsov 3c96cf240e account - contact 4 2014-07-04 18:13:54 +03:00
Yuri Kuznetsov 2583c77108 accoun - contact 3 2014-07-04 17:37:01 +03:00
Yuri Kuznetsov a3ad210ec2 account - contact 2 2014-07-04 14:55:02 +03:00
Yuri Kuznetsov 6bd7d2398d account - contact 1 2014-07-03 19:12:34 +03:00
Yuri Kuznetsov 017cd3d978 fix rerender fields 2014-07-03 19:12:22 +03:00
Yuri Kuznetsov a3af86d248 fix primary button 2014-07-03 11:19:16 +03:00
Yuri Kuznetsov 99e9cbde84 email and phone fields: hide primary button 2014-07-03 10:44:47 +03:00
Yuri Kuznetsov 85d60d2555 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2014-07-02 17:31:31 +03:00
Yuri Kuznetsov 1fe4555209 phone number 3 2014-07-02 17:19:14 +03:00
Yuri Kuznetsov 8b80df2f91 phone number 2 2014-07-02 16:33:02 +03:00
Yuri Kuznetsov 3d7d4eb7db clone deep in model 2014-07-02 15:53:31 +03:00
Taras Machyshyn 4215621eb7 FieldManager fixes 2014-07-02 13:01:16 +03:00
Yuri Kuznetsov d138a5d0b7 phone number 1 2014-07-02 12:24:42 +03:00
Yuri Kuznetsov b3023192d0 fix search field view and lead source 2014-07-01 18:18:16 +03:00
Yuri Kuznetsov 7cc0c527e0 merge manual with stable 2014-07-01 18:09:52 +03:00
Yuri Kuznetsov f3f36e87a4 Merge branch 'hotfix/1.2.1' into stable 2014-07-01 18:06:29 +03:00
Yuri Kuznetsov b55b133ddf lead source 2 2014-07-01 18:05:38 +03:00
Yuri Kuznetsov 28eafdbc6c version 2014-07-01 17:43:33 +03:00
Yuri Kuznetsov 0036ab1ed2 Merge branch 'hotfix/1.2.1' of ssh://172.20.0.1/var/git/espo/backend into hotfix/1.2.1 2014-07-01 17:42:49 +03:00
Yuri Kuznetsov a60378a9bd lead source 1 2014-07-01 17:42:25 +03:00
Taras Machyshyn 30b246f67c fixed issue with Basic Authorization for cgi/fastcgi 2014-07-01 17:41:26 +03:00
Yuri Kuznetsov 3a23b28657 convert layout 2014-07-01 16:29:34 +03:00
Yuri Kuznetsov 42e21d585f bug with follow button and lead convert 2014-07-01 16:18:27 +03:00
Yuri Kuznetsov a30ab9a1a0 fix notification in admin panel 2014-07-01 15:33:40 +03:00
Yuri Kuznetsov 4510295792 change opp layout 2014-07-01 15:09:22 +03:00
Yuri Kuznetsov ed5de64cd3 roles dev 2014-07-01 15:04:59 +03:00
Yuri Kuznetsov 1c89187c49 update Role 2014-07-01 12:55:43 +03:00
Yuri Kuznetsov 169a3f6e6d -20px for panel 2014-07-01 11:36:27 +03:00
Yuri Kuznetsov 7e2ead7949 clonedeep role 2014-07-01 11:24:58 +03:00
Yuri Kuznetsov 8413067194 role field dev 2014-07-01 10:45:42 +03:00
Yuri Kuznetsov 81c7dcaa63 roles dev 2014-06-30 18:10:13 +03:00
Yuri Kuznetsov 3b12c7f1cf role field dev 2014-06-30 17:17:06 +03:00
Yuri Kuznetsov cdab200898 fix linkMultiple 2014-06-30 16:46:21 +03:00
Yuri Kuznetsov 0f9c3e1c1c getSelectFilters 2014-06-30 16:42:47 +03:00
Yuri Kuznetsov fe5a5b76ca fix linkMultiple fields 2014-06-30 16:23:03 +03:00
Yuri Kuznetsov 0e3720c038 columns for linkMultiple 2014-06-30 16:01:57 +03:00
Yuri Kuznetsov 99f1ba8987 fix layout manager 2014-06-30 12:06:49 +03:00
Yuri Kuznetsov 8ff5d79788 fix array fiel tpl 2014-06-30 11:52:28 +03:00
Yuri Kuznetsov 9d6485194f table header font weight 2014-06-30 11:28:11 +03:00
Yuri Kuznetsov 21e306774e link multiple li padding 2014-06-30 11:25:45 +03:00
Yuri Kuznetsov ffa340f255 contacts field in opportunity 2014-06-30 11:21:31 +03:00
Yuri Kuznetsov d6b65edfb1 fix select related 2014-06-30 11:12:08 +03:00
Yuri Kuznetsov a3ed33a443 filters for select related 2014-06-30 11:09:21 +03:00
Yuri Kuznetsov 38ff477cef stick stream panel 2014-06-27 18:21:40 +03:00
Yuri Kuznetsov c5c638b007 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2014-06-27 18:06:12 +03:00
Yuri Kuznetsov 181fd8dc73 record panel margin 2014-06-27 17:59:46 +03:00
Taras Machyshyn e57f58bec3 fixed label string for .po file 2014-06-27 16:47:10 +03:00
Taras Machyshyn 5c2067f61a chnaged 'php version' label 2014-06-27 16:36:55 +03:00
Yuri Kuznetsov 3ec5cf259b refactor auth 2014-06-27 12:15:54 +03:00
Yuri Kuznetsov e5e898222b Authentification refactor 2014-06-27 12:01:30 +03:00
Yuri Kuznetsov 18b2876853 ability to remove entity in calendar 2014-06-26 17:51:50 +03:00
Yuri Kuznetsov cec4ffcb6c Merge branch 'hotfix/1.2.1' 2014-06-26 17:13:14 +03:00
Yuri Kuznetsov 1063aa74dd fix create related 2014-06-26 17:12:54 +03:00
Yuri Kuznetsov b59dba0e6d fix lang 2014-06-26 16:47:36 +03:00
Yuri Kuznetsov 0d9a182833 change version 2014-06-26 16:45:50 +03:00
Yuri Kuznetsov 8674f66b4d Merge branch 'hotfix/1.2.1' 2014-06-26 16:44:35 +03:00
Yuri Kuznetsov 605a720694 auth tokens page title 2014-06-26 16:44:21 +03:00
Yuri Kuznetsov aa761d5ef7 auth tokens in admin 2014-06-26 16:32:18 +03:00
Yuri Kuznetsov 3d4d8aa7d3 prepareEntityForOutput 2014-06-26 15:19:43 +03:00
Yuri Kuznetsov 556b72edb2 fix install lang 2014-06-24 18:31:10 +03:00
Yuri Kuznetsov be372b90f9 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2014-06-24 18:19:41 +03:00
Yuri Kuznetsov 846e93741f disable targets 2014-06-24 18:15:40 +03:00
Yuri Kuznetsov b82af3a0c6 prospects to targets 2014-06-24 18:13:35 +03:00
Taras Machyshyn 02b429cb32 installer: design changes 2014-06-24 11:43:37 +03:00
Taras Machyshyn 3332469c35 chnaged Config.php . Now use ->save() to save the configuration 2014-06-19 12:42:57 +03:00
Taras Machyshyn 0a2469aaa4 fixed labels 2014-06-17 12:33:02 +03:00
Yuri Kuznetsov e7e15a32bf fix lang 2014-06-16 18:04:03 +03:00
Yuri Kuznetsov fc3fa53dba fix label 2014-06-16 17:59:41 +03:00
Yuri Kuznetsov 4604f27176 fix double metadata load 2014-06-16 11:12:04 +03:00
Yuri Kuznetsov b7b118c122 update timepicker 2014-06-13 16:42:11 +03:00
Yuri Kuznetsov 371f243a6d Merge branch 'stable' 2014-06-13 12:35:28 +03:00
599 changed files with 23259 additions and 7375 deletions
+2
View File
@@ -4,5 +4,7 @@ RedirectMatch 403 \.config$
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_ESPO_CGI_AUTH:%{HTTP:Authorization}]
RewriteRule reset/?$ reset.html [QSA,L]
</IfModule>
+1 -5
View File
@@ -52,8 +52,7 @@ module.exports = function (grunt) {
'frontend/client/lib/base64.js',
'frontend/client/lib/jquery-ui.min.js',
'frontend/client/lib/moment.min.js',
'frontend/client/lib/moment-timezone.min.js',
'frontend/client/lib/moment-timezone-data.js',
'frontend/client/lib/moment-timezone-with-data.min.js',
'frontend/client/lib/jquery.timepicker.min.js',
'frontend/client/lib/jquery.autocomplete.js',
'frontend/client/lib/bootstrap.min.js',
@@ -88,9 +87,6 @@ module.exports = function (grunt) {
'frontend/client/src/models/user.js',
'frontend/client/src/models/preferences.js',
'frontend/client/src/controllers/base.js',
'frontend/client/src/controllers/record.js',
'frontend/client/src/controllers/role.js',
'frontend/client/src/controllers/admin.js',
'frontend/client/src/view.js',
'frontend/client/src/views/base.js',
'frontend/client/src/views/login.js',
+2
View File
@@ -6,5 +6,7 @@ RewriteEngine On
#
# RewriteBase /
RewriteRule .* - [E=HTTP_ESPO_CGI_AUTH:%{HTTP:Authorization}]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]
@@ -0,0 +1,66 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
namespace Espo\Controllers;
use \Espo\Core\Exceptions\Forbidden;
class AuthToken extends \Espo\Core\Controllers\Record
{
protected function checkControllerAccess()
{
if (!$this->getUser()->isAdmin()) {
throw new Forbidden();
}
}
public function actionUpdate($params, $data)
{
throw new Forbidden();
}
public function actionCreate($params, $data)
{
throw new Forbidden();
}
public function actionListLinked($params, $data)
{
throw new Forbidden();
}
public function actionMassUpdate($params, $data)
{
throw new Forbidden();
}
public function actionCreateLink($params, $data)
{
throw new Forbidden();
}
public function actionRemoveLink($params, $data)
{
throw new Forbidden();
}
}
+5
View File
@@ -25,6 +25,7 @@ namespace Espo\Controllers;
use Espo\Core\Utils as Utils;
use \Espo\Core\Exceptions\NotFound;
use \Espo\Core\Exceptions\Error;
use \Espo\Core\Exceptions\Forbidden;
class Layout extends \Espo\Core\Controllers\Base
{
@@ -39,6 +40,10 @@ class Layout extends \Espo\Core\Controllers\Base
public function actionUpdate($params, $data)
{
if (!$this->getUser()->isAdmin()) {
throw new Forbidden();
}
$result = $this->getContainer()->get('layout')->set($data, $params['scope'], $params['name']);
if ($result === false) {
@@ -54,7 +54,13 @@ class Notification extends \Espo\Core\Controllers\Base
public function actionNotReadCount()
{
$userId = $this->getUser()->id;
return $this->getService('Notification')->getNotReadCount($userId);
return $this->getService('Notification')->getNotReadCount($userId);
}
public function actionMarkAllRead($params, $data, $request)
{
$userId = $this->getUser()->id;
return $this->getService('Notification')->markAllRead($userId);
}
}
+7 -6
View File
@@ -30,9 +30,9 @@ class Settings extends \Espo\Core\Controllers\Base
protected function getConfigData()
{
$data = $this->getConfig()->getData($this->getUser()->isAdmin());
$fieldDefs = $this->getMetadata()->get('entityDefs.Settings.fields');
foreach ($fieldDefs as $field => $d) {
if ($d['type'] == 'password') {
unset($data[$field]);
@@ -40,7 +40,7 @@ class Settings extends \Espo\Core\Controllers\Base
}
return $data;
}
public function actionRead($params, $data)
{
return $this->getConfigData();
@@ -52,12 +52,13 @@ class Settings extends \Espo\Core\Controllers\Base
}
public function actionPatch($params, $data)
{
{
if (!$this->getUser()->isAdmin()) {
throw new Forbidden();
}
$result = $this->getConfig()->setData($data, $this->getUser()->isAdmin());
$this->getConfig()->setData($data, $this->getUser()->isAdmin());
$result = $this->getConfig()->save();
if ($result === false) {
throw new Error('Cannot save settings');
}
+1 -4
View File
@@ -128,12 +128,9 @@ class Application
$cronManager = new \Espo\Core\CronManager($this->container);
$cronManager->run();
}
public function runRebuild()
{
$auth = $this->getAuth();
$auth->useNoAuth(true);
$dataManager = $this->getContainer()->get('dataManager');
$dataManager->rebuild();
}
+12 -6
View File
@@ -38,13 +38,17 @@ class Record extends Base
return $this->getContainer()->get('entityManager');
}
protected function getRecordService()
protected function getRecordService($name = null)
{
if ($this->getServiceFactory()->checkExists($this->name)) {
$service = $this->getServiceFactory()->create($this->name);
if (empty($name)) {
$name = $this->name;
}
if ($this->getServiceFactory()->checkExists($name)) {
$service = $this->getServiceFactory()->create($name);
} else {
$service = $this->getServiceFactory()->create('Record');
$service->setEntityName($this->name);
$service->setEntityName($name);
}
return $service;
@@ -178,8 +182,10 @@ class Record extends Base
}
public function actionExport($params, $data, $request)
{
// TODO move to service
{
if ($this->getConfig()->get('disableExport') && !$this->getUser()->isAdmin()) {
throw new Forbidden();
}
if (!$this->getAcl()->check($this->name, 'read')) {
throw new Forbidden();
+23 -3
View File
@@ -24,19 +24,39 @@ namespace Espo\Core\ORM;
class Entity extends \Espo\ORM\Entity
{
public function loadLinkMultipleField($field)
public function loadLinkMultipleField($field, $columns = null)
{
if ($this->hasRelation($field) && $this->hasField($field . 'Ids')) {
$collection = $this->get($field);
$defs = array();
if (!empty($columns)) {
$defs['additionalColumns'] = $columns;
}
$collection = $this->get($field, $defs);
$ids = array();
$names = new \stdClass();
$names = new \stdClass();
if (!empty($columns)) {
$columnsData = new \stdClass();
}
foreach ($collection as $e) {
$id = $e->id;
$ids[] = $id;
$names->$id = $e->get('name');
if (!empty($columns)) {
$columnsData->$id = new \stdClass();
foreach ($columns as $column => $f) {
$columnsData->$id->$column = $e->get($f);
}
}
}
$this->set($field . 'Ids', $ids);
$this->set($field . 'Names', $names);
if (!empty($columns)) {
$this->set($field . 'Columns', $columnsData);
}
}
}
}
+6 -1
View File
@@ -56,7 +56,12 @@ class EntityManager extends \Espo\ORM\EntityManager
public function getUser()
{
return $this->user;
}
}
public function getEspoMetadata()
{
return $this->espoMetadata;
}
public function setEspoMetadata($espoMetadata)
{
+78 -13
View File
@@ -59,16 +59,15 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
return $this->getInjection('metadata');
}
protected function handleSelectParams(&$params, $entityName = false)
public function handleSelectParams(&$params)
{
$this->handleEmailAddressParams($params, $entityName);
$this->handleEmailAddressParams($params);
$this->handlePhoneNumberParams($params);
}
protected function handleEmailAddressParams(&$params, $entityName = false)
protected function handleEmailAddressParams(&$params)
{
if (empty($entityName)) {
$entityName = $this->entityName;
}
$entityName = $this->entityName;
$defs = $this->getEntityManager()->getMetadata()->get($entityName);
if (!empty($defs['relations']) && array_key_exists('emailAddresses', $defs['relations'])) {
@@ -81,11 +80,31 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
if (empty($params['joinConditions'])) {
$params['joinConditions'] = array();
}
$params['leftJoins'] = array('emailAddresses');
$params['joinConditions'] = array(
'emailAddresses' => array(
'primary' => 1
)
$params['leftJoins'][] = 'emailAddresses';
$params['joinConditions']['emailAddresses'] = array(
'primary' => 1
);
}
}
protected function handlePhoneNumberParams(&$params)
{
$entityName = $this->entityName;
$defs = $this->getEntityManager()->getMetadata()->get($entityName);
if (!empty($defs['relations']) && array_key_exists('phoneNumbers', $defs['relations'])) {
if (empty($params['leftJoins'])) {
$params['leftJoins'] = array();
}
if (empty($params['whereClause'])) {
$params['whereClause'] = array();
}
if (empty($params['joinConditions'])) {
$params['joinConditions'] = array();
}
$params['leftJoins'][] = 'phoneNumbers';
$params['joinConditions']['phoneNumbers'] = array(
'primary' => 1
);
}
}
@@ -172,6 +191,7 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
$entity->set($restoreData);
$this->handleEmailAddressSave($entity);
$this->handlePhoneNumberSave($entity);
$this->handleSpecifiedRelations($entity);
return $result;
@@ -183,6 +203,13 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
$emailAddressRepository = $this->getEntityManager()->getRepository('EmailAddress')->storeEntityEmailAddress($entity);
}
}
protected function handlePhoneNumberSave(Entity $entity)
{
if ($entity->hasRelation('phoneNumbers') && $entity->hasField('phoneNumber')) {
$emailAddressRepository = $this->getEntityManager()->getRepository('PhoneNumber')->storeEntityPhoneNumber($entity);
}
}
protected function handleSpecifiedRelations(Entity $entity)
{
@@ -195,22 +222,60 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
if (is_array($specifiedIds)) {
$toRemoveIds = array();
$existingIds = array();
foreach ($entity->get($name) as $foreignEntity) {
$toUpdateIds = array();
$existingColumnsData = new \stdClass();
$defs = array();
$columns = $this->getMetadata()->get("entityDefs." . $entity->getEntityName() . ".fields.{$name}.columns");
if (!empty($columns)) {
$columnData = $entity->get($name . 'Columns');
$defs['additionalColumns'] = $columns;
}
foreach ($entity->get($name, $defs) as $foreignEntity) {
$existingIds[] = $foreignEntity->id;
if (!empty($columns)) {
$data = new \stdClass();
foreach ($columns as $columnName => $columnField) {
$foreignId = $foreignEntity->id;
$data->$columnName = $foreignEntity->get($columnField);
}
$existingColumnsData->$foreignId = $data;
}
}
foreach ($existingIds as $id) {
if (!in_array($id, $specifiedIds)) {
$toRemoveIds[] = $id;
} else {
if (!empty($columns)) {
foreach ($columns as $columnName => $columnField) {
if ($columnData->$id->$columnName != $existingColumnsData->$id->$columnName) {
$toUpdateIds[] = $id;
}
}
}
}
}
foreach ($specifiedIds as $id) {
if (!in_array($id, $existingIds)) {
$this->relate($entity, $name, $id);
$data = null;
if (!empty($columns)) {
$data = $columnData->$id;
}
$this->relate($entity, $name, $id, $data);
}
}
foreach ($toRemoveIds as $id) {
$this->unrelate($entity, $name, $id);
}
if (!empty($columns)) {
foreach ($toUpdateIds as $id) {
$data = $columnData->$id;
$this->updateRelation($entity, $name, $id, $data);
}
}
}
}
}
+35 -4
View File
@@ -82,6 +82,11 @@ class Base
}
}
}
protected function getTextFilterFields()
{
return $this->metadata->get("entityDefs.{$this->entityName}.collection.textFilterFields", array('name'));
}
protected function where($params, &$result)
{
@@ -96,6 +101,18 @@ class Base
$params['where'][] = $p;
}
}
} else if ($item['type'] == 'textFilter' && !empty($item['value'])) {
if (!empty($item['value'])) {
if (empty($result['whereClause'])) {
$result['whereClause'] = array();
}
$fieldList = $this->getTextFilterFields();
$d = array();
foreach ($fieldList as $field) {
$d[$field . '*'] = $item['value'] . '%';
}
$where['OR'] = $d;
}
}
}
@@ -108,7 +125,7 @@ class Base
$where[] = $part;
}
} else {
if ($item['type'] == 'linkedWith') {
if ($item['type'] == 'linkedWith' && !empty($item['value'])) {
$linkedWith[$item['field']] = $item['value'];
}
}
@@ -159,18 +176,23 @@ class Base
}
$result['whereClause']['assignedUserId'] = $this->user->id;
}
if ($this->acl->checkReadOnlyTeam($this->entityName)) {
if (!$this->user->isAdmin() && $this->acl->checkReadOnlyTeam($this->entityName)) {
if (!array_key_exists('whereClause', $result)) {
$result['whereClause'] = array();
}
$result['distinct'] = true;
if (!array_key_exists('joins', $result)) {
$result['joins'] = array();
}
if (!in_array('teams', $result['joins'])) {
$result['joins'][] = 'teams';
$result['leftJoins'][] = 'teams';
}
$result['whereClause']['Team.id'] = $this->user->get('teamsIds');
$result['whereClause']['OR'] = array(
'Team.id' => $this->user->get('teamsIds'),
'assignedUserId' => $this->user->id
);
//$result['whereClause']['Team.id'] = $this->user->get('teamsIds');
}
}
@@ -255,6 +277,15 @@ class Base
case 'isFalse':
$part[$item['field'] . '='] = false;
break;
case 'today':
$part[$item['field'] . '='] = date('Y-m-d');
break;
case 'past':
$part[$item['field'] . '<'] = date('Y-m-d');
break;
case 'future':
$part[$item['field'] . '>'] = date('Y-m-d');
break;
case 'between':
if (is_array($item['value'])) {
$part['AND'] = array(
+20 -1
View File
@@ -26,7 +26,11 @@ use \Espo\Core\Interfaces\Injectable;
abstract class Base implements Injectable
{
protected $dependencies = array();
protected $dependencies = array(
'config',
'entityManager',
'user',
);
protected $injections = array();
@@ -53,5 +57,20 @@ abstract class Base implements Injectable
{
return $this->dependencies;
}
protected function getEntityManager()
{
return $this->getInjection('entityManager');
}
protected function getConfig()
{
return $this->getInjection('config');
}
protected function getUser()
{
return $this->getInjection('user');
}
}
+10 -1
View File
@@ -316,7 +316,13 @@ abstract class Base
{
if (!isset($this->data['manifest'])) {
$upgradePath = $this->getUpgradePath();
$manifestJson = $this->getFileManager()->getContents(array($upgradePath, $this->manifestName));
$manifestPath = Util::concatPath($upgradePath, $this->manifestName);
if (!file_exists($manifestPath)) {
throw new Error('It\'s not an uprgade package.');
}
$manifestJson = $this->getFileManager()->getContents($manifestPath);
$this->data['manifest'] = Json::decode($manifestJson, true);
if (!$this->data['manifest']) {
@@ -375,6 +381,9 @@ abstract class Base
$manifest = $this->getManifest();
$res = $this->getConfig()->set('version', $manifest['version']);
if (method_exists($this->getConfig(), 'save')) {
$res = $this->getConfig()->save();
}
$res &= $this->getContainer()->get('dataManager')->rebuild();
return $res;
+6 -3
View File
@@ -51,9 +51,12 @@ class Auth extends \Slim\Middleware
$espoAuth = $req->headers('HTTP_ESPO_AUTHORIZATION');
if (isset($espoAuth)) {
$credentials = explode(':', base64_decode($espoAuth));
$authUsername = $credentials[0];
$authPassword = $credentials[1];
list($authUsername, $authPassword) = explode(':', base64_decode($espoAuth));
}
$espoCgiAuth = $req->headers('HTTP_ESPO_CGI_AUTH');
if ( !isset($authUsername) && !isset($authPassword) && isset($espoCgiAuth) ) {
list($authUsername, $authPassword) = explode(':' , base64_decode(substr($espoCgiAuth, 6)));
}
if (is_null($this->authRequired)) {
+39 -36
View File
@@ -18,91 +18,94 @@
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
************************************************************************/
namespace Espo\Core\Utils;
use \Espo\Core\Exceptions\Error;
class Auth
class Auth
{
protected $container;
protected $authentication;
protected $config;
protected $entityManager;
public function __construct(\Espo\Core\Container $container)
{
$this->container = $container;
$this->entityManager = $this->container->get('entityManager');
$this->config = $this->container->get('config');
$authenticationMethod = $this->config->get('authenticationMethod', 'Espo');
$authenticationClassName = "\\Espo\\Core\\Utils\\Authentication\\" . $authenticationMethod;
$this->authentication = new $authenticationClassName($this->config, $this->entityManager, $this);
}
public function useNoAuth($isAdmin = false)
{
$entityManager = $this->container->get('entityManager');
$entityManager = $this->container->get('entityManager');
$user = $entityManager->getRepository('User')->get('system');
$user->set('isAdmin', $isAdmin);
$user->set('isAdmin', $isAdmin);
if (!$user) {
throw new Error('System user is not found');
throw new Error('System user is not found');
}
$entityManager->setUser($user);
$this->container->setUser($user);
}
public function login($username, $password)
{
$GLOBALS['log']->debug('AUTH: Try to authenticate');
$hash = md5($password);
$entityManager = $this->container->get('entityManager');
$entityManager = $this->entityManager;
$authToken = $entityManager->getRepository('AuthToken')->where(array('token' => $password))->findOne();
if ($authToken) {
$hash = $authToken->get('hash');
}
$user = $entityManager->getRepository('User')->findOne(array(
'whereClause' => array(
'userName' => $username,
'password' => $hash
),
));
$user = $this->authentication->login($username, $password, $authToken);
if ($user) {
$entityManager->setUser($user);
$this->container->setUser($user);
$GLOBALS['log']->debug('AUTH: Result of authenticate is [true]');
if (!$authToken) {
$authToken = $entityManager->getEntity('AuthToken');
$token = $this->createToken($user);
$authToken->set('token', $token);
$authToken->set('hash', $user->get('password'));
$authToken->set('ipAddress', $_SERVER['REMOTE_ADDR']);
$authToken->set('userId', $user->id);
$authToken->set('userId', $user->id);
}
$authToken->set('lastAccess', date('Y-m-d H:i:s'));
$entityManager->saveEntity($authToken);
$user->set('token', $authToken->get('token'));
return true;
}
}
protected function createToken($user)
{
return md5(uniqid($user->get('password')));
return md5(uniqid($user->get('id')));
}
public function destroyAuthToken($token)
{
{
$entityManager = $this->container->get('entityManager');
$authToken = $entityManager->getRepository('AuthToken')->where(array('token' => $token))->findOne();
if ($authToken) {
$entityManager->removeEntity($authToken);
return true;
return true;
}
}
}
@@ -0,0 +1,60 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
namespace Espo\Core\Utils\Authentication;
use \Espo\Core\Utils\Config;
use \Espo\Core\ORM\EntityManager;
use \Espo\Core\Utils\Auth;
abstract class Base
{
private $config;
private $entityManager;
private $auth;
public function __construct(Config $config, EntityManager $entityManager, Auth $auth)
{
$this->config = $config;
$this->entityManager = $entityManager;
$this->auth = $auth;
}
protected function getConfig()
{
return $this->config;
}
protected function getEntityManager()
{
return $this->entityManager;
}
protected function getAuth()
{
return $this->auth;
}
}
@@ -0,0 +1,48 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
namespace Espo\Core\Utils\Authentication;
use \Espo\Core\Exceptions\Error;
class Espo extends Base
{
public function login($username, $password, \Espo\Entities\AuthToken $authToken = null)
{
if ($authToken) {
$hash = $authToken->get('hash');
} else {
$hash = md5($password);
}
$user = $this->getEntityManager()->getRepository('User')->findOne(array(
'whereClause' => array(
'userName' => $username,
'password' => $hash
),
));
return $user;
}
}
@@ -0,0 +1,201 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
namespace Espo\Core\Utils\Authentication;
use Espo\Core\Exceptions\Error,
Espo\Core\Utils\Config,
Espo\Core\ORM\EntityManager,
Espo\Core\Utils\Auth;
class LDAP extends Base
{
private $utils;
private $zendLdap;
/**
* Espo => LDAP name
*
* @var array
*/
private $fields = array(
'userName' => 'cn',
'firstName' => 'givenname',
'lastName' => 'sn',
'title' => 'title',
'emailAddress' => 'mail',
'phoneNumber' => 'telephonenumber',
);
public function __construct(Config $config, EntityManager $entityManager, Auth $auth)
{
parent::__construct($config, $entityManager, $auth);
$this->zendLdap = new LDAP\LDAP();
$this->utils = new LDAP\Utils($config);
}
protected function getZendLdap()
{
return $this->zendLdap;
}
protected function getUtils()
{
return $this->utils;
}
/**
* LDAP login
*
* @param string $username
* @param string $password
* @param \Espo\Entities\AuthToken $authToken
* @return \Espo\Entities\User | null
*/
public function login($username, $password, \Espo\Entities\AuthToken $authToken = null)
{
if ($authToken) {
return $this->loginByToken($username, $authToken);
}
$options = $this->getUtils()->getZendOptions();
$ldap = $this->getZendLdap();
$ldap = $ldap->setOptions($options);
try {
$ldap->bind($username, $password);
$dn = $ldap->getDn($username);
$loginFilter = $this->getUtils()->getOption('userLoginFilter');
$userData = $ldap->searchByLoginFilter($loginFilter, $dn, 3);
} catch (\Zend\Ldap\Exception\LdapException $zle) {
$admin = $this->adminLogin($username, $password);
if (!isset($admin)) {
$GLOBALS['log']->info('LDAP Authentication: ' . $zle->getMessage());
return null;
}
$GLOBALS['log']->info('LDAP Authentication: Administrator login by username ['.$username.']');
}
$user = $this->getEntityManager()->getRepository('User')->findOne(array(
'whereClause' => array(
'userName' => $username,
),
));
$isCreateUser = $this->getUtils()->getOption('createEspoUser');
if (!isset($user) && $isCreateUser) {
$this->getAuth()->useNoAuth(); /** Required to fix Acl "isFetched()" error */
$user = $this->createUser($userData);
}
return $user;
}
/**
* Login by authorization token
*
* @param string $username
* @param \Espo\Entities\AuthToken $authToken
* @return \Espo\Entities\User | null
*/
protected function loginByToken($username, \Espo\Entities\AuthToken $authToken = null)
{
if (!isset($authToken)) {
return null;
}
$userId = $authToken->get('userId');
$user = $this->getEntityManager()->getEntity('User', $userId);
$tokenUsername = $user->get('userName');
if ($username != $tokenUsername) {
$GLOBALS['log']->alert('Unauthorized access attempt for user ['.$username.'] from IP ['.$_SERVER['REMOTE_ADDR'].']');
return null;
}
$user = $this->getEntityManager()->getRepository('User')->findOne(array(
'whereClause' => array(
'userName' => $username,
),
));
return $user;
}
/**
* Login user with administrator rights
*
* @param string $username
* @param string $password
* @return \Espo\Entities\User | null
*/
protected function adminLogin($username, $password)
{
$hash = md5($password);
$user = $this->getEntityManager()->getRepository('User')->findOne(array(
'whereClause' => array(
'userName' => $username,
'password' => $hash,
'isAdmin' => 1
),
));
return $user;
}
/**
* Create Espo user with data gets from LDAP server
*
* @param array $userData LDAP entity data
* @return \Espo\Entities\User
*/
protected function createUser(array $userData)
{
$data = array();
foreach ($this->fields as $espo => $ldap) {
if (isset($userData[$ldap][0])) {
$data[$espo] = $userData[$ldap][0];
}
}
$user = $this->getEntityManager()->getEntity('User');
$user->set($data);
$this->getEntityManager()->saveEntity($user);
return $user;
}
}
@@ -0,0 +1,123 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
namespace Espo\Core\Utils\Authentication\LDAP;
class LDAP extends \Zend\Ldap\Ldap
{
protected $usernameAttribute = 'cn';
/**
* Get DN depends on options, ex. "cn=test,ou=People,dc=maxcrc,dc=com"
*
* @return string DN format
*/
public function getDn($acctname)
{
return $this->getAccountDn($acctname, \Zend\Ldap\Ldap::ACCTNAME_FORM_DN);
}
/**
* Fix a bug, ex. CN=Alice Baker,CN=Users,DC=example,DC=com
*
* @param string $acctname
* @return string - Account DN
*/
protected function getAccountDn($acctname)
{
$baseDn = $this->getBaseDn();
if ($this->getBindRequiresDn() && isset($baseDn)) {
try {
return parent::getAccountDn($acctname);
} catch (\Zend\Ldap\Exception\LdapException $zle) {
if ($zle->getCode() != \Zend\Ldap\Exception\LdapException::LDAP_NO_SUCH_OBJECT) {
throw $zle;
}
}
$acctname = $this->usernameAttribute . '=' . \Zend\Ldap\Filter\AbstractFilter::escapeValue($acctname) . ',' . $baseDn;
}
return parent::getAccountDn($acctname);
}
/**
* Search a user using userLoginFilter
*
* @param string $filter
* @param string $basedn
* @param int $scope
* @param array $attributes
* @return array
*/
public function searchByLoginFilter($filter, $basedn = null, $scope = self::SEARCH_SCOPE_SUB, array $attributes = array())
{
$filter = $this->getLoginFilter($filter);
$result = $this->search($filter, $basedn, $scope, $attributes);
if ($result->count() > 0) {
return $result->getFirst();
}
throw new \Zend\Ldap\Exception\LdapException($this, 'searching: ' . $filter);
}
/**
* Get login filter in LDAP format
*
* @param string $filter
* @return string
*/
protected function getLoginFilter($filter)
{
$baseFilter = '(objectClass=*)';
if (!empty($filter)) {
$baseFilter = '(&' . $baseFilter . $this->convertToFilterFormat($filter). ')';
}
return $baseFilter;
}
/**
* Check and convert filter item in LDAP format
*
* @param string $filter [description]
* @return string
*/
protected function convertToFilterFormat($filter)
{
$filter = trim($filter);
if (substr($filter, 0, 1) != '(') {
$filter = '(' . $filter;
}
if (substr($filter, -1) != ')') {
$filter = $filter . ')';
}
return $filter;
}
}
@@ -0,0 +1,155 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
namespace Espo\Core\Utils\Authentication\LDAP;
use \Espo\Core\Utils\Config;
class Utils
{
private $config;
protected $options = null;
/**
* Association between LDAP and Espo fields
* @var array
*/
protected $fieldMap = array(
'host' => 'ldapHost',
'port' => 'ldapPort',
'useSsl' => 'ldapSecurity',
'useStartTls' => 'ldapSecurity',
'username' => 'ldapUsername',
'password' => 'ldapPassword',
'bindRequiresDn' => 'ldapBindRequiresDn',
'baseDn' => 'ldapBaseDn',
'accountCanonicalForm' => 'ldapAccountCanonicalForm',
'accountDomainName' => 'ldapAccountDomainName',
'accountDomainNameShort' => 'ldapAccountDomainNameShort',
'accountFilterFormat' => 'ldapAccountFilterFormat',
'optReferrals' => 'ldapOptReferrals',
'tryUsernameSplit' => 'ldapTryUsernameSplit',
'networkTimeout' => 'ldapNetworkTimeout',
'createEspoUser' => 'ldapCreateEspoUser',
'userLoginFilter' => 'ldapUserLoginFilter',
);
/**
* Permitted Espo Options
*
* @var array
*/
protected $permittedEspoOptions = array(
'createEspoUser' => false,
'userLoginFilter' => null,
);
/**
* accountCanonicalForm Map between Espo and Zend value
*
* @var array
*/
protected $accountCanonicalFormMap = array(
'Dn' => 1,
'Username' => 2,
'Backslash' => 3,
'Principal' => 4,
);
public function __construct(Config $config)
{
$this->config = $config;
}
protected function getConfig()
{
return $this->config;
}
/**
* Get Options from espo config according to $this->fieldMap
*
* @return array
*/
public function getOptions()
{
if (isset($this->options)) {
return $this->options;
}
$options = array();
foreach ($this->fieldMap as $ldapName => $espoName) {
$option = $this->getConfig()->get($espoName);
if (isset($option)) {
$options[$ldapName] = $option;
}
}
/** peculiar fields */
$options['useSsl'] = (bool) ($options['useSsl'] == 'SSL');
$options['useStartTls'] = (bool) ($options['useStartTls'] == 'TLS');
$options['accountCanonicalForm'] = $this->accountCanonicalFormMap[ $options['accountCanonicalForm'] ];
$this->options = $options;
return $this->options;
}
/**
* Get an ldap option
*
* @param string $name
* @param mixed $returns Return value
* @return mixed
*/
public function getOption($name, $returns = null)
{
if (isset($this->options)) {
$this->getOptions();
}
if (isset($this->options[$name])) {
return $this->options[$name];
}
return $returns;
}
/**
* Get Zend options for using Zend\Ldap
*
* @return array
*/
public function getZendOptions()
{
$options = $this->getOptions();
$espoOptions = array_keys($this->permittedEspoOptions);
$zendOptions = array_diff_key($options, array_flip($espoOptions));
return $zendOptions;
}
}
+24 -25
View File
@@ -53,7 +53,9 @@ class Config
* @access private
* @var array
*/
private $configData;
private $data;
private $changedData = array();
private $fileManager;
@@ -109,28 +111,25 @@ class Config
$name = array($name => $value);
}
return $this->setArray($name);
foreach ($name as $key => $value) {
$this->data[$key] = $value;
$this->changedData[$key] = $value;
}
}
/**
* Set options from array
*
* @param array $values
* @return bool
*/
protected function setArray($values)
public function save()
{
if (!is_array($values)) {
return false;
}
$values = $this->changedData;
if (!isset($values[$this->cacheTimestamp])) {
$values = array_merge($this->updateCacheTimestamp(true), $values);
}
$result = $this->getFileManager()->mergeContentsPHP($this->configPath, $values, true);
$this->loadConfig(true);
if ($result) {
$this->changedData = array();
$this->loadConfig(true);
}
return $result;
}
@@ -147,18 +146,18 @@ class Config
*/
protected function loadConfig($reload = false)
{
if (!$reload && isset($this->configData) && !empty($this->configData)) {
return $this->configData;
if (!$reload && isset($this->data) && !empty($this->data)) {
return $this->data;
}
$configPath = file_exists($this->configPath) ? $this->configPath : $this->defaultConfigPath;
$this->configData = $this->getFileManager()->getContents($configPath);
$this->data = $this->getFileManager()->getContents($configPath);
$systemConfig = $this->getFileManager()->getContents($this->systemConfigPath);
$this->configData = Util::merge($systemConfig, $this->configData);
$this->data = Util::merge($systemConfig, $this->data);
return $this->configData;
return $this->data;
}
@@ -170,9 +169,9 @@ class Config
*/
public function getData($isAdmin = false)
{
$configData = $this->loadConfig();
$data = $this->loadConfig();
$restrictedConfig = $configData;
$restrictedConfig = $data;
foreach($this->getRestrictItems($isAdmin) as $name) {
if (isset($restrictedConfig[$name])) {
unset($restrictedConfig[$name]);
@@ -200,7 +199,7 @@ class Config
}
}
return $this->setArray($values);
return $this->set($values);
}
/**
@@ -229,14 +228,14 @@ class Config
*/
protected function getRestrictItems($onlySystemItems = false)
{
$configData = $this->loadConfig();
$data = $this->loadConfig();
if ($onlySystemItems) {
return $configData['systemItems'];
return $data['systemItems'];
}
if (empty($this->adminItems)) {
$this->adminItems= Util::merge($configData['systemItems'], $configData['adminItems']);
$this->adminItems= Util::merge($data['systemItems'], $data['adminItems']);
}
return $this->adminItems;
+183 -50
View File
@@ -26,18 +26,21 @@ use Espo\Core\Utils\Util;
class Base
{
private $itemName = null;
private $entityName = null;
private $metadata;
private $params;
private $ormEntityDefs;
private $foreignParams;
private $entityDefs;
protected $allowParams = array();
public function __construct(\Espo\Core\Utils\Metadata $metadata)
public function __construct(\Espo\Core\Utils\Metadata $metadata, array $ormEntityDefs, array $entityDefs)
{
$this->metadata = $metadata;
$this->ormEntityDefs = $ormEntityDefs;
$this->entityDefs = $entityDefs;
}
protected function getMetadata()
@@ -45,80 +48,197 @@ class Base
return $this->metadata;
}
protected function getParams()
protected function getOrmEntityDefs()
{
return $this->params;
return $this->ormEntityDefs;
}
protected function getForeignParams()
protected function getEntityDefs()
{
return $this->foreignParams;
return $this->entityDefs;
}
protected function setParams(array $params)
/**
* Set current Field name OR Link name
*
* @param void
*/
protected function setItemName($itemName)
{
$this->params = $params;
$this->itemName = $itemName;
}
protected function setForeignParams(array $foreignParams)
/**
* Get current Field name
*
* @return string
*/
protected function getFieldName()
{
$this->foreignParams = $foreignParams;
return $this->itemName;
}
public function process($params, $foreignParams)
/**
* Get current Link name
*
* @return string
*/
protected function getLinkName()
{
$this->setParams($params);
$this->setForeignParams($foreignParams);
$loads = $this->load($params, $foreignParams);
$loads = $this->mergeAllowedParams($loads);
return $loads;
return $this->itemName;
}
private function mergeAllowedParams($loads)
/**
* Set current Entity Name
*
* @param void
*/
protected function setEntityName($entityName)
{
$params = $this->getParams();
$this->entityName = $entityName;
}
if (!empty($this->allowParams)) {
$linkParams = &$loads[$params['entityName']] ['relations'] [$params['link']['name']];
/**
* Get current Entity Name
*
* @return string
*/
protected function getEntityName()
{
return $this->entityName;
}
foreach ($this->allowParams as $name) {
$additionalParrams = $this->getAllowedAdditionalParams($name);
if (isset($additionalParrams) && !isset($linkParams[$name])) {
$linkParams[$name] = $additionalParrams;
}
protected function setMethods(array $keyValueList)
{
foreach ($keyValueList as $key => $value) {
$methodName = 'set' . ucfirst($key);
if (method_exists($this, $methodName)) {
$this->$methodName($value);
}
}
return $loads;
}
private function getAllowedAdditionalParams($allowedItemName)
/**
* Start process Orm converting for fields/relations
*
* @param string $itemName Field name OR Link name
* @param string $entityName
* @return array
*/
public function process($itemName, $entityName)
{
$params = $this->getParams();
$foreignParams = $this->getForeignParams();
$inputs = array(
'itemName' => $itemName,
'entityName' => $entityName,
);
$this->setMethods($inputs);
$linkParams = isset($params['link']['params'][$allowedItemName]) ? $params['link']['params'][$allowedItemName] : null;
$foreignLinkParams = isset($foreignParams['link']['params'][$allowedItemName]) ? $foreignParams['link']['params'][$allowedItemName] : null;
$convertedDefs = $this->load($itemName, $entityName);
$additionalParrams = null;
$inputs = $this->setArrayValue(null, $inputs);
$this->setMethods($inputs);
if (isset($linkParams) && isset($foreignLinkParams)) {
$additionalParrams = Util::merge($linkParams, $foreignLinkParams);
} else if (isset($linkParams)) {
$additionalParrams = $linkParams;
} else if (isset($foreignLinkParams)) {
$additionalParrams = $foreignLinkParams;
return $convertedDefs;
}
/**
* Get Entity Defs by type (entity/orm)
*
* @param boolean $isOrmEntityDefs
* @return array
*/
protected function getDefs($isOrmEntityDefs = false)
{
$entityDefs = $isOrmEntityDefs ? $this->getOrmEntityDefs() : $this->getEntityDefs();
return $entityDefs;
}
/**
* Get entity params by name
*
* @param string $entityName
* @param bool $isOrmEntityDefs
* @param mixed $returns
* @return mixed
*/
protected function getEntityParams($entityName = null, $isOrmEntityDefs = false, $returns = null)
{
if (!isset($entityName)) {
$entityName = $this->getEntityName();
}
return $additionalParrams;
$entityDefs = $this->getDefs($isOrmEntityDefs);
if (isset($entityDefs[$entityName])) {
return $entityDefs[$entityName];
}
return $returns;
}
/**
* Get field params by name for a specified entity
*
* @param string $fieldName
* @param string $entityName
* @param bool $isOrmEntityDefs
* @param mixed $returns
* @return mixed
*/
protected function getFieldParams($fieldName = null, $entityName = null, $isOrmEntityDefs = false, $returns = null)
{
if (!isset($fieldName)) {
$fieldName = $this->getFieldName();
}
if (!isset($entityName)) {
$entityName = $this->getEntityName();
}
$entityDefs = $this->getDefs($isOrmEntityDefs);
if (isset($entityDefs[$entityName]) && isset($entityDefs[$entityName]['fields'][$fieldName])) {
return $entityDefs[$entityName]['fields'][$fieldName];
}
return $returns;
}
/**
* Get relation params by name for a specified entity
*
* @param string $linkName
* @param string $entityName
* @param bool $isOrmEntityDefs
* @param mixed $returns
* @return mixed
*/
protected function getLinkParams($linkName = null, $entityName = null, $isOrmEntityDefs = false, $returns = null)
{
if (!isset($linkName)) {
$linkName = $this->getLinkName();
}
if (!isset($entityName)) {
$entityName = $this->getEntityName();
}
$entityDefs = $this->getDefs($isOrmEntityDefs);
$relationKeyName = $isOrmEntityDefs ? 'relations' : 'links';
if (isset($entityDefs[$entityName]) && isset($entityDefs[$entityName][$relationKeyName][$linkName])) {
return $entityDefs[$entityName][$relationKeyName][$linkName];
}
return $returns;
}
/**
* Get Foreign field
*
* @param string $name
* @param string $entityName
* @return string
*/
protected function getForeignField($name, $entityName)
{
$foreignField = $this->getMetadata()->get('entityDefs.'.$entityName.'.fields.'.$name);
@@ -141,6 +261,19 @@ class Base
return $name;
}
/**
* Set a value for all elements of array. So, in result all elements will have the same values
*
* @param string $value
* @param array $array
*/
protected function setArrayValue($inputValue, array $array)
{
foreach ($array as &$value) {
$value = $inputValue;
}
return $array;
}
}
@@ -33,6 +33,8 @@ class Converter
private $relationManager;
private $entityDefs;
protected $defaultFieldType = 'varchar';
protected $defaultNaming = 'postfix';
@@ -69,6 +71,9 @@ class Converter
'default' => '{0}',
),
),
'select' => 'select',
'orderBy' => 'orderBy',
'where' => 'where',
);
protected $idParams = array(
@@ -85,6 +90,8 @@ class Converter
$this->relationManager = new RelationManager($this->metadata);
$this->metadataUtils = new \Espo\Core\Utils\Metadata\Utils($this->metadata);
$this->entityDefs = $this->getMetadata()->get('entityDefs');
}
@@ -93,6 +100,11 @@ class Converter
return $this->metadata;
}
protected function getEntityDefs()
{
return $this->entityDefs;
}
protected function getFileManager()
{
return $this->fileManager;
@@ -108,11 +120,9 @@ class Converter
return $this->metadataUtils;
}
public function process()
{
$entityDefs = $this->getMetadata()->get('entityDefs');
$entityDefs = $this->getEntityDefs();
$ormMeta = array();
foreach($entityDefs as $entityName => $entityMeta) {
@@ -122,7 +132,7 @@ class Converter
continue;
}
$ormMeta = Util::merge($ormMeta, $this->convertEntity($entityName, $entityMeta, $entityDefs));
$ormMeta = Util::merge($ormMeta, $this->convertEntity($entityName, $entityMeta));
}
$ormMeta = $this->afterProcess($ormMeta);
@@ -130,10 +140,7 @@ class Converter
return $ormMeta;
}
//convertToDatabaseFormat
protected function convertEntity($entityName, $entityMeta, $entityDefs)
protected function convertEntity($entityName, $entityMeta)
{
$ormMeta = array();
$ormMeta[$entityName] = array(
@@ -145,18 +152,19 @@ class Converter
$ormMeta[$entityName]['fields'] = $this->convertFields($entityName, $entityMeta);
$convertedLinks = $this->convertLinks($entityName, $entityMeta, $entityDefs);
$convertedLinks = $this->convertLinks($entityName, $entityMeta, $ormMeta);
$ormMeta = Util::merge($ormMeta, $convertedLinks);
return $ormMeta;
}
public function afterProcess(array $meta)
public function afterProcess(array $ormMeta)
{
$entityDefs = $this->getEntityDefs();
//load custom field definitions and customCodes
foreach($meta as $entityName => &$entityParams) {
foreach($ormMeta as $entityName => &$entityParams) {
foreach($entityParams['fields'] as $fieldName => $fieldParams) {
//load custom field definitions
@@ -167,17 +175,14 @@ class Converter
}
if (class_exists($className) && method_exists($className, 'load')) {
$helperClass = new $className($this->metadata);
$fieldResult = $helperClass->load(
array('name' => $entityName, 'params' => $entityParams),
array('name' => $fieldName, 'params' => $fieldParams)
);
$helperClass = new $className($this->metadata, $ormMeta, $entityDefs);
$fieldResult = $helperClass->process( $fieldName, $entityName );
if (isset($fieldResult['unset'])) {
$meta = Util::unsetInArray($meta, $fieldResult['unset']);
$ormMeta = Util::unsetInArray($ormMeta, $fieldResult['unset']);
unset($fieldResult['unset']);
}
$meta = Util::merge($meta, $fieldResult);
$ormMeta = Util::merge($ormMeta, $fieldResult);
} //END: load custom field definitions
@@ -196,7 +201,7 @@ class Converter
}
}
foreach($meta as $entityName => &$entityParams) {
foreach($ormMeta as $entityName => &$entityParams) {
foreach($entityParams['fields'] as $fieldName => &$fieldParams) {
switch ($fieldParams['type']) {
@@ -224,10 +229,9 @@ class Converter
}
return $meta;
return $ormMeta;
}
/**
* Metadata conversion from Espo format into Doctrine
*
@@ -293,8 +297,6 @@ class Converter
return $outputMeta;
}
protected function convertField($entityName, $fieldName, array $fieldParams, $fieldTypeMeta = null)
{
/** set default type if exists */
@@ -376,34 +378,20 @@ class Converter
return $subField;
}
protected function convertLinks($entityName, $entityMeta, array $entityDefs)
protected function convertLinks($entityName, $entityMeta, $ormMeta)
{
if (!isset($entityMeta['links'])) {
return array();
}
$entityDefs = $this->getEntityDefs();
$relationships = array();
foreach($entityMeta['links'] as $linkName => $linkParams) {
$linkEntityName = $this->getRelationManager()->getLinkEntityName($entityName, $linkParams);
$convertedLink = $this->getRelationManager()->convert($linkName, $linkParams, $entityName, $ormMeta);
$currentType = $linkParams['type'];
$foreignLink = $this->getForeignLink($linkName, $linkParams, $entityDefs[$linkEntityName]);
$method = $currentType;
if ($foreignLink !== false) {
$method .= '-'.$foreignLink['params']['type'];
}
$method = Util::toCamelCase($method);
if ( $this->getRelationManager()->isRelationExists($method) ) { //ex. hasManyHasMany
$convertedLink = $this->getRelationManager()->process($method, $entityName, array('name'=>$linkName, 'params'=>$linkParams), $foreignLink);
} else { //ex. hasMany
$convertedLink = $this->getRelationManager()->process($currentType, $entityName, array('name'=>$linkName, 'params'=>$linkParams), $foreignLink);
}
if ($convertedLink !== false) {
if (isset($convertedLink)) {
$relationships = Util::merge($convertedLink, $relationships);
}
}
@@ -411,27 +399,6 @@ class Converter
return $relationships;
}
/**
* Get foreign Link
*
* @param string $parentLinkName
* @param array $parentLinkParams
* @param array $currentEntityDefs
*
* @return array - in format array('name', 'params')
*/
protected function getForeignLink($parentLinkName, $parentLinkParams, $currentEntityDefs)
{
if (isset($parentLinkParams['foreign']) && isset($currentEntityDefs['links'][$parentLinkParams['foreign']])) {
return array(
'name' => $parentLinkParams['foreign'],
'params' => $currentEntityDefs['links'][$parentLinkParams['foreign']],
);
}
return false;
}
protected function getInitValues(array $fieldParams)
{
$values = array();
@@ -460,4 +427,4 @@ class Converter
return $values;
}
}
}
@@ -18,34 +18,47 @@
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
************************************************************************/
namespace Espo\Core\Utils\Database\Orm\Fields;
class Email extends \Espo\Core\Utils\Database\Orm\Base
{
public function load($entity, $field)
protected function load($fieldName, $entityName)
{
return array(
$entity['name'] => array(
'fields' => array(
$field['name'] => array(
return array(
$entityName => array(
'fields' => array(
$fieldName => array(
'select' => 'email_address.name',
'where' =>
array (
'LIKE' => 'email_address.name LIKE \'{text}\'',
'=' => 'email_address.name = \'{text}\'',
),
'orderBy' => 'email_address.name {direction}',
'where' =>
array (
'LIKE' => \Espo\Core\Utils\Util::toUnderScore($entityName) . ".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
email_address.deleted = 0 AND email_address.name LIKE '{text}'
)",
'=' => \Espo\Core\Utils\Util::toUnderScore($entityName) . ".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
email_address.deleted = 0 AND email_address.name = '{text}'
)"
),
'orderBy' => 'email_address.name {direction}',
),
$field['name'] .'Data' => array(
$fieldName .'Data' => array(
'type' => 'text',
'notStorable' => true
),
),
'relations' => array(
$field['name'].'es' => array(
$fieldName.'es' => array(
'type' => 'manyMany',
'entity' => 'EmailAddress',
'relationName' => 'entityEmailAddress',
@@ -54,16 +67,16 @@ class Email extends \Espo\Core\Utils\Database\Orm\Base
'email_address_id',
),
'conditions' => array(
'entityType' => $entity['name'],
'entityType' => $entityName,
),
'additionalColumns' => array(
'entityType' => array(
'entityType' => array(
'type' => 'varchar',
'len' => 100,
'len' => 100,
),
'primary' => array(
'type' => 'bool',
'default' => false,
'default' => false,
),
),
),
@@ -18,35 +18,44 @@
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
************************************************************************/
namespace Espo\Core\Utils\Database\Orm\Fields;
class LinkMultiple extends \Espo\Core\Utils\Database\Orm\Base
{
public function load($entity, $field)
protected function load($fieldName, $entityName)
{
return array(
$entity['name'] => array (
'fields' => array(
$field['name'].'Ids' => array(
$data = array(
$entityName => array (
'fields' => array(
$fieldName.'Ids' => array(
'type' => 'varchar',
'notStorable' => true,
),
$field['name'].'Names' => array(
$fieldName.'Names' => array(
'type' => 'varchar',
'notStorable' => true,
),
),
),
'unset' => array(
$entity['name'] => array(
'fields.'.$field['name'],
),
),
'unset' => array(
$entityName => array(
'fields.'.$fieldName,
),
),
);
$columns = $this->getMetadata()->get("entityDefs.{$entityName}.fields.{$fieldName}.columns");
if (!empty($columns)) {
$data[$entityName]['fields'][$fieldName . 'Columns'] = array(
'type' => 'varchar',
'notStorable' => true,
);
}
return $data;
}
}
}
@@ -24,28 +24,27 @@ namespace Espo\Core\Utils\Database\Orm\Fields;
class LinkParent extends \Espo\Core\Utils\Database\Orm\Base
{
public function load($entity, $field)
protected function load($fieldName, $entityName)
{
return array(
$entity['name'] => array (
'fields' => array(
$field['name'].'Id' => array(
return array(
$entityName => array (
'fields' => array(
$fieldName.'Id' => array(
'type' => 'foreignId',
'index' => $field['name'],
'index' => $fieldName,
),
$field['name'].'Type' => array(
$fieldName.'Type' => array(
'type' => 'foreignType',
'notNull' => false,
'index' => $field['name'],
'index' => $fieldName,
),
$field['name'].'Name' => array(
$fieldName.'Name' => array(
'type' => 'varchar',
'notStorable' => true,
),
),
)
);
),
);
}
@@ -26,11 +26,10 @@ use Espo\Core\Utils\Util;
class PersonName extends \Espo\Core\Utils\Database\Orm\Base
{
public function load($entity, $field)
protected function load($fieldName, $entityName)
{
$foreignField = $this->getForeignField($field['name'], $entity['name']);
$tableName = Util::toUnderScore($entity['name']);
$foreignField = $this->getForeignField($fieldName, $entityName);
$tableName = Util::toUnderScore($entityName);
$fullList = array(); //contains empty string (" ") like delimiter
$fullListReverse = array(); //reverse of $fullList
@@ -38,9 +37,9 @@ class PersonName extends \Espo\Core\Utils\Database\Orm\Base
$like = array();
$equal = array();
foreach($foreignField as $fieldName) {
foreach($foreignField as $foreignFieldName) {
$fieldNameTrimmed = trim($fieldName);
$fieldNameTrimmed = trim($foreignFieldName);
if (!empty($fieldNameTrimmed)) {
$columnName = $tableName.'.'.Util::toUnderScore($fieldNameTrimmed);
@@ -48,16 +47,16 @@ class PersonName extends \Espo\Core\Utils\Database\Orm\Base
$like[] = $columnName." LIKE '{text}'";
$equal[] = $columnName." = '{text}'";
} else {
$fullList[] = "'".$fieldName."'";
$fullList[] = "'".$foreignFieldName."'";
}
}
$fullListReverse = array_reverse($fullList);
return array(
$entity['name'] => array (
$entityName => array (
'fields' => array(
$field['name'] => array(
$fieldName => array(
'type' => 'varchar',
'select' => $this->getSelect($fullList),
'where' => array(
@@ -0,0 +1,89 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
namespace Espo\Core\Utils\Database\Orm\Fields;
class Phone extends \Espo\Core\Utils\Database\Orm\Base
{
protected function load($fieldName, $entityName)
{
return array(
$entityName => array(
'fields' => array(
$fieldName => array(
'select' => 'phone_number.name',
'where' =>
array (
'LIKE' => \Espo\Core\Utils\Util::toUnderScore($entityName) . ".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
phone_number.deleted = 0 AND phone_number.name LIKE '{text}'
)",
'=' => \Espo\Core\Utils\Util::toUnderScore($entityName) . ".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
phone_number.deleted = 0 AND phone_number.name = '{text}'
)"
),
'orderBy' => 'phone_number.name {direction}',
),
$fieldName .'Data' => array(
'type' => 'text',
'notStorable' => true
),
),
'relations' => array(
$fieldName.'s' => array(
'type' => 'manyMany',
'entity' => 'PhoneNumber',
'relationName' => 'entityPhoneNumber',
'midKeys' => array(
'entity_id',
'phone_number_id',
),
'conditions' => array(
'entityType' => $entityName,
),
'additionalColumns' => array(
'entityType' => array(
'type' => 'varchar',
'len' => 100,
),
'primary' => array(
'type' => 'bool',
'default' => false,
),
),
),
),
),
);
}
}
@@ -28,10 +28,14 @@ class RelationManager
{
private $metadata;
private $entityDefs;
public function __construct(\Espo\Core\Utils\Metadata $metadata)
{
$this->metadata = $metadata;
$this->entityDefs = $this->getMetadata()->get('entityDefs');
}
protected function getMetadata()
@@ -39,14 +43,15 @@ class RelationManager
return $this->metadata;
}
public function getLinkEntityName($entityName, $link)
protected function getEntityDefs()
{
if (isset($link['params'])) {
return isset($link['params']['entity']) ? $link['params']['entity'] : $entityName;
}
return $this->entityDefs;
}
return isset($link['entity']) ? $link['entity'] : $entityName;
public function getLinkEntityName($entityName, $linkParams)
{
return isset($linkParams['entity']) ? $linkParams['entity'] : $entityName;
}
public function isRelationExists($relationName)
@@ -81,33 +86,58 @@ class RelationManager
return method_exists($className, 'load');
}
public function process($method, $entityName, $link, $foreignLink = array())
/**
* Get foreign Link
*
* @param string $parentLinkName
* @param array $parentLinkParams
* @param array $currentEntityDefs
*
* @return array - in format array('name', 'params')
*/
private function getForeignLink($parentLinkName, $parentLinkParams, $currentEntityDefs)
{
$params = array();
$params['entityName'] = $entityName;
$params['link'] = $link;
$foreignParams = array();
$foreignParams['entityName'] = $this->getLinkEntityName($entityName, $link);
$foreignParams['link'] = $foreignLink;
$params['targetEntity'] = $foreignParams['entityName'];
$foreignParams['targetEntity'] = $params['entityName'];
//relationDefs defined in separate file
if (isset($link['params']['relationName']) && $this->isMethodExists($link['params']['relationName'])) {
$className = $this->getRelationClass($link['params']['relationName']);
} else if ($this->isMethodExists($method)) {
$className = $this->getRelationClass($method);
if (isset($parentLinkParams['foreign']) && isset($currentEntityDefs['links'][$parentLinkParams['foreign']])) {
return array(
'name' => $parentLinkParams['foreign'],
'params' => $currentEntityDefs['links'][$parentLinkParams['foreign']],
);
}
if (isset($className) && $className !== false) {
$helperClass = new $className($this->metadata);
return $helperClass->process($params, $foreignParams);
}
//END: relationDefs defined in separate file
return false;
}
public function convert($linkName, $linkParams, $entityName, $ormMeta)
{
$entityDefs = $this->getEntityDefs();
$foreignEntityName = $this->getLinkEntityName($entityName, $linkParams);
$foreignLink = $this->getForeignLink($linkName, $linkParams, $entityDefs[$foreignEntityName]);
$currentType = $linkParams['type'];
$method = $currentType;
if ($foreignLink !== false) {
$method .= '-'.$foreignLink['params']['type'];
}
$method = Util::toCamelCase($method);
$relationName = $this->isRelationExists($method) ? $method /*hasManyHasMany*/ : $currentType /*hasMany*/;
//relationDefs defined in separate file
if (isset($linkParams['relationName']) && $this->isMethodExists($linkParams['relationName'])) {
$className = $this->getRelationClass($linkParams['relationName']);
} else if ($this->isMethodExists($relationName)) {
$className = $this->getRelationClass($relationName);
}
if (isset($className) && $className !== false) {
$helperClass = new $className($this->metadata, $ormMeta, $entityDefs);
return $helperClass->process($linkName, $entityName, $foreignLink['name'], $foreignEntityName);
}
//END: relationDefs defined in separate file
return null;
}
}
@@ -0,0 +1,146 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
namespace Espo\Core\Utils\Database\Orm\Relations;
class Base extends \Espo\Core\Utils\Database\Orm\Base
{
private $params;
private $foreignParams;
protected $foreignLinkName = null;
protected $foreignEntityName = null;
protected $allowParams = array();
protected function getParams()
{
return $this->params;
}
protected function getForeignParams()
{
return $this->foreignParams;
}
protected function setParams(array $params)
{
$this->params = $params;
}
protected function setForeignParams(array $foreignParams)
{
$this->foreignParams = $foreignParams;
}
protected function setForeignLinkName($foreignLinkName)
{
$this->foreignLinkName = $foreignLinkName;
}
protected function getForeignLinkName()
{
return $this->foreignLinkName;
}
protected function setForeignEntityName($foreignEntityName)
{
$this->foreignEntityName = $foreignEntityName;
}
protected function getForeignEntityName()
{
return $this->foreignEntityName;
}
protected function getForeignLinkParams()
{
$foreignLinkName = $this->getForeignLinkName();
$foreignEntityName = $this->getForeignEntityName();
$foreignLinkParams = $this->getLinkParams($foreignLinkName, $foreignEntityName);
return $foreignLinkParams;
}
public function process($linkName, $entityName, $foreignLinkName, $foreignEntityName)
{
$inputs = array(
'itemName' => $linkName,
'entityName' => $entityName,
'foreignLinkName' => $foreignLinkName,
'foreignEntityName' => $foreignEntityName,
);
$this->setMethods($inputs);
$convertedDefs = $this->load($linkName, $entityName);
$convertedDefs = $this->mergeAllowedParams($convertedDefs);
$inputs = $this->setArrayValue(null, $inputs);
$this->setMethods($inputs);
return $convertedDefs;
}
private function mergeAllowedParams($loads)
{
$linkName = $this->getLinkName();
$entityName = $this->getEntityName();
if (!empty($this->allowParams)) {
$linkParams = &$loads[$entityName]['relations'][$linkName];
foreach ($this->allowParams as $name) {
$additionalParrams = $this->getAllowedAdditionalParams($name);
if (isset($additionalParrams) && !isset($linkParams[$name])) {
$linkParams[$name] = $additionalParrams;
}
}
}
return $loads;
}
private function getAllowedAdditionalParams($allowedItemName)
{
$linkParams = $this->getLinkParams();
$foreignLinkParams = $this->getForeignLinkParams();
$itemLinkParams = isset($linkParams[$allowedItemName]) ? $linkParams[$allowedItemName] : null;
$itemForeignLinkParams = isset($foreignLinkParams[$allowedItemName]) ? $foreignLinkParams[$allowedItemName] : null;
$additionalParrams = null;
if (isset($itemLinkParams) && isset($itemForeignLinkParams)) {
$additionalParrams = Util::merge($itemLinkParams, $itemForeignLinkParams);
} else if (isset($itemLinkParams)) {
$additionalParrams = $itemLinkParams;
} else if (isset($itemForeignLinkParams)) {
$additionalParrams = $itemForeignLinkParams;
}
return $additionalParrams;
}
}
@@ -18,33 +18,34 @@
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
************************************************************************/
namespace Espo\Core\Utils\Database\Orm\Relations;
class BelongsTo extends \Espo\Core\Utils\Database\Orm\Base
class BelongsTo extends Base
{
public function load($params, $foreignParams)
protected function load($linkName, $entityName)
{
$foreignEntityName = $this->getForeignEntityName();
return array (
$params['entityName'] => array (
$entityName => array (
'fields' => array(
$params['link']['name'].'Name' => array(
$linkName.'Name' => array(
'type' => 'foreign',
'relation' => $params['link']['name'],
'foreign' => $this->getForeignField('name', $foreignParams['entityName']),
'relation' => $linkName,
'foreign' => $this->getForeignField('name', $foreignEntityName),
),
$params['link']['name'].'Id' => array(
$linkName.'Id' => array(
'type' => 'foreignId',
'index' => true,
),
),
'relations' => array(
$params['link']['name'] => array(
$linkName => array(
'type' => 'belongsTo',
'entity' => $params['targetEntity'],
'key' => $params['link']['name'].'Id',
'entity' => $foreignEntityName,
'key' => $linkName.'Id',
'foreignKey' => 'id', //????
),
),
@@ -52,5 +53,4 @@ class BelongsTo extends \Espo\Core\Utils\Database\Orm\Base
);
}
}
@@ -18,32 +18,31 @@
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
************************************************************************/
namespace Espo\Core\Utils\Database\Orm\Relations;
class EmailEmailAddress extends \Espo\Core\Utils\Database\Orm\Relations\HasMany
class EmailEmailAddress extends HasMany
{
public function load($params, $foreignParams)
protected function load($linkName, $entityName)
{
$parentRelation = parent::load($linkName, $entityName);
$parentRelation = parent::load($params, $foreignParams);
$foreignEntityName = $this->getForeignEntityName();
$relation = array(
$params['entityName'] => array (
$entityName => array (
'relations' => array(
$params['link']['name'] => array(
$linkName => array(
'midKeys' => array(
lcfirst($params['entityName']).'Id',
lcfirst($foreignParams['entityName']).'Id',
lcfirst($entityName).'Id',
lcfirst($foreignEntityName).'Id',
),
),
),
),
);
$relation = \Espo\Core\Utils\Util::merge($parentRelation, $relation);
return $relation;
@@ -18,28 +18,30 @@
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
************************************************************************/
namespace Espo\Core\Utils\Database\Orm\Relations;
class EntityTeam extends \Espo\Core\Utils\Database\Orm\Base
class EntityTeam extends Base
{
public function load($params, $foreignParams)
protected function load($linkName, $entityName)
{
$linkParams = $this->getLinkParams();
$foreignEntityName = $this->getForeignEntityName();
return array(
$params['entityName'] => array(
$entityName => array(
'relations' => array(
$params['link']['name'] => array(
$linkName => array(
'type' => 'manyMany',
'entity' => $params['targetEntity'],
'relationName' => lcfirst($params['link']['params']['relationName']),
'entity' => $foreignEntityName,
'relationName' => lcfirst($linkParams['relationName']),
'midKeys' => array(
'entity_id',
'team_id',
),
'conditions' => array(
'entityType' => $params['entityName'],
'entityType' => $entityName,
),
'additionalColumns' => array(
'entityType' => array(
@@ -18,33 +18,35 @@
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
************************************************************************/
namespace Espo\Core\Utils\Database\Orm\Relations;
class HasChildren extends \Espo\Core\Utils\Database\Orm\Base
class HasChildren extends Base
{
public function load($params, $foreignParams)
protected function load($linkName, $entityName)
{
$foreignLinkName = $this->getForeignLinkName();
$foreignEntityName = $this->getForeignEntityName();
return array(
$params['entityName'] => array (
$entityName => array (
'fields' => array(
$params['link']['name'].'Ids' => array(
$linkName.'Ids' => array(
'type' => 'varchar',
'notStorable' => true,
),
$params['link']['name'].'Names' => array(
$linkName.'Names' => array(
'type' => 'varchar',
'notStorable' => true,
),
),
'relations' => array(
$params['link']['name'] => array(
$linkName => array(
'type' => 'hasChildren',
'entity' => $params['targetEntity'],
'foreignKey' => $foreignParams['link']['name'].'Id', //???: 'foreignKey' => $params['link']['name'].'Id',
'foreignType' => $foreignParams['link']['name'].'Type', //???: 'foreignKey' => $params['link']['name'].'Id',
'entity' => $foreignEntityName,
'foreignKey' => $foreignLinkName.'Id',
'foreignType' => $foreignLinkName.'Type',
),
),
),
@@ -18,11 +18,11 @@
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
************************************************************************/
namespace Espo\Core\Utils\Database\Orm\Relations;
class HasMany extends \Espo\Core\Utils\Database\Orm\Base
class HasMany extends Base
{
protected $allowParams = array(
'relationName',
@@ -30,32 +30,35 @@ class HasMany extends \Espo\Core\Utils\Database\Orm\Base
'additionalColumns',
);
public function load($params, $foreignParams)
protected function load($linkName, $entityName)
{
$relationType = isset($params['link']['params']['relationName']) ? 'manyMany' : 'hasMany';
$linkParams = $this->getLinkParams();
$foreignLinkName = $this->getForeignLinkName();
$foreignEntityName = $this->getForeignEntityName();
$relationType = isset($linkParams['relationName']) ? 'manyMany' : 'hasMany';
$relation = array(
$params['entityName'] => array (
$entityName => array (
'fields' => array(
$params['link']['name'].'Ids' => array(
$linkName.'Ids' => array(
'type' => 'varchar',
'notStorable' => true,
),
$params['link']['name'].'Names' => array(
$linkName.'Names' => array(
'type' => 'varchar',
'notStorable' => true,
),
),
'relations' => array(
$params['link']['name'] => array(
//'type' => 'hasMany',
$linkName => array(
'type' => $relationType,
'entity' => $params['targetEntity'],
'foreignKey' => lcfirst($foreignParams['link']['name'].'Id'), //???: 'foreignKey' => $params['link']['name'].'Id',
'entity' => $foreignEntityName,
'foreignKey' => lcfirst($foreignLinkName.'Id'),
),
),
),
);
);
return $relation;
}
@@ -18,45 +18,46 @@
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
************************************************************************/
namespace Espo\Core\Utils\Database\Orm\Relations;
use Espo\Core\Utils\Util;
class ManyMany extends \Espo\Core\Utils\Database\Orm\Base
class ManyMany extends Base
{
protected $allowParams = array(
'relationName',
'conditions',
'additionalColumns',
);
public function load($params, $foreignParams)
protected function load($linkName, $entityName)
{
$foreignEntityName = $this->getForeignEntityName();
return array(
$params['entityName'] => array(
$entityName => array(
'fields' => array(
$params['link']['name'].'Ids' => array(
$linkName.'Ids' => array(
'type' => 'varchar',
'notStorable' => true,
),
$params['link']['name'].'Names' => array(
$linkName.'Names' => array(
'type' => 'varchar',
'notStorable' => true,
),
),
'relations' => array(
$params['link']['name'] => array(
$linkName => array(
'type' => 'manyMany',
'entity' => $params['targetEntity'],
'relationName' => $this->getJoinTable($params['entityName'], $foreignParams['entityName']),
'entity' => $foreignEntityName,
'relationName' => $this->getJoinTable($entityName, $foreignEntityName),
'key' => 'id', //todo specify 'key'
'foreignKey' => 'id', //todo specify 'foreignKey'
'midKeys' => array(
lcfirst($params['entityName']).'Id',
lcfirst($foreignParams['entityName']).'Id',
lcfirst($entityName).'Id',
lcfirst($foreignEntityName).'Id',
),
),
),
@@ -18,26 +18,29 @@
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
************************************************************************/
namespace Espo\Core\Utils\Database\Orm\Relations;
class NoteAttachments extends \Espo\Core\Utils\Database\Orm\Relations\HasChildren
class NoteAttachments extends HasChildren
{
public function load($params, $foreignParams)
protected function load($linkName, $entityName)
{
$parentRelation = parent::load($params, $foreignParams);
$parentRelation = parent::load($linkName, $entityName);
$relation = array(
$params['entityName'] => array (
$entityName => array (
'fields' => array(
$params['link']['name'].'Types' => array(
$linkName.'Types' => array(
'type' => 'varchar',
'notStorable' => true,
),
),
),
);
);
$relation = \Espo\Core\Utils\Util::merge($parentRelation, $relation);
return $relation;
}
}
@@ -275,6 +275,7 @@ class Converter
switch ($fieldParams['type']) {
case 'array':
case 'jsonArray':
case 'text':
$dbFieldParams['default'] = ''; //for db type TEXT can't be defined a default value
break;
@@ -360,4 +361,4 @@ class Converter
}
}
}
+1 -1
View File
@@ -166,7 +166,7 @@ class FieldManager
}
foreach ($fieldDef as $defName => $defValue) {
if (!isset($defValue)) {
if (!isset($defValue) || (is_string($defValue) && $defValue == '') ) {
unset($fieldDef[$defName]);
}
}
+1 -1
View File
@@ -176,7 +176,7 @@ class Manager
$fullPath = $this->concatPaths($path); //todo remove after changing the params
if ($this->checkCreateFile($fullPath) === false) {
throw new Error('Permission denied in '. $path);
throw new Error('Permission denied in '. $fullPath);
}
$res = (file_put_contents($fullPath, $data, $flags, $context) !== FALSE);
+14 -5
View File
@@ -62,9 +62,14 @@ return array (
'smtpUsername' => '',
'smtpPassword' => '',
'languageList' => array(
'en_US',
'en_US',
'de_DE',
'es_ES',
'fr_FR',
'tr_TR',
'ro_RO',
'pt_BR',
'vi_VN'
),
'language' => 'en_US',
'logger' =>
@@ -74,16 +79,20 @@ return array (
'isRotate' => true, /** rotate log files every day */
'maxRotateFiles' => 30, /** max number of rotate files */
),
'authenticationMethod' => 'Espo',
'globalSearchEntityList' =>
array (
0 => 'Account',
1 => 'Contact',
2 => 'Lead',
3 => 'Prospect',
4 => 'Opportunity',
3 => 'Opportunity',
),
"tabList" => array("Account", "Contact", "Lead", "Opportunity", "Calendar", "Meeting", "Call", "Task", "Case", "Prospect", "Email"),
"quickCreateList" => array("Account", "Contact", "Lead", "Opportunity", "Meeting", "Call", "Task", "Case", "Prospect"),
"tabList" => array("Account", "Contact", "Lead", "Opportunity", "Calendar", "Meeting", "Call", "Task", "Case", "Email"),
"quickCreateList" => array("Account", "Contact", "Lead", "Opportunity", "Meeting", "Call", "Task", "Case"),
'calendarDefaultEntity' => 'Meeting',
'disableExport' => false,
'assignmentEmailNotifications' => false,
'assignmentEmailNotificationsEntityList' => array('Lead', 'Opportunity', 'Task', 'Case'),
'isInstalled' => false,
);
@@ -1 +1 @@
[{"name":"name"},{"name":"email","type":"base","sortable":false,"params":{}},{"name":"city"}]
[{"name":"name"}]
@@ -96,6 +96,23 @@ return array (
'smtpUsername',
'smtpPassword',
'cron',
'authenticationMethod',
'ldapHost',
'ldapPort',
'ldapSecurity',
'ldapAuth',
'ldapUsername',
'ldapPassword',
'ldapBindRequiresDn',
'ldapBaseDn',
'ldapUserLoginFilter',
'ldapAccountCanonicalForm',
'ldapAccountDomainName',
'ldapAccountDomainNameShort',
'ldapAccountFilterFormat',
'ldapTryUsernameSplit',
'ldapOptReferrals',
'ldapCreateEspoUser',
),
'isInstalled' => false,
);
+37
View File
@@ -0,0 +1,37 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
namespace Espo\Entities;
use Espo\Core\Exceptions\Error;
class PhoneNumber extends \Espo\Core\ORM\Entity
{
protected function setName($value)
{
if (empty($value)) {
throw new Error("Phone number can't be empty");
}
$this->valuesContainer['name'] = $value;
}
}
@@ -0,0 +1,58 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
namespace Espo\Hooks\Common;
use Espo\ORM\Entity;
class AssignmentEmailNotification extends \Espo\Core\Hooks\Base
{
public function afterSave(Entity $entity)
{
if (
$this->getConfig()->get('assignmentEmailNotifications')
&&
in_array($entity->getEntityName(), $this->getConfig()->get('assignmentEmailNotificationsEntityList', array()))
) {
$userId = $entity->get('assignedUserId');
if (!empty($userId) && $userId != $this->getUser()->id && $entity->isFieldChanged('assignedUserId')) {
$job = $this->getEntityManager()->getEntity('Job');
$job->set(array(
'serviceName' => 'EmailNotification',
'method' => 'notifyAboutAssignmentJob',
'data' => json_encode(array(
'userId' => $userId,
'assignerUserId' => $this->getUser()->id,
'entityId' => $entity->id,
'entityType' => $entity->getEntityName()
)),
'executeTime' => date('Y-m-d H:i:s'),
));
$this->getEntityManager()->saveEntity($job);
}
}
}
}
+15 -1
View File
@@ -53,11 +53,25 @@ class Notifications extends \Espo\Core\Hooks\Base
WHERE entity_id = " . $pdo->quote($parentId) . " AND entity_type = " . $pdo->quote($parentType);
$sth = $pdo->prepare($sql);
$sth->execute();
$userIdList = array();
while ($row = $sth->fetch(\PDO::FETCH_ASSOC)) {
if ($this->getUser()->id != $row['userId']) {
$this->getNotificationService()->notifyAboutNote($row['userId'], $entity->id);
$userIdList[] = $row['userId'];
}
}
if (!empty($userIdList)) {
$job = $this->getEntityManager()->getEntity('Job');
$job->set(array(
'serviceName' => 'Notification',
'method' => 'notifyAboutNoteFromJob',
'data' => json_encode(array(
'userIdList' => $userIdList,
'noteId' => $entity->id
)),
'executeTime' => date('Y-m-d H:i:s'),
));
$this->getEntityManager()->saveEntity($job);
}
}
}
}
@@ -25,7 +25,7 @@ namespace Espo\Modules\Crm\Controllers;
use \Espo\Core\Exceptions\Error;
use \Espo\Core\Exceptions\BadRequest;
class Prospect extends \Espo\Core\Controllers\Record
class Target extends \Espo\Core\Controllers\Record
{
public function actionConvert($params, $data)
@@ -22,7 +22,7 @@
namespace Espo\Modules\Crm\Entities;
class Prospect extends \Espo\Core\Entities\Person
class Target extends \Espo\Core\Entities\Person
{
}
@@ -0,0 +1,50 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
namespace Espo\Modules\Crm\Repositories;
use Espo\ORM\Entity;
class Call extends \Espo\Core\ORM\Repositories\RDB
{
protected function beforeSave(Entity $entity)
{
parent::beforeSave($entity);
$parentId = $entity->get('parentId');
$parentType = $entity->get('parentType');
if (!empty($parentId) || !empty($parentType)) {
$parent = $this->getEntityManager()->getEntity($parentType, $parentId);
if (!empty($parent)) {
if ($parent->getEntityName() == 'Account') {
$accountId = $parent->id;
} else if ($parent->has('accountId')) {
$accountId = $parent->get('accountId');
}
if (!empty($accountId)) {
$entity->set('accountId', $accountId);
}
}
}
}
}
@@ -0,0 +1,95 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
namespace Espo\Modules\Crm\Repositories;
use Espo\ORM\Entity;
class Contact extends \Espo\Core\ORM\Repositories\RDB
{
public function handleSelectParams(&$params)
{
parent::handleSelectParams($params);
if (empty($params['customJoin'])) {
$params['customJoin'] = '';
}
$params['customJoin'] .= "
LEFT JOIN `account_contact` AS accountContact
ON accountContact.contact_id = contact.id AND accountContact.account_id = contact.account_id AND accountContact.deleted = 0
";
}
public function save(Entity $entity)
{
$result = parent::save($entity);
$accountIdChanged = $entity->has('accountId') && $entity->get('accountId') != $entity->getFetched('accountId');
$titleChanged = $entity->has('title') && $entity->get('title') != $entity->getFetched('title');
if ($accountIdChanged) {
$accountId = $entity->get('accountId');
if (empty($accountId)) {
$this->unrelate($entity, 'accounts', $entity->getFetched('accountId'));
return $result;
}
}
if ($titleChanged) {
if (empty($accountId)) {
$accountId = $entity->getFetched('accountId');
if (empty($accountId)) {
return $result;
}
}
}
if ($accountIdChanged || $titleChanged) {
$pdo = $this->getEntityManager()->getPDO();
$sql = "
SELECT id, role FROM account_contact
WHERE
account_id = ".$pdo->quote($accountId)." AND
contact_id = ".$pdo->quote($entity->id)." AND
deleted = 0
";
$sth = $pdo->prepare($sql);
if ($row = $sth->fetch()) {
if ($titleChanged && $entity->get('title') != $row['role']) {
$this->updateRelation($entity, 'accounts', $accountId, array(
'role' => $entity->get('title')
));
}
} else {
$this->relate($entity, 'accounts', $accountId, array(
'role' => $entity->get('title')
));
}
}
return $result;
}
}
@@ -0,0 +1,50 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
namespace Espo\Modules\Crm\Repositories;
use Espo\ORM\Entity;
class Meeting extends \Espo\Core\ORM\Repositories\RDB
{
protected function beforeSave(Entity $entity)
{
parent::beforeSave($entity);
$parentId = $entity->get('parentId');
$parentType = $entity->get('parentType');
if (!empty($parentId) || !empty($parentType)) {
$parent = $this->getEntityManager()->getEntity($parentType, $parentId);
if (!empty($parent)) {
if ($parent->getEntityName() == 'Account') {
$accountId = $parent->id;
} else if ($parent->has('accountId')) {
$accountId = $parent->get('accountId');
}
if (!empty($accountId)) {
$entity->set('accountId', $accountId);
}
}
}
}
}
@@ -3,14 +3,14 @@
"name": "Name",
"emailAddress": "E-Mail",
"website": "Webseite",
"phone": "Telefon",
"fax": "Fax",
"phoneNumber": "Telefon",
"billingAddress": "Rechnungsadresse",
"shippingAddress": "Lieferadresse",
"description": "Beschreibung",
"sicCode": "WKN Nummer",
"industry": "Branche",
"type": "Typ"
"type": "Typ",
"contactRole": "Rolle"
},
"links": {
"contacts": "Kontakte",
@@ -4,13 +4,14 @@
"emailAddress": "E-Mail",
"title": "Funktion",
"account": "Firma",
"phone": "Telefon",
"phoneOffice": "Phone (Office)",
"fax": "Fax",
"accounts": "Firmen",
"phoneNumber": "Telefon",
"accountType": "Firmentyp",
"doNotCall": "Nicht anrufen",
"address": "Adresse",
"description": "Beschreibung"
"opportunityRole": "Opportunity Role",
"accountRole": "Rolle",
"description": "Beschreibung"
},
"links": {
"opportunities": "Verkaufschancen",
@@ -18,5 +19,13 @@
},
"labels": {
"Create Contact": "Neuer Kontakt"
},
"options": {
"opportunityRole": {
"": "--None--",
"Decision Maker": "Decision Maker",
"Evaluator": "Evaluator",
"Influencer": "Influencer"
}
}
}
@@ -3,7 +3,7 @@
"Account": "Firma",
"Contact": "Kontakt",
"Lead": "Interessent",
"Prospect": "Zielkontakt",
"Target": "Zielkontakt",
"Opportunity": "Verkaufschance",
"Meeting": "Meeting",
"Calendar": "Kalender",
@@ -16,7 +16,7 @@
"Account": "Firmen",
"Contact": "Kontakte",
"Lead": "Interessenten",
"Prospect": "Zielkontakte",
"Target": "Targets",
"Opportunity": "Verkaufschancen",
"Meeting": "Meetings",
"Calendar": "Kalender",
@@ -36,5 +36,8 @@
"IMAP": "IMAP",
"Actions": "Aktionen",
"Main": "Wichtig"
},
"messages": {
"couldNotConnectToImap": "Kann keine Verbindung zum IMAP Server herstellen"
}
}
@@ -9,9 +9,7 @@
"emailAddress": "E-Mail",
"title": "Funktion",
"website": "Webseite",
"phone": "Telefon",
"phoneOffice": "Phone (Office)",
"fax": "Fax",
"phoneNumber": "Telefon",
"accountName": "Firmenname",
"doNotCall": "Nicht anrufen",
"address": "Adresse",
@@ -8,6 +8,7 @@
"leadSource": "Quelle",
"doNotCall": "Nicht anrufen",
"closeDate": "Abschlussdatum",
"contacts": "Kontakte",
"description": "Beschreibung"
},
"links": {
@@ -19,22 +20,12 @@
"Qualification": "Qualifikation",
"Needs Analysis": "Bedarfserhebung",
"Value Proposition": "Richtangebot",
"Id. Decision Makers": "id. Entscheider",
"Id. Decision Makers": "Entscheider ident.",
"Perception Analysis": "Analyse Sichtweise",
"Proposal/Price Quote": "Preisangebot",
"Negotiation/Review": "Verhandlung/Überarbeitung",
"Closed Won": "Gewonnen",
"Closed Lost": "Verloren"
},
"leadSource": {
"Call": "Anruf",
"Email": "E-Mail",
"Existing Customer": "Bestehender Kunde",
"Partner": "Partner",
"Public Relations": "Public Relations",
"Web Site": "Web Seite",
"Campaign": "Kampagne",
"Other": "Andere"
}
},
"labels": {
@@ -5,9 +5,7 @@
"title": "Funktion",
"website": "Webseite",
"accountName": "Firmenname",
"phone": "Telefon",
"phoneOffice": "Phone (Office)",
"fax": "Fax",
"phoneNumber": "Telefon",
"doNotCall": "Nicht anrufen",
"address": "Adresse",
"description": "Beschreibung"
@@ -15,7 +13,7 @@
"links": {
},
"labels": {
"Create Prospect": "Neuer Zielkontakt",
"Create Target": "Create Target",
"Convert to Lead": "Zu Interessent umwandeln"
}
}
@@ -3,14 +3,14 @@
"name": "Name",
"emailAddress": "Email",
"website": "Website",
"phone": "Phone",
"fax": "Fax",
"phoneNumber": "Phone",
"billingAddress": "Billing Address",
"shippingAddress": "Shipping Address",
"description": "Description",
"sicCode": "Sic Code",
"industry": "Industry",
"type": "Type"
"type": "Type",
"contactRole": "Role"
},
"links": {
"contacts": "Contacts",
@@ -30,5 +30,10 @@
"Set Held": "Set Held",
"Set Not Held": "Set Not Held",
"Send Invitations": "Send Invitations"
},
"presetFilters": {
"planned": "Planned",
"held": "Held",
"todays": "Today's"
}
}
@@ -34,5 +34,9 @@
},
"labels": {
"Create Case": "Create Case"
},
"presetFilters": {
"open": "Open",
"closed": "Closed"
}
}
@@ -4,13 +4,14 @@
"emailAddress": "Email",
"title": "Title",
"account": "Account",
"phone": "Phone",
"phoneOffice": "Phone (Office)",
"fax": "Fax",
"accounts": "Accounts",
"phoneNumber": "Phone",
"accountType": "Account Type",
"doNotCall": "Do Not Call",
"address": "Address",
"description": "Description"
"opportunityRole": "Opportunity Role",
"accountRole": "Role",
"description": "Description"
},
"links": {
"opportunities": "Opportunities",
@@ -18,5 +19,13 @@
},
"labels": {
"Create Contact": "Create Contact"
},
"options": {
"opportunityRole": {
"": "--None--",
"Decision Maker": "Decision Maker",
"Evaluator": "Evaluator",
"Influencer": "Influencer"
}
}
}
@@ -3,7 +3,7 @@
"Account": "Account",
"Contact": "Contact",
"Lead": "Lead",
"Prospect": "Prospect",
"Target": "Target",
"Opportunity": "Opportunity",
"Meeting": "Meeting",
"Calendar": "Calendar",
@@ -16,7 +16,7 @@
"Account": "Accounts",
"Contact": "Contacts",
"Lead": "Leads",
"Prospect": "Prospects",
"Target": "Targets",
"Opportunity": "Opportunities",
"Meeting": "Meetings",
"Calendar": "Calendar",
@@ -31,6 +31,8 @@
"Tasks": "My Tasks",
"Cases": "My Cases",
"Calendar": "Calendar",
"Calls": "My Calls",
"Meetings": "My Meetings",
"OpportunitiesByStage": "Opportunities by Stage",
"OpportunitiesByLeadSource": "Opportunities by Lead Source",
"SalesByMonth": "Sales by Month",
@@ -9,9 +9,7 @@
"emailAddress": "Email",
"title": "Title",
"website": "Website",
"phone": "Phone",
"phoneOffice": "Phone (Office)",
"fax": "Fax",
"phoneNumber": "Phone",
"accountName": "Account Name",
"doNotCall": "Do Not Call",
"address": "Address",
@@ -44,5 +42,8 @@
"Campaign": "Campaign",
"Other": "Other"
}
},
"presetFilters": {
"active": "Active"
}
}
@@ -24,6 +24,13 @@
"Create Meeting": "Create Meeting",
"Set Held": "Set Held",
"Set Not Held": "Set Not Held",
"Send Invitations": "Send Invitations"
"Send Invitations": "Send Invitations",
"Saved as Held": "Saved as Held",
"Saved as Not Held": "Saved as Not Held"
},
"presetFilters": {
"planned": "Planned",
"held": "Held",
"todays": "Today's"
}
}
@@ -8,6 +8,7 @@
"leadSource": "Lead Source",
"doNotCall": "Do Not Call",
"closeDate": "Close Date",
"contacts": "Contacts",
"description": "Description"
},
"links": {
@@ -25,19 +26,13 @@
"Negotiation/Review": "Negotiation/Review",
"Closed Won": "Closed Won",
"Closed Lost": "Closed Lost"
},
"leadSource": {
"Call": "Call",
"Email": "Email",
"Existing Customer": "Existing Customer",
"Partner": "Partner",
"Public Relations": "Public Relations",
"Web Site": "Web Site",
"Campaign": "Campaign",
"Other": "Other"
}
},
"labels": {
"Create Opportunity": "Create Opportunity"
},
"presetFilters": {
"open": "Open",
"won": "Won"
}
}
@@ -5,9 +5,7 @@
"title": "Title",
"website": "Website",
"accountName": "Account Name",
"phone": "Phone",
"phoneOffice": "Phone (Office)",
"fax": "Fax",
"phoneNumber": "Phone",
"doNotCall": "Do Not Call",
"address": "Address",
"description": "Description"
@@ -15,7 +13,7 @@
"links": {
},
"labels": {
"Create Prospect": "Create Prospect",
"Create Target": "Create Target",
"Convert to Lead": "Convert to Lead"
}
}
@@ -27,5 +27,11 @@
},
"labels": {
"Create Task": "Create Task"
},
"presetFilters": {
"active": "Active",
"completed": "Completed",
"todays": "Today's",
"overdue": "Overdue"
}
}
@@ -3,7 +3,7 @@
"Account": "Cuenta",
"Contact": "Contacto",
"Lead": "Potenciales",
"Prospect": "Prospecto",
"Target": "Prospecto",
"Opportunity": "Opportunidad",
"Meeting": "Reunión",
"Calendar": "Calendario",
@@ -16,7 +16,7 @@
"Account": "Cuentas",
"Contact": "Contactos",
"Lead": "Potenciales",
"Prospect": "Prospectos",
"Targets": "Prospectos",
"Opportunity": "Oportunidades",
"Meeting": "Reuniones",
"Calendar": "Calendario",
@@ -15,7 +15,7 @@
"links": {
},
"labels": {
"Create Prospect": "Crear Prospecto",
"Create Target": "Crear Prospecto",
"Convert to Lead": "Convertir en Lider"
}
}
@@ -0,0 +1,40 @@
{
"fields": {
"name": "Nom",
"emailAddress": "Email",
"website": "Site Web",
"phoneNumber": "Téléphone",
"billingAddress": "Adresse de Facturation",
"shippingAddress": "Adresse de Livraison",
"description": "Description",
"sicCode": "Code SIC",
"industry": "Industrie",
"type": "Type",
"contactRole": "Rôle"
},
"links": {
"contacts": "Contacts",
"opportunities": "Opportunités",
"cases": "Tickets"
},
"options": {
"type": {
"Customer": "Clients",
"Investor": "Investisseur",
"Partner": "Partenaire",
"Reseller": "Revendeur"
},
"industry": {
"Apparel": "Vêtements",
"Banking": "Banque",
"Computer Software": "Logiciel Informatique",
"Education": "Education",
"Electronics": "Electronique",
"Finance": "Finance",
"Insurance": "Assurance"
}
},
"labels": {
"Create Account": "Nouveau Compte"
}
}
@@ -0,0 +1,13 @@
{
"modes": {
"month": "Mois",
"week": "Semaine",
"day": "Jour",
"agendaWeek": "Semaine",
"agendaDay": "Jour"
},
"labels": {
"Today": "Aujourd'hui",
"Create": "Créer"
}
}
@@ -0,0 +1,34 @@
{
"fields": {
"name": "Nom",
"parent": "Parent",
"status": "Statut",
"dateStart": "Date de Début",
"dateEnd": "Date de Fin",
"direction": "Direction",
"duration": "Durée",
"description": "Description",
"users": "Utilisateurs",
"contacts": "Contacts",
"leads": "Leads"
},
"links": {
},
"options": {
"status": {
"Planned": "Plannifié",
"Held": "Fait",
"Not Held": "Pas Fait"
},
"direction": {
"Outbound": "Sortie",
"Inbound": "Entrée"
}
},
"labels": {
"Create Call": "Générer l'appel",
"Set Held": "Marquer Fait",
"Set Not Held": "Marquer Pas Fait",
"Send Invitations": "Envoi d'Invitations"
}
}
@@ -0,0 +1,38 @@
{
"fields": {
"name": "Nom",
"number": "Numéro",
"status": "Statut",
"account": "Compte",
"contact": "Contact",
"priority": "Priorité",
"type": "Type",
"description": "Description"
},
"links": {
},
"options": {
"status": {
"New": "Nouveau",
"Assigned": "Assigné",
"Pending": "En attente",
"Closed": "Fermé",
"Rejected": "Rejeté",
"Duplicate": "Doublon"
},
"priority" : {
"Low": "Bas",
"Normal": "Normal",
"High": "Haut",
"Urgent": "Urgent"
},
"type": {
"Question": "Question",
"Incident": "Incident",
"Problem": "Problème"
}
},
"labels": {
"Create Case": "Créer un Ticket"
}
}
@@ -0,0 +1,31 @@
{
"fields": {
"name": "Nom",
"emailAddress": "Email",
"title": "Titre",
"account": "Compte",
"accounts": "Comptes",
"phoneNumber": "Téléphone",
"accountType": "Type de Compte",
"doNotCall": "Ne Pas Appeler",
"address": "Adresse",
"opportunityRole": "Rôle de Challenge",
"accountRole": "Rôle",
"description": "Description"
},
"links": {
"opportunities": "Opportunités",
"cases": "Tickets"
},
"labels": {
"Create Contact": "Créer un contact"
},
"options": {
"opportunityRole": {
"": "--Aucun--",
"Decision Maker": "Décisionnaire",
"Evaluator": "*Evaluator",
"Influencer": "Prescripteur"
}
}
}
@@ -0,0 +1,79 @@
{
"scopeNames": {
"Account": "Compte",
"Contact": "Contact",
"Lead": "Lead",
"Target": "Cible",
"Opportunity": "Challenge",
"Meeting": "Rendez-vous",
"Calendar": "Calendrier",
"Call": "Appel",
"Task": "Tâche",
"Case": "Ticket",
"InboundEmail": "Email Entrant"
},
"scopeNamesPlural": {
"Account": "Comptes",
"Contact": "Contacts",
"Lead": "Leads",
"Target": "Cibles",
"Opportunity": "Opportunités",
"Meeting": "Rendez-vous",
"Calendar": "Calendrier",
"Call": "Appels",
"Task": "Tâches",
"Case": "Tickets",
"InboundEmail": "Emails Entrants"
},
"dashlets": {
"Leads": "My Leads",
"Opportunities": "Mes Challenges",
"Tasks": "Mes Tâches",
"Cases": "Mes Tickets",
"Calendar": "Calendrier",
"OpportunitiesByStage": "*Challenges par étages",
"OpportunitiesByLeadSource": "Challenges par Lead Source",
"SalesByMonth": "Ventes par Mois",
"SalesPipeline": "Tuyaux des ventes"
},
"labels": {
"Create InboundEmail": "Créer une boîte email",
"Activities": "Activités",
"History": "Historique",
"Attendees": "Participants",
"Schedule Meeting": "Rendez-vous Planifiés",
"Schedule Call": "Appels Planifiés",
"Compose Email": "Créer un Email",
"Log Meeting": "Journal des Rendez-vous",
"Log Call": "Journal d'appels",
"Archive Email": "Archiver les Emails",
"Create Task": "Créer une tâche",
"Tasks": "Tâches"
},
"fields": {
"billingAddressCity": "Ville",
"billingAddressCountry": "Pays",
"billingAddressPostalCode": "Code Postal",
"billingAddressState": "Département",
"billingAddressStreet": "Rue",
"addressCity": "Ville",
"addressStreet": "Rue",
"addressCountry": "Pays",
"addressState": "Département",
"addressPostalCode": "Code Postal",
"shippingAddressCity": "City (Shipping)",
"shippingAddressStreet": "Street (Shipping)",
"shippingAddressCountry": "Country (Shipping)",
"shippingAddressState": "State (Shipping)",
"shippingAddressPostalCode": "Postal Code (Shipping)"
},
"links": {
"contacts": "Contacts",
"opportunities": "Opportunités",
"leads": "Leads",
"meetings": "Rendez-vous",
"calls": "Appels",
"tasks": "Tâches",
"emails": "Courriers"
}
}
@@ -0,0 +1,43 @@
{
"fields": {
"name": "Nom",
"team": "Equipe",
"status": "Statut",
"assignToUser": "Assigné à l'Utilisateur",
"host": "Hôte",
"username": "Nom d'utilisateur",
"password": "Mot de Passe",
"port": "Porte",
"monitoredFolders": "Dossiers Surveillés",
"trashFolder": "Corbeille",
"ssl": "SSL",
"createCase": "Créer un Ticket",
"reply": "Répondre",
"caseDistribution": "Distribution de tickets",
"replyEmailTemplate": "Modèle de Réponse par Email",
"replyFromAddress": "Répondre Depuis l'Adresse",
"replyFromName": "Répondre Depuis le Nom"
},
"links": {
},
"options": {
"status": {
"Active": "Activer",
"Inactive": "Désactiver"
},
"caseDistribution": {
"Direct-Assignment": "Assignation Directe",
"Round-Robin": "Round-Robin",
"Least-Busy": "Least-Busy"
}
},
"labels": {
"Create InboundEmail": "Créer une boîte email",
"IMAP": "IMAP",
"Actions": "Actions",
"Main": "Principal"
},
"messages": {
"couldNotConnectToImap": "Impossible de se connecter au serveur IMAP"
}
}
@@ -0,0 +1,46 @@
{
"labels": {
"Converted To": "Convertir Vers",
"Create Lead": "Créer Lead",
"Convert": "Convertir"
},
"fields": {
"name": "Nom",
"emailAddress": "Email",
"title": "Titre",
"website": "Site Web",
"phoneNumber": "Téléphone",
"accountName": "Nom du Compte",
"doNotCall": "Ne Pas Appeler",
"address": "Adresse",
"status": "Statut",
"source": "Source",
"opportunityAmount": "Montant du Challenge",
"description": "Description",
"createdAccount": "Compte",
"createdContact": "Contact",
"createdOpportunity": "Challenge"
},
"links": {
},
"options": {
"status": {
"New": "Nouveau",
"Assigned": "Assigné",
"In Process": "En Cours de Traitement",
"Converted": "Converti",
"Recycled": "Supprimé",
"Dead": "Mort"
},
"source": {
"Call": "Appel",
"Email": "Email",
"Existing Customer": "Client Existant",
"Partner": "Partenaire",
"Public Relations": "Relations publiques",
"Web Site": "Site Web",
"Campaign": "Campagne",
"Other": "Autre"
}
}
}
@@ -0,0 +1,29 @@
{
"fields": {
"name": "Nom",
"parent": "Parent",
"status": "Statut",
"dateStart": "Date de Début",
"dateEnd": "Date de Fin",
"duration": "Durée",
"description": "Description",
"users": "Utilisateurs",
"contacts": "Contacts",
"leads": "Leads"
},
"links": {
},
"options": {
"status": {
"Planned": "Plannifié",
"Held": "Fait",
"Not Held": "Pas Fait"
}
},
"labels": {
"Create Meeting": "Créer un Rendez-vous",
"Set Held": "Marquer Fait",
"Set Not Held": "Marquer Pas Fait",
"Send Invitations": "Envoi d'Invitations"
}
}
@@ -0,0 +1,34 @@
{
"fields": {
"name": "Nom",
"account": "Compte",
"stage": "Etage",
"amount": "Montant",
"probability": "Probabilité, %",
"leadSource": "Lead Source",
"doNotCall": "Ne Pas Appeler",
"closeDate": "Date de fermeture",
"contacts": "Contacts",
"description": "Description"
},
"links": {
"contacts": "Contacts"
},
"options": {
"stage": {
"Prospecting": "En Prospection",
"Qualification": "Qualification",
"Needs Analysis": "Analyse des besoins",
"Value Proposition": "Montant de la Proposition",
"Id. Decision Makers": "Id. Décisionnaire",
"Perception Analysis": "Analyse de la perception",
"Proposal/Price Quote": "Proposition/Devis",
"Negotiation/Review": "Négociation/avis",
"Closed Won": "Closed Won",
"Closed Lost": "Closed Lost"
}
},
"labels": {
"Create Opportunity": "Créer un Challenge"
}
}
@@ -0,0 +1,19 @@
{
"fields": {
"name": "Nom",
"emailAddress": "Email",
"title": "Titre",
"website": "Site Web",
"accountName": "Nom du Compte",
"phoneNumber": "Téléphone",
"doNotCall": "Ne Pas Appeler",
"address": "Adresse",
"description": "Description"
},
"links": {
},
"labels": {
"Create Target": "Créer une Cible",
"Convert to Lead": "Convertir en Lead"
}
}
@@ -0,0 +1,31 @@
{
"fields": {
"name": "Nom",
"parent": "Parent",
"status": "Statut",
"dateStart": "Date de Début",
"dateEnd": "Date d'échéance",
"priority": "Priorité",
"description": "Description",
"isOverdue": "Est en Retard"
},
"links": {
},
"options": {
"status": {
"Not Started": "Pas Commencé",
"Started": "Commencé",
"Completed": "Terminé",
"Canceled": "Annulé"
},
"priority" : {
"Low": "Bas",
"Normal": "Normal",
"High": "Haut",
"Urgent": "Urgent"
}
},
"labels": {
"Create Task": "Créer une tâche"
}
}
@@ -0,0 +1,40 @@
{
"fields": {
"name": "Nome",
"emailAddress": "E-mail",
"website": "Website",
"phone": "Telefone",
"fax": "Fax",
"billingAddress": "Endereço de Cobrança",
"shippingAddress": "Endereço de Entrega",
"description": "Descrição",
"sicCode": "Código SIC",
"industry": "Indústria",
"type": "Tipo"
},
"links": {
"contacts": "Contatos",
"opportunities": "Oportunidades",
"cases": "Atendimentos"
},
"options": {
"type": {
"Customer": "Cliente",
"Investor": "Investidor",
"Partner": "Parceiro",
"Reseller": "Revendedor"
},
"industry": {
"Apparel": "Apparel",
"Banking": "Banking",
"Computer Software": "Computer Software",
"Education": "Education",
"Electronics": "Electronics",
"Finance": "Finance",
"Insurance": "Insurance"
}
},
"labels": {
"Create Account": "Criar Conta"
}
}
@@ -0,0 +1,13 @@
{
"modes": {
"month": "Mês",
"week": "Semana",
"day": "Dia",
"agendaWeek": "Semana",
"agendaDay": "Dia"
},
"labels": {
"Today": "Hoje",
"Create": "Criar"
}
}
@@ -0,0 +1,34 @@
{
"fields": {
"name": "Nome",
"parent": "Relação",
"status": "Status",
"dateStart": "Data de Início",
"dateEnd": "Data Final",
"direction": "Direção",
"duration": "Duração",
"description": "Descrição",
"users": "Usuários",
"contacts": "Contatos",
"leads": "Leads"
},
"links": {
},
"options": {
"status": {
"Planned": "Planejado",
"Held": "Realizada",
"Not Held": "Não Realizada"
},
"direction": {
"Outbound": "Saída",
"Inbound": "Entrada"
}
},
"labels": {
"Create Call": "Criar Ligação",
"Set Held": "Marcar como realizada",
"Set Not Held": "Marcar como não realizada",
"Send Invitations": "Enviar Convites"
}
}
@@ -0,0 +1,38 @@
{
"fields": {
"name": "Nome",
"number": "Número",
"status": "Status",
"account": "Conta",
"contact": "Contato",
"priority": "Prioridade",
"type": "Tipo",
"description": "Descrição"
},
"links": {
},
"options": {
"status": {
"New": "Novo",
"Assigned": "Designado",
"Pending": "Pendente",
"Closed": "Fechado",
"Rejected": "Rejeitado",
"Duplicate": "Duplicado"
},
"priority": {
"Low": "Baixa",
"Normal": "Normal",
"High": "Alta",
"Urgent": "Urgente"
},
"type": {
"Question": "Questão",
"Incident": "Incidente",
"Problem": "Problema"
}
},
"labels": {
"Create Case": "Criar Atendimento"
}
}
@@ -0,0 +1,22 @@
{
"fields": {
"name": "Nome",
"emailAddress": "E-mail",
"title": "Cargo",
"account": "Conta",
"phone": "Telefone",
"phoneOffice": "Telefone (Comercial)",
"fax": "Fax",
"accountType": "Tipo de Conta",
"doNotCall": "Não Ligar",
"address": "Endereço",
"description": "Descrição"
},
"links": {
"opportunities": "Oportunidades",
"cases": "Atendimento"
},
"labels": {
"Create Contact": "Criar Contato"
}
}
@@ -0,0 +1,79 @@
{
"scopeNames": {
"Account": "Conta",
"Contact": "Contato",
"Lead": "Lead",
"Prospect": "Prospecto",
"Opportunity": "Oportunidade",
"Meeting": "Reunião",
"Calendar": "Calendário",
"Call": "Ligação",
"Task": "Tarefa",
"Case": "Atendimento",
"InboundEmail": "E-mail de Entrada"
},
"scopeNamesPlural": {
"Account": "Contas",
"Contact": "Contatos",
"Lead": "Leads",
"Prospect": "Prospectos",
"Opportunity": "Oportunidades",
"Meeting": "Reuniões",
"Calendar": "Calendário",
"Call": "Ligações",
"Task": "Tarefas",
"Case": "Atendimentos",
"InboundEmail": "E-mails de Entrada"
},
"dashlets": {
"Leads": "Meus Leads",
"Opportunities": "Minhas Oportunidades",
"Tasks": "Minhas Tarefas",
"Cases": "Meus Atendimentos",
"Calendar": "Calendário",
"OpportunitiesByStage": "Oportunidades por Estágio",
"OpportunitiesByLeadSource": "Oportunities por Origem do Lead",
"SalesByMonth": "Vendas Por Mês",
"SalesPipeline": "Funil de Vendas"
},
"labels": {
"Create InboundEmail": "Criar E-mail de Entrada",
"Activities": "Atividades",
"History": "Histórico",
"Attendees": "participantes",
"Schedule Meeting": "Agendar Reunião",
"Schedule Call": "Agendar Ligação",
"Compose Email": "Compor E-mail",
"Log Meeting": "Logar Reunião",
"Log Call": "Logar Ligação",
"Archive Email": "Arquivar E-mail",
"Create Task": "Criar Tarefa",
"Tasks": "Tarefas"
},
"fields": {
"billingAddressCity": "Cidade",
"billingAddressCountry": "País",
"billingAddressPostalCode": "CEP",
"billingAddressState": "Estado",
"billingAddressStreet": "Endereço",
"addressCity": "Cidade",
"addressStreet": "Logradouro",
"addressCountry": "País",
"addressState": "Estado",
"addressPostalCode": "CEP",
"shippingAddressCity": "Cidade (Envio)",
"shippingAddressStreet": "Logradouro (Envio)",
"shippingAddressCountry": "País (Envio)",
"shippingAddressState": "Estado (Envio)",
"shippingAddressPostalCode": "CEP (Envio)"
},
"links": {
"contacts": "Contatos",
"opportunities": "Oportunidades",
"leads": "Leads",
"meetings": "Reuniões",
"calls": "Ligações",
"tasks": "Tarefas",
"emails": "E-mails"
}
}
@@ -0,0 +1,43 @@
{
"fields": {
"name": "Nome",
"team": "Time",
"status": "Status",
"assignToUser": "Atribuído a",
"host": "Host",
"username": "Usuário",
"password": "Senha",
"port": "Porta",
"monitoredFolders": "Pastas Monitoradas",
"trashFolder": "Pasta da Lixeira",
"ssl": "SSL",
"createCase": "Criar Atendimento",
"reply": "Responder",
"caseDistribution": "Distribuição do Atendimento",
"replyEmailTemplate": "Template do E-mail de Resposta",
"replyFromAddress": "E-mail de Resposta (From)",
"replyFromName": "Nome de Resposta (FromName)"
},
"links": {
},
"options": {
"status": {
"Active": "Ativo",
"Inactive": "Inativo"
},
"caseDistribution": {
"Direct-Assignment": "Atribuição Direta",
"Round-Robin": "Rodízio",
"Least-Busy": "Menos Ocupado"
}
},
"labels": {
"Create InboundEmail": "Criar E-mail de Entrada",
"IMAP": "IMAP",
"Actions": "Ações",
"Main": "Principal"
},
"messages": {
"couldNotConnectToImap": "Não foi possível conectar ao servidor IMAP"
}
}
@@ -0,0 +1,48 @@
{
"labels": {
"Converted To": "Convertido Para",
"Create Lead": "Criar Lead",
"Convert": "Converter"
},
"fields": {
"name": "Nome",
"emailAddress": "E-mail",
"title": "Cargo",
"website": "Website",
"phone": "Telefone",
"phoneOffice": "Telefone (Comercial)",
"fax": "Fax",
"accountName": "Conta",
"doNotCall": "Não Ligar",
"address": "Endereço",
"status": "Status",
"source": "Origem",
"opportunityAmount": "Valor da Oportunidade",
"description": "Descrição",
"createdAccount": "Conta",
"createdContact": "Contato",
"createdOpportunity": "Oportunidade"
},
"links": {
},
"options": {
"status": {
"New": "Novo",
"Assigned": "Atribuído",
"In Process": "Em Processo",
"Converted": "Convertido",
"Recycled": "Reciclado",
"Dead": "Morto"
},
"source": {
"Call": "Ligação",
"Email": "E-mail",
"Existing Customer": "Cliente Existente",
"Partner": "Parceiro",
"Public Relations": "Relacionamento",
"Web Site": "Website",
"Campaign": "Campanha",
"Other": "Outro"
}
}
}
@@ -0,0 +1,29 @@
{
"fields": {
"name": "Nome",
"parent": "Relação",
"status": "Status",
"dateStart": "Data de Início",
"dateEnd": "Data final",
"duration": "Duração",
"description": "Descrição",
"users": "Usuários",
"contacts": "Contatos",
"leads": "Leads"
},
"links": {
},
"options": {
"status": {
"Planned": "Planejada",
"Held": "Realizada",
"Not Held": "Não Realizada"
}
},
"labels": {
"Create Meeting": "Criar Reunião",
"Set Held": "Marcar como realizada",
"Set Not Held": "Marcar como não realizada",
"Send Invitations": "Enviar Convites"
}
}
@@ -0,0 +1,43 @@
{
"fields": {
"name": "Nome",
"account": "Conta",
"stage": "Estágio",
"amount": "Valor",
"probability": "Probabilidade, %",
"leadSource": "Lead de Origem",
"doNotCall": "Não ligar",
"closeDate": "Data para Fechamento",
"description": "Descrição"
},
"links": {
"contacts": "Contatos"
},
"options": {
"stage": {
"Prospecting": "Prospectando",
"Qualification": "Qualificação",
"Needs Analysis": "Necessita de Análise",
"Value Proposition": "Proposta de Valor",
"Id. Decision Makers": "Identificação dos Decisores",
"Perception Analysis": "Análise da Percepção",
"Proposal/Price Quote": "Proposta/Cotação",
"Negotiation/Review": "Negociação/Revisão",
"Closed Won": "Ganha",
"Closed Lost": "Perdida"
},
"leadSource": {
"Call": "Ligação",
"Email": "E-mail",
"Existing Customer": "Cliente Existente",
"Partner": "Parceiro",
"Public Relations": "Relacionamento",
"Web Site": "Website",
"Campaign": "Campanha",
"Other": "Outros"
}
},
"labels": {
"Create Opportunity": "Create Opportunity"
}
}
@@ -0,0 +1,21 @@
{
"fields": {
"name": "Nome",
"emailAddress": "E-mail",
"title": "Cargo",
"website": "Website",
"accountName": "Conta",
"phone": "Telefone",
"phoneOffice": "Telefone (Comercial)",
"fax": "Fax",
"doNotCall": "Não Ligar",
"address": "Endereço",
"description": "Descrição"
},
"links": {
},
"labels": {
"Create Prospect": "Criar Prospecto",
"Convert to Lead": "Converter para Lead"
}
}
@@ -0,0 +1,31 @@
{
"fields": {
"name": "Name",
"parent": "Relação",
"status": "Status",
"dateStart": "Data de Início",
"dateEnd": "Até a Data",
"priority": "Prioridade",
"description": "Descrição",
"isOverdue": "Atrasada"
},
"links": {
},
"options": {
"status": {
"Not Started": "Não Iniciada",
"Started": "Iniciada",
"Completed": "Completa",
"Canceled": "Cancelada"
},
"priority": {
"Low": "Baixa",
"Normal": "Normal",
"High": "Alta",
"Urgent": "Urgente"
}
},
"labels": {
"Create Task": "Criar Tarefa"
}
}
@@ -0,0 +1,40 @@
{
"fields": {
"name": "Nume",
"emailAddress": "Email",
"website": "Site Web",
"phoneNumber": "Telefon",
"billingAddress": "Adresa de Facturare",
"shippingAddress": "Adresa de Transport",
"description": "Descriere",
"sicCode": "Cod Sic",
"industry": "Industrie",
"type": "Tip",
"contactRole": "Rol"
},
"links": {
"contacts": "Contacte",
"opportunities": "Oportunitati",
"cases": "Cazuri"
},
"options": {
"type": {
"Customer": "Client",
"Investor": "Investitor",
"Partner": "Partener",
"Reseller": "Reseller"
},
"industry": {
"Apparel": "Haine",
"Banking": "Banking",
"Computer Software": "Software Computer",
"Education": "Educatie",
"Electronics": "Electronice",
"Finance": "Finante",
"Insurance": "Asigurari"
}
},
"labels": {
"Create Account": "Creare cont"
}
}
@@ -0,0 +1,13 @@
{
"modes": {
"month": "Luna",
"week": "Saptamana",
"day": "Zi",
"agendaWeek": "Saptamana",
"agendaDay": "Zi"
},
"labels": {
"Today": "Astazi",
"Create": "Creaza"
}
}
@@ -0,0 +1,39 @@
{
"fields": {
"name": "Nume",
"parent": "Parinte",
"status": "Status",
"dateStart": "Data inceput",
"dateEnd": "Data terminare",
"direction": "Directie",
"duration": "Durata",
"description": "Descriere",
"users": "Utilizatori",
"contacts": "Contacte",
"leads": "Lead-uri"
},
"links": {
},
"options": {
"status": {
"Planned": "Planificat",
"Held": "Detinut",
"Not Held": "Nedetinut"
},
"direction": {
"Outbound": "Iesire",
"Inbound": "Intrare"
}
},
"labels": {
"Create Call": "Creare Apelare",
"Set Held": "Setare detinere",
"Set Not Held": "Setare nedetinere",
"Send Invitations": "Trimite invitatii"
},
"presetFilters": {
"planned": "Planificat",
"held": "Detinut",
"todays": "Today's"
}
}
@@ -0,0 +1,42 @@
{
"fields": {
"name": "Nume",
"number": "Numar",
"status": "Status",
"account": "Cont",
"contact": "Contact",
"priority": "Prioritate",
"type": "Tip",
"description": "Descriere"
},
"links": {
},
"options": {
"status": {
"New": "Nou",
"Assigned": "Alocat",
"Pending": "In asteptare",
"Closed": "Inchis",
"Rejected": "Respins",
"Duplicate": "Duplicat"
},
"priority" : {
"Low": "Scazut",
"Normal": "Normal",
"High": "Inalt",
"Urgent": "Urgent"
},
"type": {
"Question": "Intrebare",
"Incident": "Incident",
"Problem": "Problema"
}
},
"labels": {
"Create Case": "Creare Caz"
},
"presetFilters": {
"open": "Deschide",
"closed": "Inchis"
}
}

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