Compare commits

...

210 Commits
2.2.0 ... 2.5.2

Author SHA1 Message Date
Yuri Kuznetsov
0a2c79ba81 version 2014-09-11 18:17:15 +03:00
Yuri Kuznetsov
5360ba0147 fix dutch 2014-09-11 18:14:30 +03:00
Yuri Kuznetsov
6620254d00 fix lang 2014-09-11 18:12:27 +03:00
Yuri Kuznetsov
f6eb0bcce8 Merge branch 'hotfix/2.5.2' of ssh://172.20.0.1/var/git/espo/backend into hotfix/2.5.2 2014-09-11 18:06:10 +03:00
Yuri Kuznetsov
c1116ff5dc fix email account 2014-09-11 18:05:58 +03:00
Taras Machyshyn
7dcdfcd231 DBAL fixed changing column length 2014-09-11 15:20:18 +03:00
Taras Machyshyn
0eadb9af69 log filename if invalid json file 2014-09-11 14:07:12 +03:00
Yuri Kuznetsov
3c3518b857 fix global search panel 2014-09-10 11:19:14 +03:00
Yuri Kuznetsov
f3ab0a734c css 2014-09-09 15:11:01 +03:00
Yuri Kuznetsov
33adc67dee version 2014-09-09 15:06:12 +03:00
Yuri Kuznetsov
18e475e1b6 fix clear cache 2014-09-09 15:05:18 +03:00
Yuri Kuznetsov
438c0f7edd upgrade full calendar 2014-09-09 12:20:56 +03:00
Yuri Kuznetsov
3159c9d288 libs to metadata 2014-09-09 11:50:11 +03:00
Yuri Kuznetsov
fe0f93784b fix lib loader 2014-09-09 11:21:54 +03:00
Yuri Kuznetsov
2d237eae29 remove isHtml dependency 2014-09-09 11:14:29 +03:00
Yuri Kuznetsov
747aa2d882 dashlet ch 2014-09-09 11:07:24 +03:00
Yuri Kuznetsov
46a7119ce1 month names in sales by month dashlet 2014-09-09 11:00:19 +03:00
Yuri Kuznetsov
c18342694f change max resolution 2014-09-09 10:54:02 +03:00
Yuri Kuznetsov
bb47b70f31 fix tooltip 2014-09-09 10:48:02 +03:00
Yuri Kuznetsov
07767b2dae fix ref 2014-09-05 17:26:21 +03:00
Yuri Kuznetsov
88037c8291 Merge branch 'release/2.5' into stable 2014-09-05 17:05:40 +03:00
Taras Machyshyn
ee03c686ca cron job chnages: remove duplicate jobs, check if ruuning 2014-09-05 15:27:17 +03:00
Yuri Kuznetsov
78140f7d5e change default dashlet layout 2014-09-05 14:59:33 +03:00
Yuri Kuznetsov
549acfd032 copy billing address 2014-09-05 14:52:43 +03:00
Yuri Kuznetsov
e4dab33792 acceptance status 2014-09-05 12:41:28 +03:00
Yuri Kuznetsov
287f95a8ac sort entity list in field/layout manager 2014-09-05 11:30:20 +03:00
Yuri Kuznetsov
4951f8464e role acl table 2014-09-05 11:27:28 +03:00
Yuri Kuznetsov
8ed9b9ebd2 fix get user Acl 2014-09-05 11:20:12 +03:00
Yuri Kuznetsov
96ed9612d4 email address lookup minChar 1 2014-09-05 11:11:35 +03:00
Yuri Kuznetsov
1fd77a4c13 link field can be w/o link 2014-09-05 10:58:04 +03:00
Yuri Kuznetsov
2390ac10a8 dont rerender if edit inline edit 2014-09-05 10:46:58 +03:00
Yuri Kuznetsov
03572ecaad footer padding 2014-09-05 10:28:43 +03:00
Yuri Kuznetsov
c1ac692304 version 2014-09-05 10:21:23 +03:00
Yuri Kuznetsov
f371090bde Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2014-09-05 10:20:45 +03:00
Yuri Kuznetsov
e28341f154 pl_PL 2014-09-05 10:20:27 +03:00
Taras Machyshyn
870f871ceb changed allowed params of 'link' metadata 2014-09-04 15:55:54 +03:00
Yuri Kuznetsov
c18d892a4e bottom panel label 2014-09-04 13:05:27 +03:00
Yuri Kuznetsov
c8bfd1c6dd fix side field label 2014-09-04 13:01:49 +03:00
Yuri Kuznetsov
c582860d5c fix notification about link appears twice 2014-09-04 13:00:11 +03:00
Yuri Kuznetsov
1796db30fa external account 1 2014-09-02 18:26:48 +03:00
Yuri Kuznetsov
25aa0ec564 show/hide integration fields 2014-09-01 18:17:35 +03:00
Yuri Kuznetsov
f3dc904c65 helpText 2014-09-01 17:36:54 +03:00
Yuri Kuznetsov
343d33e741 integrations 1 2014-09-01 16:59:59 +03:00
Yuri Kuznetsov
276480dcfa opp reports 2014-08-29 11:40:28 +03:00
Yuri Kuznetsov
b00b725af5 text earch 2014-08-29 11:26:50 +03:00
Yuri Kuznetsov
ab71ced531 full form keeps attributes 2014-08-29 11:06:09 +03:00
Yuri Kuznetsov
ba7988a842 ssl-port dependance 2014-08-29 10:51:46 +03:00
Yuri Kuznetsov
e1355acda1 email account 5 2014-08-28 17:15:44 +03:00
Yuri Kuznetsov
1853f4f113 fix 2014-08-27 18:00:14 +03:00
Yuri Kuznetsov
da677cfae8 fix 2014-08-27 17:56:37 +03:00
Yuri Kuznetsov
272bccc2fd fix 2014-08-27 17:41:29 +03:00
Yuri Kuznetsov
5372155773 email account 3 2014-08-27 17:34:32 +03:00
Yuri Kuznetsov
e1579295da email account 2014-08-27 17:01:31 +03:00
Yuri Kuznetsov
cc708e03d1 email account 1 2014-08-27 12:07:59 +03:00
Yuri Kuznetsov
34e91958d6 hide preferences password 2014-08-27 10:55:08 +03:00
Yuri Kuznetsov
928e4c2fb5 allowQuickEdit 2014-08-26 17:19:14 +03:00
Yuri Kuznetsov
cf86fcda85 email resrict fields 2014-08-26 15:39:56 +03:00
Yuri Kuznetsov
db3b36a639 fix email fields view 2014-08-26 13:35:33 +03:00
Yuri Kuznetsov
a4a731437e fix email fields 2014-08-26 13:19:35 +03:00
Yuri Kuznetsov
51f4c7fa09 fix field re-render 2014-08-26 13:19:25 +03:00
Yuri Kuznetsov
cbce4fc327 fix datetime short 2014-08-26 12:06:02 +03:00
Yuri Kuznetsov
4e953f6fa8 add user to address seek 2014-08-26 11:26:56 +03:00
Yuri Kuznetsov
0ed0420a4b fix email 2014-08-26 11:20:49 +03:00
Yuri Kuznetsov
16961795af email address field 2014-08-25 17:00:55 +03:00
Yuri Kuznetsov
c52c7fed59 acl check 2014-08-22 18:23:57 +03:00
Yuri Kuznetsov
95c00fc49d reply action 2014-08-22 18:22:52 +03:00
Yuri Kuznetsov
9f8606dd06 fix datetime 2014-08-22 18:05:03 +03:00
Yuri Kuznetsov
b33dc9baae link multiple select 2014-08-22 17:38:04 +03:00
Yuri Kuznetsov
8cb95f1b0e user filter by teams 2014-08-22 17:32:59 +03:00
Yuri Kuznetsov
86cd1ab864 blockquote size 2014-08-22 17:14:29 +03:00
Yuri Kuznetsov
df842f3e3e forward attachments 2014-08-22 17:10:04 +03:00
Yuri Kuznetsov
572455ecf6 attachment roles 2014-08-22 16:45:01 +03:00
Yuri Kuznetsov
7692f9e3ad align list 2014-08-22 16:16:00 +03:00
Yuri Kuznetsov
be0c1d8ab1 stream date 2014-08-22 15:53:00 +03:00
Yuri Kuznetsov
4a6c3d9e18 date time short fix and side 2014-08-22 15:50:11 +03:00
Yuri Kuznetsov
e0fdbd90fd datetime short 2014-08-22 15:37:22 +03:00
Yuri Kuznetsov
18a3f2658a email improve 2014-08-22 14:45:49 +03:00
Yuri Kuznetsov
80ee06ec1d email reply 2014-08-22 12:34:26 +03:00
Yuri Kuznetsov
f16adeed99 fornawesome 2014-08-22 11:33:08 +03:00
Yuri Kuznetsov
9ca39f3559 email create in menu 2014-08-22 11:06:00 +03:00
Yuri Kuznetsov
19285372d6 default search data 2014-08-22 11:02:21 +03:00
Yuri Kuznetsov
61df6bec45 cleanup 2014-08-22 10:41:34 +03:00
Yuri Kuznetsov
8904d4ddaf acl solid in metadata 2014-08-22 10:20:24 +03:00
Yuri Kuznetsov
ca5fba97df Merge branch 'hotfix/2.4.1' 2014-08-21 18:14:30 +03:00
Yuri Kuznetsov
fa755efefb email layouts 2014-08-21 18:14:21 +03:00
Yuri Kuznetsov
d6085f4985 plain text email 2014-08-21 16:02:57 +03:00
Yuri Kuznetsov
4c01e9329d timeout 0 2014-08-21 12:37:28 +03:00
Yuri Kuznetsov
9813eeb5bc nl lang 2014-08-21 11:32:11 +03:00
Yuri Kuznetsov
09d571a639 de lang 2014-08-21 11:30:09 +03:00
Yuri Kuznetsov
fffd44316e Merge branch 'hotfix/2.4.1' 2014-08-20 16:55:24 +03:00
Yuri Kuznetsov
a704830b86 fix inline edit for hidden fields 2014-08-20 16:55:05 +03:00
Yuri Kuznetsov
7910f71428 Merge branch 'hotfix/2.4.1' 2014-08-20 15:51:37 +03:00
Yuri Kuznetsov
52dcc51f4f version 2014-08-20 14:20:44 +03:00
Yuri Kuznetsov
f870ded9e0 rename default and class properties 2014-08-20 11:45:02 +03:00
Yuri Kuznetsov
224c72c875 remove upload timeout 2014-08-20 10:51:51 +03:00
Yuri Kuznetsov
ed50802073 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2014-08-19 17:34:10 +03:00
Yuri Kuznetsov
1f5036eb5f small refactor utils/database 2014-08-19 16:19:54 +03:00
Yuri Kuznetsov
701a60a07f autocomlate off 2014-08-19 12:38:24 +03:00
Yuri Kuznetsov
c760f7364c some refactor 2014-08-19 12:25:41 +03:00
Yuri Kuznetsov
4c28409a36 showNewRecords for stream 2014-08-19 11:59:30 +03:00
Taras Machyshyn
e932681166 install improvements (fix 'pdo_mysql' checking, turkish lang file) 2014-08-18 16:09:35 +03:00
Taras Machyshyn
a8209488ee install improvements (fix 'pdo_mysql' checking, turkish lang file) 2014-08-18 16:07:46 +03:00
Taras Machyshyn
d48642ca81 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2014-08-18 16:03:14 +03:00
Taras Machyshyn
6561c4b6c2 fix PATCH method 2014-08-18 16:02:58 +03:00
Yuri Kuznetsov
4f5f84c8f7 add phone/email button manage 2014-08-18 15:42:06 +03:00
Yuri Kuznetsov
56a04336fe Merge branch 'master' into stable 2014-08-15 17:43:02 +03:00
Yuri Kuznetsov
1814529d3e Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2014-08-15 17:13:52 +03:00
Taras Machyshyn
1dbe2d9eac installer: missing files 2014-08-15 17:12:26 +03:00
Taras Machyshyn
4ca099afc4 fix installation problem 2014-08-15 16:57:13 +03:00
Yuri Kuznetsov
7ca09bc893 grunt change 2014-08-15 16:22:44 +03:00
Yuri Kuznetsov
0a4a8c956a Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2014-08-15 16:17:28 +03:00
Yuri Kuznetsov
6f2a7ae08f fix Show More 2014-08-15 16:17:23 +03:00
Taras Machyshyn
45a9dd1226 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2014-08-15 16:10:52 +03:00
Taras Machyshyn
9d904171c9 added block rules for IIS 2014-08-15 16:10:23 +03:00
Yuri Kuznetsov
53abea7cdd fix reports 2014-08-15 16:01:35 +03:00
Yuri Kuznetsov
6427604724 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2014-08-15 15:33:07 +03:00
Taras Machyshyn
0d72dc7c2f changes rules for nginx server 2014-08-15 15:32:33 +03:00
Yuri Kuznetsov
9ee5a98ff4 fix label 2014-08-15 15:06:01 +03:00
Taras Machyshyn
3f0c92744c install improvements 2014-08-15 13:27:19 +03:00
Taras Machyshyn
7a87749c4c fix problem with MySQL 'int' 2014-08-15 13:18:44 +03:00
Yuri Kuznetsov
0eaab6b7ee userName unique fix 2014-08-15 11:22:57 +03:00
Yuri Kuznetsov
b52722565a Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2014-08-14 17:13:12 +03:00
Yuri Kuznetsov
80fd2a2006 fix tooltips 2014-08-14 17:13:08 +03:00
Yuri Kuznetsov
1c1fdffb7d some padding 2014-08-14 16:29:50 +03:00
Yuri Kuznetsov
53481890db pdf shown 2014-08-14 16:25:54 +03:00
Yuri Kuznetsov
b82a3d0f31 stream post padding 2014-08-14 16:17:39 +03:00
Yuri Kuznetsov
1700b27501 fix revert 2014-08-14 15:30:14 +03:00
Yuri Kuznetsov
635064e116 revert import imporve 2014-08-14 14:46:54 +03:00
Yuri Kuznetsov
f6e45d8a68 fix import id 2014-08-14 13:40:41 +03:00
Yuri Kuznetsov
ee0cf4bca5 fix see more 2014-08-14 13:31:04 +03:00
Taras Machyshyn
ae98290721 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2014-08-14 13:20:40 +03:00
Taras Machyshyn
24bbcf0e0a installer minor fixes 2014-08-14 13:20:06 +03:00
Yuri Kuznetsov
23b1945a3e change label 2014-08-14 13:19:42 +03:00
Yuri Kuznetsov
e1fe3c03dd Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2014-08-14 13:17:05 +03:00
Yuri Kuznetsov
7e0034f674 see more 2014-08-14 13:13:05 +03:00
Yuri Kuznetsov
d73cfbca53 strem post 2014-08-14 12:40:53 +03:00
Taras Machyshyn
3eec2b2a33 fixed wrong default TLS port 2014-08-14 11:58:43 +03:00
Yuri Kuznetsov
d60dc744e6 no clean js 2014-08-14 11:29:54 +03:00
Yuri Kuznetsov
bd66211c3c grunt fix 2014-08-14 11:27:58 +03:00
Yuri Kuznetsov
2f341c8967 fix grunt 2014-08-14 11:21:01 +03:00
Yuri Kuznetsov
5c7612c9cf version and grunt 2014-08-14 11:19:46 +03:00
Taras Machyshyn
b41c0f34aa Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2014-08-14 11:17:26 +03:00
Taras Machyshyn
14eebc7392 added indexes for EmailAddress.lower and PhoneNumber.name 2014-08-14 11:17:09 +03:00
Yuri Kuznetsov
5a86d0e396 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2014-08-13 17:08:44 +03:00
Yuri Kuznetsov
a4e0c8107d fix modals 2014-08-13 17:08:26 +03:00
Yuri Kuznetsov
1652de16a7 muted tooltip text 2014-08-13 15:36:29 +03:00
Yuri Kuznetsov
ad539e19a2 fix lang 2014-08-13 15:34:31 +03:00
Yuri Kuznetsov
592f073bf9 tooltips 2014-08-13 15:32:11 +03:00
Taras Machyshyn
d5c1d1b86e added no-suPHP support 2014-08-13 15:27:37 +03:00
Yuri Kuznetsov
bd70e2424c isRequired fix 2014-08-13 14:21:03 +03:00
Taras Machyshyn
06d6299d7f fixed language merge issue 2014-08-13 12:52:38 +03:00
Yuri Kuznetsov
d00d2bdb0f login css 2014-08-13 12:20:22 +03:00
Yuri Kuznetsov
9f481f6887 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2014-08-13 11:37:55 +03:00
Yuri Kuznetsov
47d0501828 rowactions 2014-08-13 11:37:31 +03:00
Yuri Kuznetsov
a4cd8dccfe fix dutch 2014-08-12 18:28:39 +03:00
Taras Machyshyn
eee0f0eb13 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2014-08-12 18:23:45 +03:00
Taras Machyshyn
67b9600341 added getRequestMethod() method for Controller 2014-08-12 18:22:14 +03:00
Yuri Kuznetsov
4c0329e6be Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2014-08-12 17:14:58 +03:00
Yuri Kuznetsov
ff6cd18411 nl_NL lang 2014-08-12 17:10:27 +03:00
Taras Machyshyn
8d4219802b added rebuild for Currency Settings 2014-08-12 13:44:23 +03:00
Taras Machyshyn
07e7374a18 added 'remove' possibility to config 2014-08-12 13:04:08 +03:00
Taras Machyshyn
060f517591 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2014-08-12 12:56:36 +03:00
Taras Machyshyn
1af5e93bd3 added test file to ignore list 2014-08-12 12:56:17 +03:00
Yuri Kuznetsov
38df3162b3 fix xss 2014-08-12 12:12:27 +03:00
Yuri Kuznetsov
e03298b041 opportunity currency converted 2014-08-12 11:54:09 +03:00
Yuri Kuznetsov
80278b7213 quote {value} and lead currency converted 2014-08-12 11:47:37 +03:00
Yuri Kuznetsov
a701905e9c required inbound \email template 2014-08-11 18:06:34 +03:00
Yuri Kuznetsov
59b896e594 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2014-08-11 17:36:16 +03:00
Taras Machyshyn
ba4c8e0afd config: possibility to save StdClass 2014-08-11 17:35:49 +03:00
Yuri Kuznetsov
9175d8c7ce change layout 2014-08-11 17:26:00 +03:00
Yuri Kuznetsov
052fee6ba2 fix css input group 2014-08-11 17:24:17 +03:00
Yuri Kuznetsov
aae3afc895 currency 1 2014-08-11 17:11:30 +03:00
Yuri Kuznetsov
1892ff6cae Merge branch 'hotfix/2.3.1' 2014-08-11 15:49:34 +03:00
Yuri Kuznetsov
aa3e9fd8b0 Merge branch 'hotfix/2.3.1' of ssh://172.20.0.1/var/git/espo/backend into hotfix/2.3.1 2014-08-11 14:14:33 +03:00
Yuri Kuznetsov
2863092911 compose email from case 2014-08-11 14:13:52 +03:00
Taras Machyshyn
1c36421157 chnaged currencyRate in config 2014-08-11 13:58:33 +03:00
Yuri Kuznetsov
77f7da0ba5 case and email improvoments 2014-08-11 12:45:04 +03:00
Yuri Kuznetsov
a5f858dda2 fix detail view false field 2014-08-11 11:44:04 +03:00
Taras Machyshyn
a5b7bfc00e fixed access LOGs issue 2014-08-11 11:30:28 +03:00
Yuri Kuznetsov
4d2cb6c38e varchar search 2014-08-11 10:53:40 +03:00
Yuri Kuznetsov
b6bd70d47c currency symbol 2014-08-11 10:49:34 +03:00
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
466 changed files with 12475 additions and 3119 deletions

1
.gitignore vendored
View File

@@ -15,3 +15,4 @@
/client
/test.php
/main.html
/tests/testData/Utils/Config/config.php

View File

@@ -1,10 +1,27 @@
<ifModule mod_headers.c>
Header always set Access-Control-Allow-Methods "POST, GET, PUT, PATCH, DELETE"
</ifModule>
DirectoryIndex index.php index.html
RedirectMatch 403 \.config$
# PROTECTED DIRECTORIES
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule (?i)(data|api) - [F]
</IfModule>
RedirectMatch 403 (?i)/data/config\.php$
RedirectMatch 403 (?i)/data/logs
RedirectMatch 403 (?i)/data/cache
RedirectMatch 403 (?i)/data/upload
RedirectMatch 403 (?i)/application
RedirectMatch 403 (?i)/custom
RedirectMatch 403 (?i)/vendor
#END PROTECTED DIRECTORIES
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_ESPO_CGI_AUTH:%{HTTP:Authorization}]
RewriteEngine On
RewriteRule .* - [E=HTTP_ESPO_CGI_AUTH:%{HTTP:Authorization}]
RewriteRule reset/?$ reset.html [QSA,L]
RewriteRule reset/?$ reset.html [QSA,L]
</IfModule>

View File

@@ -1,4 +1,50 @@
module.exports = function (grunt) {
var jsFilesToMinify = [
'client/lib/jquery-2.0.2.min.js',
'client/lib/underscore-min.js',
'client/lib/backbone-min.js',
'client/lib/handlebars.js',
'client/lib/base64.js',
'client/lib/jquery-ui.min.js',
'client/lib/moment.min.js',
'client/lib/moment-timezone-with-data.min.js',
'client/lib/jquery.timepicker.min.js',
'client/lib/jquery.autocomplete.js',
'client/lib/bootstrap.min.js',
'client/lib/bootstrap-datepicker.js',
'client/lib/bull.min.js',
'client/src/namespace.js',
'client/src/exceptions.js',
'client/src/app.js',
'client/src/utils.js',
'client/src/storage.js',
'client/src/loader.js',
'client/src/pre-loader.js',
'client/src/ui.js',
'client/src/acl.js',
'client/src/model.js',
'client/src/model-offline.js',
'client/src/metadata.js',
'client/src/language.js',
'client/src/cache.js',
'client/src/controller.js',
'client/src/router.js',
'client/src/date-time.js',
'client/src/field-manager.js',
'client/src/search-manager.js',
'client/src/collection.js',
'client/src/multi-collection.js',
'client/src/view-helper.js',
'client/src/layout-manager.js',
'client/src/model-factory.js',
'client/src/collection-factory.js',
'client/src/models/settings.js',
'client/src/models/user.js',
'client/src/models/preferences.js',
'client/src/controllers/base.js',
'client/src/view.js',
];
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
@@ -44,53 +90,9 @@ module.exports = function (grunt) {
mangle: false,
banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n',
},
'build/tmp/client/espo.min.js': [
'frontend/client/lib/jquery-2.0.2.min.js',
'frontend/client/lib/underscore-min.js',
'frontend/client/lib/backbone-min.js',
'frontend/client/lib/handlebars.js',
'frontend/client/lib/base64.js',
'frontend/client/lib/jquery-ui.min.js',
'frontend/client/lib/moment.min.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',
'frontend/client/lib/bootstrap-datepicker.js',
'frontend/client/lib/bull.min.js',
'frontend/client/src/namespace.js',
'frontend/client/src/exceptions.js',
'frontend/client/src/app.js',
'frontend/client/src/utils.js',
'frontend/client/src/storage.js',
'frontend/client/src/loader.js',
'frontend/client/src/pre-loader.js',
'frontend/client/src/ui.js',
'frontend/client/src/acl.js',
'frontend/client/src/model.js',
'frontend/client/src/model-offline.js',
'frontend/client/src/metadata.js',
'frontend/client/src/language.js',
'frontend/client/src/cache.js',
'frontend/client/src/controller.js',
'frontend/client/src/router.js',
'frontend/client/src/date-time.js',
'frontend/client/src/field-manager.js',
'frontend/client/src/search-manager.js',
'frontend/client/src/collection.js',
'frontend/client/src/multi-collection.js',
'frontend/client/src/view-helper.js',
'frontend/client/src/layout-manager.js',
'frontend/client/src/model-factory.js',
'frontend/client/src/collection-factory.js',
'frontend/client/src/models/settings.js',
'frontend/client/src/models/user.js',
'frontend/client/src/models/preferences.js',
'frontend/client/src/controllers/base.js',
'frontend/client/src/view.js',
'frontend/client/src/views/base.js',
'frontend/client/src/views/login.js',
]
'build/tmp/client/espo.min.js': jsFilesToMinify.map(function (item) {
return 'frontend/' + item;
})
},
copy: {
frontendFolders: {
@@ -134,7 +136,7 @@ module.exports = function (grunt) {
'index.php',
'LICENSE.txt',
'.htaccess',
'Web.config',
'web.config',
],
dest: 'build/tmp/',
},

View File

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="rule 1X" stopProcessing="true">
<match url="reset/?$" />
<action type="Rewrite" url="reset.html" appendQueryString="true" />
</rule>
</rules>
</rewrite>
<defaultDocument>
<files>
<clear />
<add value="index.php" />
</files>
</defaultDocument>
</system.webServer>
</configuration>

View File

View File

@@ -1,2 +0,0 @@
Order Deny,Allow
Deny from all

View File

@@ -24,5 +24,11 @@ namespace Espo\Controllers;
class Email extends \Espo\Core\Controllers\Record
{
public function actionGetCopiedAttachments($params, $data, $request)
{
$id = $request->get('id');
return $this->getRecordService()->getCopiedAttachments($id);
}
}

View File

@@ -0,0 +1,40 @@
<?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;
class EmailAccount extends \Espo\Core\Controllers\Record
{
public function actionGetFolders($params, $data, $request)
{
return $this->getRecordService()->getFolders(array(
'host' => $request->get('host'),
'port' => $request->get('port'),
'ssl' => $request->get('ssl'),
'username' => $request->get('username'),
'password' => $request->get('password'),
'id' => $request->get('id')
));
}
}

View File

@@ -24,5 +24,14 @@ namespace Espo\Controllers;
class EmailAddress extends \Espo\Core\Controllers\Record
{
public function actionSearchInAddressBook($params, $data, $request)
{
$q = $request->get('q');
$limit = intval($request->get('limit'));
if (empty($limit) || $limit > 30) {
$limit = 5;
}
return $this->getRecordService()->searchInAddressBook($q, $limit);
}
}

View File

@@ -0,0 +1,104 @@
<?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\Error;
use \Espo\Core\Exceptions\Forbidden;
class ExternalAccount extends \Espo\Core\Controllers\Record
{
public static $defaultAction = 'list';
public function actionList($params, $data, $request)
{
$integrations = $this->getEntityManager()->getRepository('Integration')->find();
$arr = array();
foreach ($integrations as $entity) {
if ($entity->get('enabled') && $this->getMetadata()->get('integrations.' . $entity->id .'.allowUserAccounts')) {
$arr[] = array(
'id' => $entity->id
);
}
}
return array(
'list' => $arr
);
}
public function actionGetOAuthCredentials($params, $data, $request)
{
$id = $request->get('id');
list($integration, $userId) = explode('__', $id);
if (!$this->getUser()->isAdmin()) {
if ($this->getUser()->id != $userId) {
throw new Forbidden();
}
}
$entity = $this->getEntityManager()->getEntity('Integration', $integration);
if ($entity) {
return array(
'clientId' => $entity->get('clientId'),
'redirectUri' => $this->getConfig()->get('siteUrl') . '/oauthcallback'
);
}
}
public function actionRead($params, $data, $request)
{
list($integration, $userId) = explode('__', $params['id']);
if (!$this->getUser()->isAdmin()) {
if ($this->getUser()->id != $userId) {
throw new Forbidden();
}
}
$entity = $this->getEntityManager()->getEntity('ExternalAccount', $params['id']);
return $entity->toArray();
}
public function actionUpdate($params, $data)
{
return $this->actionPatch($params, $data);
}
public function actionPatch($params, $data)
{
list($integration, $userId) = explode('__', $params['id']);
if (!$this->getUser()->isAdmin()) {
if ($this->getUser()->id != $userId) {
throw new Forbidden();
}
}
$entity = $this->getEntityManager()->getEntity('ExternalAccount', $params['id']);
$entity->set($data);
$this->getEntityManager()->saveEntity($entity);
return $entity->toArray();
}
}

View File

@@ -34,7 +34,7 @@ class GlobalSearch extends \Espo\Core\Controllers\Base
$offset = $request->get('offset');
$maxSize = $request->get('maxSize');
return $this->getService('GlobalSearch')->find($query, $offset);
return $this->getService('GlobalSearch')->find($query, $offset, $maxSize);
}
}

View File

@@ -44,7 +44,8 @@ class Import extends \Espo\Core\Controllers\Base
$contents = $data;
$attachment = $this->getEntityManager()->getEntity('Attachment');
$attachment->set('type', 'text/csv');
$attachment->set('type', 'text/csv');
$attachment->set('role', 'Import File');
$this->getEntityManager()->saveEntity($attachment);
$this->getFileManager()->putContents('data/upload/' . $attachment->id, $contents);

View File

@@ -0,0 +1,61 @@
<?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\Error;
class Integration extends \Espo\Core\Controllers\Record
{
protected function checkControllerAccess()
{
if (!$this->getUser()->isAdmin()) {
throw new Forbidden();
}
}
public function actionIndex($params, $data, $request)
{
return false;
}
public function actionRead($params, $data, $request)
{
$entity = $this->getEntityManager()->getEntity('Integration', $params['id']);
return $entity->toArray();
}
public function actionUpdate($params, $data)
{
return $this->actionPatch($params, $data);
}
public function actionPatch($params, $data)
{
$entity = $this->getEntityManager()->getEntity('Integration', $params['id']);
$entity->set($data);
$this->getEntityManager()->saveEntity($entity);
return $entity->toArray();
}
}

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) {

View File

@@ -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);
}
}

View File

@@ -68,6 +68,8 @@ class Preferences extends \Espo\Core\Controllers\Base
$entity->set('smtpEmailAddress', $user->get('emailAddress'));
$entity->set('name', $user->get('name'));
$entity->clear('smtpPassword');
return $entity->toArray();
}
throw new Error();
@@ -83,6 +85,9 @@ class Preferences extends \Espo\Core\Controllers\Base
$entity->set('smtpEmailAddress', $user->get('emailAddress'));
$entity->set('name', $user->get('name'));
$entity->clear('smtpPassword');
if ($entity) {
return $entity->toArray();
}

View File

@@ -63,6 +63,12 @@ class Settings extends \Espo\Core\Controllers\Base
throw new Error('Cannot save settings');
}
/** Rebuild for Currency Settings */
if (isset($data['baseCurrency']) || isset($data['currencyRates'])) {
$this->getContainer()->get('dataManager')->rebuildDatabase(array());
}
/** END Rebuild for Currency Settings */
return $this->getConfigData();
}
}

View File

@@ -44,7 +44,7 @@ class User extends \Espo\Core\Controllers\Record
throw new NotFound();
}
$acl = new \Espo\Core\Acl($user);
$acl = new \Espo\Core\Acl($user, $this->getConfig(), $this->getContainer()->get('fileManager'), $this->getMetadata());
return $acl->toArray();
}

View File

@@ -34,11 +34,15 @@ class Acl
private $levelList = array('all', 'team', 'own', 'no');
private $fileManager;
protected $fileManager;
protected $metadata;
public function __construct(\Espo\Entities\User $user, $config = null, $fileManager = null)
public function __construct(\Espo\Entities\User $user, $config = null, $fileManager = null, $metadata = null)
{
$this->user = $user;
$this->user = $user;
$this->metadata = $metadata;
if (!$this->user->isFetched()) {
throw new Error();
@@ -197,32 +201,11 @@ class Acl
private function initSolid()
{
$this->data['User'] = array(
'read' => 'all',
'edit' => 'no',
'delete' => 'no',
);
$this->data['Team'] = array(
'read' => 'all',
'edit' => 'no',
'delete' => 'no',
);
$this->data['Role'] = false;
$this->data['Note'] = array(
'read' => 'own',
'edit' => 'own',
'delete' => 'own',
);
$this->data['EmailAddress'] = array(
'read' => 'no',
'edit' => 'no',
'delete' => 'no',
);
$this->data['Note'] = array(
'read' => 'all',
'edit' => 'own',
'delete' => 'own',
);
$data = $this->metadata->get('app.acl.solid', array());
foreach ($data as $entityName => $item) {
$this->data[$entityName] = $item;
}
}
private function merge($tables)

View File

@@ -51,7 +51,6 @@ class Container
$obj = $this->$loadMethod();
$this->data[$name] = $obj;
} else {
//external loader class \Espo\Core\Loaders\<className> or \Espo\Custom\Core\Loaders\<className> with load() method
$className = '\Espo\Custom\Core\Loaders\\'.ucfirst($name);
if (!class_exists($className)) {
$className = '\Espo\Core\Loaders\\'.ucfirst($name);
@@ -63,13 +62,18 @@ class Container
}
}
// TODO throw an exception
return null;
}
protected function getServiceClassName($name, $default)
{
$metadata = $this->get('metadata');
$className = $metadata->get('app.serviceContainer.classNames.' . $name, $default);
return $className;
}
private function loadSlim()
{
//return new \Slim\Slim();
return new \Espo\Core\Utils\Api\Slim();
}
@@ -108,7 +112,8 @@ class Container
private function loadMailSender()
{
return new \Espo\Core\Mail\Sender(
$className = $this->getServiceClassName('mailSernder', '\\Espo\\Core\\Mail\\Sender');
return new $className(
$this->get('config')
);
}
@@ -157,10 +162,12 @@ class Container
private function loadAcl()
{
return new \Espo\Core\Acl(
$className = $this->getServiceClassName('acl', '\\Espo\\Core\\Acl');
return new $className(
$this->get('user'),
$this->get('config'),
$this->get('fileManager')
$this->get('fileManager'),
$this->get('metadata')
);
}

View File

@@ -78,7 +78,7 @@ class ControllerManager
throw new NotFound("Controller '$controllerName' is not found");
}
$controller = new $controllerClassName($this->container);
$controller = new $controllerClassName($this->container, $request->getMethod());
if ($actionName == 'index') {
$actionName = $controllerClassName::$defaultAction;

View File

@@ -18,7 +18,7 @@
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
************************************************************************/
namespace Espo\Core\Controllers;
@@ -29,15 +29,21 @@ use \Espo\Core\Utils\Util;
abstract class Base
{
protected $name;
private $container;
private $requestMethod;
public static $defaultAction = 'index';
public function __construct(Container $container)
public function __construct(Container $container, $requestMethod = null)
{
$this->container = $container;
if (isset($requestMethod)) {
$this->setRequestMethod($requestMethod);
}
if (empty($this->name)) {
$name = get_class($this);
if (preg_match('@\\\\([\w]+)$@', $name, $matches)) {
@@ -45,40 +51,55 @@ abstract class Base
}
$this->name = $name;
}
$this->checkControllerAccess();
}
protected function checkControllerAccess()
{
return;
}
protected function getContainer()
{
return $this->container;
}
/**
* Get request method name (Uppercase)
*
* @return string
*/
protected function getRequestMethod()
{
return $this->requestMethod;
}
protected function setRequestMethod($requestMethod)
{
$this->requestMethod = strtoupper($requestMethod);
}
protected function getUser()
{
return $this->container->get('user');
}
protected function getAcl()
{
return $this->container->get('acl');
}
protected function getConfig()
{
return $this->container->get('config');
}
protected function getPreferences()
{
return $this->container->get('preferences');
}
protected function getMetadata()
{
return $this->container->get('metadata');
@@ -88,7 +109,7 @@ abstract class Base
{
return $this->container->get('serviceFactory');
}
protected function getService($name)
{
return $this->getServiceFactory()->create($name);

View File

@@ -18,7 +18,7 @@
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
************************************************************************/
namespace Espo\Core\Cron;
@@ -46,22 +46,22 @@ class Service
$serviceName = $job['service_name'];
if (!$this->getServiceFactory()->checkExists($serviceName)) {
throw new NotFound();
throw new NotFound();
}
$service = $this->getServiceFactory()->create($serviceName);
$serviceMethod = $job['method'];
$service = $this->getServiceFactory()->create($serviceName);
$serviceMethod = $job['method'];
if (!method_exists($service, $serviceMethod)) {
throw new NotFound();
}
throw new NotFound();
}
$data = $job['data'];
if (Json::isJSON($data)) {
$data = Json::decode($data, true);
}
$service->$serviceMethod($data);
$service->$serviceMethod($data);
}
}

View File

@@ -34,6 +34,11 @@ class CronManager
private $jobService;
private $scheduledJobService;
const PENDING = 'Pending';
const RUNNING = 'Running';
const SUCCESS = 'Success';
const FAILED = 'Failed';
protected $lastRunTime = 'data/cache/application/cronLastRunTime.php';
@@ -128,13 +133,12 @@ class CronManager
//Check scheduled jobs and create related jobs
$this->createJobsFromScheduledJobs();
$pendingJobs = $this->getJobService()->getPendingJobs();
foreach ($pendingJobs as $job) {
$this->getJobService()->updateEntity($job['id'], array(
'status' => 'Running',
'status' => self::RUNNING,
));
$isSuccess = true;
@@ -150,7 +154,7 @@ class CronManager
$GLOBALS['log']->error('Failed job running, job ['.$job['id'].']. Error Details: '.$e->getMessage());
}
$status = $isSuccess ? 'Success' : 'Failed';
$status = $isSuccess ? self::SUCCESS : self::FAILED;
$this->getJobService()->updateEntity($job['id'], array(
'status' => $status,
@@ -180,7 +184,6 @@ class CronManager
$cronExpression = \Cron\CronExpression::factory($scheduling);
try {
//$nextDate = $cronExpression->getNextRunDate()->format('Y-m-d H:i:s');
$prevDate = $cronExpression->getPreviousRunDate()->format('Y-m-d H:i:s');
} catch (\Exception $e) {
$GLOBALS['log']->error('ScheduledJob ['.$scheduledJob['id'].']: CronExpression - Impossible CRON expression ['.$scheduling.']');
@@ -197,7 +200,7 @@ class CronManager
//create a job
$data = array(
'name' => $scheduledJob['name'],
'status' => 'Pending',
'status' => self::PENDING,
'scheduledJobId' => $scheduledJob['id'],
'executeTime' => $prevDate,
'method' => $scheduledJob['job'],

View File

@@ -121,8 +121,9 @@ class DataManager
*/
public function updateCacheTimestamp()
{
return $this->getContainer()->get('config')->updateCacheTimestamp();
$this->getContainer()->get('config')->updateCacheTimestamp();
$this->getContainer()->get('config')->save();
return true;
}
}
}

View File

@@ -25,7 +25,7 @@ namespace Espo\Core\Loaders;
use Doctrine\ORM\Tools\Setup,
Espo\Core\Doctrine\ORM\Mapping\Driver\EspoPHPDriver;
class EntityManager
class EntityManager implements Loader
{
private $container;

View File

@@ -0,0 +1,31 @@
<?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\Loaders;
interface Loader
{
public function load();
}

View File

@@ -25,7 +25,7 @@ namespace Espo\Core\Loaders;
use Espo\Core\Utils,
Espo\Core\Utils\Log\Monolog\Handler;
class Log
class Log implements Loader
{
private $container;

View File

@@ -0,0 +1,244 @@
<?php
namespace Espo\Core\Mail;
use \Zend\Mime\Mime as Mime;
class Importer
{
private $entityManager;
private $fileManager;
public function __construct($entityManager, $fileManager)
{
$this->entityManager = $entityManager;
$this->fileManager = $fileManager;
}
protected function getEntityManager()
{
return $this->entityManager;
}
protected function getFileManager()
{
return $this->fileManager;
}
public function importMessage($message, $userId, $teamsIds = array())
{
try {
$email = $this->getEntityManager()->getEntity('Email');
$email->set('isHtml', false);
$email->set('name', $message->subject);
$email->set('status', 'Archived');
$email->set('attachmentsIds', array());
$email->set('assignedUserId', $userId);
$email->set('teamsIds', $teamsIds);
$fromArr = $this->getAddressListFromMessage($message, 'from');
if (isset($message->from)) {
$email->set('fromName', $message->from);
}
$email->set('from', $fromArr[0]);
$email->set('to', implode(';', $this->getAddressListFromMessage($message, 'to')));
$email->set('cc', implode(';', $this->getAddressListFromMessage($message, 'cc')));
if (isset($message->messageId) && !empty($message->messageId)) {
$email->set('messageId', $message->messageId);
if (isset($message->deliveredTo)) {
$email->set('messageIdInternal', $message->messageId . '-' . $message->deliveredTo);
}
}
if ($this->checkIsDuplicate($email)) {
return false;
}
if (isset($message->date)) {
$dt = new \DateTime($message->date);
if ($dt) {
$dateSent = $dt->setTimezone(new \DateTimeZone('UTC'))->format('Y-m-d H:i:s');
$email->set('dateSent', $dateSent);
}
}
if (isset($message->deliveryDate)) {
$dt = new \DateTime($message->deliveryDate);
if ($dt) {
$deliveryDate = $dt->setTimezone(new \DateTimeZone('UTC'))->format('Y-m-d H:i:s');
$email->set('deliveryDate', $deliveryDate);
}
}
$inlineIds = array();
if ($message->isMultipart()) {
foreach (new \RecursiveIteratorIterator($message) as $part) {
$this->importPartDataToEmail($email, $part, $inlineIds);
}
} else {
$this->importPartDataToEmail($email, $message, $inlineIds);
}
$body = $email->get('body');
if (!empty($body)) {
foreach ($inlineIds as $cid => $attachmentId) {
$body = str_replace('cid:' . $cid, '?entryPoint=attachment&amp;id=' . $attachmentId, $body);
}
$email->set('body', $body);
}
$this->getEntityManager()->saveEntity($email);
return $email;
} catch (\Exception $e) {}
}
protected function checkIsDuplicate($email)
{
if ($email->get('messageIdInternal')) {
$duplicate = $this->getEntityManager()->getRepository('Email')->where(array(
'messageIdInternal' => $email->get('messageIdInternal')
))->findOne();
if ($duplicate) {
return true;
}
}
}
protected function getAddressListFromMessage($message, $type)
{
$addressList = array();
if (isset($message->$type)) {
$list = $message->getHeader($type)->getAddressList();
foreach ($list as $address) {
$addressList[] = $address->getEmail();
}
}
return $addressList;
}
protected function importPartDataToEmail(\Espo\Entities\Email $email, $part, &$inlineIds = array())
{
try {
$type = strtok($part->contentType, ';');
$encoding = null;
switch ($type) {
case 'text/plain':
$content = $this->getContentFromPart($part);
if (!$email->get('body')) {
$email->set('body', $content);
}
$email->set('bodyPlain', $content);
break;
case 'text/html':
$content = $this->getContentFromPart($part);
$email->set('body', $content);
$email->set('isHtml', true);
break;
default:
$content = $part->getContent();
$disposition = null;
$fileName = null;
$contentId = null;
if (isset($part->ContentDisposition)) {
if (strpos($part->ContentDisposition, 'attachment') === 0) {
if (preg_match('/filename="?([^"]+)"?/i', $part->ContentDisposition, $m)) {
$fileName = $m[1];
$disposition = 'attachment';
}
} else if (strpos($part->ContentDisposition, 'inline') === 0) {
$contentId = trim($part->contentID, '<>');
$fileName = $contentId;
$disposition = 'inline';
}
}
if (isset($part->contentTransferEncoding)) {
$encoding = strtolower($part->getHeader('Content-Transfer-Encoding')->getTransferEncoding());
}
$attachment = $this->getEntityManager()->getEntity('Attachment');
$attachment->set('name', $fileName);
$attachment->set('type', $type);
if ($disposition == 'inline') {
$attachment->set('role', 'Inline Attachment');
} else {
$attachment->set('role', 'Attachment');
}
if ($encoding == 'base64') {
$content = base64_decode($content);
}
$attachment->set('size', strlen($content));
$this->getEntityManager()->saveEntity($attachment);
$path = 'data/upload/' . $attachment->id;
$this->getFileManager()->putContents($path, $content);
if ($disposition == 'attachment') {
$attachmentsIds = $email->get('attachmentsIds');
$attachmentsIds[] = $attachment->id;
$email->set('attachmentsIds', $attachmentsIds);
} else if ($disposition == 'inline') {
$inlineIds[$contentId] = $attachment->id;
}
}
} catch (\Exception $e) {}
}
protected function getContentFromPart($part)
{
if ($part instanceof \Zend\Mime\Part) {
$content = $part->getRawContent();
if (strtolower($part->charset) != 'utf-8') {
$content = mb_convert_encoding($content, 'UTF-8', $part->charset);
}
} else {
$content = $part->getContent();
$encoding = null;
if (isset($part->contentTransferEncoding)) {
$cteHeader = $part->getHeader('Content-Transfer-Encoding');
$encoding = strtolower($cteHeader->getTransferEncoding());
}
if ($encoding == 'base64') {
$content = base64_decode($content);
}
$charset = 'UTF-8';
if (isset($part->contentType)) {
$ctHeader = $part->getHeader('Content-Type');
$charsetParamValue = $ctHeader->getParameter('charset');
if (!empty($charsetParamValue)) {
$charset = strtoupper($charsetParamValue);
}
}
if ($charset !== 'UTF-8') {
$content = mb_convert_encoding($content, 'UTF-8', $charset);
}
if (isset($part->contentTransferEncoding)) {
$cteHeader = $part->getHeader('Content-Transfer-Encoding');
if ($cteHeader->getTransferEncoding() == 'quoted-printable') {
$content = quoted_printable_decode($content);
}
}
}
return $content;
}
}

View File

@@ -124,23 +124,25 @@ class Sender
return $this;
}
public function send(Email $email)
public function send(Email $email, $params = array())
{
$message = new Message();
$config = $this->config;
$params = $this->params + $params;
if ($email->get('from')) {
$fromName = null;
if (!empty($this->params['fromName'])) {
$fromName = $this->params['fromName'];
if (!empty($params['fromName'])) {
$fromName = $params['fromName'];
} else {
$fromName = $config->get('outboundEmailFromName');
}
$message->addFrom(trim($email->get('from')), $fromName);
} else {
if (!empty($this->params['fromAddress'])) {
$fromAddress = $this->params['fromAddress'];
if (!empty($params['fromAddress'])) {
$fromAddress = $params['fromAddress'];
} else {
if (!$config->get('outboundEmailFromAddress')) {
throw new Error('outboundEmailFromAddress is not specified in config.');
@@ -148,14 +150,22 @@ class Sender
$fromAddress = $config->get('outboundEmailFromAddress');
}
if (!empty($this->params['fromName'])) {
$fromName = $this->params['fromName'];
if (!empty($params['fromName'])) {
$fromName = $params['fromName'];
} else {
$fromName = $config->get('outboundEmailFromName');
}
$message->addFrom($fromAddress, $fromName);
}
if (!empty($params['replyToAddress'])) {
$replyToName = null;
if (!empty($params['replyToName'])) {
$replyToName = $params['replyToName'];
}
$message->setReplyTo($params['replyToAddress'], $replyToName);
}
$value = $email->get('to');
if ($value) {
@@ -191,23 +201,21 @@ class Sender
$body = new MimeMessage;
$parts = array();
$bodyPart = new MimePart($email->getBodyPlainForSending());
$bodyPart->type = 'text/plain';
$bodyPart->charset = 'utf-8';
$parts[] = $bodyPart;
if ($email->get('isHtml')) {
$bodyPart = new MimePart($email->getBodyForSending());
$bodyPart->type = 'text/html';
$bodyPart->charset = 'utf-8';
} else {
if ($email->get('bodyPlain')) {
$bodyPart = new MimePart($email->get('bodyPlain'));
} else {
$bodyPart = new MimePart($email->get('body'));
}
$bodyPart->type = 'text/plain';
$bodyPart->charset = 'utf-8';
$parts[] = $bodyPart;
}
$parts[] = $bodyPart;
$aCollection = $email->get('attachments');
if (!empty($aCollection)) {
@@ -241,9 +249,19 @@ class Sender
$body->setParts($parts);
$message->setBody($body);
if ($email->get('isHtml')) {
$message->getHeaders()->get('content-type')->setType('multipart/alternative');
}
try {
$this->transport->send($message);
$headers = $message->getHeaders();
if ($headers->has('messageId')) {
$email->set('messageId', $headers->get('messageId')->getId());
}
$email->set('status', 'Sent');
$email->set('dateSent', date("Y-m-d H:i:s"));
} catch (\Exception $e) {

View File

@@ -0,0 +1,19 @@
<?php
namespace Espo\Core\Mail\Storage;
class Imap extends \Zend\Mail\Storage\Imap
{
public function getIdsFromUID($uid)
{
$uid = intval($uid) + 1;
return $this->protocol->search(array('UID ' . $uid . ':*'));
}
public function getIdsFromDate($date)
{
return $this->protocol->search(array('SINCE "' . $date . '"'));
}
}

View File

@@ -39,6 +39,8 @@ class Base
protected $entityName;
protected $metadata;
const MIN_LENGTH_FOR_CONTENT_SEARCH = 4;
public function __construct($entityManager, \Espo\Entities\User $user, Acl $acl, $metadata)
{
@@ -106,10 +108,19 @@ class Base
if (empty($result['whereClause'])) {
$result['whereClause'] = array();
}
$fieldDefs = $this->entityManager->getEntity($this->entityName)->getFields();
$fieldList = $this->getTextFilterFields();
$d = array();
foreach ($fieldList as $field) {
$d[$field . '*'] = $item['value'] . '%';
foreach ($fieldList as $field) {
if (
strlen($item['value']) >= self::MIN_LENGTH_FOR_CONTENT_SEARCH
&&
!empty($fieldDefs[$field]['type']) && $fieldDefs[$field]['type'] == 'text'
) {
$d[$field . '*'] = '%' . $item['value'] . '%';
} else {
$d[$field . '*'] = $item['value'] . '%';
}
}
$where['OR'] = $d;
}
@@ -176,18 +187,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');
}
}

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');
}
}

View File

@@ -319,7 +319,7 @@ abstract class Base
$manifestPath = Util::concatPath($upgradePath, $this->manifestName);
if (!file_exists($manifestPath)) {
throw new Error('It\'s not an uprgade package.');
throw new Error('It\'s not an upgrade package.');
}
$manifestJson = $this->getFileManager()->getContents($manifestPath);
@@ -390,4 +390,4 @@ abstract class Base
}
}
}

View File

@@ -51,11 +51,12 @@ class Auth
$entityManager = $this->container->get('entityManager');
$user = $entityManager->getRepository('User')->get('system');
$user->set('isAdmin', $isAdmin);
if (!$user) {
throw new Error('System user is not found');
}
$user->set('isAdmin', $isAdmin);
$entityManager->setUser($user);
$this->container->setUser($user);
}

View File

@@ -56,6 +56,7 @@ class Config
private $data;
private $changedData = array();
private $removeData = array();
private $fileManager;
@@ -112,11 +113,33 @@ class Config
}
foreach ($name as $key => $value) {
if (is_object($value)) {
$value = (array) $value;
}
$this->data[$key] = $value;
$this->changedData[$key] = $value;
}
}
/**
* Remove an option in config
*
* @param string $name
* @return bool | null - null if an option doesn't exist
*/
public function remove($name)
{
if (array_key_exists($name, $this->data)) {
unset($this->data[$name]);
$this->removeData[] = $name;
return true;
}
return null;
}
public function save()
{
$values = $this->changedData;
@@ -125,9 +148,12 @@ class Config
$values = array_merge($this->updateCacheTimestamp(true), $values);
}
$result = $this->getFileManager()->mergeContentsPHP($this->configPath, $values, true);
$removeData = empty($this->removeData) ? null : $this->removeData;
$result = $this->getFileManager()->mergeContentsPHP($this->configPath, $values, 1, $removeData);
if ($result) {
$this->changedData = array();
$this->removeData = array();
$this->loadConfig(true);
}

View File

@@ -18,15 +18,13 @@
*
* 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\DBAL\FieldTypes;
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\BooleanType;
class Bool extends Type
class Bool extends BooleanType
{
const BOOL = 'bool';
@@ -39,27 +37,4 @@ class Bool extends Type
{
return 'TINYINT';
}
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
{
return $platform->getBooleanTypeDeclarationSQL($fieldDeclaration);
}
public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
return $platform->convertBooleans($value);
}
public function convertToPHPValue($value, AbstractPlatform $platform)
{
return (null === $value) ? null : (bool) $value;
}
public function getBindingType()
{
return \PDO::PARAM_BOOL;
}
}

View File

@@ -18,38 +18,18 @@
*
* 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\DBAL\FieldTypes;
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\IntegerType;
class Int extends Type
class Int extends IntegerType
{
const INTtype = 'int';
public function getName()
{
return self::INTtype;
}
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
{
return $platform->getIntegerTypeDeclarationSQL($fieldDeclaration);
}
public function convertToPHPValue($value, AbstractPlatform $platform)
{
return (null === $value) ? null : (int) $value;
}
public function getBindingType()
{
return \PDO::PARAM_INT;
}
}

View File

@@ -18,14 +18,13 @@
*
* 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\DBAL\FieldTypes;
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\StringType;
class Password extends Type
class Password extends StringType
{
const PASSWORD = 'password';
@@ -38,23 +37,5 @@ class Password extends Type
{
return 'VARCHAR';
}
public function getSqlDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
{
return $platform->getVarcharTypeDeclarationSQL($fieldDeclaration);
//return "MD5";
}
/*public function convertToPHPValue($value, AbstractPlatform $platform)
{
return $value;
}
public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
return $value;
} */
}

View File

@@ -18,15 +18,13 @@
*
* 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\DBAL\FieldTypes;
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\StringType;
class Varchar extends Type
class Varchar extends StringType
{
const VARCHAR = 'varchar';
@@ -34,16 +32,4 @@ class Varchar extends Type
{
return self::VARCHAR;
}
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
{
return $platform->getVarcharTypeDeclarationSQL($fieldDeclaration);
//return 'varchar';
}
public function getDefaultLength(AbstractPlatform $platform)
{
return $platform->getVarcharDefaultLength();
}
}

View File

@@ -244,17 +244,8 @@ class Base
$foreignField = $this->getMetadata()->get('entityDefs.'.$entityName.'.fields.'.$name);
if ($foreignField['type'] != 'varchar') {
$fieldDefs = $this->getMetadata()->get('fields.'.$foreignField['type']);
$naming = isset($fieldDefs['naming']) ? $fieldDefs['naming'] : 'postfix';
if (isset($fieldDefs['actualFields']) && is_array($fieldDefs['actualFields'])) {
$foreignFieldArray = array();
foreach($fieldDefs['actualFields'] as $fieldName) {
if ($fieldName != 'salutation') {
$foreignFieldArray[] = Util::getNaming($name, $fieldName, $naming);
}
}
return explode('|', implode('| |', $foreignFieldArray)); //add an empty string between items
if ($foreignField['type'] == 'personName') {
return array('first' . ucfirst($name), ' ', 'last' . ucfirst($name));
}
}

View File

@@ -57,6 +57,7 @@ class Converter
'len' => 'len',
'notNull' => 'notNull',
'autoincrement' => 'autoincrement',
'entity' => 'entity',
'notStorable' => 'notStorable',
'link' => 'relation',
'field' => 'foreign', //todo change "foreign" to "field"

View File

@@ -39,7 +39,7 @@ class Email extends \Espo\Core\Utils\Database\Orm\Base
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}'
email_address.deleted = 0 AND email_address.name LIKE {value}
)",
'=' => \Espo\Core\Utils\Util::toUnderScore($entityName) . ".id IN (
SELECT entity_id
@@ -47,7 +47,15 @@ class Email extends \Espo\Core\Utils\Database\Orm\Base
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}'
email_address.deleted = 0 AND email_address.name = {value}
)",
'<>' => \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 <> {value}
)"
),
'orderBy' => 'email_address.name {direction}',

View File

@@ -28,7 +28,8 @@ class PersonName extends \Espo\Core\Utils\Database\Orm\Base
{
protected function load($fieldName, $entityName)
{
$foreignField = $this->getForeignField($fieldName, $entityName);
$foreignField = array('first' . ucfirst($fieldName), ' ', 'last' . ucfirst($fieldName));
$tableName = Util::toUnderScore($entityName);
$fullList = array(); //contains empty string (" ") like delimiter
@@ -44,8 +45,8 @@ class PersonName extends \Espo\Core\Utils\Database\Orm\Base
$columnName = $tableName.'.'.Util::toUnderScore($fieldNameTrimmed);
$fullList[] = $fieldList[] = $columnName;
$like[] = $columnName." LIKE '{text}'";
$equal[] = $columnName." = '{text}'";
$like[] = $columnName." LIKE {value}";
$equal[] = $columnName." = {value}";
} else {
$fullList[] = "'".$foreignFieldName."'";
}
@@ -60,8 +61,8 @@ class PersonName extends \Espo\Core\Utils\Database\Orm\Base
'type' => 'varchar',
'select' => $this->getSelect($fullList),
'where' => array(
'LIKE' => "(".implode(" OR ", $like)." OR CONCAT(".implode(", ", $fullList).") LIKE '{text}' OR CONCAT(".implode(", ", $fullListReverse).") LIKE '{text}')",
'=' => "(".implode(" OR ", $equal)." OR CONCAT(".implode(", ", $fullList).") = '{text}' OR CONCAT(".implode(", ", $fullListReverse).") = '{text}')",
'LIKE' => "(".implode(" OR ", $like)." OR CONCAT(".implode(", ", $fullList).") LIKE {value} OR CONCAT(".implode(", ", $fullListReverse).") LIKE {value})",
'=' => "(".implode(" OR ", $equal)." OR CONCAT(".implode(", ", $fullList).") = {value} OR CONCAT(".implode(", ", $fullListReverse).") = {value})",
),
'orderBy' => implode(", ", array_map(function ($item) {return $item . ' {direction}';}, $fieldList)),
),
@@ -86,4 +87,4 @@ class PersonName extends \Espo\Core\Utils\Database\Orm\Base
return $select;
}
}
}

View File

@@ -39,7 +39,7 @@ class Phone extends \Espo\Core\Utils\Database\Orm\Base
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}'
phone_number.deleted = 0 AND phone_number.name LIKE {value}
)",
'=' => \Espo\Core\Utils\Util::toUnderScore($entityName) . ".id IN (
SELECT entity_id
@@ -47,7 +47,15 @@ class Phone extends \Espo\Core\Utils\Database\Orm\Base
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}'
phone_number.deleted = 0 AND phone_number.name = {value}
)",
'<>' => \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 <> {value}
)"
),
'orderBy' => 'phone_number.name {direction}',

View File

@@ -31,7 +31,12 @@ class Base extends \Espo\Core\Utils\Database\Orm\Base
protected $foreignLinkName = null;
protected $foreignEntityName = null;
protected $allowParams = array();
protected $allowedParams = array(
'relationName',
'conditions',
'additionalColumns',
'midKeys',
);
protected function getParams()
{
@@ -106,10 +111,10 @@ class Base extends \Espo\Core\Utils\Database\Orm\Base
$linkName = $this->getLinkName();
$entityName = $this->getEntityName();
if (!empty($this->allowParams)) {
if (!empty($this->allowedParams)) {
$linkParams = &$loads[$entityName]['relations'][$linkName];
foreach ($this->allowParams as $name) {
foreach ($this->allowedParams as $name) {
$additionalParrams = $this->getAllowedAdditionalParams($name);

View File

@@ -24,12 +24,6 @@ namespace Espo\Core\Utils\Database\Orm\Relations;
class HasMany extends Base
{
protected $allowParams = array(
'relationName',
'conditions',
'additionalColumns',
);
protected function load($linkName, $entityName)
{
$linkParams = $this->getLinkParams();

View File

@@ -26,12 +26,6 @@ use Espo\Core\Utils\Util;
class ManyMany extends Base
{
protected $allowParams = array(
'relationName',
'conditions',
'additionalColumns',
);
protected function load($linkName, $entityName)
{
$foreignEntityName = $this->getForeignEntityName();

View File

@@ -276,7 +276,8 @@ class Converter
case 'array':
case 'jsonArray':
case 'text':
$dbFieldParams['default'] = ''; //for db type TEXT can't be defined a default value
case 'longtext':
unset($dbFieldParams['default']); //for db type TEXT can't be defined a default value
break;
case 'bool':

View File

@@ -18,24 +18,32 @@
*
* 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\Schema\rebuildActions;
class Currency extends \Espo\Core\Utils\Database\Schema\BaseRebuildActions
{
public function afterRebuild()
{
$currencyConfig = $this->getConfig()->get('currency');
$currencyConfig['rate'][ $currencyConfig['base'] ] = '1.00';
$pdo = $this->getEntityManager()->getPDO();
public function afterRebuild()
{
$defaultCurrency = $this->getConfig()->get('defaultCurrency');
$baseCurrency = $this->getConfig()->get('baseCurrency');
$currencyRates = $this->getConfig()->get('currencyRates');
if ($defaultCurrency != $baseCurrency) {
$currencyRates = $this->exchangeRates($baseCurrency, $defaultCurrency, $currencyRates);
}
$currencyRates[$defaultCurrency] = '1.00';
$pdo = $this->getEntityManager()->getPDO();
$sql = "TRUNCATE `currency`";
$pdo->prepare($sql)->execute();
foreach ($currencyConfig['rate'] as $currencyName => $rate) {
foreach ($currencyRates as $currencyName => $rate) {
$sql = "
INSERT INTO `currency`
@@ -43,9 +51,34 @@ class Currency extends \Espo\Core\Utils\Database\Schema\BaseRebuildActions
VALUES
(".$pdo->quote($currencyName) . ", " . $pdo->quote($rate) . ")
";
$pdo->prepare($sql)->execute();
}
}
$pdo->prepare($sql)->execute();
}
}
/**
* Calculate exchange rates if defaultCurrency doesn't equals baseCurrency
*
* @param string $baseCurrency
* @param string $defaultCurrency
* @param array $currencyRates [description]
* @return array - List of new currency rates
*/
protected function exchangeRates($baseCurrency, $defaultCurrency, array $currencyRates)
{
$precision = 5;
$defaultCurrencyRate = round(1 / $currencyRates[$defaultCurrency], $precision);
$exchangedRates = array();
$exchangedRates[$baseCurrency] = $defaultCurrencyRate;
unset($currencyRates[$baseCurrency], $currencyRates[$defaultCurrency]);
foreach ($currencyRates as $currencyName => $rate) {
$exchangedRates[$currencyName] = round($rate * $defaultCurrencyRate, $precision);
}
return $exchangedRates;
}
}

View File

@@ -225,22 +225,33 @@ class Manager
* @param string | array $path
* @param string $content JSON string
* @param bool $isJSON
* @param array $mergeOptions
* @param string | array $mergeOptions
* @param string | array $removeOptions - List of unset keys from content
* @param bool $isReturn - Is result to be returned or stored
*
* @return bool
* @return bool | array
*/
public function mergeContents($path, $content, $isJSON = false, $mergeOptions = null)
public function mergeContents($path, $content, $isJSON = false, $mergeOptions = null, $removeOptions = null, $isReturn = false)
{
$fileContent = $this->getContents($path);
$savedDataArray = Utils\Json::getArrayData($fileContent);
$newDataArray = Utils\Json::getArrayData($content);
if (isset($removeOptions)) {
$savedDataArray = Utils\Util::unsetInArray($savedDataArray, $removeOptions);
$newDataArray = Utils\Util::unsetInArray($newDataArray, $removeOptions);
}
$data = Utils\Util::merge($savedDataArray, $newDataArray, $mergeOptions);
if ($isJSON) {
$data = Utils\Json::encode($data, JSON_PRETTY_PRINT);
}
if ($isReturn) {
return $data;
}
return $this->putContents($path, $data);
}
@@ -248,26 +259,14 @@ class Manager
* Merge PHP content and save it to a file
*
* @param string | array $path
* @param string $content
* @param bool $onlyFirstLevel - Merge only first level. Ex. current: array('test'=>array('item1', 'item2')). $content= array('test'=>array('item1'),). Result will be array('test'=>array('item1')).
*
* @param string $content JSON string
* @param string | array $mergeOptions
* @param string | array $removeOptions - List of unset keys from content
* @return bool
*/
public function mergeContentsPHP($path, $content, $onlyFirstLevel = false, $mergeOptions = null)
public function mergeContentsPHP($path, $content, $mergeOptions = null, $removeOptions = null)
{
$fileContent = $this->getContents($path);
$savedDataArray = Utils\Json::getArrayData($fileContent);
$newDataArray = Utils\Json::getArrayData($content);
if ($onlyFirstLevel) {
foreach($newDataArray as $key => $val) {
$setVal = is_array($val) ? array() : '';
$savedDataArray[$key] = $setVal;
}
}
$data = Utils\Util::merge($savedDataArray, $newDataArray, $mergeOptions);
$data = $this->mergeContents($path, $content, false, $mergeOptions, $removeOptions, true);
return $this->putContentsPHP($path, $data);
}

View File

@@ -480,11 +480,12 @@ class Permission
if (file_exists($item)) {
try {
$res = $this->chmod($item, $permission, true);
$this->chmod($item, $permission, true);
} catch (\Exception $e) {
$res = false;
}
$res = is_readable($item);
/** check is wtitable */
if ($type == 'writable') {

View File

@@ -101,8 +101,8 @@ class Unifier
$dirName = $this->getFileManager()->getDirName($dirPath, false);
$defaultValues = $this->loadDefaultValues($dirName, $type);
$content= array();
$unsets= array();
$content = array();
$unsets = array();
foreach($fileList as $dirName => $fileName) {
if (is_array($fileName)) { /*get content from files in a sub directory*/
@@ -125,7 +125,7 @@ class Unifier
}
//unset content
$content= Utils\Util::unsetInArray($content, $unsets);
$content = Utils\Util::unsetInArray($content, $unsets);
//END: unset content
return $content;
@@ -141,27 +141,16 @@ class Unifier
*/
protected function unifyGetContents($paths, $defaults)
{
$fileContent= $this->getFileManager()->getContents($paths);
$decoded= Utils\Json::getArrayData($fileContent);
$fileContent = $this->getFileManager()->getContents($paths);
if (empty($decoded) && !is_array($decoded)) {
$GLOBALS['log']->emergency('Syntax error or empty file - '.Utils\Util::concatPath($folderPath, $fileName));
} else {
//Default values
if (is_string($defaults) && !empty($defaults)) {
$defType= $defaults;
unset($defaults);
$name= $this->getFileManager()->getFileName($fileName, '.json');
$decoded = Utils\Json::getArrayData($fileContent, null);
$defaults= $this->loadDefaultValues($name, $defType);
}
$mergedValues= Utils\Util::merge($defaults, $decoded);
//END: Default values
return $mergedValues;
if (!isset($decoded)) {
$GLOBALS['log']->emergency('Syntax error in '.Utils\Util::concatPath($paths));
return array();
}
return array();
return $decoded;
}
/**
@@ -174,10 +163,10 @@ class Unifier
*/
protected function loadDefaultValues($name, $type='metadata')
{
$defaultPath= $this->params['defaultsPath'];
$defaultPath = $this->params['defaultsPath'];
$defaultValue= $this->getFileManager()->getContents( array($defaultPath, $type, $name.'.json') );
if ($defaultValue!==false) {
$defaultValue = $this->getFileManager()->getContents( array($defaultPath, $type, $name.'.json') );
if ($defaultValue !== false) {
//return default array
return Utils\Json::decode($defaultValue, true);
}

View File

@@ -118,7 +118,7 @@ class Json
*
* @return array
*/
public static function getArrayData($data)
public static function getArrayData($data, $returns = array())
{
if (is_array($data)) {
return $data;
@@ -127,7 +127,7 @@ class Json
return static::decode($data, true);
}
return array();
return $returns;
}

View File

@@ -154,10 +154,10 @@ class Language
return $translated;
}
public function translateOption($value, $field, $scope)
{
$options = $this->get($scope. '.options.' . $field);
$options = $this->get($scope. '.options.' . $field);
if (array_key_exists($value, $options)) {
return $options[$value];
}
@@ -241,7 +241,7 @@ class Language
$i18nCacheFile = str_replace('{*}', $i18nName, $this->cacheFile);
if ($i18nName != $this->defaultLanguage) {
$i18nData = Util::merge($this->fullData[$this->defaultLanguage], $i18nData);
$i18nData = Util::merge($this->fullData[$this->defaultLanguage], $i18nData, null, null, true);
}
$result &= $this->getFileManager()->putContentsPHP($i18nCacheFile, $i18nData);
}

View File

@@ -142,13 +142,13 @@ class Metadata
* Get Metadata
*
* @param string $key
* @param mixed $return
* @param mixed $default
*
* @return array
*/
public function get($key = null, $returns = null)
public function get($key = null, $default = null)
{
return Util::getValueByKey($this->getData(), $key, $returns);
return Util::getValueByKey($this->getData(), $key, $default);
}
@@ -430,4 +430,4 @@ class Metadata
return false;
}
}
}

View File

@@ -125,10 +125,13 @@ class Util
* ),
* )
* @param $rewriteKeyName string - Rewrite key name. It is ignored if $rewriteLevel is NULL.
* @param $rewriteArrays bool - Rewrite single arrays. Examples:
* TRUE: array is [0, 1, 2], main array is [3, 4, 5], Result is [3, 4, 5].
* FALSE: array is [0, 1, 2], main array is [3, 4, 5], Result is [0, 1, 2, 3, 4, 5].
*
* @return array
*/
public static function merge($array, $mainArray, $rewriteLevel = null, $rewriteKeyName = null)
public static function merge($array, $mainArray, $rewriteLevel = null, $rewriteKeyName = null, $rewriteArrays = false)
{
if (is_array($array) && !is_array($mainArray)) {
return $array;
@@ -169,7 +172,7 @@ class Util
} /** END: check the $rewriteKeyName */
if (!isset($rowRewriteLevel) || $rowRewriteLevel != 1) {
$array[$mainKey] = static::merge((array) $value, (array) $mainValue, --$rowRewriteLevel, $rewriteKeyName);
$array[$mainKey] = static::merge((array) $value, (array) $mainValue, --$rowRewriteLevel, $rewriteKeyName, $rewriteArrays);
continue;
}
@@ -185,7 +188,11 @@ class Util
$array[$mainKey] = $mainValue;
}
elseif (!in_array($mainValue, $array)) {
$array[] = $mainValue;
if ($rewriteArrays) {
$array[$mainKey] = $mainValue;
} else {
$array[] = $mainValue;
}
} /** END: merge logic */
break;
@@ -359,11 +366,17 @@ class Util
if (!empty($unsetSett)){
$keyItems = explode('.', $unsetSett);
$currVal = isset($content[$rootKey]) ? "\$content['{$rootKey}']" : "\$content";
$lastKey = array_pop($keyItems);
foreach($keyItems as $keyItem){
$currVal .= "['{$keyItem}']";
}
$currVal = "if (isset({$currVal})) unset({$currVal});";
$unsetElem = $currVal . "['{$lastKey}']";
$currVal = "
if (isset({$unsetElem}) || array_key_exists({$lastKey}, {$currVal})) {
unset({$unsetElem});
} ";
eval($currVal);
}
}
@@ -395,10 +408,10 @@ class Util
*
* @param array $array
* @param string $key Ex. of key is "entityDefs", "entityDefs.User"
* @param mixed $returns
* @param mixed $default
* @return mixed
*/
public static function getValueByKey(array $array, $key = null, $returns = null)
public static function getValueByKey(array $array, $key = null, $default = null)
{
if (!isset($key) || empty($key)) {
return $array;
@@ -411,7 +424,7 @@ class Util
if (isset($lastItem[$keyName]) && is_array($lastItem)) {
$lastItem = $lastItem[$keyName];
} else {
return $returns;
return $default;
}
}

View File

@@ -46,11 +46,8 @@ return array (
array (
),
'defaultCurrency' => 'USD',
'currency' =>
array(
'base' => 'USD',
'rate' => array(
),
'baseCurrency' => 'USD',
'currencyRates' => array(
),
'outboundEmailIsShared' => true,
'outboundEmailFromName' => 'EspoCRM',
@@ -62,12 +59,14 @@ return array (
'smtpUsername' => '',
'smtpPassword' => '',
'languageList' => array(
'en_US',
'en_US',
'de_DE',
'es_ES',
'fr_FR',
'nl_NL',
'tr_TR',
'ro_RO',
'pl_PL',
'pt_BR',
'vi_VN'
),
@@ -90,6 +89,10 @@ return array (
"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'),
'emailMessageMaxSize' => 10,
'isInstalled' => false,
);

View File

@@ -46,6 +46,21 @@ class Email extends \Espo\Core\ORM\Entity
}
}
public function getBodyPlainForSending()
{
$bodyPlain = $this->get('bodyPlain');
if (!empty($bodyPlain)) {
return $bodyPlain;
}
$body = $this->get('body');
$breaks = array("<br />","<br>","<br/>","<br />","&lt;br /&gt;","&lt;br/&gt;","&lt;br&gt;");
$body = str_ireplace($breaks, "\r\n", $body);
$body = strip_tags($body);
return $body;
}
public function getBodyForSending()
{
$body = $this->get('body');
@@ -55,6 +70,7 @@ class Email extends \Espo\Core\ORM\Entity
$body = str_replace("?entryPoint=attachment&amp;id={$attachment->id}", "cid:{$attachment->id}", $body);
}
}
$body = str_replace("<table class=\"table table-bordered\">", "<table class=\"table table-bordered\" width=\"100%\">", $body);
return $body;

View File

@@ -0,0 +1,30 @@
<?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;
class EmailAccount extends \Espo\Core\ORM\Entity
{
}

View File

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

View File

@@ -0,0 +1,152 @@
<?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;
class Integration extends \Espo\Core\ORM\Entity
{
public function get($name)
{
if ($name == 'id') {
return $this->id;
}
if ($this->hasField($name)) {
if (array_key_exists($name, $this->valuesContainer)) {
return $this->valuesContainer[$name];
}
} else {
if ($this->get('data')) {
$data = json_decode($this->get('data'), true);
} else {
$data = array();
}
if (isset($data[$name])) {
return $data[$name];
}
}
return null;
}
public function set($p1, $p2)
{
if (is_array($p1)) {
if ($p2 === null) {
$p2 = false;
}
$this->populateFromArray($p1, $p2);
return;
}
$name = $p1;
$value = $p2;
if ($name == 'id') {
$this->id = $value;
return;
}
if ($this->hasField($name)) {
$this->valuesContainer[$name] = $value;
} else {
$data = json_decode($this->get('data'), true);
if (empty($data)) {
$data = array();
}
$data[$name] = $value;
$this->set('data', json_encode($data));
}
}
public function populateFromArray(array $arr, $onlyAccessible = true, $reset = false)
{
if ($reset) {
$this->reset();
}
foreach ($arr as $field => $value) {
if (is_string($field)) {
if (is_array($value)) {
$value = json_encode($value);
}
if ($this->hasField($field)) {
$fields = $this->getFields();
$fieldDefs = $fields[$field];
if (!is_null($value)) {
switch ($fieldDefs['type']) {
case self::VARCHAR:
break;
case self::BOOL:
$value = ($value === 'true' || $value === '1' || $value === true);
break;
case self::INT:
$value = intval($value);
break;
case self::FLOAT:
$value = floatval($value);
break;
case self::JSON_ARRAY:
$value = is_string($value) ? json_decode($value) : $value;
if (!is_array($value)) {
$value = null;
}
break;
default:
break;
}
}
}
$this->set($field, $value);
}
}
}
public function toArray()
{
$arr = array();
if (isset($this->id)) {
$arr['id'] = $this->id;
}
foreach ($this->fields as $field => $defs) {
if ($field == 'id') {
continue;
}
if ($this->has($field)) {
$arr[$field] = $this->get($field);
}
}
$data = json_decode($this->get('data'), true);
if (empty($data)) {
$data = array();
}
$arr = array_merge($arr, $data);
return $arr;
}
}

View File

@@ -30,6 +30,10 @@ class Download extends \Espo\Core\EntryPoints\Base
{
public static $authRequired = true;
protected $fileTypesToShowInline = array(
'application/pdf',
);
public function run()
{
$id = $_GET['id'];
@@ -54,13 +58,21 @@ class Download extends \Espo\Core\EntryPoints\Base
if (!file_exists($fileName)) {
throw new NotFound();
}
}
$type = $attachment->get('type');
$disposition = 'attachment';
if (in_array($type, $this->fileTypesToShowInline)) {
$disposition = 'inline';
}
header('Content-Description: File Transfer');
if ($attachment->get('type')) {
header('Content-Type: ' . $attachment->get('type'));
if ($type) {
header('Content-Type: ' . $type);
}
header('Content-Disposition: attachment; filename=' . $attachment->get('name'));
header('Content-Disposition: ' . $disposition . '; filename=' . $attachment->get('name'));
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');

View File

@@ -0,0 +1,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);
}
}
}
}

View File

@@ -69,26 +69,29 @@ class Stream extends \Espo\Core\Hooks\Base
{
$linkDefs = $this->getMetadata()->get("entityDefs." . $entity->getEntityName() . ".links", array());
$scopeNotifiedList = array();
foreach ($linkDefs as $link => $defs) {
if ($defs['type'] == 'belongsTo') {
$foreign = $defs['foreign'];
$scope = $defs['entity'];
$entityId = $entity->get($link . 'Id');
if (!empty($scope) && !empty($entityId)) {
if (!$this->isLinkObservableInStream($scope, $foreign)) {
if (in_array($scope, $scopeNotifiedList) || !$this->isLinkObservableInStream($scope, $foreign)) {
continue;
}
$this->getStreamService()->noteCreateRelated($entity, $scope, $entityId);
$scopeNotifiedList[] = $scope;
}
} else if ($defs['type'] == 'belongsToParent') {
$foreign = $defs['foreign'];
$scope = $entity->get($link . 'Type');
$entityId = $entity->get($link . 'Id');
if (!empty($scope) && !empty($entityId)) {
if (!$this->isLinkObservableInStream($scope, $foreign)) {
if (in_array($scope, $scopeNotifiedList) || !$this->isLinkObservableInStream($scope, $foreign)) {
continue;
}
$this->getStreamService()->noteCreateRelated($entity, $scope, $entityId);
$scopeNotifiedList[] = $scope;
}
} else if ($defs['type'] == 'hasMany') {
@@ -96,11 +99,12 @@ class Stream extends \Espo\Core\Hooks\Base
$scope = $defs['entity'];
$entityIds = $entity->get($link . 'Ids');
if (!empty($scope) && is_array($entityIds) && !empty($entityIds)) {
if (!$this->isLinkObservableInStream($scope, $foreign)) {
if (in_array($scope, $scopeNotifiedList) || !$this->isLinkObservableInStream($scope, $foreign)) {
continue;
}
$entityId = $entityIds[0];
$this->getStreamService()->noteCreateRelated($entity, $scope, $entityId);
$this->getStreamService()->noteCreateRelated($entity, $scope, $entityId);
$scopeNotifiedList[] = $scope;
}
}
}

View File

@@ -28,32 +28,8 @@ class Opportunity extends \Espo\Core\Controllers\Record
{
$dateFrom = $request->get('dateFrom');
$dateTo = $request->get('dateTo');
$pdo = $this->getEntityManager()->getPDO();
$sql = "
SELECT opportunity.lead_source AS `leadSource`, SUM(opportunity.amount * currency.rate * opportunity.probability / 100) as `amount`
FROM opportunity
JOIN currency ON currency.id = opportunity.amount_currency
WHERE
opportunity.deleted = 0 AND
opportunity.close_date >= ".$pdo->quote($dateFrom)." AND
opportunity.close_date < ".$pdo->quote($dateTo)." AND
opportunity.stage <> 'Closed Lost' AND
opportunity.lead_source <> ''
GROUP BY opportunity.lead_source
";
$sth = $pdo->prepare($sql);
$sth->execute();
$rows = $sth->fetchAll(\PDO::FETCH_ASSOC);
$result = array();
foreach ($rows as $row) {
$result[$row['leadSource']] = floatval($row['amount']);
}
return $result;
return $this->getService('Opportunity')->reportByLeadSource($dateFrom, $dateTo);
}
public function actionReportByStage($params, $data, $request)
@@ -61,32 +37,7 @@ class Opportunity extends \Espo\Core\Controllers\Record
$dateFrom = $request->get('dateFrom');
$dateTo = $request->get('dateTo');
$pdo = $this->getEntityManager()->getPDO();
$sql = "
SELECT opportunity.stage AS `stage`, SUM(opportunity.amount * currency.rate) as `amount`
FROM opportunity
JOIN currency ON currency.id = opportunity.amount_currency
WHERE
opportunity.deleted = 0 AND
opportunity.close_date >= ".$pdo->quote($dateFrom)." AND
opportunity.close_date < ".$pdo->quote($dateTo)." AND
opportunity.stage <> 'Closed Lost'
GROUP BY opportunity.lead_source
ORDER BY `amount` DESC
";
$sth = $pdo->prepare($sql);
$sth->execute();
$rows = $sth->fetchAll(\PDO::FETCH_ASSOC);
$result = array();
foreach ($rows as $row) {
$result[$row['stage']] = floatval($row['amount']);
}
return $result;
return $this->getService('Opportunity')->reportByStage($dateFrom, $dateTo);
}
public function actionReportSalesByMonth($params, $data, $request)
@@ -94,34 +45,7 @@ class Opportunity extends \Espo\Core\Controllers\Record
$dateFrom = $request->get('dateFrom');
$dateTo = $request->get('dateTo');
$pdo = $this->getEntityManager()->getPDO();
$sql = "
SELECT DATE_FORMAT(opportunity.close_date, '%Y-%m') AS `month`, SUM(opportunity.amount * currency.rate) as `amount`
FROM opportunity
JOIN currency ON currency.id = opportunity.amount_currency
WHERE
opportunity.deleted = 0 AND
opportunity.close_date >= ".$pdo->quote($dateFrom)." AND
opportunity.close_date < ".$pdo->quote($dateTo)." AND
opportunity.stage = 'Closed Won'
GROUP BY DATE_FORMAT(opportunity.close_date, '%Y-%m')
ORDER BY opportunity.close_date
";
$sth = $pdo->prepare($sql);
$sth->execute();
$rows = $sth->fetchAll(\PDO::FETCH_ASSOC);
$result = array();
foreach ($rows as $row) {
$result[$row['month']] = floatval($row['amount']);
}
return $result;
return $this->getService('Opportunity')->reportSalesByMonth($dateFrom, $dateTo);
}
public function actionReportSalesPipeline($params, $data, $request)

View File

@@ -28,12 +28,23 @@ class CheckInboundEmails extends \Espo\Core\Jobs\Base
{
public function run()
{
$service = $this->getServiceFactory()->create('InboundEmail');
$collection = $this->getEntityManager()->getRepository('InboundEmail')->find();
$service = $this->getServiceFactory()->create('InboundEmail');
$collection = $this->getEntityManager()->getRepository('InboundEmail')->where(array('status' => 'Active'))->find();
foreach ($collection as $entity) {
$service->fetchFromMailServer($entity->id);
try {
$service->fetchFromMailServer($entity);
} catch (\Exception $e) {}
}
$service = $this->getServiceFactory()->create('EmailAccount');
$collection = $this->getEntityManager()->getRepository('EmailAccount')->where(array('status' => 'Active'))->find();
foreach ($collection as $entity) {
try {
$service->fetchFromMailServer($entity);
} catch (\Exception $e) {}
}
return true;
}
}

View File

@@ -0,0 +1,43 @@
<?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 Lead extends \Espo\Core\ORM\Repositories\RDB
{
public function handleSelectParams(&$params)
{
parent::handleSelectParams($params);
if (empty($params['customJoin'])) {
$params['customJoin'] = '';
}
$params['customJoin'] .= "
LEFT JOIN currency ON currency.id = lead.opportunity_amount_currency
";
}
}

View File

@@ -0,0 +1,43 @@
<?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 Opportunity extends \Espo\Core\ORM\Repositories\RDB
{
public function handleSelectParams(&$params)
{
parent::handleSelectParams($params);
if (empty($params['customJoin'])) {
$params['customJoin'] = '';
}
$params['customJoin'] .= "
LEFT JOIN currency ON currency.id = opportunity.amount_currency
";
}
}

View File

@@ -30,5 +30,10 @@
"Set Held": "Auf Gehalten setzen",
"Set Not Held": "Auf nicht gehalten setzen",
"Send Invitations": "Einladungen versenden"
},
"presetFilters": {
"planned": "Geplant",
"held": "Durchgeführt",
"todays": "Heutige"
}
}

View File

@@ -34,5 +34,9 @@
},
"labels": {
"Create Case": "Neuer Fall"
},
"presetFilters": {
"open": "Offen",
"closed": "Abgeschlossen"
}
}

View File

@@ -9,7 +9,7 @@
"accountType": "Firmentyp",
"doNotCall": "Nicht anrufen",
"address": "Adresse",
"opportunityRole": "Opportunity Role",
"opportunityRole": "Verkaufschance Rolle",
"accountRole": "Rolle",
"description": "Beschreibung"
},
@@ -22,10 +22,10 @@
},
"options": {
"opportunityRole": {
"": "--None--",
"Decision Maker": "Decision Maker",
"Evaluator": "Evaluator",
"Influencer": "Influencer"
"": "--Kein(e)--",
"Decision Maker": "Entscheider",
"Evaluator": "Vorentscheider",
"Influencer": "Einflussreiche Person"
}
}
}

View File

@@ -16,7 +16,7 @@
"Account": "Firmen",
"Contact": "Kontakte",
"Lead": "Interessenten",
"Target": "Targets",
"Target": "Zielkontakte",
"Opportunity": "Verkaufschancen",
"Meeting": "Meetings",
"Calendar": "Kalender",
@@ -31,6 +31,8 @@
"Tasks": "Meine Aufgaben",
"Cases": "Meine Fälle",
"Calendar": "Kalender",
"Calls": "Meine Anrufe",
"Meetings": "Meine Meetings",
"OpportunitiesByStage": "Verkaufschancen nach Verkaufsphase",
"OpportunitiesByLeadSource": "Verkaufschancen nach Quelle",
"SalesByMonth": "Umsätze nach Monat",
@@ -61,11 +63,11 @@
"addressCountry": "Land",
"addressState": "Bundesland",
"addressPostalCode": "PLZ",
"shippingAddressCity": "City (Shipping)",
"shippingAddressStreet": "Street (Shipping)",
"shippingAddressCountry": "Country (Shipping)",
"shippingAddressState": "State (Shipping)",
"shippingAddressPostalCode": "Postal Code (Shipping)"
"shippingAddressCity": "Ort (Lieferadresse)",
"shippingAddressStreet": "Straße (Lieferadresse)",
"shippingAddressCountry": "Land (Lieferadresse)",
"shippingAddressState": "Bundesland\/Kanton (Lieferadresse)",
"shippingAddressPostalCode": "PLZ (Lieferadresse)"
},
"links": {
"contacts": "Kontakte",

View File

@@ -16,8 +16,17 @@
"caseDistribution": "Fall Verteilung",
"replyEmailTemplate": "Vorlage E-Mail Antwort",
"replyFromAddress": "Von Adresse antworten",
"replyToAddress": "Antwort an Adresse",
"replyFromName": "Von Name antworten"
},
"tooltips": {
"reply": "Absender informieren dass die E-Mail empfangen wurde.",
"createCase": "Fall aus eingehender E-Mail automatisch erstellen.",
"replyToAddress": "Geben Sie die E-Mail Adresse dieser Mailbox an, um Antworten hier zu empfangen.",
"caseDistribution": "Wie Fälle zugewiesen werden. Entweder direkt dem Benutzer oder im Team.",
"assignToUser": "Benutzer E-Mails\/Fälle werden zugewiesen an",
"team": "Team E-Mails\/Fälle werden verknüpft mit"
},
"links": {
},
"options": {

View File

@@ -16,6 +16,7 @@
"status": "Status",
"source": "Quelle",
"opportunityAmount": "Verkaufschance Betrag",
"opportunityAmountConverted": "Verkaufschance Betrag (konvertiert)",
"description": "Beschreibung",
"createdAccount": "Firma",
"createdContact": "Kontakt",
@@ -42,5 +43,8 @@
"Campaign": "Kampagne",
"Other": "Andere"
}
},
"presetFilters": {
"active": "Aktiv"
}
}

View File

@@ -24,6 +24,13 @@
"Create Meeting": "Neues Meeting",
"Set Held": "Auf Gehalten setzen",
"Set Not Held": "Auf nicht gehalten setzen",
"Send Invitations": "Einladungen versenden"
"Send Invitations": "Einladungen versenden",
"Saved as Held": "Gespeichert als durchgeführt",
"Saved as Not Held": "Gespeichert als nicht durchgeführt"
},
"presetFilters": {
"planned": "Geplant",
"held": "Durchgeführt",
"todays": "Heutige"
}
}

View File

@@ -9,7 +9,8 @@
"doNotCall": "Nicht anrufen",
"closeDate": "Abschlussdatum",
"contacts": "Kontakte",
"description": "Beschreibung"
"description": "Beschreibung",
"amountConverted": "Betrag (konvertiert)"
},
"links": {
"contacts": "Kontakte"
@@ -23,12 +24,16 @@
"Id. Decision Makers": "Entscheider ident.",
"Perception Analysis": "Analyse Sichtweise",
"Proposal/Price Quote": "Preisangebot",
"Negotiation/Review": "Verhandlung/Überarbeitung",
"Negotiation/Review": "Verhandlung\/Überarbeitung",
"Closed Won": "Gewonnen",
"Closed Lost": "Verloren"
}
},
"labels": {
"Create Opportunity": "Neue Verkaufschance"
},
"presetFilters": {
"open": "Offen",
"won": "Gewonnen"
}
}

View File

@@ -13,7 +13,7 @@
"links": {
},
"labels": {
"Create Target": "Create Target",
"Create Target": "Neuer Zielkontakt",
"Convert to Lead": "Zu Interessent umwandeln"
}
}

View File

@@ -27,5 +27,11 @@
},
"labels": {
"Create Task": "Neue Aufgabe"
},
"presetFilters": {
"active": "Aktiv",
"completed": "Abgeschlossen",
"todays": "Heutige",
"overdue": "Überfällig"
}
}

View File

@@ -35,6 +35,7 @@
}
},
"labels": {
"Create Account": "Create Account"
"Create Account": "Create Account",
"Copy Billing": "Copy Billing"
}
}

View File

@@ -23,6 +23,11 @@
"direction": {
"Outbound": "Outbound",
"Inbound": "Inbound"
},
"acceptanceStatus": {
"None": "None",
"Accepted": "Accepted",
"Declined": "Declined"
}
},
"labels": {

View File

@@ -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",

View File

@@ -16,8 +16,17 @@
"caseDistribution": "Case Distribution",
"replyEmailTemplate": "Reply Email Template",
"replyFromAddress": "Reply From Address",
"replyToAddress": "Reply To Address",
"replyFromName": "Reply From Name"
},
"tooltips": {
"reply": "Notify email senders that their emails has been received.",
"createCase": "Automatically create case from incoming emails.",
"replyToAddress": "Specify email address of this mailbox to make response come here.",
"caseDistribution": "How cases will be assigned to. Assigned directly to the user or among the team.",
"assignToUser": "User emails/cases will be assigned to.",
"team": "Team emails/cases will be related to."
},
"links": {
},
"options": {

View File

@@ -16,6 +16,7 @@
"status": "Status",
"source": "Source",
"opportunityAmount": "Opportunity Amount",
"opportunityAmountConverted": "Opportunity Amount (converted)",
"description": "Description",
"createdAccount": "Account",
"createdContact": "Contact",

View File

@@ -18,6 +18,11 @@
"Planned": "Planned",
"Held": "Held",
"Not Held": "Not Held"
},
"acceptanceStatus": {
"None": "None",
"Accepted": "Accepted",
"Declined": "Declined"
}
},
"labels": {

View File

@@ -9,7 +9,8 @@
"doNotCall": "Do Not Call",
"closeDate": "Close Date",
"contacts": "Contacts",
"description": "Description"
"description": "Description",
"amountConverted": "Amount (converted)"
},
"links": {
"contacts": "Contacts"

View File

@@ -0,0 +1,40 @@
{
"fields": {
"name": "Record Naam",
"emailAddress": "Email",
"website": "Website",
"phoneNumber": "Telefoon",
"billingAddress": "Factuur Adres",
"shippingAddress": "Bezoek adres",
"description": "Beschrijving",
"sicCode": "Sic Code",
"industry": "Industrie",
"type": "Type",
"contactRole": "Voorwaarde"
},
"links": {
"contacts": "Contactpersoon",
"opportunities": "Kansen",
"cases": "Zaken"
},
"options": {
"type": {
"Customer": "Klant",
"Investor": "Investeerder",
"Partner": "Partner",
"Reseller": "Reseller"
},
"industry": {
"Apparel": "Apparel",
"Banking": "Banking",
"Computer Software": "Computer Software",
"Education": "Educatie",
"Electronics": "Electronica",
"Finance": "Finance",
"Insurance": "Verzekeringen"
}
},
"labels": {
"Create Account": "Maak Relatie"
}
}

View File

@@ -0,0 +1,13 @@
{
"modes": {
"month": "Maand",
"week": "Week",
"day": "Dag",
"agendaWeek": "Week",
"agendaDay": "Dag"
},
"labels": {
"Today": "Vandaag",
"Create": "Maak"
}
}

View File

@@ -0,0 +1,39 @@
{
"fields": {
"name": "Record Naam",
"parent": "Behorende bij",
"status": "Status",
"dateStart": "Start Datum",
"dateEnd": "Eind Datum",
"direction": "Richting",
"duration": "Gedurende",
"description": "Beschrijving",
"users": "Gebruikers",
"contacts": "Contactpersoon",
"leads": "Leads"
},
"links": {
},
"options": {
"status": {
"Planned": "Plannen",
"Held": "Volgen",
"Not Held": "Niet volgen"
},
"direction": {
"Outbound": "Uitgaande",
"Inbound": "Binnenkomende"
}
},
"labels": {
"Create Call": "Maak Call",
"Set Held": "Vasthouden",
"Set Not Held": "Niet Vasthouden",
"Send Invitations": "Uitnodiging versturen"
},
"presetFilters": {
"planned": "Plannen",
"held": "Volgen",
"todays": "Vandaag"
}
}

View File

@@ -0,0 +1,42 @@
{
"fields": {
"name": "Record Naam",
"number": "Nummer",
"status": "Status",
"account": "Relatie",
"contact": "Contact",
"priority": "Prioriteit",
"type": "Type",
"description": "Beschrijving"
},
"links": {
},
"options": {
"status": {
"New": "Nieuw",
"Assigned": "Toegewezen",
"Pending": "Pending",
"Closed": "Gesloten",
"Rejected": "Geweigerd",
"Duplicate": "Dupliceer"
},
"priority" : {
"Low": "Laag",
"Normal": "Normaal",
"High": "Hoog",
"Urgent": "Urgent"
},
"type": {
"Question": "Vraag",
"Incident": "Incident",
"Problem": "Probleem"
}
},
"labels": {
"Create Case": "Maak Case"
},
"presetFilters": {
"open": "Open",
"closed": "Gesloten"
}
}

View File

@@ -0,0 +1,31 @@
{
"fields": {
"name": "Record Naam",
"emailAddress": "Email",
"title": "Titel",
"account": "Relatie",
"accounts": "Relaties",
"phoneNumber": "Telefoon",
"accountType": "Relatie Type",
"doNotCall": "Niet Bellen",
"address": "Adres",
"opportunityRole": "Opportunity Role",
"accountRole": "Voorwaarde",
"description": "Beschrijving"
},
"links": {
"opportunities": "Kansen",
"cases": "Zaken"
},
"labels": {
"Create Contact": "Maak Contact"
},
"options": {
"opportunityRole": {
"": "--Geen--",
"Decision Maker": "Beslisser",
"Evaluator": "Evaluator",
"Influencer": "Beinvloeder"
}
}
}

View File

@@ -0,0 +1,81 @@
{
"scopeNames": {
"Account": "Relatie",
"Contact": "Contact",
"Lead": "Lead",
"Target": "Doel",
"Opportunity": "Kans",
"Meeting": "Vergadering",
"Calendar": "Kalender",
"Call": "Oproep",
"Task": "Taak",
"Case": "Zaak",
"InboundEmail": "Binnenkomende Email"
},
"scopeNamesPlural": {
"Account": "Relaties",
"Contact": "Contactpersoon",
"Lead": "Leads",
"Target": "Doel",
"Opportunity": "Kansen",
"Meeting": "Afspraken",
"Calendar": "Kalender",
"Call": "Tel.gesprekken",
"Task": "Taken",
"Case": "Zaken",
"InboundEmail": "Inkomende Emails"
},
"dashlets": {
"Leads": "Mijn Leads",
"Opportunities": "Mijn Kansen",
"Tasks": "Mijn Taken",
"Cases": "Mijn Zaken",
"Calendar": "Kalender",
"Calls": "Mijn Tel.gesprekken",
"Meetings": "Mijn Afspraken",
"OpportunitiesByStage": "Status van Kansen",
"OpportunitiesByLeadSource": "Kans van Lead bron",
"SalesByMonth": "Verkopen per maand",
"SalesPipeline": "Verkoop mogelijkheden"
},
"labels": {
"Create InboundEmail": "Instellen Email Gebruiker",
"Activities": "Activiteiten",
"History": "Historie",
"Attendees": "Genodigden",
"Schedule Meeting": "Plan Afspraak",
"Schedule Call": "Geplande Oproep",
"Compose Email": "Maak een Email",
"Log Meeting": "Leg afspraak vast",
"Log Call": "Leg tel.afspraak vast",
"Archive Email": "Archiveer Email",
"Create Task": "Maak een Taak",
"Tasks": "Taken"
},
"fields": {
"billingAddressCity": "Plaats",
"billingAddressCountry": "Land",
"billingAddressPostalCode": "Postcode",
"billingAddressState": "Deelstaat",
"billingAddressStreet": "Straat",
"addressCity": "Plaats",
"addressStreet": "Straat",
"addressCountry": "Land",
"addressState": "Deelstaat",
"addressPostalCode": "Postcode",
"shippingAddressCity": "City (Shipping)",
"shippingAddressStreet": "Street (Shipping)",
"shippingAddressCountry": "Country (Shipping)",
"shippingAddressState": "State (Shipping)",
"shippingAddressPostalCode": "Postal Code (Shipping)"
},
"links": {
"contacts": "Contactpersoon",
"opportunities": "Kansen",
"leads": "Leads",
"meetings": "Afspraken",
"calls": "Tel.gesprekken",
"tasks": "Taken",
"emails": "Emails"
}
}

View File

@@ -0,0 +1,52 @@
{
"fields": {
"name": "Record Naam",
"team": "Team",
"status": "Status",
"assignToUser": "Toegewezen aan gebruiker",
"host": "Host",
"username": "Gebruikersnaam",
"password": "Wachtwoord",
"port": "Poort",
"monitoredFolders": "Gecontroleerde Folders",
"trashFolder": "Prullenbak Folder",
"ssl": "SSL",
"createCase": "Maak Case",
"reply": "Antw.",
"caseDistribution": "Zaak Verdelen",
"replyEmailTemplate": "Antw. Email Template",
"replyFromAddress": "Antw. van Adres",
"replyToAddress": "Reply To Address",
"replyFromName": "Antw. van Naam"
},
"tooltips": {
"reply": "Notify email senders that their emails has been received.",
"createCase": "Automatically create case from incoming emails.",
"replyToAddress": "Specify email address of this mailbox to make response come here.",
"caseDistribution": "How cases will be assigned to. Assigned directly to the user or among the team.",
"assignToUser": "User emails/cases will be assigned to.",
"team": "Team emails/cases will be related to."
},
"links": {
},
"options": {
"status": {
"Active": "Active",
"Inactive": "Inactive"
},
"caseDistribution": {
"Direct-Assignment": "Directe-Toewijzing",
"Round-Robin": "Round-Robin",
"Least-Busy": "Minst-Bezet"
}
},
"labels": {
"Create InboundEmail": "Instellen Email Gebruiker",
"IMAP": "IMAP",
"Actions": "Acties",
"Main": "Hoofd"
},
"messages": {
"couldNotConnectToImap": "Kan geen verbinding maken met IMAP server"
}
}

View File

@@ -0,0 +1,50 @@
{
"labels": {
"Converted To": "Vertaald naar",
"Create Lead": "Grote Lead",
"Convert": "Converteer"
},
"fields": {
"name": "Record Naam",
"emailAddress": "Email",
"title": "Titel",
"website": "Website",
"phoneNumber": "Telefoon",
"accountName": "Relatie Naam",
"doNotCall": "Niet Bellen",
"address": "Adres",
"status": "Status",
"source": "Bron",
"opportunityAmount": "Aantal uitdagingen",
"opportunityAmountConverted": "Opportunity Amount (converted)",
"description": "Beschrijving",
"createdAccount": "Relatie",
"createdContact": "Contact",
"createdOpportunity": "Kans"
},
"links": {
},
"options": {
"status": {
"New": "Nieuw",
"Assigned": "Toegewezen",
"In Process": "In Behandeling",
"Converted": "Converteren",
"Recycled": "Opnieuw verwerkt",
"Dead": "Dood"
},
"source": {
"Call": "Oproep",
"Email": "Email",
"Existing Customer": "Bestaande Klant",
"Partner": "Partner",
"Public Relations": "Public Relations",
"Web Site": "Web Pagina",
"Campaign": "Campagne",
"Other": "Ander"
}
},
"presetFilters": {
"active": "Active"
}
}

View File

@@ -0,0 +1,36 @@
{
"fields": {
"name": "Record Naam",
"parent": "Behorende bij",
"status": "Status",
"dateStart": "Start Datum",
"dateEnd": "Eind Datum",
"duration": "Gedurende",
"description": "Beschrijving",
"users": "Gebruikers",
"contacts": "Contactpersoon",
"leads": "Leads"
},
"links": {
},
"options": {
"status": {
"Planned": "Plannen",
"Held": "Volgen",
"Not Held": "Niet volgen"
}
},
"labels": {
"Create Meeting": "Plan Vergadering",
"Set Held": "Vasthouden",
"Set Not Held": "Niet Vasthouden",
"Send Invitations": "Uitnodiging versturen",
"Saved as Held": "Opslaan Bewaren",
"Saved as Not Held": "Opslaan niet Bewaren"
},
"presetFilters": {
"planned": "Plannen",
"held": "Volgen",
"todays": "Vandaag"
}
}

View File

@@ -0,0 +1,39 @@
{
"fields": {
"name": "Record Naam",
"account": "Relatie",
"stage": "Status",
"amount": "Aantal",
"probability": "Naar waarschijnlijkheid, %",
"leadSource": "Lead Bron",
"doNotCall": "Niet Bellen",
"closeDate": "Datum gesloten",
"contacts": "Contactpersoon",
"description": "Beschrijving",
"amountConverted": "Amount (converted)"
},
"links": {
"contacts": "Contactpersoon"
},
"options": {
"stage": {
"Prospecting": "Prospecting",
"Qualification": "Kwalificeren",
"Needs Analysis": "Benodigde Analyse",
"Value Proposition": "Waarde Schatting",
"Id. Decision Makers": "Id. Beslissiers",
"Perception Analysis": "Perceptie Analyse",
"Proposal/Price Quote": "Prijs/Voorstel",
"Negotiation/Review": "Onderhandeling/Herziening",
"Closed Won": "Gesloten Gewonnen",
"Closed Lost": "Gesloten Verloren"
}
},
"labels": {
"Create Opportunity": "Grote Uitdaging"
},
"presetFilters": {
"open": "Open",
"won": "Gewonnen"
}
}

View File

@@ -0,0 +1,19 @@
{
"fields": {
"name": "Record Naam",
"emailAddress": "Email",
"title": "Titel",
"website": "Website",
"accountName": "Relatie Naam",
"phoneNumber": "Telefoon",
"doNotCall": "Niet Bellen",
"address": "Adres",
"description": "Beschrijving"
},
"links": {
},
"labels": {
"Create Target": "Maak een doel",
"Convert to Lead": "Naar Lead omzetten"
}
}

View File

@@ -0,0 +1,37 @@
{
"fields": {
"name": "Record Naam",
"parent": "Behorende bij",
"status": "Status",
"dateStart": "Start Datum",
"dateEnd": "Vervaldatum",
"priority": "Prioriteit",
"description": "Beschrijving",
"isOverdue": "Te Laat"
},
"links": {
},
"options": {
"status": {
"Not Started": "Niet Gestart",
"Started": "Gestart",
"Completed": "Beeindigd",
"Canceled": "Geannuleerd"
},
"priority" : {
"Low": "Laag",
"Normal": "Normaal",
"High": "Hoog",
"Urgent": "Urgent"
}
},
"labels": {
"Create Task": "Maak een Taak"
},
"presetFilters": {
"active": "Active",
"completed": "Beeindigd",
"todays": "Vandaag",
"overdue": "Laat"
}
}

View File

@@ -0,0 +1,40 @@
{
"fields": {
"name": "Imię",
"emailAddress": "E-mail",
"website": "Strona Internetowa",
"phoneNumber": "Telefon",
"billingAddress": "Adres Firmy",
"shippingAddress": "Adres Dostawy",
"description": "Opis ",
"sicCode": "PKD",
"industry": "Branża",
"type": "Rodzaj",
"contactRole": "Rola"
},
"links": {
"contacts": "Kontakty",
"opportunities": "Szanse Sprzedaży",
"cases": "Zlecenia"
},
"options": {
"type": {
"Customer": "Klient",
"Investor": "Inwestor",
"Partner": "Partner",
"Reseller": "Sprzedawca"
},
"industry": {
"Apparel": "Odzież",
"Banking": "Bankowość",
"Computer Software": "Oprogramowanie Komputerowe",
"Education": "Edukacja",
"Electronics": "Elektronika",
"Finance": "Finanse",
"Insurance": "Ubezpieczenia"
}
},
"labels": {
"Create Account": "Utwórz Konto"
}
}

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