mirror of
https://github.com/espocrm/espocrm.git
synced 2026-03-09 18:07:00 +00:00
Compare commits
830 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
83332178a0 | ||
|
|
fb33c2acf1 | ||
|
|
3c5914b5d9 | ||
|
|
8c48f72a3c | ||
|
|
a8ce780718 | ||
|
|
31eff86578 | ||
|
|
892f9f01f6 | ||
|
|
860a58847d | ||
|
|
1ed90d2306 | ||
|
|
aff0b350f3 | ||
|
|
651b0be5ed | ||
|
|
cead33c09d | ||
|
|
80602c084f | ||
|
|
b0420b8b93 | ||
|
|
b47018cbab | ||
|
|
ea69ab6eaf | ||
|
|
92887401b2 | ||
|
|
df0bda6324 | ||
|
|
9d67808496 | ||
|
|
cf33a98f20 | ||
|
|
afbda344ec | ||
|
|
1079597584 | ||
|
|
af9b411a4c | ||
|
|
cd49d951d8 | ||
|
|
7d63c114aa | ||
|
|
af6f05ba07 | ||
|
|
46f8eb0cdb | ||
|
|
c543bb5a5b | ||
|
|
d1997089e4 | ||
|
|
57dd0177f9 | ||
|
|
ba76e5ee3d | ||
|
|
76c93b842c | ||
|
|
e73bff5ddf | ||
|
|
7d5c0d754e | ||
|
|
e08a74b129 | ||
|
|
be07aafaf4 | ||
|
|
e2bf27e524 | ||
|
|
b0157adbe2 | ||
|
|
239593afb5 | ||
|
|
b97d764588 | ||
|
|
bf8e7af984 | ||
|
|
bf68c475e5 | ||
|
|
29624239ba | ||
|
|
4cb3c4918b | ||
|
|
26fa0d65e1 | ||
|
|
958baed3df | ||
|
|
40d6fb7565 | ||
|
|
a7551b7dbf | ||
|
|
9ed33c4dc4 | ||
|
|
6b18fbe8f7 | ||
|
|
53b9880ea8 | ||
|
|
62538664df | ||
|
|
f678fb16cc | ||
|
|
fe09aa1cd9 | ||
|
|
710decc610 | ||
|
|
5d481d3528 | ||
|
|
8bcd3b72c5 | ||
|
|
0a0da810e9 | ||
|
|
e630c046fe | ||
|
|
2857b5c53d | ||
|
|
0965c61f55 | ||
|
|
9dd318b176 | ||
|
|
f8beca720c | ||
|
|
d8f58dcc1d | ||
|
|
32aaaa350e | ||
|
|
0f6d1c6817 | ||
|
|
8c5a342a5a | ||
|
|
257401898b | ||
|
|
e7ff3f903a | ||
|
|
6f82638b5d | ||
|
|
0625ce51c4 | ||
|
|
f5670436d0 | ||
|
|
5d20a56ac8 | ||
|
|
020cf9713a | ||
|
|
fe1361fc9c | ||
|
|
36621be496 | ||
|
|
25690030d1 | ||
|
|
fcd6be3a0c | ||
|
|
ad8dfa487c | ||
|
|
7f6f54628d | ||
|
|
8ea0b072c6 | ||
|
|
0df6248226 | ||
|
|
26a132658e | ||
|
|
d02529ee86 | ||
|
|
0cec0fef51 | ||
|
|
e56f6ab75d | ||
|
|
a4980aa8f2 | ||
|
|
fd219b49be | ||
|
|
b90579aadc | ||
|
|
0b39d7f6b6 | ||
|
|
a44ce06162 | ||
|
|
f1eb2ab5d5 | ||
|
|
9a965bfba4 | ||
|
|
7349f37786 | ||
|
|
3d03dfaef7 | ||
|
|
5bf88eaff1 | ||
|
|
f3c4b2ba39 | ||
|
|
92f6fb4588 | ||
|
|
2d6d7fd618 | ||
|
|
3a1d997d5a | ||
|
|
5bbb259c95 | ||
|
|
b8e44767b3 | ||
|
|
6b486beb5c | ||
|
|
aab1265e4e | ||
|
|
676181a4be | ||
|
|
839e5b7c74 | ||
|
|
aff7003ceb | ||
|
|
66aa60f303 | ||
|
|
b196811cf9 | ||
|
|
3fdddadd53 | ||
|
|
7af2d1d3ac | ||
|
|
63670e3245 | ||
|
|
389931bf22 | ||
|
|
76a67d15a3 | ||
|
|
8e65f2d375 | ||
|
|
56737f5d67 | ||
|
|
20f8fae2f0 | ||
|
|
17b5d77d33 | ||
|
|
cbe32c88b6 | ||
|
|
dee291f3f6 | ||
|
|
0b20c8e13f | ||
|
|
c0af0173e9 | ||
|
|
79f4c4b5d2 | ||
|
|
31c8be2ee1 | ||
|
|
061b237032 | ||
|
|
d3466e201d | ||
|
|
c69c7d2194 | ||
|
|
05e1624e47 | ||
|
|
03e32412ca | ||
|
|
451a2376a8 | ||
|
|
167c774446 | ||
|
|
054262a002 | ||
|
|
ecbc3c537f | ||
|
|
d51bbfdfab | ||
|
|
ae343cddbe | ||
|
|
bb03fec604 | ||
|
|
248c9a6b00 | ||
|
|
dec5e0be5f | ||
|
|
bcbb29d94e | ||
|
|
3e2f995ba8 | ||
|
|
56b3b9d94d | ||
|
|
9b2ac0d443 | ||
|
|
3ee78c29cb | ||
|
|
548e829f2f | ||
|
|
749c161aaa | ||
|
|
aee6b99a47 | ||
|
|
700262e07b | ||
|
|
c4cd9d3ee8 | ||
|
|
6268f84b45 | ||
|
|
e2b49931c3 | ||
|
|
66114e4748 | ||
|
|
4ea9868285 | ||
|
|
2c09eacbd4 | ||
|
|
2d5c01f25d | ||
|
|
9610695701 | ||
|
|
b7062a2b75 | ||
|
|
3100d43af0 | ||
|
|
53606cbde5 | ||
|
|
ca6d1d24f4 | ||
|
|
928a8c75e8 | ||
|
|
693df2d6fb | ||
|
|
bc3cea0b86 | ||
|
|
feba3cd05e | ||
|
|
e6b31b8c45 | ||
|
|
79efc87ed2 | ||
|
|
e17b88ca74 | ||
|
|
0020a5ea4a | ||
|
|
5778167b01 | ||
|
|
2b11414445 | ||
|
|
e7b51ef61d | ||
|
|
ca969ee79b | ||
|
|
9ff62f9e28 | ||
|
|
5c5cbf2933 | ||
|
|
91c5caee63 | ||
|
|
7aa439efbb | ||
|
|
7baf49aa7e | ||
|
|
d7476c1de8 | ||
|
|
d3e99a6de8 | ||
|
|
893f9d50c3 | ||
|
|
a565766fcb | ||
|
|
89e517e47b | ||
|
|
39d51bb68f | ||
|
|
5e3168039a | ||
|
|
f75062694f | ||
|
|
cb3c3f18ee | ||
|
|
2415b29d45 | ||
|
|
7fb162be77 | ||
|
|
bf93834fd3 | ||
|
|
63452d1bb2 | ||
|
|
5e26dbd870 | ||
|
|
fb782d67ed | ||
|
|
864c91cd18 | ||
|
|
038479564b | ||
|
|
08c0dfc4ef | ||
|
|
019291b707 | ||
|
|
1d84bbb548 | ||
|
|
5ceb5c848e | ||
|
|
b00e94bae9 | ||
|
|
46f6584577 | ||
|
|
e3c651fcb9 | ||
|
|
ab726e9490 | ||
|
|
b9f75395b6 | ||
|
|
c0e4f83d83 | ||
|
|
08e94694b2 | ||
|
|
5112fd4fd3 | ||
|
|
2aafc6a82e | ||
|
|
1f200d53f8 | ||
|
|
77a5c75007 | ||
|
|
c44d4357a7 | ||
|
|
2bf70e88ba | ||
|
|
442bb998e4 | ||
|
|
106a5a77e7 | ||
|
|
2f3f13f34c | ||
|
|
bc4a9a07b6 | ||
|
|
5ca5ee55ee | ||
|
|
08636b550f | ||
|
|
b2aab26ded | ||
|
|
6d629d40e6 | ||
|
|
6e433ec9a4 | ||
|
|
802a0229fa | ||
|
|
41b4b86033 | ||
|
|
63dca2712b | ||
|
|
e06d104e10 | ||
|
|
4ab2ee875e | ||
|
|
ac8debbb4b | ||
|
|
8a7a2071b6 | ||
|
|
df471cf5a0 | ||
|
|
79665cc552 | ||
|
|
9c6932b124 | ||
|
|
4e30caf894 | ||
|
|
1bbff54c07 | ||
|
|
81ccf2a860 | ||
|
|
463007b218 | ||
|
|
ee53fffc67 | ||
|
|
63351ed6b7 | ||
|
|
87ba6f6ccc | ||
|
|
efeceb3a5b | ||
|
|
bb6fddad09 | ||
|
|
def9f6b70c | ||
|
|
95bbbb1daa | ||
|
|
0c759b9016 | ||
|
|
cb2d2dd363 | ||
|
|
88948c4b66 | ||
|
|
799626cfdc | ||
|
|
ab82a8748f | ||
|
|
6f78840aec | ||
|
|
b94dc3c733 | ||
|
|
de3c6bc45b | ||
|
|
c17ae13fcf | ||
|
|
e936ecbabd | ||
|
|
0f5105fee2 | ||
|
|
07571ecc32 | ||
|
|
3a4bc97abb | ||
|
|
412966d8d0 | ||
|
|
d8bf6048c1 | ||
|
|
f13f72ea16 | ||
|
|
a7818227bd | ||
|
|
a4c45ecdcd | ||
|
|
a21671f1b7 | ||
|
|
3fc0349550 | ||
|
|
fa177c41b7 | ||
|
|
f3675fb838 | ||
|
|
2196020705 | ||
|
|
81465a50d6 | ||
|
|
a0ccbfcac5 | ||
|
|
eaa3cf6137 | ||
|
|
32d4e2f281 | ||
|
|
6454f6843b | ||
|
|
22e67d39b0 | ||
|
|
7efbf6fd66 | ||
|
|
eaf9c97f52 | ||
|
|
5f7cee768d | ||
|
|
9c3b0aa289 | ||
|
|
b3bf52bd9b | ||
|
|
1f07688881 | ||
|
|
aafa30dbf9 | ||
|
|
e8f2e5efd1 | ||
|
|
c39e5bc02c | ||
|
|
6576b5ef79 | ||
|
|
bec4b2d627 | ||
|
|
a36798534d | ||
|
|
dbd945e828 | ||
|
|
b5655bf8ee | ||
|
|
3637cf4512 | ||
|
|
4693d2aae8 | ||
|
|
ece49bc4dd | ||
|
|
f60115f18d | ||
|
|
90fdbc872e | ||
|
|
7d5ad5d030 | ||
|
|
bd0e7909a1 | ||
|
|
cf3229a3c9 | ||
|
|
407f5fa53a | ||
|
|
b1edce46ff | ||
|
|
551ed091e7 | ||
|
|
7645fa4e90 | ||
|
|
396ce79d22 | ||
|
|
60ad79fd64 | ||
|
|
2c9a6aa456 | ||
|
|
3577dfda9f | ||
|
|
31549f2851 | ||
|
|
c8ea3f869e | ||
|
|
bc01c9f7c6 | ||
|
|
592df35acf | ||
|
|
66f98aadf9 | ||
|
|
17e420f01b | ||
|
|
44f6d57832 | ||
|
|
ae2cb10382 | ||
|
|
d68e08c3fd | ||
|
|
3921e8c287 | ||
|
|
caf79d1657 | ||
|
|
7477ba64d2 | ||
|
|
e006261fcf | ||
|
|
2ddd9c2fc6 | ||
|
|
5a7253f89b | ||
|
|
b68bc0f0b8 | ||
|
|
edc4984f58 | ||
|
|
c8a2ca87ba | ||
|
|
4dcc9c42c8 | ||
|
|
912fa4a85c | ||
|
|
a64a4608be | ||
|
|
b20773f2f5 | ||
|
|
f9a440c708 | ||
|
|
d002768a5f | ||
|
|
e0fcb9f225 | ||
|
|
a7ed16c72f | ||
|
|
1129d1c145 | ||
|
|
ee599c7ab8 | ||
|
|
1a5be5bf91 | ||
|
|
db0020d428 | ||
|
|
138ca4299b | ||
|
|
e9af1ebd80 | ||
|
|
4d26e404b0 | ||
|
|
b41813fa23 | ||
|
|
b18796fd45 | ||
|
|
d44898ed8c | ||
|
|
bb246cb4fd | ||
|
|
17ceb3e721 | ||
|
|
8435c579c9 | ||
|
|
3b4eb893b6 | ||
|
|
54704bcee6 | ||
|
|
8bbfd862e4 | ||
|
|
c909de0271 | ||
|
|
bd7f80a64e | ||
|
|
2e15b5b271 | ||
|
|
0a61820f15 | ||
|
|
4b1af45cd6 | ||
|
|
756e3fcf39 | ||
|
|
bb34ec1be3 | ||
|
|
f98b86ae4d | ||
|
|
eaa9a869f2 | ||
|
|
d1f3dbf89b | ||
|
|
58c6208716 | ||
|
|
83eb252e82 | ||
|
|
fd7da4d2fc | ||
|
|
4f76c98cec | ||
|
|
b7186e5da9 | ||
|
|
b3276fb329 | ||
|
|
5d2a31e65e | ||
|
|
8eaa01f1bc | ||
|
|
9bee6e12b4 | ||
|
|
50b2de8ce2 | ||
|
|
78b9e608e9 | ||
|
|
944c91d842 | ||
|
|
a936b9fec8 | ||
|
|
1efdc626e0 | ||
|
|
afca969668 | ||
|
|
f72178f142 | ||
|
|
2f4d744078 | ||
|
|
8563129dd5 | ||
|
|
ef1427bc95 | ||
|
|
20c07aaca1 | ||
|
|
4470b3fdec | ||
|
|
1e4705d20b | ||
|
|
8fdc0ab84d | ||
|
|
7c96fd391f | ||
|
|
2acbde70cc | ||
|
|
cb7d100315 | ||
|
|
d96e1bb8a8 | ||
|
|
d27a67f567 | ||
|
|
47ae974304 | ||
|
|
6ce168faa2 | ||
|
|
f9a8e1d9a7 | ||
|
|
18543534fb | ||
|
|
5349bd8d36 | ||
|
|
9a140571c3 | ||
|
|
a4e0dc8bb7 | ||
|
|
14788e2a65 | ||
|
|
c8118245e4 | ||
|
|
fcff23e0c1 | ||
|
|
85f07ca15d | ||
|
|
ed5a83c4f2 | ||
|
|
98e3ac0603 | ||
|
|
df01ef4e14 | ||
|
|
24a3e6872c | ||
|
|
3d491b63f2 | ||
|
|
65dda61ff3 | ||
|
|
6d72a784fc | ||
|
|
388e4a71e1 | ||
|
|
1f77c6468c | ||
|
|
a242c283f3 | ||
|
|
a09ed79242 | ||
|
|
5116469856 | ||
|
|
81c5583b5c | ||
|
|
9d1233fe24 | ||
|
|
43c59f8d3e | ||
|
|
3bebb7ff07 | ||
|
|
05409dacde | ||
|
|
65929eec4b | ||
|
|
3d7a04be8e | ||
|
|
0f1b8ecb39 | ||
|
|
177d48e5a6 | ||
|
|
8b6b7dec06 | ||
|
|
7011ef7615 | ||
|
|
a8beabcd23 | ||
|
|
c114ee8acb | ||
|
|
aa121e326b | ||
|
|
ad0c5a01dd | ||
|
|
b66d60dd7d | ||
|
|
14993ad169 | ||
|
|
dbf7147f22 | ||
|
|
d3a8de29e3 | ||
|
|
98c42569ea | ||
|
|
1deebf0833 | ||
|
|
3bc6b156a2 | ||
|
|
0a52c50c47 | ||
|
|
04c29294d6 | ||
|
|
9c28933882 | ||
|
|
a5d8aed6fb | ||
|
|
4c52e36a0e | ||
|
|
7c2403f470 | ||
|
|
99dfa52915 | ||
|
|
3c5b61eca4 | ||
|
|
2185479c01 | ||
|
|
fcbd48bc49 | ||
|
|
db03f4f408 | ||
|
|
2f0e0d2a3b | ||
|
|
1aeb5639c7 | ||
|
|
d14da8af12 | ||
|
|
38e529ec14 | ||
|
|
b44f055013 | ||
|
|
18aee37057 | ||
|
|
83b5b9051d | ||
|
|
e4141283f1 | ||
|
|
209ad19164 | ||
|
|
52afb8d9d1 | ||
|
|
ec6c1b0578 | ||
|
|
7e97a9b2a4 | ||
|
|
8230dd07c4 | ||
|
|
16e7826981 | ||
|
|
17b9ae5089 | ||
|
|
85b38bc0e1 | ||
|
|
ab8a9c8d36 | ||
|
|
c8fb6d9805 | ||
|
|
35d0d7956d | ||
|
|
1e6cc7e8a2 | ||
|
|
6dff9d06c9 | ||
|
|
6de31ead9d | ||
|
|
6dc8cd7031 | ||
|
|
60209027fc | ||
|
|
7d186672ec | ||
|
|
6dd757e70f | ||
|
|
56207f7f48 | ||
|
|
bf5dc8a282 | ||
|
|
0ec56ef0d3 | ||
|
|
07b49d0147 | ||
|
|
39d8b8f8ec | ||
|
|
50564c5db8 | ||
|
|
e9aad35e70 | ||
|
|
47d82ac120 | ||
|
|
4cb7d22657 | ||
|
|
e7992e356a | ||
|
|
96c598d454 | ||
|
|
7cddf329d0 | ||
|
|
f95660406f | ||
|
|
338caebfaf | ||
|
|
77ba946861 | ||
|
|
cb37282a14 | ||
|
|
3d40aefef5 | ||
|
|
e7b0489294 | ||
|
|
2690fcdacd | ||
|
|
0cc6d69c66 | ||
|
|
3312d440c1 | ||
|
|
dba5650f1a | ||
|
|
81cab7acbf | ||
|
|
42cb2eb9bb | ||
|
|
1d0476e1b8 | ||
|
|
4ea2455958 | ||
|
|
2338c57357 | ||
|
|
019e1579f6 | ||
|
|
df547b2022 | ||
|
|
ce3b8b6f50 | ||
|
|
4e24ee66f5 | ||
|
|
f82495c1cc | ||
|
|
c78c88cf92 | ||
|
|
03b82e81f0 | ||
|
|
90fe6a7416 | ||
|
|
30014d640b | ||
|
|
2be2b7f459 | ||
|
|
659ae36ca6 | ||
|
|
c92a4bcf36 | ||
|
|
931786eadb | ||
|
|
068e8121dc | ||
|
|
4e938d98fc | ||
|
|
da3e214ecd | ||
|
|
e88799265f | ||
|
|
dcb717db48 | ||
|
|
9e78c0f9e1 | ||
|
|
3b0596a93c | ||
|
|
0c8593a958 | ||
|
|
4e75524d6f | ||
|
|
e17b8dba4b | ||
|
|
11dc206fac | ||
|
|
f2ebd47ded | ||
|
|
904830579c | ||
|
|
a37d380440 | ||
|
|
1d6889879d | ||
|
|
b54cd7fbd9 | ||
|
|
113b0ac446 | ||
|
|
e30061e2a0 | ||
|
|
2b406aa7cc | ||
|
|
6cf5bbed95 | ||
|
|
1579671259 | ||
|
|
567d8416ec | ||
|
|
644e848011 | ||
|
|
dc4eccb043 | ||
|
|
be8ea6bab3 | ||
|
|
246d42469f | ||
|
|
8890aa6618 | ||
|
|
2b2620c954 | ||
|
|
e594957c0b | ||
|
|
a3b2f18a32 | ||
|
|
742b28a3c8 | ||
|
|
8340c5ba1d | ||
|
|
d44935bc29 | ||
|
|
efcbae5554 | ||
|
|
73c22bf815 | ||
|
|
e715d9e5b5 | ||
|
|
d1c1995018 | ||
|
|
bea8bd4057 | ||
|
|
5d3996ae03 | ||
|
|
3d5bc3bb0c | ||
|
|
2ec8022001 | ||
|
|
2dbbf72a6c | ||
|
|
fbbe0e06cf | ||
|
|
455ef1138f | ||
|
|
2398ef7efe | ||
|
|
f68c594a7e | ||
|
|
8004e4eda8 | ||
|
|
1e682a3e9a | ||
|
|
6546d987e5 | ||
|
|
e4195f7b60 | ||
|
|
27cdb68240 | ||
|
|
a59ea11aac | ||
|
|
5fde8f9f4b | ||
|
|
3d05606ad6 | ||
|
|
f2da244a3c | ||
|
|
c6e26b1323 | ||
|
|
1550d289df | ||
|
|
81a6d05621 | ||
|
|
78c372b2f8 | ||
|
|
59ec071908 | ||
|
|
687e67faed | ||
|
|
46e98d4660 | ||
|
|
0e851eb687 | ||
|
|
61de366e05 | ||
|
|
ab582b92c9 | ||
|
|
506a6a7779 | ||
|
|
f87dcda391 | ||
|
|
2d94ad60f3 | ||
|
|
333ed98e37 | ||
|
|
69b5f8a1cc | ||
|
|
cefad1c76f | ||
|
|
b42ebfde2f | ||
|
|
1bd73d7872 | ||
|
|
6d83f77fcf | ||
|
|
17c1070fc1 | ||
|
|
f86a493ba6 | ||
|
|
32119b6afd | ||
|
|
e11661eadc | ||
|
|
0fc2201d80 | ||
|
|
bbe96f67f6 | ||
|
|
f4b2d9b1df | ||
|
|
42d894fd5a | ||
|
|
e8c750c18f | ||
|
|
89cb84d784 | ||
|
|
daa6e8360a | ||
|
|
24429255dc | ||
|
|
7c55ae98e5 | ||
|
|
81ed21962f | ||
|
|
d9752ed970 | ||
|
|
c6293580d9 | ||
|
|
5ddb76c137 | ||
|
|
20893622d4 | ||
|
|
35445dbf2e | ||
|
|
fbe6e3c1e6 | ||
|
|
1d35922155 | ||
|
|
ea31174cc4 | ||
|
|
f5ea5f673c | ||
|
|
827e16cf93 | ||
|
|
1b61205281 | ||
|
|
269f2b4850 | ||
|
|
d346e35901 | ||
|
|
2f87691309 | ||
|
|
8e6c778a82 | ||
|
|
f36732f7d4 | ||
|
|
e63d60ab3d | ||
|
|
460f3fcfd6 | ||
|
|
1f52ab9a3f | ||
|
|
397a9cdf1f | ||
|
|
4560ef7ef7 | ||
|
|
dc41dff9a0 | ||
|
|
3e6d9d1b11 | ||
|
|
1513f45662 | ||
|
|
3e8426da1a | ||
|
|
bb29373e16 | ||
|
|
da7ac548c5 | ||
|
|
b37441eaa8 | ||
|
|
a546b77c10 | ||
|
|
392cb956d2 | ||
|
|
76273a4d86 | ||
|
|
eb32b41bd4 | ||
|
|
767456ca9a | ||
|
|
c12354138a | ||
|
|
1fd93a1fb8 | ||
|
|
9c36937a9e | ||
|
|
3d382e67b6 | ||
|
|
ecdcab7371 | ||
|
|
a5609f3cad | ||
|
|
5c0bc397c1 | ||
|
|
21e9131c65 | ||
|
|
8159e33fef | ||
|
|
d2411e380b | ||
|
|
a2017a0a75 | ||
|
|
16fcc4d61e | ||
|
|
b6b4909f3c | ||
|
|
773abf3028 | ||
|
|
f8f7c4fbff | ||
|
|
75386b4355 | ||
|
|
434b3020a0 | ||
|
|
64b0adee30 | ||
|
|
6351dc030c | ||
|
|
f261690d76 | ||
|
|
de37982a64 | ||
|
|
4266a38c01 | ||
|
|
8daec77bb4 | ||
|
|
4267c78f68 | ||
|
|
2f7843687f | ||
|
|
1648eb78f0 | ||
|
|
2b8143b4fb | ||
|
|
b2d6e7c37e | ||
|
|
22d41a144f | ||
|
|
f71d52c5ba | ||
|
|
1a86438f32 | ||
|
|
20aadf26a5 | ||
|
|
d6514bc946 | ||
|
|
3f417198d7 | ||
|
|
62da2245c1 | ||
|
|
627fa8f89e | ||
|
|
13e24501e5 | ||
|
|
0002ec1a8e | ||
|
|
62d7948b67 | ||
|
|
54ca25571e | ||
|
|
a64bf2e97f | ||
|
|
029a5587e6 | ||
|
|
918ce84b1b | ||
|
|
4622e9fe01 | ||
|
|
d707979b7d | ||
|
|
0c224e2a22 | ||
|
|
b4b4ec2f89 | ||
|
|
07b297f7ef | ||
|
|
e7804062f3 | ||
|
|
efed7969fc | ||
|
|
c8a1edea1a | ||
|
|
8a0d2c9b6a | ||
|
|
784b0e8e40 | ||
|
|
9ac3cc23f7 | ||
|
|
0e1b88fd25 | ||
|
|
7084b38a5a | ||
|
|
7cba7fad95 | ||
|
|
73d67763c9 | ||
|
|
29217453ab | ||
|
|
34fe3f53c3 | ||
|
|
78353460d1 | ||
|
|
210c76bad1 | ||
|
|
d7326b927d | ||
|
|
a4cf749d6d | ||
|
|
b7d256687a | ||
|
|
ccce2eb137 | ||
|
|
46a0c1f7bf | ||
|
|
a84d6f4121 | ||
|
|
10793ebdda | ||
|
|
9c6bec3761 | ||
|
|
b15148f9f7 | ||
|
|
ae6f669629 | ||
|
|
a50c66557e | ||
|
|
9a09822f7a | ||
|
|
0a3174cba9 | ||
|
|
d5414ece84 | ||
|
|
fc4de43db3 | ||
|
|
e4138b894c | ||
|
|
5b74430751 | ||
|
|
22f6d2d1b9 | ||
|
|
318e164a01 | ||
|
|
969d40f5d8 | ||
|
|
c7f8b4899a | ||
|
|
a81086493f | ||
|
|
b773f22bd2 | ||
|
|
2b34b86746 | ||
|
|
78c4350ff7 | ||
|
|
ecd33b0f54 | ||
|
|
7753eb32af | ||
|
|
9d3fbf8d27 | ||
|
|
7f3d099148 | ||
|
|
518ed97ed4 | ||
|
|
6b46ac2fde | ||
|
|
6b9f0f07ec | ||
|
|
553c7e9caa | ||
|
|
bef0826f9d | ||
|
|
8fa4ec20a8 | ||
|
|
5ad7562f17 | ||
|
|
b74d95894b | ||
|
|
562cd42344 | ||
|
|
078bdfc28f | ||
|
|
3c5e84bc44 | ||
|
|
e61a6a00d8 | ||
|
|
f7b1441050 | ||
|
|
d0e6689dc4 | ||
|
|
9af8dde878 | ||
|
|
bfed745758 | ||
|
|
cc22abba50 | ||
|
|
ca84177e87 | ||
|
|
4508991ca2 | ||
|
|
0b1ddecd32 | ||
|
|
59025f7d4c | ||
|
|
8c75651650 | ||
|
|
44ea2aee40 | ||
|
|
7401647d6a | ||
|
|
fbd496e663 | ||
|
|
e22818f51f | ||
|
|
1933194d35 | ||
|
|
aef08dcd75 | ||
|
|
fd4c49d3d2 | ||
|
|
c1518cb509 | ||
|
|
43e424d44d | ||
|
|
4dfd313d10 | ||
|
|
9e68f4cd2c | ||
|
|
3fe373e20c | ||
|
|
1f2852a369 | ||
|
|
abe4d91622 | ||
|
|
0649b18952 | ||
|
|
aee81c1533 | ||
|
|
451b8ea1e5 | ||
|
|
578cf90f85 | ||
|
|
869fa35353 | ||
|
|
4d8cfb05ee | ||
|
|
b9d9463cc9 | ||
|
|
2c01caa7be | ||
|
|
65e925ec22 | ||
|
|
1144956c99 | ||
|
|
cf586dcd75 | ||
|
|
6c5dd4a120 | ||
|
|
e87490aad8 | ||
|
|
cb55cec3f0 | ||
|
|
8bfec9ec7e | ||
|
|
6eaab67081 | ||
|
|
d1ff4b0ede | ||
|
|
5bc4dc297d | ||
|
|
79f8a5e87c | ||
|
|
5c0eec9c83 | ||
|
|
42f6dab4ce | ||
|
|
7bcb6a320e | ||
|
|
771acbea35 | ||
|
|
8520bbcf85 | ||
|
|
253838bba9 | ||
|
|
53db712015 | ||
|
|
6f2fc808cd | ||
|
|
6b2fdf5400 | ||
|
|
b2fa393673 | ||
|
|
5292d13ae6 | ||
|
|
bfa3a2948d | ||
|
|
2340a58e8f | ||
|
|
6d696d94d8 | ||
|
|
57360e5fea | ||
|
|
5c8d6ee901 | ||
|
|
7850eda22e | ||
|
|
38bda84426 | ||
|
|
954267c830 | ||
|
|
0dc5437ddd | ||
|
|
36dbc16fcf | ||
|
|
d20cc4c45e | ||
|
|
8b9e376609 | ||
|
|
bc9395029b | ||
|
|
6a2af9ffe4 | ||
|
|
9005656062 | ||
|
|
965dc73351 | ||
|
|
5dc31fac5b | ||
|
|
4254382e7f | ||
|
|
9c0a8266ab | ||
|
|
407c8204aa | ||
|
|
e218aaaed2 | ||
|
|
d07a21fdaa | ||
|
|
e8b69e133e | ||
|
|
b02d2a7f8d | ||
|
|
b9e3c22a1e | ||
|
|
80ab030c4a | ||
|
|
5483919dd3 | ||
|
|
b21daf47a7 | ||
|
|
917ec1091a | ||
|
|
30bb9f4fcc | ||
|
|
626da4c8bb | ||
|
|
813462744b | ||
|
|
970b648145 | ||
|
|
b9fce4527a | ||
|
|
597b157f83 | ||
|
|
4f82ae38cf | ||
|
|
74e4012fee | ||
|
|
74d18be94a | ||
|
|
56788af48b | ||
|
|
e618b3d0aa | ||
|
|
9f9cfbd581 | ||
|
|
c2e5f3ac2f | ||
|
|
9ccf4f6083 | ||
|
|
eb3fb9ec1c | ||
|
|
69f0f8d8bf | ||
|
|
af7a73ec42 | ||
|
|
c0b68e7da5 | ||
|
|
050c88ca55 | ||
|
|
cc4f5b9ff5 | ||
|
|
1645034f6e | ||
|
|
b5e9f98fd1 |
10
Gruntfile.js
10
Gruntfile.js
@@ -49,6 +49,7 @@ module.exports = function (grunt) {
|
||||
'client/lib/bull.js',
|
||||
'client/lib/marked.min.js',
|
||||
'client/lib/autobahn.js',
|
||||
'client/lib/gridstack.all.js',
|
||||
|
||||
'client/src/namespace.js',
|
||||
'client/src/exceptions.js',
|
||||
@@ -211,6 +212,7 @@ module.exports = function (grunt) {
|
||||
'daemon.php',
|
||||
'rebuild.php',
|
||||
'clear_cache.php',
|
||||
'preload.php',
|
||||
'upgrade.php',
|
||||
'extension.php',
|
||||
'websocket.php',
|
||||
@@ -288,8 +290,8 @@ module.exports = function (grunt) {
|
||||
},
|
||||
files: [
|
||||
{
|
||||
src: 'build/tmp/application/Espo/Core/defaults/config.php',
|
||||
dest: 'build/tmp/application/Espo/Core/defaults/config.php'
|
||||
src: 'build/tmp/application/Espo/Resources/defaults/config.php',
|
||||
dest: 'build/tmp/application/Espo/Resources/defaults/config.php'
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -313,11 +315,11 @@ module.exports = function (grunt) {
|
||||
});
|
||||
|
||||
grunt.registerTask("unit-tests-run", function() {
|
||||
cp.execSync("vendor/bin/phpunit --bootstrap=./vendor/autoload.php tests/unit", {stdio: 'inherit'});
|
||||
cp.execSync("vendor/bin/phpunit --bootstrap=./vendor/autoload.php ./tests/unit", {stdio: 'inherit'});
|
||||
});
|
||||
|
||||
grunt.registerTask("integration-tests-run", function() {
|
||||
cp.execSync("vendor/bin/phpunit --bootstrap=./vendor/autoload.php tests/integration", {stdio: 'inherit'});
|
||||
cp.execSync("vendor/bin/phpunit --bootstrap=./vendor/autoload.php ./tests/integration", {stdio: 'inherit'});
|
||||
});
|
||||
|
||||
grunt.registerTask("zip", function() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
@@ -29,5 +29,9 @@
|
||||
|
||||
require_once('../../bootstrap.php');
|
||||
|
||||
$app = new \Espo\Core\Application();
|
||||
$app->run();
|
||||
use Espo\Core\{
|
||||
Application,
|
||||
ApplicationRunners\Api,
|
||||
};
|
||||
|
||||
(new Application())->run(Api::class);
|
||||
|
||||
@@ -29,11 +29,15 @@
|
||||
|
||||
require_once('../../../bootstrap.php');
|
||||
|
||||
use Espo\Core\{
|
||||
Portal\Application,
|
||||
Portal\ApplicationRunners\Api,
|
||||
};
|
||||
|
||||
if (!empty($_GET['portalId'])) {
|
||||
$portalId = $_GET['portalId'];
|
||||
} else {
|
||||
$portalId = explode('/', $_SERVER['REQUEST_URI'])[count(explode('/', $_SERVER['SCRIPT_NAME'])) - 1];
|
||||
}
|
||||
|
||||
$app = new \Espo\Core\Portal\Application($portalId);
|
||||
$app->run();
|
||||
(new Application($portalId))->run(Api::class);
|
||||
|
||||
@@ -29,14 +29,13 @@
|
||||
|
||||
namespace Espo\Acl;
|
||||
|
||||
use \Espo\Entities\User as EntityUser;
|
||||
use \Espo\ORM\Entity;
|
||||
use Espo\Entities\User as EntityUser;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
class ActionHistoryRecord extends \Espo\Core\Acl\Base
|
||||
class ActionHistoryRecord extends \Espo\Core\Acl\Acl
|
||||
{
|
||||
public function checkIsOwner(EntityUser $user, Entity $entity)
|
||||
{
|
||||
return $entity->get('userId') === $user->id;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,10 +29,10 @@
|
||||
|
||||
namespace Espo\Acl;
|
||||
|
||||
use \Espo\Entities\User as EntityUser;
|
||||
use \Espo\ORM\Entity;
|
||||
use Espo\Entities\User as EntityUser;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
class Attachment extends \Espo\Core\Acl\Base
|
||||
class Attachment extends \Espo\Core\Acl\Acl
|
||||
{
|
||||
public function checkEntityRead(EntityUser $user, Entity $entity, $data)
|
||||
{
|
||||
@@ -90,4 +90,3 @@ class Attachment extends \Espo\Core\Acl\Base
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,10 +29,10 @@
|
||||
|
||||
namespace Espo\Acl;
|
||||
|
||||
use \Espo\Entities\User as EntityUser;
|
||||
use \Espo\ORM\Entity;
|
||||
use Espo\Entities\User as EntityUser;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
class Email extends \Espo\Core\Acl\Base
|
||||
class Email extends \Espo\Core\Acl\Acl
|
||||
{
|
||||
protected $ownerUserIdAttribute = 'usersIds';
|
||||
|
||||
|
||||
@@ -29,10 +29,10 @@
|
||||
|
||||
namespace Espo\Acl;
|
||||
|
||||
use \Espo\Entities\User as EntityUser;
|
||||
use \Espo\ORM\Entity;
|
||||
use Espo\Entities\User as EntityUser;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
class EmailAddress extends \Espo\Core\Acl\Base
|
||||
class EmailAddress extends \Espo\Core\Acl\Acl
|
||||
{
|
||||
public function checkEditInEntity(EntityUser $user, Entity $entity, Entity $excludeEntity)
|
||||
{
|
||||
@@ -62,4 +62,3 @@ class EmailAddress extends \Espo\Core\Acl\Base
|
||||
return !$isFobidden;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,10 +29,10 @@
|
||||
|
||||
namespace Espo\Acl;
|
||||
|
||||
use \Espo\Entities\User as EntityUser;
|
||||
use \Espo\ORM\Entity;
|
||||
use Espo\Entities\User as EntityUser;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
class EmailFilter extends \Espo\Core\Acl\Base
|
||||
class EmailFilter extends \Espo\Core\Acl\Acl
|
||||
{
|
||||
public function checkIsOwner(EntityUser $user, Entity $entity)
|
||||
{
|
||||
@@ -53,4 +53,3 @@ class EmailFilter extends \Espo\Core\Acl\Base
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,12 +29,11 @@
|
||||
|
||||
namespace Espo\Acl;
|
||||
|
||||
use \Espo\Entities\User as EntityUser;
|
||||
use \Espo\ORM\Entity;
|
||||
use Espo\Entities\User as EntityUser;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
class Import extends \Espo\Core\Acl\Base
|
||||
class Import extends \Espo\Core\Acl\Acl
|
||||
{
|
||||
|
||||
public function checkEntityRead(EntityUser $user, Entity $entity, $data)
|
||||
{
|
||||
if ($user->isAdmin()) return true;
|
||||
|
||||
@@ -29,10 +29,10 @@
|
||||
|
||||
namespace Espo\Acl;
|
||||
|
||||
use \Espo\Entities\User as EntityUser;
|
||||
use \Espo\ORM\Entity;
|
||||
use Espo\Entities\User as EntityUser;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
class Note extends \Espo\Core\Acl\Base
|
||||
class Note extends \Espo\Core\Acl\Acl
|
||||
{
|
||||
protected $deleteThresholdPeriod = '1 month';
|
||||
|
||||
|
||||
@@ -29,10 +29,10 @@
|
||||
|
||||
namespace Espo\Acl;
|
||||
|
||||
use \Espo\Entities\User as EntityUser;
|
||||
use \Espo\ORM\Entity;
|
||||
use Espo\Entities\User as EntityUser;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
class Notification extends \Espo\Core\Acl\Base
|
||||
class Notification extends \Espo\Core\Acl\Acl
|
||||
{
|
||||
public function checkIsOwner(EntityUser $user, Entity $entity)
|
||||
{
|
||||
@@ -42,4 +42,3 @@ class Notification extends \Espo\Core\Acl\Base
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,10 +29,10 @@
|
||||
|
||||
namespace Espo\Acl;
|
||||
|
||||
use \Espo\Entities\User as EntityUser;
|
||||
use \Espo\ORM\Entity;
|
||||
use Espo\Entities\User as EntityUser;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
class PhoneNumber extends \Espo\Core\Acl\Base
|
||||
class PhoneNumber extends \Espo\Core\Acl\Acl
|
||||
{
|
||||
public function checkEditInEntity(EntityUser $user, Entity $entity, Entity $excludeEntity)
|
||||
{
|
||||
|
||||
@@ -29,10 +29,10 @@
|
||||
|
||||
namespace Espo\Acl;
|
||||
|
||||
use \Espo\Entities\User as EntityUser;
|
||||
use \Espo\ORM\Entity;
|
||||
use Espo\Entities\User as EntityUser;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
class ScheduledJob extends \Espo\Core\Acl\Base
|
||||
class ScheduledJob extends \Espo\Core\Acl\Acl
|
||||
{
|
||||
public function checkEntityRead(EntityUser $user, Entity $entity, $data)
|
||||
{
|
||||
@@ -58,4 +58,3 @@ class ScheduledJob extends \Espo\Core\Acl\Base
|
||||
return $this->checkEntity($user, $entity, $data, 'create');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,13 +29,15 @@
|
||||
|
||||
namespace Espo\Acl;
|
||||
|
||||
use \Espo\ORM\Entity;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
class Team extends \Espo\Core\Acl\Base
|
||||
use Espo\Entities\User as UserEntity;
|
||||
|
||||
class Team extends \Espo\Core\Acl\Acl
|
||||
{
|
||||
public function checkInTeam(\Espo\Entities\User $user, Entity $entity)
|
||||
public function checkInTeam(UserEntity $user, Entity $entity)
|
||||
{
|
||||
$userTeamIdList = $user->getLinkMultipleIdList('teams');
|
||||
return in_array($entity->id, $userTeamIdList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,12 +29,12 @@
|
||||
|
||||
namespace Espo\Acl;
|
||||
|
||||
use \Espo\ORM\Entity;
|
||||
use \Espo\Entities\User as EntityUser;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Entities\User as EntityUser;
|
||||
|
||||
class User extends \Espo\Core\Acl\Base
|
||||
class User extends \Espo\Core\Acl\Acl
|
||||
{
|
||||
public function checkIsOwner(\Espo\Entities\User $user, Entity $entity)
|
||||
public function checkIsOwner(EntityUser $user, Entity $entity)
|
||||
{
|
||||
return $user->id === $entity->id;
|
||||
}
|
||||
|
||||
@@ -29,10 +29,10 @@
|
||||
|
||||
namespace Espo\Acl;
|
||||
|
||||
use \Espo\Entities\User as EntityUser;
|
||||
use \Espo\ORM\Entity;
|
||||
use Espo\Entities\User as EntityUser;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
class Webhook extends \Espo\Core\Acl\Base
|
||||
class Webhook extends \Espo\Core\Acl\Acl
|
||||
{
|
||||
public function checkIsOwner(EntityUser $user, Entity $entity)
|
||||
{
|
||||
|
||||
@@ -27,31 +27,44 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\AppParams;
|
||||
namespace Espo\Classes\AppParams;
|
||||
|
||||
class TemplateEntityTypeList extends \Espo\Core\Injectable
|
||||
use Espo\Core\{
|
||||
Acl,
|
||||
Select\SelectManagerFactory,
|
||||
ORM\EntityManager,
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a list of entity types for which a PDF template exists.
|
||||
*/
|
||||
class TemplateEntityTypeList
|
||||
{
|
||||
protected function init()
|
||||
protected $acl;
|
||||
protected $selectManagerFactory;
|
||||
protected $entityManager;
|
||||
|
||||
public function __construct(Acl $acl, SelectManagerFactory $selectManagerFactory, EntityManager $entityManager)
|
||||
{
|
||||
$this->addDependency('acl');
|
||||
$this->addDependency('selectManagerFactory');
|
||||
$this->addDependency('entityManager');
|
||||
$this->acl = $acl;
|
||||
$this->selectManagerFactory = $selectManagerFactory;
|
||||
$this->entityManager = $entityManager;
|
||||
}
|
||||
|
||||
public function get()
|
||||
{
|
||||
if (!$this->getInjection('acl')->checkScope('Template')) {
|
||||
if (!$this->acl->checkScope('Template')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$list = [];
|
||||
|
||||
$selectManager = $this->getInjection('selectManagerFactory')->create('Template');
|
||||
$selectManager = $this->selectManagerFactory->create('Template');
|
||||
|
||||
$selectParams = $selectManager->getEmptySelectParams();
|
||||
$selectManager->applyAccess($selectParams);
|
||||
|
||||
$templateList = $this->getInjection('entityManager')->getRepository('Template')
|
||||
$templateList = $this->entityManager->getRepository('Template')
|
||||
->select(['entityType'])
|
||||
->groupBy(['entityType'])
|
||||
->find($selectParams);
|
||||
@@ -27,42 +27,44 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Authentication;
|
||||
namespace Espo\Classes\Cleanup;
|
||||
|
||||
use \Espo\Core\Utils\Config;
|
||||
use \Espo\Core\ORM\EntityManager;
|
||||
use \Espo\Core\Utils\Auth;
|
||||
use Espo\Core\{
|
||||
Utils\Config,
|
||||
ORM\EntityManager,
|
||||
};
|
||||
|
||||
abstract class Base
|
||||
use DateTime;
|
||||
|
||||
class Reminders
|
||||
{
|
||||
protected $config;
|
||||
|
||||
protected $entityManager;
|
||||
|
||||
private $passwordHash;
|
||||
|
||||
public function __construct(Config $config, EntityManager $entityManager)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->entityManager = $entityManager;
|
||||
}
|
||||
|
||||
protected function getConfig()
|
||||
{
|
||||
return $this->config;
|
||||
}
|
||||
protected $cleanupRemindersPeriod = '15 days';
|
||||
|
||||
protected function getEntityManager()
|
||||
public function process()
|
||||
{
|
||||
return $this->entityManager;
|
||||
}
|
||||
$period = '-' . $this->config->get('cleanupRemindersPeriod', $this->cleanupRemindersPeriod);
|
||||
|
||||
protected function getPasswordHash()
|
||||
{
|
||||
if (!isset($this->passwordHash)) {
|
||||
$this->passwordHash = new \Espo\Core\Utils\PasswordHash($this->config);
|
||||
}
|
||||
$dt = new DateTime();
|
||||
|
||||
return $this->passwordHash;
|
||||
$dt->modify($period);
|
||||
|
||||
$delete = $this->entityManager->getQueryBuilder()
|
||||
->delete()
|
||||
->from('Reminder')
|
||||
->where([
|
||||
'remindAt<' => $dt->format('Y-m-d'),
|
||||
])
|
||||
->build();
|
||||
|
||||
$this->entityManager->getQueryExecutor()->execute($delete);
|
||||
}
|
||||
}
|
||||
@@ -27,33 +27,63 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Cleanup;
|
||||
namespace Espo\Classes\Cleanup;
|
||||
|
||||
class WebhookQueue extends Base
|
||||
use Espo\Core\{
|
||||
Utils\Config,
|
||||
ORM\EntityManager,
|
||||
};
|
||||
|
||||
use DateTime;
|
||||
|
||||
class WebhookQueue
|
||||
{
|
||||
protected $cleanupWebhookQueuePeriod = '10 days';
|
||||
|
||||
protected $config;
|
||||
protected $entityManager;
|
||||
|
||||
public function __construct(Config $config, EntityManager $entityManager)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->entityManager = $entityManager;
|
||||
}
|
||||
|
||||
public function process()
|
||||
{
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
$period = '-' . $this->config->get('cleanupWebhookQueuePeriod', $this->cleanupWebhookQueuePeriod);
|
||||
|
||||
$datetime = new DateTime();
|
||||
|
||||
$period = '-' . $this->getConfig()->get('cleanupWebhookQueuePeriod', $this->cleanupWebhookQueuePeriod);
|
||||
$datetime = new \DateTime();
|
||||
$datetime->modify($period);
|
||||
$from = $datetime->format('Y-m-d H:i:s');
|
||||
|
||||
$query = "
|
||||
DELETE FROM `webhook_queue_item`
|
||||
WHERE
|
||||
DATE(created_at) < ".$pdo->quote($from)." AND
|
||||
(status <> 'Pending' OR deleted = 1)
|
||||
";
|
||||
$pdo->query($query);
|
||||
$query = $this->entityManager->getQueryBuilder()
|
||||
->delete()
|
||||
->from('WebhookQueueItem')
|
||||
->where([
|
||||
'DATE:(createdAt)<' => $from,
|
||||
'OR' => [
|
||||
'status!=' => 'Pending',
|
||||
'deleted' => true,
|
||||
],
|
||||
])
|
||||
->build();
|
||||
|
||||
$query = "
|
||||
DELETE FROM `webhook_event_queue_item`
|
||||
WHERE DATE(created_at) < ".$pdo->quote($from)." AND (is_processed = 1 OR deleted = 1)
|
||||
";
|
||||
$pdo->query($query);
|
||||
$this->entityManager->getQueryExecutor()->execute($query);
|
||||
|
||||
$query = $this->entityManager->getQueryBuilder()
|
||||
->delete()
|
||||
->from('WebhookEventQueueItem')
|
||||
->where([
|
||||
'DATE:(createdAt)<' => $from,
|
||||
'OR' => [
|
||||
'isProcessed' => true,
|
||||
'deleted' => true,
|
||||
],
|
||||
])
|
||||
->build();
|
||||
|
||||
$this->entityManager->getQueryExecutor()->execute($query);
|
||||
}
|
||||
}
|
||||
138
application/Espo/Classes/ConsoleCommands/Import.php
Normal file
138
application/Espo/Classes/ConsoleCommands/Import.php
Normal file
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2020 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\ConsoleCommands;
|
||||
|
||||
use Espo\Core\{
|
||||
ServiceFactory,
|
||||
Console\Commands\Command,
|
||||
};
|
||||
|
||||
use Throwable;
|
||||
|
||||
class Import implements Command
|
||||
{
|
||||
protected $serviceFactory;
|
||||
|
||||
public function __construct(ServiceFactory $serviceFactory)
|
||||
{
|
||||
$this->serviceFactory = $serviceFactory;
|
||||
}
|
||||
|
||||
public function run(array $options, array $flagList)
|
||||
{
|
||||
$id = $options['id'] ?? null;
|
||||
$filePath = $options['file'] ?? null;
|
||||
$paramsId = $options['paramsId'] ?? null;
|
||||
|
||||
$service = $this->serviceFactory->create('Import');
|
||||
|
||||
$forceResume = in_array('resume', $flagList);
|
||||
$revert = in_array('revert', $flagList);
|
||||
|
||||
if (!$id && $filePath) {
|
||||
if (!$paramsId) {
|
||||
$this->out("You need to specify --params-id option.\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!file_exists($filePath)) {
|
||||
$this->out("File not found.\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$contents = file_get_contents($filePath);
|
||||
|
||||
try {
|
||||
$result = $service->importFileWithParamsId($contents, $paramsId);
|
||||
|
||||
$resultId = $result->id;
|
||||
$countCreated = $result->countCreated;
|
||||
$countUpdated = $result->countUpdated;
|
||||
}
|
||||
catch (Throwable $e) {
|
||||
$this->out("Error occured: ".$e->getMessage()."\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->out("Finished. Import ID: {$resultId}. Created: {$countCreated}. Updated: {$countUpdated}.\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($id && $revert) {
|
||||
$this->out("Reverting import...\n");
|
||||
|
||||
try {
|
||||
$service->revert($id);
|
||||
}
|
||||
catch (Throwable $e) {
|
||||
$this->out("Error occured: " . $e->getMessage() . "\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->out("Finished.\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($id) {
|
||||
$this->out("Running import, this may take a while...\n");
|
||||
|
||||
try {
|
||||
$result = $service->importById($id, true, $forceResume);
|
||||
}
|
||||
catch (Throwable $e) {
|
||||
$this->out("Error occured: " . $e->getMessage() . "\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$countCreated = $result->countCreated;
|
||||
$countUpdated = $result->countUpdated;
|
||||
|
||||
$this->out("Finished. Created: {$countCreated}. Updated: {$countUpdated}.\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->out("Not enough params passed.\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
protected function out($string)
|
||||
{
|
||||
fwrite(\STDOUT, $string);
|
||||
}
|
||||
}
|
||||
@@ -27,13 +27,15 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Layout\Defaults;
|
||||
namespace Espo\Classes\DefaultLayouts;
|
||||
|
||||
use Espo\Core\Utils\Metadata;
|
||||
|
||||
class DefaultSidePanelType
|
||||
{
|
||||
protected $metadata;
|
||||
|
||||
public function __construct(\Espo\Core\Utils\Metadata $metadata)
|
||||
public function __construct(Metadata $metadata)
|
||||
{
|
||||
$this->metadata = $metadata;
|
||||
}
|
||||
@@ -27,7 +27,7 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\FieldValidators;
|
||||
namespace Espo\Classes\FieldValidators;
|
||||
|
||||
class ArrayIntType extends ArrayType
|
||||
{
|
||||
@@ -27,16 +27,18 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\FieldValidators;
|
||||
namespace Espo\Classes\FieldValidators;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
class ArrayType extends BaseType
|
||||
{
|
||||
public function checkRequired(\Espo\ORM\Entity $entity, string $field, $validationValue, $data) : bool
|
||||
public function checkRequired(Entity $entity, string $field, $validationValue, $data) : bool
|
||||
{
|
||||
return $this->isNotEmpty($entity, $field);
|
||||
}
|
||||
|
||||
public function checkMaxCount(\Espo\ORM\Entity $entity, string $field, $validationValue, $data) : bool
|
||||
public function checkMaxCount(Entity $entity, string $field, $validationValue, $data) : bool
|
||||
{
|
||||
if (!$this->isNotEmpty($entity, $field)) return true;
|
||||
$list = $entity->get($field);
|
||||
@@ -44,7 +46,7 @@ class ArrayType extends BaseType
|
||||
return true;
|
||||
}
|
||||
|
||||
public function checkArray(\Espo\ORM\Entity $entity, string $field, $validationValue, $data) : bool
|
||||
public function checkArray(Entity $entity, string $field, $validationValue, $data) : bool
|
||||
{
|
||||
if (isset($data->$field) && $data->$field !== null && !is_array($data->$field)) {
|
||||
return false;
|
||||
@@ -53,7 +55,7 @@ class ArrayType extends BaseType
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function isNotEmpty(\Espo\ORM\Entity $entity, $field)
|
||||
protected function isNotEmpty(Entity $entity, $field)
|
||||
{
|
||||
if (!$entity->has($field) || $entity->get($field) === null) return false;
|
||||
$list = $entity->get($field);
|
||||
@@ -27,33 +27,33 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\FieldValidators;
|
||||
namespace Espo\Classes\FieldValidators;
|
||||
|
||||
use \Espo\ORM\Entity;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
use Espo\Core\{
|
||||
Utils\Metadata,
|
||||
Utils\FieldUtil,
|
||||
};
|
||||
|
||||
class BaseType
|
||||
{
|
||||
private $metadata;
|
||||
private $fieldManagerUtil;
|
||||
protected $metadata;
|
||||
protected $fieldUtil;
|
||||
|
||||
public function __construct(\Espo\Core\Utils\Metadata $metadata, \Espo\Core\Utils\FieldManagerUtil $fieldManagerUtil)
|
||||
public function __construct(Metadata $metadata, FieldUtil $fieldUtil)
|
||||
{
|
||||
$this->metadata = $metadata;
|
||||
$this->fieldManagerUtil = $fieldManagerUtil;
|
||||
$this->fieldUtil = $fieldUtil;
|
||||
}
|
||||
|
||||
protected function getActualAttributeList(Entity $entity, string $field) : array
|
||||
{
|
||||
return $this->getFieldManagerUtil()->getActualAttributeList($entity->getEntityType(), $field);
|
||||
return $this->fieldUtil->getActualAttributeList($entity->getEntityType(), $field);
|
||||
}
|
||||
|
||||
protected function getMetadata() : \Espo\Core\Utils\Metadata
|
||||
protected function getMetadata() : Metadata
|
||||
{
|
||||
return $this->metadata;
|
||||
}
|
||||
|
||||
protected function getFieldManagerUtil() : \Espo\Core\Utils\FieldManagerUtil
|
||||
{
|
||||
return $this->fieldManagerUtil;
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\FieldValidators;
|
||||
namespace Espo\Classes\FieldValidators;
|
||||
|
||||
class ChecklistType extends ArrayType
|
||||
{
|
||||
@@ -27,11 +27,13 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\FieldValidators;
|
||||
namespace Espo\Classes\FieldValidators;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
class CurrencyType extends FloatType
|
||||
{
|
||||
protected function isNotEmpty(\Espo\ORM\Entity $entity, $field)
|
||||
protected function isNotEmpty(Entity $entity, $field)
|
||||
{
|
||||
return
|
||||
$entity->has($field) && $entity->get($field) !== null &&
|
||||
@@ -27,16 +27,18 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\FieldValidators;
|
||||
namespace Espo\Classes\FieldValidators;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
class DateType extends BaseType
|
||||
{
|
||||
public function checkRequired(\Espo\ORM\Entity $entity, string $field, $validationValue, $data) : bool
|
||||
public function checkRequired(Entity $entity, string $field, $validationValue, $data) : bool
|
||||
{
|
||||
return $this->isNotEmpty($entity, $field);
|
||||
}
|
||||
|
||||
protected function isNotEmpty(\Espo\ORM\Entity $entity, $field)
|
||||
protected function isNotEmpty(Entity $entity, $field)
|
||||
{
|
||||
return $entity->has($field) && $entity->get($field) !== null;
|
||||
}
|
||||
@@ -27,16 +27,18 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\FieldValidators;
|
||||
namespace Espo\Classes\FieldValidators;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
class DatetimeOptionalType extends DatetimeType
|
||||
{
|
||||
public function checkRequired(\Espo\ORM\Entity $entity, string $field, $validationValue, $data) : bool
|
||||
public function checkRequired(Entity $entity, string $field, $validationValue, $data) : bool
|
||||
{
|
||||
return $this->isNotEmpty($entity, $field);
|
||||
}
|
||||
|
||||
protected function isNotEmpty(\Espo\ORM\Entity $entity, $field)
|
||||
protected function isNotEmpty(Entity $entity, $field)
|
||||
{
|
||||
if ($entity->has($field) && $entity->get($field) !== null) return true;
|
||||
if ($entity->has($field . 'Date') && $entity->get($field . 'Date') !== null) return true;
|
||||
@@ -27,7 +27,7 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\FieldValidators;
|
||||
namespace Espo\Classes\FieldValidators;
|
||||
|
||||
class DatetimeType extends DateType
|
||||
{
|
||||
@@ -27,11 +27,13 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\FieldValidators;
|
||||
namespace Espo\Classes\FieldValidators;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
class EmailType extends BaseType
|
||||
{
|
||||
public function checkRequired(\Espo\ORM\Entity $entity, string $field, $validationValue, $data) : bool
|
||||
public function checkRequired(Entity $entity, string $field, $validationValue, $data) : bool
|
||||
{
|
||||
if ($this->isNotEmpty($entity, $field)) return true;
|
||||
|
||||
@@ -45,7 +47,7 @@ class EmailType extends BaseType
|
||||
return false;
|
||||
}
|
||||
|
||||
public function checkEmailAddress(\Espo\ORM\Entity $entity, string $field, $validationValue, $data) : bool
|
||||
public function checkEmailAddress(Entity $entity, string $field, $validationValue, $data) : bool
|
||||
{
|
||||
if ($this->isNotEmpty($entity, $field)) {
|
||||
$address = $entity->get($field);
|
||||
@@ -68,7 +70,7 @@ class EmailType extends BaseType
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function isNotEmpty(\Espo\ORM\Entity $entity, $field)
|
||||
protected function isNotEmpty(Entity $entity, $field)
|
||||
{
|
||||
return $entity->has($field) && $entity->get($field) !== '' && $entity->get($field) !== null;
|
||||
}
|
||||
@@ -27,16 +27,18 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\FieldValidators;
|
||||
namespace Espo\Classes\FieldValidators;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
class EnumType extends BaseType
|
||||
{
|
||||
public function checkRequired(\Espo\ORM\Entity $entity, string $field, $validationValue, $data) : bool
|
||||
public function checkRequired(Entity $entity, string $field, $validationValue, $data) : bool
|
||||
{
|
||||
return $this->isNotEmpty($entity, $field);
|
||||
}
|
||||
|
||||
protected function isNotEmpty(\Espo\ORM\Entity $entity, $field)
|
||||
protected function isNotEmpty(Entity $entity, $field)
|
||||
{
|
||||
return $entity->has($field) && $entity->get($field) !== null;
|
||||
}
|
||||
@@ -27,7 +27,7 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\FieldValidators;
|
||||
namespace Espo\Classes\FieldValidators;
|
||||
|
||||
class FileType extends LinkType
|
||||
{
|
||||
@@ -27,7 +27,7 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\FieldValidators;
|
||||
namespace Espo\Classes\FieldValidators;
|
||||
|
||||
class FloatType extends IntType
|
||||
{
|
||||
@@ -27,7 +27,7 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\FieldValidators;
|
||||
namespace Espo\Classes\FieldValidators;
|
||||
|
||||
class ImageType extends FileType
|
||||
{
|
||||
@@ -27,30 +27,32 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\FieldValidators;
|
||||
namespace Espo\Classes\FieldValidators;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
class IntType extends BaseType
|
||||
{
|
||||
public function checkRequired(\Espo\ORM\Entity $entity, string $field, $validationValue, $data) : bool
|
||||
public function checkRequired(Entity $entity, string $field, $validationValue, $data) : bool
|
||||
{
|
||||
return $this->isNotEmpty($entity, $field);
|
||||
}
|
||||
|
||||
public function checkMax(\Espo\ORM\Entity $entity, string $field, $validationValue, $data) : bool
|
||||
public function checkMax(Entity $entity, string $field, $validationValue, $data) : bool
|
||||
{
|
||||
if (!$this->isNotEmpty($entity, $field)) return true;
|
||||
if ($entity->get($field) > $validationValue) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public function checkMin(\Espo\ORM\Entity $entity, string $field, $validationValue, $data) : bool
|
||||
public function checkMin(Entity $entity, string $field, $validationValue, $data) : bool
|
||||
{
|
||||
if (!$this->isNotEmpty($entity, $field)) return true;
|
||||
if ($entity->get($field) < $validationValue) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function isNotEmpty(\Espo\ORM\Entity $entity, $field)
|
||||
protected function isNotEmpty(Entity $entity, $field)
|
||||
{
|
||||
return $entity->has($field) && $entity->get($field) !== null;
|
||||
}
|
||||
@@ -27,18 +27,20 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\FieldValidators;
|
||||
namespace Espo\Classes\FieldValidators;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
class JsonArrayType extends BaseType
|
||||
{
|
||||
public function checkArray(\Espo\ORM\Entity $entity, string $field, $validationValue, $data) : bool
|
||||
public function checkArray(Entity $entity, string $field, $validationValue, $data) : bool
|
||||
{
|
||||
if (!$entity->has($field) || $entity->get($field) === null) return true;
|
||||
|
||||
return is_array($entity->get($field));
|
||||
}
|
||||
|
||||
protected function isNotEmpty(\Espo\ORM\Entity $entity, $field)
|
||||
protected function isNotEmpty(Entity $entity, $field)
|
||||
{
|
||||
if (!$entity->has($field) || $entity->get($field) === null) return false;
|
||||
$list = $entity->get($field);
|
||||
@@ -27,11 +27,13 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\FieldValidators;
|
||||
namespace Espo\Classes\FieldValidators;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
class LinkMultipleType extends BaseType
|
||||
{
|
||||
public function checkRequired(\Espo\ORM\Entity $entity, string $field, $validationValue, $data) : bool
|
||||
public function checkRequired(Entity $entity, string $field, $validationValue, $data) : bool
|
||||
{
|
||||
return count($entity->getLinkMultipleIdList($field)) > 0;
|
||||
}
|
||||
@@ -27,11 +27,13 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\FieldValidators;
|
||||
namespace Espo\Classes\FieldValidators;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
class LinkParentType extends BaseType
|
||||
{
|
||||
public function checkRequired(\Espo\ORM\Entity $entity, string $field, $validationValue, $data) : bool
|
||||
public function checkRequired(Entity $entity, string $field, $validationValue, $data) : bool
|
||||
{
|
||||
$idAttribute = $field . 'Id';
|
||||
$typeAttribute = $field . 'Type';
|
||||
@@ -27,11 +27,13 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\FieldValidators;
|
||||
namespace Espo\Classes\FieldValidators;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
class LinkType extends BaseType
|
||||
{
|
||||
public function checkRequired(\Espo\ORM\Entity $entity, string $field, $validationValue, $data) : bool
|
||||
public function checkRequired(Entity $entity, string $field, $validationValue, $data) : bool
|
||||
{
|
||||
$idAttribute = $field . 'Id';
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\FieldValidators;
|
||||
namespace Espo\Classes\FieldValidators;
|
||||
|
||||
class MultiEnumType extends ArrayType
|
||||
{
|
||||
@@ -27,11 +27,13 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\FieldValidators;
|
||||
namespace Espo\Classes\FieldValidators;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
class PersonNameType extends BaseType
|
||||
{
|
||||
public function checkRequired(\Espo\ORM\Entity $entity, string $field, $validationValue, $data) : bool
|
||||
public function checkRequired(Entity $entity, string $field, $validationValue, $data) : bool
|
||||
{
|
||||
$isEmpty = true;
|
||||
foreach ($this->getActualAttributeList($entity, $field) as $attribute) {
|
||||
@@ -27,11 +27,13 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\FieldValidators;
|
||||
namespace Espo\Classes\FieldValidators;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
class PhoneType extends BaseType
|
||||
{
|
||||
public function checkRequired(\Espo\ORM\Entity $entity, string $field, $validationValue, $data) : bool
|
||||
public function checkRequired(Entity $entity, string $field, $validationValue, $data) : bool
|
||||
{
|
||||
if ($this->isNotEmpty($entity, $field)) return true;
|
||||
|
||||
@@ -45,7 +47,7 @@ class PhoneType extends BaseType
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function isNotEmpty(\Espo\ORM\Entity $entity, $field)
|
||||
protected function isNotEmpty(Entity $entity, $field)
|
||||
{
|
||||
return $entity->has($field) && $entity->get($field) !== '' && $entity->get($field) !== null;
|
||||
}
|
||||
@@ -27,16 +27,18 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\FieldValidators;
|
||||
namespace Espo\Classes\FieldValidators;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
class VarcharType extends BaseType
|
||||
{
|
||||
public function checkRequired(\Espo\ORM\Entity $entity, string $field, $validationValue, $data) : bool
|
||||
public function checkRequired(Entity $entity, string $field, $validationValue, $data) : bool
|
||||
{
|
||||
return $this->isNotEmpty($entity, $field);
|
||||
}
|
||||
|
||||
public function checkMaxLength(\Espo\ORM\Entity $entity, string $field, $validationValue, $data) : bool
|
||||
public function checkMaxLength(Entity $entity, string $field, $validationValue, $data) : bool
|
||||
{
|
||||
if ($this->isNotEmpty($entity, $field)) {
|
||||
$value = $entity->get($field);
|
||||
@@ -47,7 +49,7 @@ class VarcharType extends BaseType
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function isNotEmpty(\Espo\ORM\Entity $entity, $field)
|
||||
protected function isNotEmpty(Entity $entity, $field)
|
||||
{
|
||||
return $entity->has($field) && $entity->get($field) !== '' && $entity->get($field) !== null;
|
||||
}
|
||||
@@ -27,7 +27,7 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\TemplateHelpers;
|
||||
namespace Espo\Classes\TemplateHelpers;
|
||||
|
||||
class GoogleMaps
|
||||
{
|
||||
@@ -29,10 +29,12 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\NotFound;
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use \Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
|
||||
use Espo\Core\UpgradeManager;
|
||||
use Espo\Core\Utils\AdminNotificationManager;
|
||||
use Espo\Core\Utils\SystemRequirements;
|
||||
|
||||
class Admin extends \Espo\Core\Controllers\Base
|
||||
{
|
||||
@@ -48,15 +50,17 @@ class Admin extends \Espo\Core\Controllers\Base
|
||||
if (!$request->isPost()) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
$result = $this->getContainer()->get('dataManager')->rebuild();
|
||||
|
||||
return $result;
|
||||
$this->getContainer()->get('dataManager')->rebuild();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function postActionClearCache($params)
|
||||
{
|
||||
$result = $this->getContainer()->get('dataManager')->clearCache();
|
||||
return $result;
|
||||
$this->getContainer()->get('dataManager')->clearCache();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function actionJobs()
|
||||
@@ -73,15 +77,16 @@ class Admin extends \Espo\Core\Controllers\Base
|
||||
throw new Forbidden();
|
||||
}
|
||||
}
|
||||
$upgradeManager = new \Espo\Core\UpgradeManager($this->getContainer());
|
||||
|
||||
$upgradeManager = new UpgradeManager($this->getContainer());
|
||||
|
||||
$upgradeId = $upgradeManager->upload($data);
|
||||
$manifest = $upgradeManager->getManifest();
|
||||
|
||||
return array(
|
||||
return [
|
||||
'id' => $upgradeId,
|
||||
'version' => $manifest['version'],
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
public function postActionRunUpgrade($params, $data)
|
||||
@@ -92,7 +97,7 @@ class Admin extends \Espo\Core\Controllers\Base
|
||||
}
|
||||
}
|
||||
|
||||
$upgradeManager = new \Espo\Core\UpgradeManager($this->getContainer());
|
||||
$upgradeManager = new UpgradeManager($this->getContainer());
|
||||
$upgradeManager->install(get_object_vars($data));
|
||||
|
||||
return true;
|
||||
@@ -105,13 +110,15 @@ class Admin extends \Espo\Core\Controllers\Base
|
||||
|
||||
public function actionAdminNotificationList($params)
|
||||
{
|
||||
$adminNotificationManager = new \Espo\Core\Utils\AdminNotificationManager($this->getContainer());
|
||||
$adminNotificationManager = new AdminNotificationManager($this->getContainer());
|
||||
|
||||
return $adminNotificationManager->getNotificationList();
|
||||
}
|
||||
|
||||
public function actionSystemRequirementList($params)
|
||||
{
|
||||
$systemRequirementManager = new \Espo\Core\Utils\SystemRequirements($this->getContainer());
|
||||
$systemRequirementManager = new SystemRequirements($this->getContainer());
|
||||
|
||||
return $systemRequirementManager->getAllRequiredList();
|
||||
}
|
||||
}
|
||||
|
||||
38
application/Espo/Controllers/ApiIndex.php
Normal file
38
application/Espo/Controllers/ApiIndex.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2020 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
class ApiIndex {
|
||||
|
||||
public function getActionIndex()
|
||||
{
|
||||
return "EspoCRM REST API";
|
||||
}
|
||||
}
|
||||
@@ -29,22 +29,33 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
|
||||
class App extends \Espo\Core\Controllers\Base
|
||||
use Espo\Core\Authentication\Authentication;
|
||||
use Espo\Core\Di;
|
||||
use Espo\Core\Api\Request;
|
||||
|
||||
use StdClass;
|
||||
|
||||
class App implements
|
||||
|
||||
Di\ServiceFactoryAware,
|
||||
Di\InjectableFactoryAware
|
||||
{
|
||||
use Di\ServiceFactorySetter;
|
||||
use Di\InjectableFactorySetter;
|
||||
|
||||
public function actionUser()
|
||||
{
|
||||
return $this->getServiceFactory()->create('App')->getUserData();
|
||||
return $this->serviceFactory->create('App')->getUserData();
|
||||
}
|
||||
|
||||
public function postActionDestroyAuthToken($params, $data)
|
||||
public function postActionDestroyAuthToken(array $params, StdClass $data, Request $request)
|
||||
{
|
||||
if (empty($data->token)) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
$auth = new \Espo\Core\Utils\Auth($this->getContainer());
|
||||
return $auth->destroyAuthToken($data->token);
|
||||
$auth = $this->injectableFactory->create(Authentication::class);
|
||||
return $auth->destroyAuthToken($data->token, $request);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,8 +29,8 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use \Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
|
||||
class Attachment extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
@@ -66,10 +66,10 @@ class Attachment extends \Espo\Core\Controllers\Record
|
||||
|
||||
$fileData = $this->getRecordService()->getFileData($id);
|
||||
|
||||
$response->headers->set('Content-Type', $fileData->type);
|
||||
$response->headers->set('Content-Disposition', 'Content-Disposition: attachment; filename="'.$fileData->name.'"');
|
||||
$response->setHeader('Content-Type', $fileData->type);
|
||||
$response->setHeader('Content-Disposition', 'Content-Disposition: attachment; filename="'.$fileData->name.'"');
|
||||
if ($fileData->size) {
|
||||
$response->headers->set('Content-Length', $fileData->size);
|
||||
$response->setHeader('Content-Length', $fileData->size);
|
||||
}
|
||||
|
||||
return $fileData->contents;
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
|
||||
class AuthLogRecord extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
|
||||
class AuthToken extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
|
||||
@@ -29,31 +29,34 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Exceptions\{
|
||||
Forbidden,
|
||||
BadRequest,
|
||||
};
|
||||
|
||||
class CurrencyRate extends \Espo\Core\Controllers\Base
|
||||
use Espo\Core\{
|
||||
ServiceFactory,
|
||||
Api\Request,
|
||||
};
|
||||
|
||||
class CurrencyRate
|
||||
{
|
||||
protected function checkControllerAccess()
|
||||
protected $serviceFactory;
|
||||
|
||||
public function __construct(ServiceFactory $serviceFactory)
|
||||
{
|
||||
if (!$this->getAcl()->check('Currency')) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
$this->serviceFactory = $serviceFactory;
|
||||
}
|
||||
|
||||
public function getActionIndex()
|
||||
public function getActionIndex(Request $request)
|
||||
{
|
||||
if ($this->getAcl()->getLevel('Currency', 'read') !== 'yes') throw new Forbidden();
|
||||
|
||||
return $this->getService('CurrencyRate')->get();
|
||||
return $this->serviceFactory->create('CurrencyRate')->get();
|
||||
}
|
||||
|
||||
public function putActionUpdate($params, $data)
|
||||
public function putActionUpdate(Request $request)
|
||||
{
|
||||
if ($this->getAcl()->getLevel('Currency', 'edit') !== 'yes') throw new Forbidden();
|
||||
$data = $request->getParsedBody();
|
||||
|
||||
if (empty($data) || !is_object($data)) throw new BadRequest();
|
||||
|
||||
return $this->getService('CurrencyRate')->set($data);
|
||||
return $this->serviceFactory->create('CurrencyRate')->set($data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,8 +29,8 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use \Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
|
||||
class DashboardTemplate extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
|
||||
@@ -29,27 +29,33 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\NotFound;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
|
||||
class DataPrivacy extends \Espo\Core\Controllers\Base
|
||||
use Espo\Core\ServiceFactory;
|
||||
use Espo\Core\Acl;
|
||||
|
||||
class DataPrivacy
|
||||
{
|
||||
protected function checkControllerAccess()
|
||||
protected $serviceFactory;
|
||||
protected $acl;
|
||||
|
||||
public function __construct(ServiceFactory $serviceFactory, Acl $acl)
|
||||
{
|
||||
if ($this->getAcl()->get('dataPrivacyPermission') === 'no') {
|
||||
$this->serviceFactory = $serviceFactory;
|
||||
$this->acl = $acl;
|
||||
|
||||
if ($this->acl->get('dataPrivacyPermission') === 'no') {
|
||||
throw new Forbidden();
|
||||
}
|
||||
}
|
||||
|
||||
public function postActionErase($params, $data)
|
||||
public function postActionErase($params, \StdClass $data)
|
||||
{
|
||||
if (empty($data->entityType) || empty($data->id) || empty($data->fieldList) || !is_array($data->fieldList)) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
return $this->getServiceFactory()->create('DataPrivacy')->erase($data->entityType, $data->id, $data->fieldList);
|
||||
return $this->serviceFactory->create('DataPrivacy')->erase($data->entityType, $data->id, $data->fieldList);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -29,10 +29,9 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\BadRequest;
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
use \Espo\Core\Exceptions\NotFound;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\NotFound;
|
||||
|
||||
class Email extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
@@ -230,4 +229,15 @@ class Email extends \Espo\Core\Controllers\Record
|
||||
}
|
||||
return $this->getRecordService()->moveToFolderByIdList($idList, $data->folderId);
|
||||
}
|
||||
|
||||
public function getActionGetInsertFieldData($params, $data, $request)
|
||||
{
|
||||
if (!$this->getAcl()->checkScope('Email', 'create')) throw new Forbidden();
|
||||
|
||||
return $this->getServiceFactory()->create('EmailTemplate')->getInsertFieldData([
|
||||
'parentId' => $request->get('parentId'),
|
||||
'parentType' => $request->get('parentType'),
|
||||
'to' => $request->get('to'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,8 +29,7 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use \Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\Error;
|
||||
|
||||
class EmailAccount extends \Espo\Core\Controllers\Record
|
||||
@@ -40,7 +39,7 @@ class EmailAccount extends \Espo\Core\Controllers\Record
|
||||
return $this->getRecordService()->getFolders([
|
||||
'host' => $data->host ?? null,
|
||||
'port' => $data->port ?? null,
|
||||
'ssl' => $data->ssl ?? false,
|
||||
'security' => $data->security ?? null,
|
||||
'username' => $data->username ?? null,
|
||||
'password' => $data->password ?? null,
|
||||
'id' => $data->id ?? null,
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
|
||||
class EmailAddress extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
|
||||
@@ -33,4 +33,3 @@ class EmailFilter extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
|
||||
class EmailFolder extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Exceptions\Error;
|
||||
|
||||
class EmailTemplate extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
@@ -49,6 +49,4 @@ class EmailTemplate extends \Espo\Core\Controllers\Record
|
||||
'relatedId' => $request->get('relatedId')
|
||||
), true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -29,26 +29,49 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\BadRequest;
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
use Espo\{
|
||||
Entities\User,
|
||||
Tools\EntityManager\EntityManager as EntityManagerTool,
|
||||
};
|
||||
|
||||
class EntityManager extends \Espo\Core\Controllers\Base
|
||||
use Espo\Core\{
|
||||
Exceptions\Error,
|
||||
Exceptions\Forbidden,
|
||||
Exceptions\BadRequest,
|
||||
Api\Request,
|
||||
DataManager,
|
||||
Utils\Config,
|
||||
};
|
||||
|
||||
class EntityManager
|
||||
{
|
||||
protected $user;
|
||||
protected $dataManager;
|
||||
protected $config;
|
||||
protected $entityManagerTool;
|
||||
|
||||
public function __construct(User $user, DataManager $dataManager, Config $config, EntityManagerTool $entityManagerTool)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->dataManager = $dataManager;
|
||||
$this->config = $config;
|
||||
$this->entityManagerTool = $entityManagerTool;
|
||||
|
||||
$this->checkControllerAccess();
|
||||
}
|
||||
|
||||
protected function checkControllerAccess()
|
||||
{
|
||||
if (!$this->getUser()->isAdmin()) {
|
||||
if (!$this->user->isAdmin()) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
}
|
||||
|
||||
public function actionCreateEntity($params, $data, $request)
|
||||
public function postActionCreateEntity(Request $request)
|
||||
{
|
||||
$data = get_object_vars($data);
|
||||
$data = $request->getParsedBody();
|
||||
|
||||
if (!$request->isPost()) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
$data = get_object_vars($data);
|
||||
|
||||
if (empty($data['name']) || empty($data['type'])) {
|
||||
throw new BadRequest();
|
||||
@@ -101,18 +124,18 @@ class EntityManager extends \Espo\Core\Controllers\Base
|
||||
$params['kanbanStatusIgnoreList'] = $data['kanbanStatusIgnoreList'];
|
||||
}
|
||||
|
||||
$result = $this->getContainer()->get('entityManagerUtil')->create($name, $type, $params);
|
||||
$result = $this->entityManagerTool->create($name, $type, $params);
|
||||
|
||||
if ($result) {
|
||||
$tabList = $this->getConfig()->get('tabList', []);
|
||||
$tabList = $this->config->get('tabList', []);
|
||||
|
||||
if (!in_array($name, $tabList)) {
|
||||
$tabList[] = $name;
|
||||
$this->getConfig()->set('tabList', $tabList);
|
||||
$this->getConfig()->save();
|
||||
$this->config->set('tabList', $tabList);
|
||||
$this->config->save();
|
||||
}
|
||||
|
||||
$this->getContainer()->get('dataManager')->rebuild();
|
||||
$this->dataManager->rebuild();
|
||||
} else {
|
||||
throw new Error();
|
||||
}
|
||||
@@ -120,23 +143,23 @@ class EntityManager extends \Espo\Core\Controllers\Base
|
||||
return true;
|
||||
}
|
||||
|
||||
public function actionUpdateEntity($params, $data, $request)
|
||||
public function postActionUpdateEntity(Request $request)
|
||||
{
|
||||
$data = $request->getParsedBody();
|
||||
|
||||
$data = get_object_vars($data);
|
||||
|
||||
if (!$request->isPost()) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
if (empty($data['name'])) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
$name = $data['name'];
|
||||
$name = filter_var($name, \FILTER_SANITIZE_STRING);
|
||||
|
||||
$result = $this->getContainer()->get('entityManagerUtil')->update($name, $data);
|
||||
$result = $this->entityManagerTool->update($name, $data);
|
||||
|
||||
if ($result) {
|
||||
$this->getContainer()->get('dataManager')->clearCache();
|
||||
$this->dataManager->clearCache();
|
||||
} else {
|
||||
throw new Error();
|
||||
}
|
||||
@@ -144,32 +167,31 @@ class EntityManager extends \Espo\Core\Controllers\Base
|
||||
return true;
|
||||
}
|
||||
|
||||
public function actionRemoveEntity($params, $data, $request)
|
||||
public function postActionRemoveEntity(Request $request)
|
||||
{
|
||||
$data = get_object_vars($data);
|
||||
$data = $request->getParsedBody();
|
||||
|
||||
if (!$request->isPost()) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
$data = get_object_vars($data);
|
||||
|
||||
if (empty($data['name'])) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
$name = $data['name'];
|
||||
$name = filter_var($name, \FILTER_SANITIZE_STRING);
|
||||
|
||||
$result = $this->getContainer()->get('entityManagerUtil')->delete($name);
|
||||
$result = $this->entityManagerTool->delete($name);
|
||||
|
||||
if ($result) {
|
||||
$tabList = $this->getConfig()->get('tabList', []);
|
||||
$tabList = $this->config->get('tabList', []);
|
||||
if (($key = array_search($name, $tabList)) !== false) {
|
||||
unset($tabList[$key]);
|
||||
$tabList = array_values($tabList);
|
||||
}
|
||||
$this->getConfig()->set('tabList', $tabList);
|
||||
$this->getConfig()->save();
|
||||
$this->config->set('tabList', $tabList);
|
||||
$this->config->save();
|
||||
|
||||
$this->getContainer()->get('dataManager')->clearCache();
|
||||
$this->dataManager->clearCache();
|
||||
} else {
|
||||
throw new Error();
|
||||
}
|
||||
@@ -177,13 +199,11 @@ class EntityManager extends \Espo\Core\Controllers\Base
|
||||
return true;
|
||||
}
|
||||
|
||||
public function actionCreateLink($params, $data, $request)
|
||||
public function postActionCreateLink(Request $request)
|
||||
{
|
||||
$data = get_object_vars($data);
|
||||
$data = $request->getParsedBody();
|
||||
|
||||
if (!$request->isPost()) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
$data = get_object_vars($data);
|
||||
|
||||
$paramList = [
|
||||
'entity',
|
||||
@@ -234,10 +254,10 @@ class EntityManager extends \Espo\Core\Controllers\Base
|
||||
$params['foreignLinkEntityTypeList'] = $data['foreignLinkEntityTypeList'];
|
||||
}
|
||||
|
||||
$result = $this->getContainer()->get('entityManagerUtil')->createLink($params);
|
||||
$result = $this->entityManagerTool->createLink($params);
|
||||
|
||||
if ($result) {
|
||||
$this->getContainer()->get('dataManager')->rebuild();
|
||||
$this->dataManager->rebuild();
|
||||
} else {
|
||||
throw new Error();
|
||||
}
|
||||
@@ -245,13 +265,11 @@ class EntityManager extends \Espo\Core\Controllers\Base
|
||||
return true;
|
||||
}
|
||||
|
||||
public function actionUpdateLink($params, $data, $request)
|
||||
public function postActionUpdateLink(Request $request)
|
||||
{
|
||||
$data = get_object_vars($data);
|
||||
$data = $request->getParsedBody();
|
||||
|
||||
if (!$request->isPost()) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
$data = get_object_vars($data);
|
||||
|
||||
$paramList = [
|
||||
'entity',
|
||||
@@ -295,10 +313,10 @@ class EntityManager extends \Espo\Core\Controllers\Base
|
||||
$params['foreignLinkEntityTypeList'] = $data['foreignLinkEntityTypeList'];
|
||||
}
|
||||
|
||||
$result = $this->getContainer()->get('entityManagerUtil')->updateLink($params);
|
||||
$result = $this->entityManagerTool->updateLink($params);
|
||||
|
||||
if ($result) {
|
||||
$this->getContainer()->get('dataManager')->clearCache();
|
||||
$this->dataManager->clearCache();
|
||||
} else {
|
||||
throw new Error();
|
||||
}
|
||||
@@ -306,27 +324,27 @@ class EntityManager extends \Espo\Core\Controllers\Base
|
||||
return true;
|
||||
}
|
||||
|
||||
public function actionRemoveLink($params, $data, $request)
|
||||
public function postActionRemoveLink(Request $request)
|
||||
{
|
||||
$data = get_object_vars($data);
|
||||
$data = $request->getParsedBody();
|
||||
|
||||
if (!$request->isPost()) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
$data = get_object_vars($data);
|
||||
|
||||
$paramList = [
|
||||
'entity',
|
||||
'link',
|
||||
];
|
||||
$d = array();
|
||||
|
||||
$d = [];
|
||||
|
||||
foreach ($paramList as $item) {
|
||||
$d[$item] = filter_var($data[$item], \FILTER_SANITIZE_STRING);
|
||||
}
|
||||
|
||||
$result = $this->getContainer()->get('entityManagerUtil')->deleteLink($d);
|
||||
$result = $this->entityManagerTool->deleteLink($d);
|
||||
|
||||
if ($result) {
|
||||
$this->getContainer()->get('dataManager')->clearCache();
|
||||
$this->dataManager->clearCache();
|
||||
} else {
|
||||
throw new Error();
|
||||
}
|
||||
@@ -334,32 +352,37 @@ class EntityManager extends \Espo\Core\Controllers\Base
|
||||
return true;
|
||||
}
|
||||
|
||||
public function postActionFormula($params, $data, $request)
|
||||
public function postActionFormula(Request $request)
|
||||
{
|
||||
$data = $request->getParsedBody();
|
||||
|
||||
if (empty($data->scope)) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
if (!property_exists($data, 'data')) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
$formulaData = get_object_vars($data->data);
|
||||
|
||||
$this->getContainer()->get('entityManagerUtil')->setFormulaData($data->scope, $formulaData);
|
||||
$this->entityManagerTool->setFormulaData($data->scope, $formulaData);
|
||||
|
||||
$this->getContainer()->get('dataManager')->clearCache();
|
||||
$this->dataManager->clearCache();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function postActionResetToDefault($params, $data, $request)
|
||||
public function postActionResetToDefault(Request $request)
|
||||
{
|
||||
$data = $request->getParsedBody();
|
||||
|
||||
if (empty($data->scope)) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
$this->getContainer()->get('entityManagerUtil')->resetToDefaults($data->scope);
|
||||
$this->getContainer()->get('dataManager')->clearCache();
|
||||
$this->entityManagerTool->resetToDefaults($data->scope);
|
||||
$this->dataManager->clearCache();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -29,8 +29,7 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
|
||||
class Extension extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
|
||||
@@ -29,9 +29,9 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use \Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
|
||||
class ExternalAccount extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
|
||||
@@ -29,27 +29,52 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\NotFound;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\{
|
||||
Entities\User,
|
||||
Tools\FieldManager\FieldManager as FieldManagerTool,
|
||||
};
|
||||
|
||||
class FieldManager extends \Espo\Core\Controllers\Base
|
||||
use Espo\Core\{
|
||||
Exceptions\Error,
|
||||
Exceptions\Forbidden,
|
||||
Exceptions\NotFound,
|
||||
Exceptions\BadRequest,
|
||||
Api\Request,
|
||||
DataManager,
|
||||
};
|
||||
|
||||
class FieldManager
|
||||
{
|
||||
protected $user;
|
||||
protected $dataManager;
|
||||
protected $fieldManagerTool;
|
||||
|
||||
public function __construct(User $user, DataManager $dataManager, FieldManagerTool $fieldManagerTool)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->dataManager = $dataManager;
|
||||
$this->fieldManagerTool = $fieldManagerTool;
|
||||
|
||||
$this->checkControllerAccess();
|
||||
}
|
||||
|
||||
protected function checkControllerAccess()
|
||||
{
|
||||
if (!$this->getUser()->isAdmin()) {
|
||||
if (!$this->user->isAdmin()) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
}
|
||||
|
||||
public function actionRead($params, $data)
|
||||
public function getActionRead(Request $request)
|
||||
{
|
||||
if (empty($params['scope']) || empty($params['name'])) {
|
||||
$scope = $request->getRouteParam('scope');
|
||||
$name = $request->getRouteParam('name');
|
||||
|
||||
if (!$scope || !$name) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
$data = $this->getContainer()->get('fieldManager')->read($params['scope'], $params['name']);
|
||||
$data = $this->fieldManagerTool->read($scope, $name);
|
||||
|
||||
if (!isset($data)) {
|
||||
throw new BadRequest();
|
||||
@@ -58,72 +83,91 @@ class FieldManager extends \Espo\Core\Controllers\Base
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function postActionCreate($params, $data)
|
||||
public function postActionCreate(Request $request)
|
||||
{
|
||||
if (empty($params['scope']) || empty($data->name)) {
|
||||
$data = $request->getParsedBody();
|
||||
|
||||
$scope = $request->getRouteParam('scope');
|
||||
$name = $data->name ?? null;
|
||||
|
||||
if (!$scope || !$name) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
$fieldManager = $this->getContainer()->get('fieldManager');
|
||||
$fieldManager->create($params['scope'], $data->name, get_object_vars($data));
|
||||
$fieldManagerTool = $this->fieldManagerTool;
|
||||
|
||||
$fieldManagerTool->create($scope, $name, get_object_vars($data));
|
||||
|
||||
try {
|
||||
$this->getContainer()->get('dataManager')->rebuild($params['scope']);
|
||||
} catch (Error $e) {
|
||||
$fieldManager->delete($params['scope'], $data->name);
|
||||
$this->dataManager->rebuild([$scope]);
|
||||
}
|
||||
catch (Error $e) {
|
||||
$fieldManagerTool->delete($scope, $data->name);
|
||||
|
||||
throw new Error($e->getMessage());
|
||||
}
|
||||
|
||||
return $fieldManager->read($params['scope'], $data->name);
|
||||
return $fieldManagerTool->read($scope, $data->name);
|
||||
}
|
||||
|
||||
public function patchActionUpdate($params, $data)
|
||||
public function patchActionUpdate(Request $request)
|
||||
{
|
||||
return $this->putActionUpdate($params, $data);
|
||||
return $this->putActionUpdate($request);
|
||||
}
|
||||
|
||||
public function putActionUpdate($params, $data)
|
||||
public function putActionUpdate(Request $request)
|
||||
{
|
||||
if (empty($params['scope']) || empty($params['name'])) {
|
||||
$data = $request->getParsedBody();
|
||||
|
||||
$scope = $request->getRouteParam('scope');
|
||||
$name = $request->getRouteParam('name');
|
||||
|
||||
if (!$scope || !$name) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
$fieldManager = $this->getContainer()->get('fieldManager');
|
||||
$fieldManager->update($params['scope'], $params['name'], get_object_vars($data));
|
||||
$fieldManagerTool = $this->fieldManagerTool;
|
||||
|
||||
if ($fieldManager->isChanged()) {
|
||||
$this->getContainer()->get('dataManager')->rebuild($params['scope']);
|
||||
$fieldManagerTool->update($scope, $name, get_object_vars($data));
|
||||
|
||||
if ($fieldManagerTool->isChanged()) {
|
||||
$this->dataManager->rebuild([$scope]);
|
||||
} else {
|
||||
$this->getContainer()->get('dataManager')->clearCache();
|
||||
$this->dataManager->clearCache();
|
||||
}
|
||||
|
||||
return $fieldManager->read($params['scope'], $params['name']);
|
||||
return $fieldManagerTool->read($scope, $name);
|
||||
}
|
||||
|
||||
public function deleteActionDelete($params, $data)
|
||||
public function deleteActionDelete(Request $request)
|
||||
{
|
||||
if (empty($params['scope']) || empty($params['name'])) {
|
||||
$scope = $request->getRouteParam('scope');
|
||||
$name = $request->getRouteParam('name');
|
||||
|
||||
if (!$scope || !$name) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
$result = $this->getContainer()->get('fieldManager')->delete($params['scope'], $params['name']);
|
||||
$result = $this->fieldManagerTool->delete($scope, $name);
|
||||
|
||||
$this->getContainer()->get('dataManager')->rebuildMetadata();
|
||||
$this->dataManager->rebuildMetadata();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function postActionResetToDefault($params, $data)
|
||||
public function postActionResetToDefault(Request $request)
|
||||
{
|
||||
$data = $request->getParsedBody();
|
||||
|
||||
if (empty($data->scope) || empty($data->name)) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
$this->getContainer()->get('fieldManager')->resetToDefault($data->scope, $data->name);
|
||||
$this->fieldManagerTool->resetToDefault($data->scope, $data->name);
|
||||
|
||||
$this->getContainer()->get('dataManager')->clearCache();
|
||||
$this->dataManager->clearCache();
|
||||
|
||||
$this->getContainer()->get('dataManager')->rebuildMetadata();
|
||||
$this->dataManager->rebuildMetadata();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -29,11 +29,17 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\Error,
|
||||
\Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\ServiceFactory;
|
||||
|
||||
class GlobalSearch extends \Espo\Core\Controllers\Base
|
||||
class GlobalSearch
|
||||
{
|
||||
protected $serviceFactory;
|
||||
|
||||
public function __construct(ServiceFactory $serviceFactory)
|
||||
{
|
||||
$this->serviceFactory = $serviceFactory;
|
||||
}
|
||||
|
||||
public function actionSearch($params, $data, $request)
|
||||
{
|
||||
$query = $request->get('q');
|
||||
@@ -41,7 +47,6 @@ class GlobalSearch extends \Espo\Core\Controllers\Base
|
||||
$offset = intval($request->get('offset'));
|
||||
$maxSize = intval($request->get('maxSize'));
|
||||
|
||||
return $this->getService('GlobalSearch')->find($query, $offset, $maxSize);
|
||||
return $this->serviceFactory->create('GlobalSearch')->find($query, $offset, $maxSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,17 +29,21 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
class I18n extends \Espo\Core\Controllers\Base
|
||||
use Espo\Core\ServiceFactory;
|
||||
|
||||
class I18n
|
||||
{
|
||||
protected $serviceFactory;
|
||||
|
||||
public function __construct(ServiceFactory $serviceFactory)
|
||||
{
|
||||
$this->serviceFactory = $serviceFactory;
|
||||
}
|
||||
|
||||
public function actionRead($params, $data, $request)
|
||||
{
|
||||
$default = $request->get('default') === 'true';
|
||||
|
||||
return $this->getServiceFactory()->create('Language')->getDataForFrontend($default);
|
||||
|
||||
/*if ($request->get('default')) {
|
||||
return $this->getContainer()->get('defaultLanguage')->getAll();
|
||||
}
|
||||
return $this->getContainer()->get('language')->getAll();*/
|
||||
return $this->serviceFactory->create('Language')->getDataForFrontend($default);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,9 +30,9 @@
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use Espo\Core\Utils as Utils;
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use \Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
|
||||
class Import extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
@@ -78,24 +78,13 @@ class Import extends \Espo\Core\Controllers\Record
|
||||
return $this->getContainer()->get('entityManager');
|
||||
}
|
||||
|
||||
public function actionUploadFile($params, $data, $request)
|
||||
public function postActionUploadFile($params, $data)
|
||||
{
|
||||
$contents = $data;
|
||||
|
||||
if (!$request->isPost()) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
$attachmentId = $this->getService('Import')->uploadFile($contents);
|
||||
|
||||
$attachment = $this->getEntityManager()->getEntity('Attachment');
|
||||
$attachment->set('type', 'text/csv');
|
||||
$attachment->set('role', 'Import File');
|
||||
$attachment->set('name', 'import-file.csv');
|
||||
$attachment->set('contents', $contents);
|
||||
$this->getEntityManager()->saveEntity($attachment);
|
||||
|
||||
return [
|
||||
'attachmentId' => $attachment->id
|
||||
];
|
||||
return ['attachmentId' => $attachmentId];
|
||||
}
|
||||
|
||||
public function actionRevert($params, $data, $request)
|
||||
@@ -106,7 +95,9 @@ class Import extends \Espo\Core\Controllers\Record
|
||||
if (!$request->isPost()) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
return $this->getService('Import')->revert($data->id);
|
||||
$this->getService('Import')->revert($data->id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function actionRemoveDuplicates($params, $data, $request)
|
||||
@@ -117,7 +108,9 @@ class Import extends \Espo\Core\Controllers\Record
|
||||
if (!$request->isPost()) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
return $this->getService('Import')->removeDuplicates($data->id);
|
||||
$this->getService('Import')->removeDuplicates($data->id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function actionCreate($params, $data, $request)
|
||||
@@ -190,6 +183,8 @@ class Import extends \Espo\Core\Controllers\Record
|
||||
'skipDuplicateChecking' => !empty($data->skipDuplicateChecking),
|
||||
'idleMode' => !empty($data->idleMode),
|
||||
'silentMode' => !empty($data->silentMode),
|
||||
'manualMode' => !empty($data->manualMode),
|
||||
'defaultFieldList' => $data->defaultFieldList ?? [],
|
||||
];
|
||||
|
||||
if (property_exists($data, 'updateBy')) {
|
||||
@@ -210,7 +205,9 @@ class Import extends \Espo\Core\Controllers\Record
|
||||
if (empty($data->id) || empty($data->entityType) || empty($data->entityId)) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
$this->getService('Import')->unmarkAsDuplicate($data->id, $data->entityType, $data->entityId);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,8 +29,7 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use \Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
|
||||
class InboundEmail extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
@@ -46,7 +45,7 @@ class InboundEmail extends \Espo\Core\Controllers\Record
|
||||
return $this->getRecordService()->getFolders([
|
||||
'host' => $data->host ?? null,
|
||||
'port' => $data->port ?? null,
|
||||
'ssl' => $data->ssl ?? false,
|
||||
'security' => $data->security ?? null,
|
||||
'username' => $data->username ?? null,
|
||||
'password' => $data->password ?? null,
|
||||
'id' => $data->id ?? null,
|
||||
|
||||
@@ -29,9 +29,8 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use \Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
|
||||
class Integration extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
@@ -82,4 +81,3 @@ class Integration extends \Espo\Core\Controllers\Record
|
||||
return $entity->toArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,8 +29,7 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
|
||||
class Job extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
|
||||
@@ -29,49 +29,69 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use Espo\Core\Utils as Utils;
|
||||
use \Espo\Core\Exceptions\NotFound;
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use \Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\{
|
||||
Exceptions\Forbidden,
|
||||
Exceptions\BadRequest,
|
||||
Api\Request,
|
||||
DataManager,
|
||||
};
|
||||
|
||||
class LabelManager extends \Espo\Core\Controllers\Base
|
||||
use Espo\{
|
||||
Tools\LabelManager\LabelManager as LabelManagerTool,
|
||||
Entities\User,
|
||||
};
|
||||
|
||||
class LabelManager
|
||||
{
|
||||
protected $user;
|
||||
protected $dataManager;
|
||||
protected $labelManagerTool;
|
||||
|
||||
public function __construct(User $user, DataManager $dataManager, LabelManagerTool $labelManagerTool)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->dataManager = $dataManager;
|
||||
$this->labelManagerTool = $labelManagerTool;
|
||||
|
||||
$this->checkControllerAccess();
|
||||
}
|
||||
|
||||
protected function checkControllerAccess()
|
||||
{
|
||||
if (!$this->getUser()->isAdmin()) {
|
||||
if (!$this->user->isAdmin()) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
}
|
||||
|
||||
public function postActionGetScopeList($params)
|
||||
public function postActionGetScopeList()
|
||||
{
|
||||
$labelManager = $this->getContainer()->get('injectableFactory')->createByClassName('\\Espo\\Core\\Utils\\LabelManager');
|
||||
|
||||
return $labelManager->getScopeList();
|
||||
return $this->labelManagerTool->getScopeList();
|
||||
}
|
||||
|
||||
public function postActionGetScopeData($params, $data, $request)
|
||||
public function postActionGetScopeData(Request $request)
|
||||
{
|
||||
$data = $request->getParsedBody();
|
||||
|
||||
if (empty($data->scope) || empty($data->language)) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
$labelManager = $this->getContainer()->get('injectableFactory')->createByClassName('\\Espo\\Core\\Utils\\LabelManager');
|
||||
return $labelManager->getScopeData($data->language, $data->scope);
|
||||
|
||||
return $this->labelManagerTool->getScopeData($data->language, $data->scope);
|
||||
}
|
||||
|
||||
public function postActionSaveLabels($params, $data)
|
||||
public function postActionSaveLabels(Request $request)
|
||||
{
|
||||
$data = $request->getParsedBody();
|
||||
|
||||
if (empty($data->scope) || empty($data->language) || !isset($data->labels)) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
$labels = get_object_vars($data->labels);
|
||||
|
||||
$labelManager = $this->getContainer()->get('injectableFactory')->createByClassName('\\Espo\\Core\\Utils\\LabelManager');
|
||||
$returnData = $labelManager->saveLabels($data->language, $data->scope, $labels);
|
||||
$returnData = $this->labelManagerTool->saveLabels($data->language, $data->scope, $labels);
|
||||
|
||||
$this->getContainer()->get('dataManager')->clearCache();
|
||||
$this->dataManager->clearCache();
|
||||
|
||||
return $returnData;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
|
||||
class LastViewed extends \Espo\Core\Controllers\Base
|
||||
{
|
||||
|
||||
@@ -47,6 +47,8 @@ class Layout extends \Espo\Core\Controllers\Base
|
||||
|
||||
public function putActionUpdate($params, $data, $request)
|
||||
{
|
||||
$data = json_decode($request->getBodyContents());
|
||||
|
||||
if (is_object($data)) $data = get_object_vars($data);
|
||||
|
||||
if (!$this->getUser()->isAdmin()) throw new Forbidden();
|
||||
|
||||
@@ -29,26 +29,36 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use \Espo\Core\Exceptions\BadRequest;
|
||||
use \Espo\Core\Exceptions\NotFound;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Exceptions\NotFound;
|
||||
|
||||
class LeadCapture extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
public function postActionLeadCapture($params, $data, $request, $response)
|
||||
{
|
||||
if (empty($params['apiKey'])) throw new BadRequest('No API key provided.');
|
||||
if (empty($data)) throw new BadRequest('No payload provided.');
|
||||
if (empty($params['apiKey'])) {
|
||||
throw new BadRequest('No API key provided.');
|
||||
}
|
||||
|
||||
if (empty($data)) {
|
||||
throw new BadRequest('No payload provided.');
|
||||
}
|
||||
|
||||
$allowOrigin = $this->getConfig()->get('leadCaptureAllowOrigin', '*');
|
||||
$response->headers->set('Access-Control-Allow-Origin', $allowOrigin);
|
||||
|
||||
return $this->getRecordService()->leadCapture($params['apiKey'], $data);
|
||||
$response->setHeader('Access-Control-Allow-Origin', $allowOrigin);
|
||||
|
||||
$this->getRecordService()->leadCapture($params['apiKey'], $data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function optionsActionLeadCapture($params, $data, $request, $response)
|
||||
{
|
||||
if (empty($params['apiKey'])) throw new BadRequest('No API key provided.');
|
||||
if (empty($params['apiKey'])) {
|
||||
throw new BadRequest('No API key provided.');
|
||||
}
|
||||
|
||||
if (!$this->getRecordService()->isApiKeyValid($params['apiKey'])) {
|
||||
throw new NotFound();
|
||||
@@ -56,23 +66,27 @@ class LeadCapture extends \Espo\Core\Controllers\Record
|
||||
|
||||
$allowOrigin = $this->getConfig()->get('leadCaptureAllowOrigin', '*');
|
||||
|
||||
$response->headers->set('Access-Control-Allow-Headers', 'Content-Type, Accept');
|
||||
$response->headers->set('Access-Control-Allow-Origin', $allowOrigin);
|
||||
$response->headers->set('Access-Control-Allow-Methods', 'POST');
|
||||
$response->setHeader('Access-Control-Allow-Headers', 'Content-Type, Accept');
|
||||
$response->setHeader('Access-Control-Allow-Origin', $allowOrigin);
|
||||
$response->setHeader('Access-Control-Allow-Methods', 'POST');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function postActionGenerateNewApiKey($params, $data, $request)
|
||||
{
|
||||
if (empty($data->id)) throw new BadRequest();
|
||||
if (empty($data->id)) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
return $this->getRecordService()->generateNewApiKeyForEntity($data->id)->getValueMap();
|
||||
}
|
||||
|
||||
public function getActionSmtpAccountDataList()
|
||||
{
|
||||
if (!$this->getUser()->isAdmin()) throw new Forbidden();
|
||||
if (!$this->getUser()->isAdmin()) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
return $this->getServiceFactory()->create('LeadCapture')->getSmtpAccountDataList();
|
||||
}
|
||||
|
||||
@@ -29,9 +29,6 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use \Espo\Core\Exceptions\BadRequest;
|
||||
|
||||
class LeadCaptureLogRecord extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
|
||||
|
||||
@@ -29,11 +29,10 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
|
||||
class Metadata extends \Espo\Core\Controllers\Base
|
||||
{
|
||||
|
||||
public function actionRead($params, $data)
|
||||
{
|
||||
return $this->getServiceFactory()->create('Metadata')->getDataForFrontend();
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Exceptions\Error;
|
||||
|
||||
class Notification extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
@@ -47,18 +47,18 @@ class Notification extends \Espo\Core\Controllers\Record
|
||||
$maxSize = self::MAX_SIZE_LIMIT;
|
||||
}
|
||||
|
||||
$params = array(
|
||||
$params = [
|
||||
'offset' => $offset,
|
||||
'maxSize' => $maxSize,
|
||||
'after' => $after
|
||||
);
|
||||
'after' => $after,
|
||||
];
|
||||
|
||||
$result = $this->getService('Notification')->getList($userId, $params);
|
||||
$recordCollection = $this->getService('Notification')->getList($userId, $params);
|
||||
|
||||
return array(
|
||||
'total' => $result['total'],
|
||||
'list' => $result['collection']->toArray()
|
||||
);
|
||||
return (object) [
|
||||
'total' => $recordCollection->getTotal(),
|
||||
'list' => $recordCollection->getValueMapList(),
|
||||
];
|
||||
}
|
||||
|
||||
public function actionNotReadCount()
|
||||
|
||||
@@ -29,9 +29,9 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use \Espo\Core\Exceptions\BadRequest;
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Exceptions\Error;
|
||||
|
||||
class Pdf extends \Espo\Core\Controllers\Base
|
||||
{
|
||||
|
||||
@@ -29,8 +29,6 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
|
||||
class PhoneNumber extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
|
||||
class Portal extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
|
||||
@@ -29,10 +29,10 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use \Espo\Core\Exceptions\BadRequest;
|
||||
use \Espo\Core\Exceptions\NotFound;
|
||||
use Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Exceptions\NotFound;
|
||||
|
||||
class Preferences extends \Espo\Core\Controllers\Base
|
||||
{
|
||||
|
||||
@@ -32,6 +32,11 @@ namespace Espo\Controllers;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
|
||||
use Espo\Core\{
|
||||
Authentication\LDAP\Utils as LDAPUtils,
|
||||
Authentication\LDAP\Client as LDAPClient,
|
||||
};
|
||||
|
||||
class Settings extends \Espo\Core\Controllers\Base
|
||||
{
|
||||
protected function getConfigData()
|
||||
@@ -86,11 +91,14 @@ class Settings extends \Espo\Core\Controllers\Base
|
||||
|
||||
$data = get_object_vars($data);
|
||||
|
||||
$ldapUtils = new \Espo\Core\Utils\Authentication\LDAP\Utils();
|
||||
$ldapUtils = new LDAPUtils();
|
||||
|
||||
$options = $ldapUtils->normalizeOptions($data);
|
||||
|
||||
$ldapClient = new \Espo\Core\Utils\Authentication\LDAP\Client($options);
|
||||
$ldapClient->bind(); //an exception if no connection
|
||||
$ldapClient = new LDAPClient($options);
|
||||
|
||||
//an exception if no connection
|
||||
$ldapClient->bind();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -29,14 +29,26 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Exceptions\Error;
|
||||
|
||||
class Stream extends \Espo\Core\Controllers\Base
|
||||
use Espo\Core\ServiceFactory;
|
||||
use Espo\Core\Utils\Config;
|
||||
|
||||
class Stream
|
||||
{
|
||||
const MAX_SIZE_LIMIT = 200;
|
||||
|
||||
public static $defaultAction = 'list';
|
||||
|
||||
protected $serviceFactory;
|
||||
protected $config;
|
||||
|
||||
public function __construct(ServiceFactory $serviceFactory, Config $config)
|
||||
{
|
||||
$this->serviceFactory = $serviceFactory;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function actionList($params, $data, $request)
|
||||
{
|
||||
$scope = $params['scope'];
|
||||
@@ -48,9 +60,7 @@ class Stream extends \Espo\Core\Controllers\Base
|
||||
$filter = $request->get('filter');
|
||||
$skipOwn = $request->get('skipOwn') === 'true';
|
||||
|
||||
$service = $this->getService('Stream');
|
||||
|
||||
$maxSizeLimit = $this->getConfig()->get('recordListMaxSizeLimit', self::MAX_SIZE_LIMIT);
|
||||
$maxSizeLimit = $this->config->get('recordListMaxSizeLimit', self::MAX_SIZE_LIMIT);
|
||||
if (empty($maxSize)) {
|
||||
$maxSize = $maxSizeLimit;
|
||||
}
|
||||
@@ -58,7 +68,7 @@ class Stream extends \Espo\Core\Controllers\Base
|
||||
throw new Forbidden("Max size should should not exceed " . $maxSizeLimit . ". Use offset and limit.");
|
||||
}
|
||||
|
||||
$result = $service->find($scope, $id, [
|
||||
$result = $this->serviceFactory->create('Stream')->find($scope, $id, [
|
||||
'offset' => $offset,
|
||||
'maxSize' => $maxSize,
|
||||
'after' => $after,
|
||||
@@ -83,9 +93,7 @@ class Stream extends \Espo\Core\Controllers\Base
|
||||
|
||||
$where = $request->get('where');
|
||||
|
||||
$service = $this->getService('Stream');
|
||||
|
||||
$maxSizeLimit = $this->getConfig()->get('recordListMaxSizeLimit', self::MAX_SIZE_LIMIT);
|
||||
$maxSizeLimit = $this->config->get('recordListMaxSizeLimit', self::MAX_SIZE_LIMIT);
|
||||
if (empty($maxSize)) {
|
||||
$maxSize = $maxSizeLimit;
|
||||
}
|
||||
@@ -93,7 +101,7 @@ class Stream extends \Espo\Core\Controllers\Base
|
||||
throw new Forbidden("Max size should should not exceed " . $maxSizeLimit . ". Use offset and limit.");
|
||||
}
|
||||
|
||||
$result = $service->find($scope, $id, [
|
||||
$result = $this->serviceFactory->create('Stream')->find($scope, $id, [
|
||||
'offset' => $offset,
|
||||
'maxSize' => $maxSize,
|
||||
'after' => $after,
|
||||
|
||||
@@ -29,8 +29,6 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
|
||||
class Template extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
|
||||
|
||||
@@ -29,17 +29,26 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use Espo\Core\Utils as Utils;
|
||||
use \Espo\Core\Exceptions\NotFound;
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use \Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
|
||||
class TemplateManager extends \Espo\Core\Controllers\Base
|
||||
use Espo\Core\Utils\Metadata;
|
||||
use Espo\Core\Utils\TemplateFileManager;
|
||||
use Espo\Core\ApplicationState;
|
||||
|
||||
class TemplateManager
|
||||
{
|
||||
protected function checkControllerAccess()
|
||||
protected $metadata;
|
||||
protected $templateFileManager;
|
||||
protected $applicationState;
|
||||
|
||||
public function __construct(Metadata $metadata, TemplateFileManager $templateFileManager, ApplicationState $applicationState)
|
||||
{
|
||||
if (!$this->getUser()->isAdmin()) {
|
||||
$this->metadata = $metadata;
|
||||
$this->templateFileManager = $templateFileManager;
|
||||
$this->applicationState = $applicationState;
|
||||
|
||||
if (!$this->applicationState->isAdmin()) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
}
|
||||
@@ -50,10 +59,10 @@ class TemplateManager extends \Espo\Core\Controllers\Base
|
||||
if (empty($name)) throw new BadRequest();
|
||||
$scope = $request->get('scope');
|
||||
$module = null;
|
||||
$module = $this->getMetadata()->get(['app', 'templates', $name, 'module']);
|
||||
$hasSubject = !$this->getMetadata()->get(['app', 'templates', $name, 'noSubject']);
|
||||
$module = $this->metadata->get(['app', 'templates', $name, 'module']);
|
||||
$hasSubject = !$this->metadata->get(['app', 'templates', $name, 'noSubject']);
|
||||
|
||||
$templateFileManager = $this->getContainer()->get('templateFileManager');
|
||||
$templateFileManager = $this->templateFileManager;
|
||||
|
||||
$returnData = (object) [];
|
||||
$returnData->body = $templateFileManager->getTemplate($name, 'body', $scope, $module);
|
||||
@@ -75,7 +84,7 @@ class TemplateManager extends \Espo\Core\Controllers\Base
|
||||
$scope = $data->scope;
|
||||
}
|
||||
|
||||
$templateFileManager = $this->getContainer()->get('templateFileManager');
|
||||
$templateFileManager = $this->templateFileManager;
|
||||
|
||||
if (isset($data->subject)) {
|
||||
$templateFileManager->saveTemplate($data->name, 'subject', $data->subject, $scope);
|
||||
@@ -99,10 +108,10 @@ class TemplateManager extends \Espo\Core\Controllers\Base
|
||||
}
|
||||
|
||||
$module = null;
|
||||
$module = $this->getMetadata()->get(['app', 'templates', $data->name, 'module']);
|
||||
$hasSubject = !$this->getMetadata()->get(['app', 'templates', $data->name, 'noSubject']);
|
||||
$module = $this->metadata->get(['app', 'templates', $data->name, 'module']);
|
||||
$hasSubject = !$this->metadata->get(['app', 'templates', $data->name, 'noSubject']);
|
||||
|
||||
$templateFileManager = $this->getContainer()->get('templateFileManager');
|
||||
$templateFileManager = $this->templateFileManager;
|
||||
|
||||
if ($hasSubject) {
|
||||
$templateFileManager->resetTemplate($data->name, 'subject', $scope);
|
||||
|
||||
@@ -29,10 +29,10 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
use \Espo\Core\Exceptions\NotFound;
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use \Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Exceptions\NotFound;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
|
||||
class User extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
|
||||
@@ -29,10 +29,8 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use \Espo\Core\Exceptions\NotFound;
|
||||
use \Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
|
||||
class UserSecurity extends \Espo\Core\Controllers\Base
|
||||
{
|
||||
|
||||
@@ -29,8 +29,7 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
|
||||
class Webhook extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
|
||||
@@ -29,9 +29,12 @@
|
||||
|
||||
namespace Espo\Core;
|
||||
|
||||
use \Espo\ORM\Entity;
|
||||
use \Espo\Entities\User;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Entities\User;
|
||||
|
||||
/**
|
||||
* A wrapper for AclManager. To check access for a current user.
|
||||
*/
|
||||
class Acl
|
||||
{
|
||||
private $user;
|
||||
@@ -54,102 +57,154 @@ class Acl
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
public function getMap()
|
||||
public function getMap() : \StdClass
|
||||
{
|
||||
return $this->getAclManager()->getMap($this->getUser());
|
||||
}
|
||||
|
||||
public function getLevel($scope, $action)
|
||||
/**
|
||||
* Get an access level for a specific scope and action.
|
||||
*/
|
||||
public function getLevel(string $scope, string $action) : string
|
||||
{
|
||||
return $this->getAclManager()->getLevel($this->getUser(), $scope, $action);
|
||||
}
|
||||
|
||||
public function get($permission)
|
||||
/**
|
||||
* Get a permission. E.g. 'assignment' permission.
|
||||
*/
|
||||
public function get(string $permission) : ?string
|
||||
{
|
||||
return $this->getAclManager()->get($this->getUser(), $permission);
|
||||
}
|
||||
|
||||
public function checkReadNo($scope)
|
||||
/**
|
||||
* Whether there's no 'read' access for a specific scope.
|
||||
*/
|
||||
public function checkReadNo(string $scope) : bool
|
||||
{
|
||||
return $this->getAclManager()->checkReadNo($this->getUser(), $scope);
|
||||
}
|
||||
|
||||
public function checkReadOnlyTeam($scope)
|
||||
/**
|
||||
* Whether 'read' access is set to 'team' for a specific scope.
|
||||
*/
|
||||
public function checkReadOnlyTeam(string $scope) : bool
|
||||
{
|
||||
return $this->getAclManager()->checkReadOnlyTeam($this->getUser(), $scope);
|
||||
}
|
||||
|
||||
public function checkReadOnlyOwn($scope)
|
||||
/**
|
||||
* Whether 'read' access is set to 'own' for a specific scope.
|
||||
*/
|
||||
public function checkReadOnlyOwn(string $scope) : bool
|
||||
{
|
||||
return $this->getAclManager()->checkReadOnlyOwn($this->getUser(), $scope);
|
||||
}
|
||||
|
||||
public function check($subject, $action = null)
|
||||
/**
|
||||
* Check a scope or entity. If $action is omitted, it will check whether a scope level is set to 'enabled'.
|
||||
*/
|
||||
public function check($subject, ?string $action = null) : bool
|
||||
{
|
||||
return $this->getAclManager()->check($this->getUser(), $subject, $action);
|
||||
}
|
||||
|
||||
public function checkScope($scope, $action = null)
|
||||
/**
|
||||
* Check access to scope. If $action is omitted, it will check whether a scope level is set to 'enabled'.
|
||||
*/
|
||||
public function checkScope(string $scope, ?string $action = null) : bool
|
||||
{
|
||||
return $this->getAclManager()->checkScope($this->getUser(), $scope, $action);
|
||||
}
|
||||
|
||||
public function checkEntity(Entity $entity, $action = 'read')
|
||||
/**
|
||||
* Check access to a specific entity (record).
|
||||
*/
|
||||
public function checkEntity(Entity $entity, string $action = 'read') : bool
|
||||
{
|
||||
return $this->getAclManager()->checkEntity($this->getUser(), $entity, $action);
|
||||
}
|
||||
|
||||
public function checkUser($permission, User $entity)
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public function checkUser(string $permission, User $entity) : bool
|
||||
{
|
||||
return $this->getAclManager()->checkUser($this->getUser(), $permission, $entity);
|
||||
}
|
||||
|
||||
public function checkIsOwner(Entity $entity)
|
||||
/**
|
||||
* Whether a user is owned of an entity (record). Usually 'assignedUser' field is used for checking.
|
||||
*/
|
||||
public function checkIsOwner(Entity $entity) : bool
|
||||
{
|
||||
return $this->getAclManager()->checkIsOwner($this->getUser(), $entity);
|
||||
}
|
||||
|
||||
public function checkInTeam(Entity $entity)
|
||||
/**
|
||||
* Whether a user team list overlaps with teams set in an entity.
|
||||
*/
|
||||
public function checkInTeam(Entity $entity) : bool
|
||||
{
|
||||
return $this->getAclManager()->checkInTeam($this->getUser(), $entity);
|
||||
}
|
||||
|
||||
public function getScopeForbiddenAttributeList($scope, $action = 'read', $thresholdLevel = 'no')
|
||||
/**
|
||||
* Get attributes forbidden for a user.
|
||||
*/
|
||||
public function getScopeForbiddenAttributeList(string $scope, string $action = 'read', string $thresholdLevel = 'no') : array
|
||||
{
|
||||
return $this->getAclManager()->getScopeForbiddenAttributeList($this->getUser(), $scope, $action, $thresholdLevel);
|
||||
}
|
||||
|
||||
public function getScopeForbiddenFieldList($scope, $action = 'read', $thresholdLevel = 'no')
|
||||
/**
|
||||
* Get fields forbidden for a user.
|
||||
*/
|
||||
public function getScopeForbiddenFieldList(string $scope, string $action = 'read', string $thresholdLevel = 'no') : array
|
||||
{
|
||||
return $this->getAclManager()->getScopeForbiddenFieldList($this->getUser(), $scope, $action, $thresholdLevel);
|
||||
}
|
||||
|
||||
public function getScopeForbiddenLinkList($scope, $action = 'read', $thresholdLevel = 'no')
|
||||
/**
|
||||
* Get links forbidden for a user.
|
||||
*/
|
||||
public function getScopeForbiddenLinkList(string $scope, string $action = 'read', string $thresholdLevel = 'no') : array
|
||||
{
|
||||
return $this->getAclManager()->getScopeForbiddenLinkList($this->getUser(), $scope, $action, $thresholdLevel);
|
||||
}
|
||||
|
||||
public function checkUserPermission($target, $permissionType = 'userPermission')
|
||||
/**
|
||||
* Whether a user has an access to another user over a specific permission.
|
||||
*
|
||||
* @param $target User|string User entity or user ID.
|
||||
*/
|
||||
public function checkUserPermission($target, string $permissionType = 'user') : bool
|
||||
{
|
||||
return $this->getAclManager()->checkUserPermission($this->getUser(), $target, $permissionType);
|
||||
}
|
||||
|
||||
public function checkAssignmentPermission($target)
|
||||
/**
|
||||
* Whether a user can assign to another user.
|
||||
*
|
||||
* @param $target User|string User entity or user ID.
|
||||
*/
|
||||
public function checkAssignmentPermission($target) : bool
|
||||
{
|
||||
return $this->getAclManager()->checkAssignmentPermission($this->getUser(), $target);
|
||||
}
|
||||
|
||||
public function getScopeRestrictedFieldList($scope, $type)
|
||||
public function getScopeRestrictedFieldList(string $scope, $type) : array
|
||||
{
|
||||
return $this->getAclManager()->getScopeRestrictedFieldList($scope, $type);
|
||||
}
|
||||
|
||||
public function getScopeRestrictedAttributeList($scope, $type)
|
||||
public function getScopeRestrictedAttributeList(string $scope, $type) : array
|
||||
{
|
||||
return $this->getAclManager()->getScopeRestrictedAttributeList($scope, $type);
|
||||
}
|
||||
|
||||
public function getScopeRestrictedLinkList($scope, $type)
|
||||
public function getScopeRestrictedLinkList(string $scope, $type) : array
|
||||
{
|
||||
return $this->getAclManager()->getScopeRestrictedLinkList($scope, $type);
|
||||
}
|
||||
|
||||
296
application/Espo/Core/Acl/Acl.php
Normal file
296
application/Espo/Core/Acl/Acl.php
Normal file
@@ -0,0 +1,296 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2020 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Acl;
|
||||
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
use Espo\Core\{
|
||||
ORM\EntityManager,
|
||||
AclManager,
|
||||
Utils\Config,
|
||||
};
|
||||
|
||||
/**
|
||||
* An implementation for access checking for entities. Can be overriden in `Acl` namespece.
|
||||
*/
|
||||
class Acl implements ScopeAcl, EntityAcl
|
||||
{
|
||||
protected $scope;
|
||||
|
||||
protected $ownerUserIdAttribute = null;
|
||||
|
||||
protected $allowDeleteCreatedThresholdPeriod = '24 hours';
|
||||
|
||||
protected $entityManager;
|
||||
protected $aclManager;
|
||||
protected $config;
|
||||
|
||||
public function __construct(string $scope, EntityManager $entityManager, AclManager $aclManager, Config $config)
|
||||
{
|
||||
$this->scope = $scope;
|
||||
|
||||
$this->entityManager = $entityManager;
|
||||
$this->aclManager = $aclManager;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
protected function getConfig()
|
||||
{
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
protected function getEntityManager()
|
||||
{
|
||||
return $this->entityManager;
|
||||
}
|
||||
|
||||
protected function getAclManager()
|
||||
{
|
||||
return $this->aclManager;
|
||||
}
|
||||
|
||||
public function checkReadOnlyTeam(User $user, $data)
|
||||
{
|
||||
if (empty($data) || !is_object($data) || !isset($data->read)) {
|
||||
return false;
|
||||
}
|
||||
return $data->read === 'team';
|
||||
}
|
||||
|
||||
public function checkReadNo(User $user, $data)
|
||||
{
|
||||
if (empty($data) || !is_object($data) || !isset($data->read)) {
|
||||
return false;
|
||||
}
|
||||
return $data->read === 'no';
|
||||
}
|
||||
|
||||
public function checkReadOnlyOwn(User $user, $data)
|
||||
{
|
||||
if (empty($data) || !is_object($data) || !isset($data->read)) {
|
||||
return false;
|
||||
}
|
||||
return $data->read === 'own';
|
||||
}
|
||||
|
||||
public function checkEntity(User $user, Entity $entity, $data, $action)
|
||||
{
|
||||
if ($user->isAdmin()) {
|
||||
return true;
|
||||
}
|
||||
return $this->checkScope($user, $data, $action, $entity);
|
||||
}
|
||||
|
||||
public function checkScope(User $user, $data, $action = null, Entity $entity = null, $entityAccessData = [])
|
||||
{
|
||||
if ($user->isAdmin()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_null($data)) {
|
||||
return false;
|
||||
}
|
||||
if ($data === false) {
|
||||
return false;
|
||||
}
|
||||
if ($data === true) {
|
||||
return true;
|
||||
}
|
||||
if (is_string($data)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$isOwner = null;
|
||||
if (isset($entityAccessData['isOwner'])) {
|
||||
$isOwner = $entityAccessData['isOwner'];
|
||||
}
|
||||
$inTeam = null;
|
||||
if (isset($entityAccessData['inTeam'])) {
|
||||
$inTeam = $entityAccessData['inTeam'];
|
||||
}
|
||||
|
||||
if (is_null($action)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!isset($data->$action)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$value = $data->$action;
|
||||
|
||||
if ($value === 'all' || $value === 'yes' || $value === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!$value || $value === 'no') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_null($isOwner)) {
|
||||
if ($entity) {
|
||||
$isOwner = $this->checkIsOwner($user, $entity);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($isOwner) {
|
||||
if ($value === 'own' || $value === 'team') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (is_null($inTeam) && $entity) {
|
||||
$inTeam = $this->checkInTeam($user, $entity);
|
||||
}
|
||||
|
||||
if ($inTeam) {
|
||||
if ($value === 'team') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function checkIsOwner(User $user, Entity $entity)
|
||||
{
|
||||
if ($entity->hasAttribute('assignedUserId')) {
|
||||
if ($entity->has('assignedUserId')) {
|
||||
if ($user->id === $entity->get('assignedUserId')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if ($entity->hasAttribute('createdById')) {
|
||||
if ($entity->has('createdById')) {
|
||||
if ($user->id === $entity->get('createdById')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($entity->hasLinkMultipleField('assignedUsers')) {
|
||||
if ($entity->hasLinkMultipleId('assignedUsers', $user->id)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function checkInTeam(User $user, Entity $entity)
|
||||
{
|
||||
$userTeamIdList = $user->getLinkMultipleIdList('teams');
|
||||
|
||||
if (!$entity->hasRelation('teams') || !$entity->hasAttribute('teamsIds')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$entityTeamIdList = $entity->getLinkMultipleIdList('teams');
|
||||
|
||||
if (empty($entityTeamIdList)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($userTeamIdList as $id) {
|
||||
if (in_array($id, $entityTeamIdList)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function checkEntityDelete(User $user, Entity $entity, $data)
|
||||
{
|
||||
if ($user->isAdmin()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->checkEntity($user, $entity, $data, 'delete')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_object($data)) {
|
||||
if ($data->edit !== 'no' || $data->create !== 'no') {
|
||||
if (
|
||||
$this->config->get('aclAllowDeleteCreated')
|
||||
&&
|
||||
$entity->has('createdById') && $entity->get('createdById') == $user->id
|
||||
) {
|
||||
$isDeletedAllowed = false;
|
||||
if (!$entity->has('assignedUserId')) {
|
||||
$isDeletedAllowed = true;
|
||||
} else {
|
||||
if (!$entity->get('assignedUserId')) {
|
||||
$isDeletedAllowed = true;
|
||||
} else if ($entity->get('assignedUserId') == $entity->get('createdById')) {
|
||||
$isDeletedAllowed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($isDeletedAllowed) {
|
||||
$createdAt = $entity->get('createdAt');
|
||||
if ($createdAt) {
|
||||
$deleteThresholdPeriod = $this->config->get(
|
||||
'aclAllowDeleteCreatedThresholdPeriod',
|
||||
$this->allowDeleteCreatedThresholdPeriod
|
||||
);
|
||||
if (\Espo\Core\Utils\DateTime::isAfterThreshold($createdAt, $deleteThresholdPeriod)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getOwnerUserIdAttribute(Entity $entity)
|
||||
{
|
||||
if ($this->ownerUserIdAttribute) {
|
||||
return $this->ownerUserIdAttribute;
|
||||
}
|
||||
|
||||
if ($entity->hasLinkMultipleField('assignedUsers')) {
|
||||
return 'assignedUsersIds';
|
||||
}
|
||||
|
||||
if ($entity->hasAttribute('assignedUserId')) {
|
||||
return 'assignedUserId';
|
||||
}
|
||||
|
||||
if ($entity->hasAttribute('createdById')) {
|
||||
return 'createdById';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,39 +29,39 @@
|
||||
|
||||
namespace Espo\Core\Acl;
|
||||
|
||||
use \Espo\Core\Interfaces\Injectable;
|
||||
use Espo\Core\Interfaces\Injectable;
|
||||
|
||||
use \Espo\Entities\User;
|
||||
use \Espo\ORM\Entity;
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
class Base implements Injectable
|
||||
use Espo\Core\{
|
||||
ORM\EntityManager,
|
||||
AclManager,
|
||||
Utils\Config,
|
||||
};
|
||||
|
||||
/** @deprecated */
|
||||
class Base extends Acl implements Injectable
|
||||
{
|
||||
protected $dependencyList = [
|
||||
'config',
|
||||
'entityManager',
|
||||
'aclManager',
|
||||
];
|
||||
protected $dependencyList = [];
|
||||
|
||||
protected $dependencies = []; // for backward compatibility
|
||||
|
||||
protected $scope;
|
||||
protected $dependencies = [];
|
||||
|
||||
protected $injections = [];
|
||||
|
||||
protected $ownerUserIdAttribute = null;
|
||||
public function __construct(string $scope, EntityManager $entityManager, AclManager $aclManager, Config $config)
|
||||
{
|
||||
$this->scope = $scope;
|
||||
parent::__construct($scope, $entityManager, $aclManager, $config);
|
||||
|
||||
protected $allowDeleteCreatedThresholdPeriod = '24 hours';
|
||||
$this->init();
|
||||
}
|
||||
|
||||
public function inject($name, $object)
|
||||
{
|
||||
$this->injections[$name] = $object;
|
||||
}
|
||||
|
||||
public function __construct($scope)
|
||||
{
|
||||
$this->init();
|
||||
$this->scope = $scope;
|
||||
}
|
||||
|
||||
protected function init()
|
||||
{
|
||||
@@ -69,7 +69,7 @@ class Base implements Injectable
|
||||
|
||||
protected function getInjection($name)
|
||||
{
|
||||
return $this->injections[$name];
|
||||
return $this->injections[$name] ?? $this->$name ?? null;
|
||||
}
|
||||
|
||||
protected function addDependencyList(array $list)
|
||||
@@ -88,233 +88,4 @@ class Base implements Injectable
|
||||
{
|
||||
return array_merge($this->dependencyList, $this->dependencies);
|
||||
}
|
||||
|
||||
protected function getConfig()
|
||||
{
|
||||
return $this->getInjection('config');
|
||||
}
|
||||
|
||||
protected function getEntityManager()
|
||||
{
|
||||
return $this->getInjection('entityManager');
|
||||
}
|
||||
|
||||
protected function getAclManager()
|
||||
{
|
||||
return $this->getInjection('aclManager');
|
||||
}
|
||||
|
||||
public function checkReadOnlyTeam(User $user, $data)
|
||||
{
|
||||
if (empty($data) || !is_object($data) || !isset($data->read)) {
|
||||
return false;
|
||||
}
|
||||
return $data->read === 'team';
|
||||
}
|
||||
|
||||
public function checkReadNo(User $user, $data)
|
||||
{
|
||||
if (empty($data) || !is_object($data) || !isset($data->read)) {
|
||||
return false;
|
||||
}
|
||||
return $data->read === 'no';
|
||||
}
|
||||
|
||||
public function checkReadOnlyOwn(User $user, $data)
|
||||
{
|
||||
if (empty($data) || !is_object($data) || !isset($data->read)) {
|
||||
return false;
|
||||
}
|
||||
return $data->read === 'own';
|
||||
}
|
||||
|
||||
public function checkEntity(User $user, Entity $entity, $data, $action)
|
||||
{
|
||||
if ($user->isAdmin()) {
|
||||
return true;
|
||||
}
|
||||
return $this->checkScope($user, $data, $action, $entity);
|
||||
}
|
||||
|
||||
public function checkScope(User $user, $data, $action = null, Entity $entity = null, $entityAccessData = array())
|
||||
{
|
||||
if ($user->isAdmin()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_null($data)) {
|
||||
return false;
|
||||
}
|
||||
if ($data === false) {
|
||||
return false;
|
||||
}
|
||||
if ($data === true) {
|
||||
return true;
|
||||
}
|
||||
if (is_string($data)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$isOwner = null;
|
||||
if (isset($entityAccessData['isOwner'])) {
|
||||
$isOwner = $entityAccessData['isOwner'];
|
||||
}
|
||||
$inTeam = null;
|
||||
if (isset($entityAccessData['inTeam'])) {
|
||||
$inTeam = $entityAccessData['inTeam'];
|
||||
}
|
||||
|
||||
if (is_null($action)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!isset($data->$action)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$value = $data->$action;
|
||||
|
||||
if ($value === 'all' || $value === 'yes' || $value === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!$value || $value === 'no') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_null($isOwner)) {
|
||||
if ($entity) {
|
||||
$isOwner = $this->checkIsOwner($user, $entity);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($isOwner) {
|
||||
if ($value === 'own' || $value === 'team') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (is_null($inTeam) && $entity) {
|
||||
$inTeam = $this->checkInTeam($user, $entity);
|
||||
}
|
||||
|
||||
if ($inTeam) {
|
||||
if ($value === 'team') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function checkIsOwner(User $user, Entity $entity)
|
||||
{
|
||||
if ($entity->hasAttribute('assignedUserId')) {
|
||||
if ($entity->has('assignedUserId')) {
|
||||
if ($user->id === $entity->get('assignedUserId')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if ($entity->hasAttribute('createdById')) {
|
||||
if ($entity->has('createdById')) {
|
||||
if ($user->id === $entity->get('createdById')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($entity->hasLinkMultipleField('assignedUsers')) {
|
||||
if ($entity->hasLinkMultipleId('assignedUsers', $user->id)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function checkInTeam(User $user, Entity $entity)
|
||||
{
|
||||
$userTeamIdList = $user->getLinkMultipleIdList('teams');
|
||||
|
||||
if (!$entity->hasRelation('teams') || !$entity->hasAttribute('teamsIds')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$entityTeamIdList = $entity->getLinkMultipleIdList('teams');
|
||||
|
||||
if (empty($entityTeamIdList)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($userTeamIdList as $id) {
|
||||
if (in_array($id, $entityTeamIdList)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function checkEntityDelete(User $user, Entity $entity, $data)
|
||||
{
|
||||
if ($user->isAdmin()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->checkEntity($user, $entity, $data, 'delete')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_object($data)) {
|
||||
if ($data->edit !== 'no' || $data->create !== 'no') {
|
||||
if (
|
||||
$this->getConfig()->get('aclAllowDeleteCreated')
|
||||
&&
|
||||
$entity->has('createdById') && $entity->get('createdById') == $user->id
|
||||
) {
|
||||
$isDeletedAllowed = false;
|
||||
if (!$entity->has('assignedUserId')) {
|
||||
$isDeletedAllowed = true;
|
||||
} else {
|
||||
if (!$entity->get('assignedUserId')) {
|
||||
$isDeletedAllowed = true;
|
||||
} else if ($entity->get('assignedUserId') == $entity->get('createdById')) {
|
||||
$isDeletedAllowed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($isDeletedAllowed) {
|
||||
$createdAt = $entity->get('createdAt');
|
||||
if ($createdAt) {
|
||||
$deleteThresholdPeriod = $this->getConfig()->get('aclAllowDeleteCreatedThresholdPeriod', $this->allowDeleteCreatedThresholdPeriod);
|
||||
if (\Espo\Core\Utils\DateTime::isAfterThreshold($createdAt, $deleteThresholdPeriod)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getOwnerUserIdAttribute(Entity $entity)
|
||||
{
|
||||
if ($this->ownerUserIdAttribute) {
|
||||
return $this->ownerUserIdAttribute;
|
||||
}
|
||||
|
||||
if ($entity->hasLinkMultipleField('assignedUsers')) {
|
||||
return 'assignedUsersIds';
|
||||
}
|
||||
|
||||
if ($entity->hasAttribute('assignedUserId')) {
|
||||
return 'assignedUserId';
|
||||
}
|
||||
|
||||
if ($entity->hasAttribute('createdById')) {
|
||||
return 'createdById';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
41
application/Espo/Core/Acl/EntityAcl.php
Normal file
41
application/Espo/Core/Acl/EntityAcl.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2020 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Acl;
|
||||
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
/**
|
||||
* @todo Add methods.
|
||||
*/
|
||||
interface EntityAcl
|
||||
{
|
||||
|
||||
}
|
||||
@@ -29,6 +29,17 @@
|
||||
|
||||
namespace Espo\Core\Acl;
|
||||
|
||||
use Espo\Core\{
|
||||
Utils\Metadata,
|
||||
Utils\DataCache,
|
||||
Utils\FieldUtil,
|
||||
};
|
||||
|
||||
use StdClass;
|
||||
|
||||
/**
|
||||
* Lists of restricted fields can be obtained from here. Restricted fields are specified in metadata > entityAcl.
|
||||
*/
|
||||
class GlobalRestricton
|
||||
{
|
||||
protected $fieldTypeList = [
|
||||
@@ -47,36 +58,32 @@ class GlobalRestricton
|
||||
'nonAdminReadOnly' // read-only for non-admin users
|
||||
];
|
||||
|
||||
protected $cacheFilePath = 'data/cache/application/entityAcl.php';
|
||||
|
||||
private $metadata;
|
||||
|
||||
private $fileManager;
|
||||
|
||||
private $fieldManagerUtil;
|
||||
|
||||
private $data;
|
||||
|
||||
protected $cacheKey = 'entityAcl';
|
||||
|
||||
private $metadata;
|
||||
private $dataCache;
|
||||
private $fieldUtil;
|
||||
|
||||
public function __construct(
|
||||
\Espo\Core\Utils\Metadata $metadata,
|
||||
\Espo\Core\Utils\File\Manager $fileManager,
|
||||
\Espo\Core\Utils\FieldManagerUtil $fieldManagerUtil,
|
||||
bool $useCache = true
|
||||
)
|
||||
{
|
||||
Metadata $metadata, DataCache $dataCache, FieldUtil $fieldUtil, bool $useCache = true
|
||||
) {
|
||||
$this->metadata = $metadata;
|
||||
$this->fileManager = $fileManager;
|
||||
$this->fieldManagerUtil = $fieldManagerUtil;
|
||||
$this->dataCache = $dataCache;
|
||||
$this->fieldUtil = $fieldUtil;
|
||||
|
||||
$isFromCache = false;
|
||||
|
||||
if ($useCache) {
|
||||
if (file_exists($this->cacheFilePath)) {
|
||||
$this->data = include($this->cacheFilePath);
|
||||
if ($this->dataCache->has($this->cacheKey)) {
|
||||
$this->data = $this->dataCache->get($this->cacheKey);
|
||||
|
||||
$isFromCache = true;
|
||||
|
||||
if (!($this->data instanceof \StdClass)) {
|
||||
if (! $this->data instanceof StdClass) {
|
||||
$GLOBALS['log']->error("ACL GlobalRestricton: Bad data fetched from cache.");
|
||||
|
||||
$this->data = null;
|
||||
}
|
||||
}
|
||||
@@ -95,25 +102,25 @@ class GlobalRestricton
|
||||
|
||||
protected function storeCacheFile()
|
||||
{
|
||||
$this->getFileManager()->putPhpContents($this->cacheFilePath, $this->data, true);
|
||||
$this->dataCache->store($this->cacheKey, $this->data, true);
|
||||
}
|
||||
|
||||
protected function buildData()
|
||||
{
|
||||
$scopeList = array_keys($this->getMetadata()->get(['entityDefs'], []));
|
||||
$scopeList = array_keys($this->metadata->get(['entityDefs'], []));
|
||||
|
||||
$data = (object) [];
|
||||
|
||||
foreach ($scopeList as $scope) {
|
||||
$fieldList = array_keys($this->getMetadata()->get(['entityDefs', $scope, 'fields'], []));
|
||||
$linkList = array_keys($this->getMetadata()->get(['entityDefs', $scope, 'links'], []));
|
||||
$fieldList = array_keys($this->metadata->get(['entityDefs', $scope, 'fields'], []));
|
||||
$linkList = array_keys($this->metadata->get(['entityDefs', $scope, 'links'], []));
|
||||
|
||||
$isNotEmpty = false;
|
||||
|
||||
$scopeData = (object) [
|
||||
'fields' => (object) [],
|
||||
'attributes' => (object) [],
|
||||
'links' => (object) []
|
||||
'links' => (object) [],
|
||||
];
|
||||
|
||||
foreach ($this->fieldTypeList as $type) {
|
||||
@@ -121,10 +128,13 @@ class GlobalRestricton
|
||||
$resultAttributeList = [];
|
||||
|
||||
foreach ($fieldList as $field) {
|
||||
if ($this->getMetadata()->get(['entityAcl', $scope, 'fields', $field, $type])) {
|
||||
if ($this->metadata->get(['entityAcl', $scope, 'fields', $field, $type])) {
|
||||
$isNotEmpty = true;
|
||||
|
||||
$resultFieldList[] = $field;
|
||||
$fieldAttributeList = $this->getFieldManagerUtil()->getAttributeList($scope, $field);
|
||||
|
||||
$fieldAttributeList = $this->fieldUtil->getAttributeList($scope, $field);
|
||||
|
||||
foreach ($fieldAttributeList as $attribute) {
|
||||
$resultAttributeList[] = $attribute;
|
||||
}
|
||||
@@ -136,9 +146,11 @@ class GlobalRestricton
|
||||
}
|
||||
foreach ($this->linkTypeList as $type) {
|
||||
$resultLinkList = [];
|
||||
|
||||
foreach ($linkList as $link) {
|
||||
if ($this->getMetadata()->get(['entityAcl', $scope, 'links', $link, $type])) {
|
||||
if ($this->metadata->get(['entityAcl', $scope, 'links', $link, $type])) {
|
||||
$isNotEmpty = true;
|
||||
|
||||
$resultLinkList[] = $link;
|
||||
}
|
||||
}
|
||||
@@ -153,44 +165,52 @@ class GlobalRestricton
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
protected function getMetadata()
|
||||
public function getScopeRestrictedFieldList(string $scope, string $type) : array
|
||||
{
|
||||
return $this->metadata;
|
||||
}
|
||||
if (!property_exists($this->data, $scope)) {
|
||||
return [];
|
||||
}
|
||||
if (!property_exists($this->data->$scope, 'fields')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function getFileManager()
|
||||
{
|
||||
return $this->fileManager;
|
||||
}
|
||||
|
||||
protected function getFieldManagerUtil()
|
||||
{
|
||||
return $this->fieldManagerUtil;
|
||||
}
|
||||
|
||||
public function getScopeRestrictedFieldList($scope, $type)
|
||||
{
|
||||
if (!property_exists($this->data, $scope)) return [];
|
||||
if (!property_exists($this->data->$scope, 'fields')) return [];
|
||||
if (!property_exists($this->data->$scope->fields, $type)) return [];
|
||||
if (!property_exists($this->data->$scope->fields, $type)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->data->$scope->fields->$type;
|
||||
}
|
||||
|
||||
public function getScopeRestrictedAttributeList($scope, $type)
|
||||
public function getScopeRestrictedAttributeList(string $scope, string $type) : array
|
||||
{
|
||||
if (!property_exists($this->data, $scope)) return [];
|
||||
if (!property_exists($this->data->$scope, 'attributes')) return [];
|
||||
if (!property_exists($this->data->$scope->attributes, $type)) return [];
|
||||
if (!property_exists($this->data, $scope)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!property_exists($this->data->$scope, 'attributes')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!property_exists($this->data->$scope->attributes, $type)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->data->$scope->attributes->$type;
|
||||
}
|
||||
|
||||
public function getScopeRestrictedLinkList($scope, $type)
|
||||
public function getScopeRestrictedLinkList(string $scope, string $type) : array
|
||||
{
|
||||
if (!property_exists($this->data, $scope)) return [];
|
||||
if (!property_exists($this->data->$scope, 'links')) return [];
|
||||
if (!property_exists($this->data->$scope->links, $type)) return [];
|
||||
if (!property_exists($this->data, $scope)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!property_exists($this->data->$scope, 'links')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!property_exists($this->data->$scope->links, $type)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->data->$scope->links->$type;
|
||||
}
|
||||
|
||||
41
application/Espo/Core/Acl/ScopeAcl.php
Normal file
41
application/Espo/Core/Acl/ScopeAcl.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2020 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Acl;
|
||||
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
/**
|
||||
* @todo Add methods.
|
||||
*/
|
||||
interface ScopeAcl
|
||||
{
|
||||
|
||||
}
|
||||
@@ -29,16 +29,26 @@
|
||||
|
||||
namespace Espo\Core\Acl;
|
||||
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Exceptions\Error;
|
||||
|
||||
use \Espo\ORM\Entity;
|
||||
use \Espo\Entities\User;
|
||||
use Espo\Entities\User;
|
||||
|
||||
use \Espo\Core\Utils\Config;
|
||||
use \Espo\Core\Utils\Metadata;
|
||||
use \Espo\Core\Utils\FieldManagerUtil;
|
||||
use \Espo\Core\Utils\File\Manager as FileManager;
|
||||
use Espo\Core\{
|
||||
ORM\EntityManager,
|
||||
ORM\Entity,
|
||||
Utils\Config,
|
||||
Utils\Metadata,
|
||||
Utils\FieldUtil,
|
||||
Utils\DataCache,
|
||||
};
|
||||
|
||||
use StdClass;
|
||||
use Traversable;
|
||||
|
||||
/**
|
||||
* A table is generated for each user. Multiple roles are merged into a single table.
|
||||
* This table is used for access checking.
|
||||
*/
|
||||
class Table
|
||||
{
|
||||
protected $type = 'acl';
|
||||
@@ -47,7 +57,7 @@ class Table
|
||||
|
||||
private $data = null;
|
||||
|
||||
protected $cacheFilePath;
|
||||
protected $cacheKey;
|
||||
|
||||
protected $actionList = ['read', 'stream', 'edit', 'delete', 'create'];
|
||||
|
||||
@@ -59,26 +69,35 @@ class Table
|
||||
|
||||
protected $fieldLevelList = ['yes', 'no'];
|
||||
|
||||
protected $valuePermissionHighestLevels = array();
|
||||
protected $valuePermissionHighestLevels = [];
|
||||
|
||||
protected $valuePermissionList = [];
|
||||
|
||||
private $fileManager;
|
||||
protected $forbiddenAttributesCache = [];
|
||||
|
||||
private $metadata;
|
||||
|
||||
private $fieldManager;
|
||||
|
||||
protected $forbiddenAttributesCache = array();
|
||||
|
||||
protected $forbiddenFieldsCache = array();
|
||||
protected $forbiddenFieldsCache = [];
|
||||
|
||||
protected $isStrictModeForced = false;
|
||||
|
||||
protected $isStrictMode = false;
|
||||
|
||||
public function __construct(User $user, Config $config = null, FileManager $fileManager = null, Metadata $metadata = null, FieldManagerUtil $fieldManager = null)
|
||||
{
|
||||
protected $entityManager;
|
||||
protected $user;
|
||||
protected $config;
|
||||
protected $metadata;
|
||||
protected $fieldUtil;
|
||||
protected $dataCache;
|
||||
|
||||
public function __construct(
|
||||
EntityManager $entityManager,
|
||||
User $user,
|
||||
Config $config = null,
|
||||
Metadata $metadata = null,
|
||||
FieldUtil $fieldUtil = null,
|
||||
DataCache $dataCache
|
||||
) {
|
||||
$this->entityManager = $entityManager;
|
||||
|
||||
$this->data = (object) [
|
||||
'table' => (object) [],
|
||||
'fieldTable' => (object) [],
|
||||
@@ -92,39 +111,34 @@ class Table
|
||||
}
|
||||
|
||||
$this->user = $user;
|
||||
|
||||
$this->metadata = $metadata;
|
||||
|
||||
if ($fieldManager) {
|
||||
$this->fieldManager = $fieldManager;
|
||||
}
|
||||
$this->fieldUtil = $fieldUtil;
|
||||
$this->dataCache = $dataCache;
|
||||
|
||||
if (!$this->user->isFetched()) {
|
||||
throw new Error('User must be fetched before ACL check.');
|
||||
}
|
||||
|
||||
if ($fileManager) {
|
||||
$this->fileManager = $fileManager;
|
||||
}
|
||||
$this->valuePermissionList = $this->metadata->get(['app', $this->type, 'valuePermissionList'], []);
|
||||
$this->valuePermissionHighestLevels = $this->metadata->get(['app', $this->type, 'valuePermissionHighestLevels'], array());
|
||||
$this->valuePermissionHighestLevels = $this->metadata->get(['app', $this->type, 'valuePermissionHighestLevels'], []);
|
||||
|
||||
$this->initCacheFilePath();
|
||||
$this->initCacheKey();
|
||||
|
||||
if ($config && $config->get('useCache') && file_exists($this->cacheFilePath)) {
|
||||
$cached = include $this->cacheFilePath;
|
||||
$this->data = $cached;
|
||||
} else {
|
||||
if ($config && $config->get('useCache') && $this->dataCache->has($this->cacheKey)) {
|
||||
$this->data = $this->dataCache->get($this->cacheKey);
|
||||
}
|
||||
else {
|
||||
$this->load();
|
||||
if ($config && $fileManager && $config->get('useCache')) {
|
||||
|
||||
if ($config && $config->get('useCache')) {
|
||||
$this->buildCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function initCacheFilePath()
|
||||
protected function initCacheKey()
|
||||
{
|
||||
$this->cacheFilePath = 'data/cache/application/acl/' . $this->getUser()->id . '.php';
|
||||
$this->cacheKey = 'acl/' . $this->getUser()->id;
|
||||
}
|
||||
|
||||
protected function getUser()
|
||||
@@ -137,12 +151,12 @@ class Table
|
||||
return $this->metadata;
|
||||
}
|
||||
|
||||
protected function getFieldManager()
|
||||
protected function getFieldUtil()
|
||||
{
|
||||
return $this->fieldManager;
|
||||
return $this->fieldUtil;
|
||||
}
|
||||
|
||||
public function getMap()
|
||||
public function getMap() : StdClass
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
@@ -151,16 +165,20 @@ class Table
|
||||
{
|
||||
if (isset($this->data->table->$scope)) {
|
||||
$data = $this->data->table->$scope;
|
||||
|
||||
if (is_string($data)) {
|
||||
$data = $this->getScopeData($data);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function get($permission)
|
||||
public function get(string $permission) : ?string
|
||||
{
|
||||
if ($permission == 'table') {
|
||||
return null;
|
||||
@@ -169,32 +187,36 @@ class Table
|
||||
if (isset($this->data->$permission)) {
|
||||
return $this->data->$permission;
|
||||
}
|
||||
|
||||
return 'no';
|
||||
}
|
||||
|
||||
public function getLevel($scope, $action)
|
||||
public function getLevel(string $scope, string $action) : string
|
||||
{
|
||||
if (isset($this->data->table->$scope)) {
|
||||
if (isset($this->data->table->$scope->$action)) {
|
||||
return $this->data->table->$scope->$action;
|
||||
}
|
||||
}
|
||||
|
||||
return 'no';
|
||||
}
|
||||
|
||||
public function getHighestLevel($scope, $action)
|
||||
public function getHighestLevel(string $scope, string $action) : string
|
||||
{
|
||||
if (in_array($action, $this->booleanActionList)) {
|
||||
return 'yes';
|
||||
} else {
|
||||
$level = $this->metadata->get(['scopes', $scope, $this->type . 'HighestLevel']);
|
||||
return $level ?? 'all';
|
||||
}
|
||||
|
||||
$level = $this->metadata->get(['scopes', $scope, $this->type . 'HighestLevel']);
|
||||
|
||||
return $level ?? 'all';
|
||||
}
|
||||
|
||||
private function load()
|
||||
{
|
||||
$valuePermissionLists = (object)[];
|
||||
$valuePermissionLists = (object) [];
|
||||
|
||||
foreach ($this->valuePermissionList as $permission) {
|
||||
$valuePermissionLists->$permission = [];
|
||||
}
|
||||
@@ -208,6 +230,7 @@ class Table
|
||||
foreach ($roleList as $role) {
|
||||
$aclTableList[] = $role->get('data');
|
||||
$fieldTableList[] = $role->get('fieldData');
|
||||
|
||||
foreach ($this->valuePermissionList as $permission) {
|
||||
$valuePermissionLists->{$permission}[] = $role->get($permission);
|
||||
}
|
||||
@@ -220,17 +243,20 @@ class Table
|
||||
$this->applyDisabled($aclTable, $fieldTable);
|
||||
$this->applyMandatory($aclTable, $fieldTable);
|
||||
$this->applyAdditional($aclTable, $fieldTable, $valuePermissionLists);
|
||||
$this->applyReadOnlyFields($fieldTable);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
$aclTable = (object) [];
|
||||
|
||||
foreach ($this->getScopeList() as $scope) {
|
||||
if ($this->metadata->get("scopes.{$scope}.{$this->type}") === 'boolean') {
|
||||
$aclTable->$scope = true;
|
||||
} else {
|
||||
if ($this->metadata->get("scopes.{$scope}.entity")) {
|
||||
$aclTable->$scope = (object) [];
|
||||
|
||||
foreach ($this->actionList as $action) {
|
||||
$aclTable->$scope->$action = 'all';
|
||||
|
||||
if (in_array($action, $this->booleanActionList)) {
|
||||
$aclTable->$scope->$action = 'yes';
|
||||
}
|
||||
@@ -256,11 +282,17 @@ class Table
|
||||
|
||||
if (!$this->getUser()->isAdmin()) {
|
||||
$permissionsDefaultsGroupName = 'permissionsDefaults';
|
||||
|
||||
if ($this->isStrictMode) {
|
||||
$permissionsDefaultsGroupName = 'permissionsStrictDefaults';
|
||||
}
|
||||
|
||||
foreach ($this->valuePermissionList as $permission) {
|
||||
$this->data->$permission = $this->mergeValueList($valuePermissionLists->$permission, $this->metadata->get(['app', $this->type, $permissionsDefaultsGroupName, $permission, 'yes']));
|
||||
$this->data->$permission = $this->mergeValueList(
|
||||
$valuePermissionLists->$permission,
|
||||
$this->metadata->get(['app', $this->type, $permissionsDefaultsGroupName, $permission, 'yes'])
|
||||
);
|
||||
|
||||
if ($this->metadata->get('app.'.$this->type.'.mandatory.' . $permission)) {
|
||||
$this->data->$permission = $this->metadata->get('app.'.$this->type.'.mandatory.' . $permission);
|
||||
}
|
||||
@@ -270,8 +302,10 @@ class Table
|
||||
foreach ($this->valuePermissionList as $permission) {
|
||||
if (isset($this->valuePermissionHighestLevels[$permission])) {
|
||||
$this->data->$permission = $this->valuePermissionHighestLevels[$permission];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->data->$permission = 'all';
|
||||
}
|
||||
}
|
||||
@@ -281,20 +315,34 @@ class Table
|
||||
{
|
||||
$roleList = [];
|
||||
|
||||
$userRoleList = $this->getUser()->get('roles');
|
||||
if (!(is_array($userRoleList) || $userRoleList instanceof \Traversable)) {
|
||||
$userRoleList = $this->entityManager
|
||||
->getRepository('User')
|
||||
->getRelation($this->getUser(), 'roles')
|
||||
->find();
|
||||
|
||||
if (! $userRoleList instanceof Traversable) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
foreach ($userRoleList as $role) {
|
||||
$roleList[] = $role;
|
||||
}
|
||||
|
||||
$teamList = $this->getUser()->get('teams');
|
||||
if (!(is_array($teamList) || $teamList instanceof \Traversable)) {
|
||||
$teamList = $this->entityManager
|
||||
->getRepository('User')
|
||||
->getRelation($this->getUser(), 'teams')
|
||||
->find();
|
||||
|
||||
if (! $teamList instanceof Traversable) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
foreach ($teamList as $team) {
|
||||
$teamRoleList = $team->get('roles');
|
||||
$teamRoleList = $this->entityManager
|
||||
->getRepository('Team')
|
||||
->getRelation($team, 'roles')
|
||||
->find();
|
||||
|
||||
foreach ($teamRoleList as $role) {
|
||||
$roleList[] = $role;
|
||||
}
|
||||
@@ -303,21 +351,27 @@ class Table
|
||||
return $roleList;
|
||||
}
|
||||
|
||||
public function getScopeForbiddenAttributeList($scope, $action = 'read', $thresholdLevel = 'no')
|
||||
public function getScopeForbiddenAttributeList(string $scope, string $action = 'read', string $thresholdLevel = 'no') : array
|
||||
{
|
||||
$key = $scope . '_'. $action . '_' . $thresholdLevel;
|
||||
|
||||
if (isset($this->forbiddenAttributesCache[$key])) {
|
||||
return $this->forbiddenAttributesCache[$key];
|
||||
}
|
||||
|
||||
$fieldTableQuickAccess = $this->data->fieldTableQuickAccess;
|
||||
|
||||
if (!isset($fieldTableQuickAccess->$scope) || !isset($fieldTableQuickAccess->$scope->attributes) || !isset($fieldTableQuickAccess->$scope->attributes->$action)) {
|
||||
if (
|
||||
!isset($fieldTableQuickAccess->$scope) || !isset($fieldTableQuickAccess->$scope->attributes) ||
|
||||
!isset($fieldTableQuickAccess->$scope->attributes->$action)
|
||||
) {
|
||||
$this->forbiddenAttributesCache[$key] = [];
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
$levelList = [];
|
||||
|
||||
foreach ($this->fieldLevelList as $level) {
|
||||
if (array_search($level, $this->fieldLevelList) >= array_search($thresholdLevel, $this->fieldLevelList)) {
|
||||
$levelList[] = $level;
|
||||
@@ -327,9 +381,15 @@ class Table
|
||||
$attributeList = [];
|
||||
|
||||
foreach ($levelList as $level) {
|
||||
if (!isset($fieldTableQuickAccess->$scope->attributes->$action->$level)) continue;
|
||||
if (!isset($fieldTableQuickAccess->$scope->attributes->$action->$level)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($fieldTableQuickAccess->$scope->attributes->$action->$level as $attribute) {
|
||||
if (in_array($attribute, $attributeList)) continue;
|
||||
if (in_array($attribute, $attributeList)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$attributeList[] = $attribute;
|
||||
}
|
||||
}
|
||||
@@ -339,7 +399,7 @@ class Table
|
||||
return $attributeList;
|
||||
}
|
||||
|
||||
public function getScopeForbiddenFieldList($scope, $action = 'read', $thresholdLevel = 'no')
|
||||
public function getScopeForbiddenFieldList(string $scope, string $action = 'read', string $thresholdLevel = 'no') : array
|
||||
{
|
||||
$key = $scope . '_'. $action . '_' . $thresholdLevel;
|
||||
if (isset($this->forbiddenFieldsCache[$key])) {
|
||||
@@ -348,12 +408,17 @@ class Table
|
||||
|
||||
$fieldTableQuickAccess = $this->data->fieldTableQuickAccess;
|
||||
|
||||
if (!isset($fieldTableQuickAccess->$scope) || !isset($fieldTableQuickAccess->$scope->fields) || !isset($fieldTableQuickAccess->$scope->fields->$action)) {
|
||||
if (
|
||||
!isset($fieldTableQuickAccess->$scope) || !isset($fieldTableQuickAccess->$scope->fields) ||
|
||||
!isset($fieldTableQuickAccess->$scope->fields->$action)
|
||||
) {
|
||||
$this->forbiddenFieldsCache[$key] = [];
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
$levelList = [];
|
||||
|
||||
foreach ($this->fieldLevelList as $level) {
|
||||
if (array_search($level, $this->fieldLevelList) >= array_search($thresholdLevel, $this->fieldLevelList)) {
|
||||
$levelList[] = $level;
|
||||
@@ -363,9 +428,15 @@ class Table
|
||||
$fieldList = [];
|
||||
|
||||
foreach ($levelList as $level) {
|
||||
if (!isset($fieldTableQuickAccess->$scope->fields->$action->$level)) continue;
|
||||
if (!isset($fieldTableQuickAccess->$scope->fields->$action->$level)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($fieldTableQuickAccess->$scope->fields->$action->$level as $field) {
|
||||
if (in_array($field, $fieldList)) continue;
|
||||
if (in_array($field, $fieldList)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$fieldList[] = $field;
|
||||
}
|
||||
}
|
||||
@@ -390,6 +461,7 @@ class Table
|
||||
foreach ($this->fieldActionList as $action) {
|
||||
$fieldTableQuickAccess->$scope->attributes->$action = (object) [];
|
||||
$fieldTableQuickAccess->$scope->fields->$action = (object) [];
|
||||
|
||||
foreach ($this->fieldLevelList as $level) {
|
||||
$fieldTableQuickAccess->$scope->attributes->$action->$level = [];
|
||||
$fieldTableQuickAccess->$scope->fields->$action->$level = [];
|
||||
@@ -397,13 +469,17 @@ class Table
|
||||
}
|
||||
|
||||
foreach (get_object_vars($scopeData) as $field => $fieldData) {
|
||||
$attributeList = $this->getFieldManager()->getAttributeList($scope, $field);
|
||||
$attributeList = $this->getFieldUtil()->getAttributeList($scope, $field);
|
||||
|
||||
foreach ($this->fieldActionList as $action) {
|
||||
if (!isset($fieldData->$action)) continue;
|
||||
if (!isset($fieldData->$action)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($this->fieldLevelList as $level) {
|
||||
if ($fieldData->$action === $level) {
|
||||
$fieldTableQuickAccess->$scope->fields->$action->{$level}[] = $field;
|
||||
|
||||
foreach ($attributeList as $attribute) {
|
||||
$fieldTableQuickAccess->$scope->attributes->$action->{$level}[] = $attribute;
|
||||
}
|
||||
@@ -423,6 +499,7 @@ class Table
|
||||
}
|
||||
|
||||
$defaultsGroupName = 'default';
|
||||
|
||||
if ($this->isStrictMode) {
|
||||
$defaultsGroupName = 'strictDefault';
|
||||
}
|
||||
@@ -430,33 +507,53 @@ class Table
|
||||
$data = $this->metadata->get(['app', $this->type, $defaultsGroupName, 'scopeLevel'], []);
|
||||
|
||||
foreach ($data as $scope => $item) {
|
||||
if (isset($table->$scope)) continue;
|
||||
if (isset($table->$scope)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$value = $item;
|
||||
|
||||
if (is_array($item)) {
|
||||
$value = (object) $item;
|
||||
}
|
||||
|
||||
$table->$scope = $value;
|
||||
}
|
||||
|
||||
$defaultFieldData = $this->metadata->get(['app', $this->type, $defaultsGroupName, 'fieldLevel'], []);
|
||||
|
||||
foreach ($this->getScopeList() as $scope) {
|
||||
if (isset($table->$scope) && $table->$scope === false) continue;
|
||||
if (!$this->getMetadata()->get('scopes.' . $scope . '.entity')) continue;
|
||||
if (isset($table->$scope) && $table->$scope === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$this->getMetadata()->get('scopes.' . $scope . '.entity')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$fieldList = array_keys($this->getMetadata()->get("entityDefs.{$scope}.fields", []));
|
||||
|
||||
$defaultScopeFieldData = $this->metadata->get('app.'.$this->type.'.'.$defaultsGroupName.'.scopeFieldLevel.' . $scope, []);
|
||||
$defaultScopeFieldData = $this->metadata->get(
|
||||
'app.'.$this->type.'.'.$defaultsGroupName.'.scopeFieldLevel.' . $scope, []);
|
||||
|
||||
foreach (array_merge($defaultFieldData, $defaultScopeFieldData) as $field => $f) {
|
||||
if (!in_array($field, $fieldList)) continue;
|
||||
if (!in_array($field, $fieldList)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($fieldTable->$scope)) {
|
||||
$fieldTable->$scope = (object) [];
|
||||
}
|
||||
if (isset($fieldTable->$scope->$field)) continue;
|
||||
|
||||
if (isset($fieldTable->$scope->$field)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$fieldTable->$scope->$field = (object) [];
|
||||
|
||||
foreach ($this->fieldActionList as $action) {
|
||||
$level = 'no';
|
||||
|
||||
if ($f === true) {
|
||||
$level = 'yes';
|
||||
} else {
|
||||
@@ -472,22 +569,32 @@ class Table
|
||||
foreach ($this->getScopeWithAclList() as $scope) {
|
||||
if (!isset($table->$scope)) {
|
||||
$aclType = $this->metadata->get('scopes.' . $scope . '.' . $this->type);
|
||||
|
||||
if ($aclType === true) {
|
||||
$aclType = $this->defaultAclType;
|
||||
}
|
||||
|
||||
if (!empty($aclType)) {
|
||||
$paramDefaultsName = 'scopeLevelTypesDefaults';
|
||||
|
||||
if ($this->isStrictMode) {
|
||||
$paramDefaultsName = 'scopeLevelTypesStrictDefaults';
|
||||
}
|
||||
$defaultValue = $this->metadata->get(['app', $this->type, $paramDefaultsName, $aclType], $this->metadata->get(['app', $this->type, $paramDefaultsName, 'record']));
|
||||
|
||||
$defaultValue = $this->metadata->get(
|
||||
['app', $this->type, $paramDefaultsName, $aclType],
|
||||
$this->metadata->get(['app', $this->type, $paramDefaultsName, 'record'])
|
||||
);
|
||||
|
||||
if (is_array($defaultValue)) {
|
||||
$defaultValue = (object) $defaultValue;
|
||||
}
|
||||
|
||||
$table->$scope = $defaultValue;
|
||||
|
||||
if (is_object($table->$scope)) {
|
||||
$actionList = $this->getMetadata()->get(['scopes', $scope, $this->type . 'ActionList']);
|
||||
|
||||
if ($actionList) {
|
||||
foreach (get_object_vars($table->$scope) as $action => $level) {
|
||||
if (!in_array($action, $actionList)) {
|
||||
@@ -511,30 +618,43 @@ class Table
|
||||
|
||||
foreach ($data as $scope => $item) {
|
||||
$value = $item;
|
||||
|
||||
if (is_array($item)) {
|
||||
$value = (object) $item;
|
||||
}
|
||||
|
||||
$table->$scope = $value;
|
||||
}
|
||||
|
||||
$mandatoryFieldData = $this->metadata->get('app.'.$this->type.'.mandatory.fieldLevel', array());
|
||||
|
||||
foreach ($this->getScopeList() as $scope) {
|
||||
if (isset($table->$scope) && $table->$scope === false) continue;
|
||||
if (!$this->getMetadata()->get('scopes.' . $scope . '.entity')) continue;
|
||||
if (isset($table->$scope) && $table->$scope === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$this->getMetadata()->get('scopes.' . $scope . '.entity')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$fieldList = array_keys($this->getMetadata()->get("entityDefs.{$scope}.fields", []));
|
||||
|
||||
$mandatoryScopeFieldData = $this->metadata->get('app.'.$this->type.'.mandatory.scopeFieldLevel.' . $scope, array());
|
||||
|
||||
foreach (array_merge($mandatoryFieldData, $mandatoryScopeFieldData) as $field => $f) {
|
||||
if (!in_array($field, $fieldList)) continue;
|
||||
if (!in_array($field, $fieldList)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($fieldTable->$scope)) {
|
||||
$fieldTable->$scope = (object) [];
|
||||
}
|
||||
|
||||
$fieldTable->$scope->$field = (object) [];
|
||||
|
||||
foreach ($this->fieldActionList as $action) {
|
||||
$level = 'no';
|
||||
|
||||
if ($f === true) {
|
||||
$level = 'yes';
|
||||
} else {
|
||||
@@ -569,6 +689,7 @@ class Table
|
||||
$table->$scope = false;
|
||||
unset($fieldTable->$scope);
|
||||
}
|
||||
|
||||
foreach ($this->valuePermissionList as $permission) {
|
||||
$valuePermissionLists->{$permission}[] = 'no';
|
||||
}
|
||||
@@ -578,29 +699,38 @@ class Table
|
||||
private function mergeValueList(array $list, $defaultValue)
|
||||
{
|
||||
$result = null;
|
||||
|
||||
foreach ($list as $level) {
|
||||
if ($level != 'not-set') {
|
||||
if (is_null($result)) {
|
||||
$result = $level;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (array_search($result, $this->levelList) > array_search($level, $this->levelList)) {
|
||||
$result = $level;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_null($result)) {
|
||||
$result = $defaultValue;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function getScopeWithAclList()
|
||||
{
|
||||
$scopeList = [];
|
||||
|
||||
$scopes = $this->metadata->get('scopes');
|
||||
|
||||
foreach ($scopes as $scope => $d) {
|
||||
if (empty($d['acl'])) continue;
|
||||
if (empty($d['acl'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$scopeList[] = $scope;
|
||||
}
|
||||
return $scopeList;
|
||||
@@ -609,10 +739,13 @@ class Table
|
||||
protected function getScopeList()
|
||||
{
|
||||
$scopeList = [];
|
||||
|
||||
$scopes = $this->metadata->get('scopes');
|
||||
|
||||
foreach ($scopes as $scope => $d) {
|
||||
$scopeList[] = $scope;
|
||||
}
|
||||
|
||||
return $scopeList;
|
||||
}
|
||||
|
||||
@@ -623,7 +756,9 @@ class Table
|
||||
|
||||
foreach ($tableList as $table) {
|
||||
foreach ($scopeList as $scope) {
|
||||
if (!isset($table->$scope)) continue;
|
||||
if (!isset($table->$scope)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$row = $table->$scope;
|
||||
|
||||
@@ -631,9 +766,11 @@ class Table
|
||||
if (!isset($data->$scope)) {
|
||||
$data->$scope = false;
|
||||
}
|
||||
} else if ($row === true) {
|
||||
}
|
||||
else if ($row === true) {
|
||||
$data->$scope = true;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
if (!isset($data->$scope)) {
|
||||
$data->$scope = (object) [];
|
||||
}
|
||||
@@ -641,7 +778,9 @@ class Table
|
||||
$data->$scope = (object) [];
|
||||
}
|
||||
|
||||
if (!is_object($row)) continue;
|
||||
if (!is_object($row)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$actionList = $this->getMetadata()->get(['scopes', $scope, $this->type . 'ActionList'], $this->actionList);
|
||||
|
||||
@@ -651,7 +790,9 @@ class Table
|
||||
if (!isset($data->$scope->$action)) {
|
||||
$data->$scope->$action = $level;
|
||||
} else {
|
||||
if (array_search($data->$scope->$action, $this->levelList) > array_search($level, $this->levelList)) {
|
||||
if (
|
||||
array_search($data->$scope->$action, $this->levelList) > array_search($level, $this->levelList)
|
||||
) {
|
||||
$data->$scope->$action = $level;
|
||||
}
|
||||
}
|
||||
@@ -679,37 +820,54 @@ class Table
|
||||
private function mergeFieldTableList(array $tableList)
|
||||
{
|
||||
$data = (object) [];
|
||||
|
||||
$scopeList = $this->getScopeWithAclList();
|
||||
|
||||
foreach ($tableList as $table) {
|
||||
foreach ($scopeList as $scope) {
|
||||
if (!isset($table->$scope)) continue;
|
||||
if (!isset($table->$scope)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($data->$scope)) {
|
||||
$data->$scope = (object) [];
|
||||
}
|
||||
|
||||
if (!is_object($table->$scope)) continue;
|
||||
if (!is_object($table->$scope)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$fieldList = array_keys($this->getMetadata()->get("entityDefs.{$scope}.fields", []));
|
||||
|
||||
foreach (get_object_vars($table->$scope) as $field => $row) {
|
||||
if (!is_object($row)) continue;
|
||||
if (!is_object($row)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!in_array($field, $fieldList)) continue;
|
||||
if (!in_array($field, $fieldList)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($data->$scope->$field)) {
|
||||
$data->$scope->$field = (object) [];
|
||||
}
|
||||
|
||||
foreach ($this->fieldActionList as $i => $action) {
|
||||
if (!isset($row->$action)) continue;
|
||||
if (!isset($row->$action)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$level = $row->$action;
|
||||
|
||||
if (!isset($data->$scope->$field->$action)) {
|
||||
$data->$scope->$field->$action = $level;
|
||||
} else {
|
||||
if (array_search($data->$scope->$field->$action, $this->fieldLevelList) > array_search($level, $this->fieldLevelList)) {
|
||||
if (
|
||||
array_search(
|
||||
$data->$scope->$field->$action,
|
||||
$this->fieldLevelList
|
||||
) > array_search($level, $this->fieldLevelList)
|
||||
) {
|
||||
$data->$scope->$field->$action = $level;
|
||||
}
|
||||
}
|
||||
@@ -723,30 +881,6 @@ class Table
|
||||
|
||||
private function buildCache()
|
||||
{
|
||||
$this->fileManager->putPhpContents($this->cacheFilePath, $this->data, true);
|
||||
}
|
||||
|
||||
protected function applyReadOnlyFields(&$fieldTable)
|
||||
{
|
||||
// TODO Enable in 5.4.0
|
||||
return;
|
||||
$scopeList = $this->getScopeWithAclList();
|
||||
foreach ($scopeList as $scope) {
|
||||
if (!property_exists($fieldTable, $scope)) continue;
|
||||
$fieldList = array_keys($this->getMetadata()->get(['entityDefs', $scope, 'fields'], []));
|
||||
foreach ($fieldList as $field) {
|
||||
if ($this->getMetadata()->get(['entityDefs', $scope, 'fields', $field, 'readOnly'])) {
|
||||
if (property_exists($fieldTable->$scope, $field)) {
|
||||
$fieldTable->$scope->$field->edit = 'no';
|
||||
} else {
|
||||
$fieldTable->$scope->$field = (object) [];
|
||||
foreach ($this->fieldActionList as $action) {
|
||||
$fieldTable->$scope->$field->$action = 'yes';
|
||||
}
|
||||
$fieldTable->$scope->$field->edit = 'no';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->dataCache->store($this->cacheKey, $this->data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,79 +29,78 @@
|
||||
|
||||
namespace Espo\Core;
|
||||
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Exceptions\Error;
|
||||
|
||||
use \Espo\ORM\Entity;
|
||||
use \Espo\Entities\User;
|
||||
use \Espo\Core\Utils\Util;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Entities\User;
|
||||
|
||||
use Espo\Core\{
|
||||
Utils\ClassFinder,
|
||||
Utils\Config,
|
||||
ORM\EntityManager,
|
||||
Acl\Acl as BaseAcl,
|
||||
Acl\ScopeAcl,
|
||||
Acl\GlobalRestricton,
|
||||
Acl as UserAclWrapper,
|
||||
Acl\Table as AclTable,
|
||||
Utils\Util,
|
||||
};
|
||||
|
||||
use StdClass;
|
||||
|
||||
/**
|
||||
* Used to check access for a specific user.
|
||||
*/
|
||||
class AclManager
|
||||
{
|
||||
private $container;
|
||||
|
||||
private $metadata;
|
||||
|
||||
private $implementationHashMap = [];
|
||||
|
||||
private $tableHashMap = [];
|
||||
|
||||
protected $tableClassName = '\\Espo\\Core\\Acl\\Table';
|
||||
protected $tableClassName = AclTable::class;
|
||||
|
||||
protected $userAclClassName = '\\Espo\\Core\\Acl';
|
||||
protected $userAclClassName = UserAclWrapper::class;
|
||||
|
||||
protected $baseImplementationClassName = BaseAcl::class;
|
||||
|
||||
protected $globalRestricton;
|
||||
|
||||
public function __construct(Container $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->metadata = $container->get('metadata');
|
||||
protected $injectableFactory;
|
||||
protected $classFinder;
|
||||
protected $config;
|
||||
protected $entityManager;
|
||||
|
||||
$this->globalRestricton = new \Espo\Core\Acl\GlobalRestricton(
|
||||
$container->get('metadata'),
|
||||
$container->get('fileManager'),
|
||||
$container->get('fieldManagerUtil'),
|
||||
$container->get('config')->get('useCache')
|
||||
);
|
||||
public function __construct(
|
||||
InjectableFactory $injectableFactory, ClassFinder $classFinder, Config $config, EntityManager $entityManager
|
||||
) {
|
||||
$this->injectableFactory = $injectableFactory;
|
||||
$this->classFinder = $classFinder;
|
||||
$this->config = $config;
|
||||
$this->entityManager = $entityManager;
|
||||
|
||||
$this->globalRestricton = $this->injectableFactory->createWith(GlobalRestricton::class, [
|
||||
'useCache' => $config->get('useCache'),
|
||||
]);
|
||||
}
|
||||
|
||||
protected function getContainer()
|
||||
{
|
||||
return $this->container;
|
||||
}
|
||||
|
||||
protected function getMetadata()
|
||||
{
|
||||
return $this->metadata;
|
||||
}
|
||||
|
||||
public function getImplementation($scope)
|
||||
public function getImplementation(string $scope) : ScopeAcl
|
||||
{
|
||||
if (empty($this->implementationHashMap[$scope])) {
|
||||
$normalizedName = Util::normilizeClassName($scope);
|
||||
$className = $this->classFinder->find('Acl', $scope);
|
||||
|
||||
if (!$className) {
|
||||
$className = $this->baseImplementationClassName;
|
||||
}
|
||||
|
||||
$className = '\\Espo\\Custom\\Acl\\' . $normalizedName;
|
||||
if (!class_exists($className)) {
|
||||
$moduleName = $this->metadata->getScopeModuleName($scope);
|
||||
if ($moduleName) {
|
||||
$className = '\\Espo\\Modules\\' . $moduleName . '\\Acl\\' . $normalizedName;
|
||||
} else {
|
||||
$className = '\\Espo\\Acl\\' . $normalizedName;
|
||||
}
|
||||
if (!class_exists($className)) {
|
||||
$className = '\\Espo\\Core\\Acl\\Base';
|
||||
}
|
||||
throw new Error("{$className} does not exist.");
|
||||
}
|
||||
|
||||
if (class_exists($className)) {
|
||||
$acl = new $className($scope);
|
||||
$dependencyList = $acl->getDependencyList();
|
||||
foreach ($dependencyList as $name) {
|
||||
$acl->inject($name, $this->getContainer()->get($name));
|
||||
}
|
||||
$this->implementationHashMap[$scope] = $acl;
|
||||
} else {
|
||||
throw new Error();
|
||||
}
|
||||
$acl = $this->injectableFactory->createWith($className, [
|
||||
'scope' => $scope,
|
||||
]);
|
||||
|
||||
$this->implementationHashMap[$scope] = $acl;
|
||||
}
|
||||
|
||||
return $this->implementationHashMap[$scope];
|
||||
@@ -110,80 +109,115 @@ class AclManager
|
||||
protected function getTable(User $user)
|
||||
{
|
||||
$key = $user->id;
|
||||
|
||||
if (empty($key)) {
|
||||
$key = spl_object_hash($user);
|
||||
}
|
||||
|
||||
if (empty($this->tableHashMap[$key])) {
|
||||
$config = $this->getContainer()->get('config');
|
||||
$fileManager = $this->getContainer()->get('fileManager');
|
||||
$metadata = $this->getContainer()->get('metadata');
|
||||
$fieldManager = $this->getContainer()->get('fieldManagerUtil');
|
||||
|
||||
$this->tableHashMap[$key] = new $this->tableClassName($user, $config, $fileManager, $metadata, $fieldManager);
|
||||
$this->tableHashMap[$key] = $this->injectableFactory->createWith($this->tableClassName, [
|
||||
'user' => $user,
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->tableHashMap[$key];
|
||||
}
|
||||
|
||||
public function getMap(User $user)
|
||||
public function getMap(User $user) : StdClass
|
||||
{
|
||||
return $this->getTable($user)->getMap();
|
||||
}
|
||||
|
||||
public function getLevel(User $user, $scope, $action)
|
||||
/**
|
||||
* Get an access level for a specific scope and action.
|
||||
*/
|
||||
public function getLevel(User $user, string $scope, string $action) : string
|
||||
{
|
||||
if ($user->isAdmin()) {
|
||||
return $this->getTable($user)->getHighestLevel($scope, $action);
|
||||
}
|
||||
|
||||
return $this->getTable($user)->getLevel($scope, $action);
|
||||
}
|
||||
|
||||
public function get(User $user, $permission)
|
||||
/**
|
||||
* Get a permission. E.g. 'assignment' permission.
|
||||
*/
|
||||
public function get(User $user, string $permission) : ?string
|
||||
{
|
||||
if (substr($permission, -10) !== 'Permission') {
|
||||
$permission .= 'Permission';
|
||||
}
|
||||
|
||||
return $this->getTable($user)->get($permission);
|
||||
}
|
||||
|
||||
public function checkReadNo(User $user, $scope)
|
||||
/**
|
||||
* Whether there's no 'read' access for a specific scope.
|
||||
*/
|
||||
public function checkReadNo(User $user, string $scope) : bool
|
||||
{
|
||||
if ($user->isAdmin()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$data = $this->getTable($user)->getScopeData($scope);
|
||||
return $this->getImplementation($scope)->checkReadNo($user, $data);
|
||||
|
||||
return (bool) $this->getImplementation($scope)->checkReadNo($user, $data);
|
||||
}
|
||||
|
||||
public function checkReadOnlyTeam(User $user, $scope)
|
||||
/**
|
||||
* Whether 'read' access is set to 'team' for a specific scope.
|
||||
*/
|
||||
public function checkReadOnlyTeam(User $user, string $scope) : bool
|
||||
{
|
||||
if ($user->isAdmin()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$data = $this->getTable($user)->getScopeData($scope);
|
||||
return $this->getImplementation($scope)->checkReadOnlyTeam($user, $data);
|
||||
|
||||
return (bool) $this->getImplementation($scope)->checkReadOnlyTeam($user, $data);
|
||||
}
|
||||
|
||||
public function checkReadOnlyOwn(User $user, $scope)
|
||||
/**
|
||||
* Whether 'read' access is set to 'own' for a specific scope.
|
||||
*/
|
||||
public function checkReadOnlyOwn(User $user, string $scope) : bool
|
||||
{
|
||||
if ($user->isAdmin()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$data = $this->getTable($user)->getScopeData($scope);
|
||||
return $this->getImplementation($scope)->checkReadOnlyOwn($user, $data);
|
||||
|
||||
return (bool) $this->getImplementation($scope)->checkReadOnlyOwn($user, $data);
|
||||
}
|
||||
|
||||
public function check(User $user, $subject, $action = null)
|
||||
/**
|
||||
* Check a scope or entity. If $action is omitted, it will check whether a scope level is set to 'enabled'.
|
||||
*/
|
||||
public function check(User $user, $subject, ?string $action = null) : bool
|
||||
{
|
||||
if (is_string($subject)) {
|
||||
return $this->checkScope($user, $subject, $action);
|
||||
} else {
|
||||
$entity = $subject;
|
||||
if ($entity instanceof Entity) {
|
||||
return $this->checkEntity($user, $entity, $action);
|
||||
}
|
||||
}
|
||||
|
||||
$entity = $subject;
|
||||
|
||||
if ($entity instanceof Entity) {
|
||||
$action = $action ?? 'read';
|
||||
|
||||
return $this->checkEntity($user, $entity, $action);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function checkEntity(User $user, Entity $entity, $action = 'read')
|
||||
/**
|
||||
* Check access to a specific entity (record).
|
||||
*/
|
||||
public function checkEntity(User $user, Entity $entity, string $action = 'read') : bool
|
||||
{
|
||||
$scope = $entity->getEntityType();
|
||||
|
||||
@@ -197,60 +231,79 @@ class AclManager
|
||||
|
||||
if ($action) {
|
||||
$methodName = 'checkEntity' . ucfirst($action);
|
||||
|
||||
if (method_exists($impl, $methodName)) {
|
||||
return $impl->$methodName($user, $entity, $data);
|
||||
return (bool) $impl->$methodName($user, $entity, $data);
|
||||
}
|
||||
}
|
||||
|
||||
return $impl->checkEntity($user, $entity, $data, $action);
|
||||
return (bool) $impl->checkEntity($user, $entity, $data, $action);
|
||||
}
|
||||
|
||||
public function checkIsOwner(User $user, Entity $entity)
|
||||
/**
|
||||
* Whether a user is owned of an entity (record). Usually 'assignedUser' field is used for checking.
|
||||
*/
|
||||
public function checkIsOwner(User $user, Entity $entity) : bool
|
||||
{
|
||||
return $this->getImplementation($entity->getEntityType())->checkIsOwner($user, $entity);
|
||||
return (bool) $this->getImplementation($entity->getEntityType())->checkIsOwner($user, $entity);
|
||||
}
|
||||
|
||||
public function checkInTeam(User $user, Entity $entity)
|
||||
/**
|
||||
* Whether a user team list overlaps with teams set in an entity.
|
||||
*/
|
||||
public function checkInTeam(User $user, Entity $entity) : bool
|
||||
{
|
||||
return $this->getImplementation($entity->getEntityType())->checkInTeam($user, $entity);
|
||||
return (bool) $this->getImplementation($entity->getEntityType())->checkInTeam($user, $entity);
|
||||
}
|
||||
|
||||
public function checkScope(User $user, $scope, $action = null)
|
||||
/**
|
||||
* Check access to scope. If $action is omitted, it will check whether a scope level is set to 'enabled'.
|
||||
*/
|
||||
public function checkScope(User $user, string $scope, ?string $action = null) : bool
|
||||
{
|
||||
$data = $this->getTable($user)->getScopeData($scope);
|
||||
return $this->getImplementation($scope)->checkScope($user, $data, $action);
|
||||
|
||||
return (bool) $this->getImplementation($scope)->checkScope($user, $data, $action);
|
||||
}
|
||||
|
||||
public function checkUser(User $user, $permission, User $entity)
|
||||
/**
|
||||
* @deprecated Use checkUserPermission instead.
|
||||
*/
|
||||
public function checkUser(User $user, string $permission, User $target) : bool
|
||||
{
|
||||
if ($user->isAdmin()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->get($user, $permission) === 'no') {
|
||||
if ($entity->id !== $user->id) {
|
||||
if ($target->id !== $user->id) {
|
||||
return false;
|
||||
}
|
||||
} else if ($this->get($user, $permission) === 'team') {
|
||||
if ($entity->id != $user->id) {
|
||||
if ($target->id != $user->id) {
|
||||
$teamIdList1 = $user->getTeamIdList();
|
||||
$teamIdList2 = $entity->getTeamIdList();
|
||||
$teamIdList2 = $target->getTeamIdList();
|
||||
|
||||
$inTeam = false;
|
||||
|
||||
foreach ($teamIdList1 as $id) {
|
||||
if (in_array($id, $teamIdList2)) {
|
||||
$inTeam = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$inTeam) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function getGlobalRestrictionTypeList(User $user, $action = 'read')
|
||||
protected function getGlobalRestrictionTypeList(User $user, string $action = 'read') : array
|
||||
{
|
||||
$typeList = ['forbidden'];
|
||||
|
||||
@@ -264,6 +317,7 @@ class AclManager
|
||||
|
||||
if ($action === 'edit') {
|
||||
$typeList[] = 'readOnly';
|
||||
|
||||
if (!$user->isAdmin()) {
|
||||
$typeList[] = 'nonAdminReadOnly';
|
||||
}
|
||||
@@ -272,8 +326,12 @@ class AclManager
|
||||
return $typeList;
|
||||
}
|
||||
|
||||
public function getScopeForbiddenAttributeList(User $user, $scope, $action = 'read', $thresholdLevel = 'no')
|
||||
{
|
||||
/**
|
||||
* Get attributes forbidden for a user.
|
||||
*/
|
||||
public function getScopeForbiddenAttributeList(
|
||||
User $user, string $scope, string $action = 'read', string $thresholdLevel = 'no'
|
||||
) : array {
|
||||
$list = [];
|
||||
|
||||
if (!$user->isAdmin()) {
|
||||
@@ -285,14 +343,19 @@ class AclManager
|
||||
$list,
|
||||
$this->getScopeRestrictedAttributeList($scope, $this->getGlobalRestrictionTypeList($user, $action))
|
||||
);
|
||||
|
||||
$list = array_values($list);
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
public function getScopeForbiddenFieldList(User $user, $scope, $action = 'read', $thresholdLevel = 'no')
|
||||
{
|
||||
/**
|
||||
* Get fields forbidden for a user.
|
||||
*/
|
||||
public function getScopeForbiddenFieldList(
|
||||
User $user, string $scope, string $action = 'read', string $thresholdLevel = 'no'
|
||||
) : array {
|
||||
$list = [];
|
||||
|
||||
if (!$user->isAdmin()) {
|
||||
@@ -304,15 +367,19 @@ class AclManager
|
||||
$list,
|
||||
$this->getScopeRestrictedFieldList($scope, $this->getGlobalRestrictionTypeList($user, $action))
|
||||
);
|
||||
|
||||
$list = array_values($list);
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
|
||||
public function getScopeForbiddenLinkList(User $user, $scope, $action = 'read', $thresholdLevel = 'no')
|
||||
{
|
||||
/**
|
||||
* Get links forbidden for a user.
|
||||
*/
|
||||
public function getScopeForbiddenLinkList(
|
||||
User $user, string $scope, string $action = 'read', string $thresholdLevel = 'no'
|
||||
) : array {
|
||||
$list = [];
|
||||
|
||||
if ($thresholdLevel === 'no') {
|
||||
@@ -320,13 +387,19 @@ class AclManager
|
||||
$list,
|
||||
$this->getScopeRestrictedLinkList($scope, $this->getGlobalRestrictionTypeList($user, $action))
|
||||
);
|
||||
|
||||
$list = array_values($list);
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
public function checkUserPermission(User $user, $target, $permissionType = 'userPermission')
|
||||
/**
|
||||
* Whether a user has an access to another user over a specific permission.
|
||||
*
|
||||
* @param $target User|string User entity or user ID.
|
||||
*/
|
||||
public function checkUserPermission(User $user, $target, string $permissionType = 'user') : bool
|
||||
{
|
||||
$permission = $this->get($user, $permissionType);
|
||||
|
||||
@@ -348,7 +421,8 @@ class AclManager
|
||||
|
||||
if ($permission === 'team') {
|
||||
$teamIdList = $user->getLinkMultipleIdList('teams');
|
||||
if (!$this->getContainer()->get('entityManager')->getRepository('User')->checkBelongsToAnyOfTeams($userId, $teamIdList)) {
|
||||
|
||||
if (!$this->entityManager->getRepository('User')->checkBelongsToAnyOfTeams($userId, $teamIdList)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -356,57 +430,77 @@ class AclManager
|
||||
return true;
|
||||
}
|
||||
|
||||
public function checkAssignmentPermission(User $user, $target)
|
||||
/**
|
||||
* Whether a user can assign to another user.
|
||||
*
|
||||
* @param $target User|string User entity or user ID.
|
||||
*/
|
||||
public function checkAssignmentPermission(User $user, $target) : bool
|
||||
{
|
||||
return $this->checkUserPermission($user, $target, 'assignmentPermission');
|
||||
return $this->checkUserPermission($user, $target, 'assignment');
|
||||
}
|
||||
|
||||
public function createUserAcl(User $user)
|
||||
/**
|
||||
* Create a wrapper for a specific user.
|
||||
*/
|
||||
public function createUserAcl(User $user) : UserAclWrapper
|
||||
{
|
||||
$className = $this->userAclClassName;
|
||||
$acl = new $className($this, $user);
|
||||
|
||||
return $acl;
|
||||
}
|
||||
|
||||
public function getScopeRestrictedFieldList($scope, $type)
|
||||
public function getScopeRestrictedFieldList(string $scope, $type) : array
|
||||
{
|
||||
if (is_array($type)) {
|
||||
$typeList = $type;
|
||||
$list = [];
|
||||
|
||||
foreach ($typeList as $type) {
|
||||
$list = array_merge($list, $this->globalRestricton->getScopeRestrictedFieldList($scope, $type));
|
||||
}
|
||||
|
||||
$list = array_values($list);
|
||||
|
||||
return $list;
|
||||
}
|
||||
return $this->globalRestricton->getScopeRestrictedFieldList($scope, $type);
|
||||
}
|
||||
|
||||
public function getScopeRestrictedAttributeList($scope, $type)
|
||||
public function getScopeRestrictedAttributeList(string $scope, $type) : array
|
||||
{
|
||||
if (is_array($type)) {
|
||||
$typeList = $type;
|
||||
$list = [];
|
||||
|
||||
foreach ($typeList as $type) {
|
||||
$list = array_merge($list, $this->globalRestricton->getScopeRestrictedAttributeList($scope, $type));
|
||||
}
|
||||
|
||||
$list = array_values($list);
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
return $this->globalRestricton->getScopeRestrictedAttributeList($scope, $type);
|
||||
}
|
||||
|
||||
public function getScopeRestrictedLinkList($scope, $type)
|
||||
public function getScopeRestrictedLinkList(string $scope, $type) : array
|
||||
{
|
||||
if (is_array($type)) {
|
||||
$typeList = $type;
|
||||
$list = [];
|
||||
|
||||
foreach ($typeList as $type) {
|
||||
$list = array_merge($list, $this->globalRestricton->getScopeRestrictedLinkList($scope, $type));
|
||||
}
|
||||
|
||||
$list = array_values($list);
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
return $this->globalRestricton->getScopeRestrictedLinkList($scope, $type);
|
||||
}
|
||||
}
|
||||
|
||||
37
application/Espo/Core/AclPortal/Acl.php
Normal file
37
application/Espo/Core/AclPortal/Acl.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2020 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\AclPortal;
|
||||
|
||||
use Espo\Core\Acl\Acl as BaseAcl;
|
||||
|
||||
class Acl extends BaseAcl implements PortalScopeAcl
|
||||
{
|
||||
use Portal;
|
||||
}
|
||||
@@ -29,188 +29,9 @@
|
||||
|
||||
namespace Espo\Core\AclPortal;
|
||||
|
||||
use \Espo\Entities\User;
|
||||
use \Espo\ORM\Entity;
|
||||
|
||||
class Base extends \Espo\Core\Acl\Base
|
||||
/** @deprecated */
|
||||
class Base extends \Espo\Core\Acl\Base implements PortalScopeAcl
|
||||
{
|
||||
public function checkScope(User $user, $data, $action = null, Entity $entity = null, $entityAccessData = array())
|
||||
{
|
||||
if ($user->isAdmin()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_null($data)) {
|
||||
return false;
|
||||
}
|
||||
if ($data === false) {
|
||||
return false;
|
||||
}
|
||||
if ($data === true) {
|
||||
return true;
|
||||
}
|
||||
if (is_string($data)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$isOwner = null;
|
||||
if (isset($entityAccessData['isOwner'])) {
|
||||
$isOwner = $entityAccessData['isOwner'];
|
||||
}
|
||||
$inAccount = null;
|
||||
if (isset($entityAccessData['inAccount'])) {
|
||||
$inAccount = $entityAccessData['inAccount'];
|
||||
}
|
||||
$isOwnContact = null;
|
||||
if (isset($entityAccessData['isOwnContact'])) {
|
||||
$isOwnContact = $entityAccessData['isOwnContact'];
|
||||
}
|
||||
|
||||
if (is_null($action)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!isset($data->$action)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$value = $data->$action;
|
||||
|
||||
if ($value === 'all' || $value === 'yes' || $value === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!$value || $value === 'no') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_null($isOwner)) {
|
||||
if ($entity) {
|
||||
$isOwner = $this->checkIsOwner($user, $entity);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($isOwner) {
|
||||
if ($value === 'own' || $value === 'account' || $value === 'contact') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($value === 'account') {
|
||||
if (is_null($inAccount) && $entity) {
|
||||
$inAccount = $this->checkInAccount($user, $entity);
|
||||
}
|
||||
if ($inAccount) {
|
||||
return true;
|
||||
} else {
|
||||
if (is_null($isOwnContact) && $entity) {
|
||||
$isOwnContact = $this->checkIsOwnContact($user, $entity);
|
||||
}
|
||||
if ($isOwnContact) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($value === 'contact') {
|
||||
if (is_null($isOwnContact) && $entity) {
|
||||
$isOwnContact = $this->checkIsOwnContact($user, $entity);
|
||||
}
|
||||
if ($isOwnContact) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function checkReadOnlyAccount(User $user, $data)
|
||||
{
|
||||
if (empty($data) || !is_object($data) || !isset($data->read)) {
|
||||
return false;
|
||||
}
|
||||
return $data->read === 'account';
|
||||
}
|
||||
|
||||
public function checkReadOnlyContact(User $user, $data)
|
||||
{
|
||||
if (empty($data) || !is_object($data) || !isset($data->read)) {
|
||||
return false;
|
||||
}
|
||||
return $data->read === 'contact';
|
||||
}
|
||||
|
||||
public function checkIsOwner(User $user, Entity $entity)
|
||||
{
|
||||
if ($entity->hasAttribute('createdById')) {
|
||||
if ($entity->has('createdById')) {
|
||||
if ($user->id === $entity->get('createdById')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function checkInAccount(User $user, Entity $entity)
|
||||
{
|
||||
$accountIdList = $user->getLinkMultipleIdList('accounts');
|
||||
if (count($accountIdList)) {
|
||||
if ($entity->hasAttribute('accountId') && $entity->getRelationParam('account', 'entity') === 'Account') {
|
||||
if (in_array($entity->get('accountId'), $accountIdList)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($entity->hasRelation('accounts') && $entity->getRelationParam('accounts', 'entity') === 'Account') {
|
||||
$repository = $this->getEntityManager()->getRepository($entity->getEntityType());
|
||||
foreach ($accountIdList as $accountId) {
|
||||
if ($repository->isRelated($entity, 'accounts', $accountId)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($entity->hasAttribute('parentId') && $entity->hasRelation('parent')) {
|
||||
if ($entity->get('parentType') === 'Account') {
|
||||
if (in_array($entity->get('parentId'), $accountIdList)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function checkIsOwnContact(User $user, Entity $entity)
|
||||
{
|
||||
$contactId = $user->get('contactId');
|
||||
if ($contactId) {
|
||||
if ($entity->hasAttribute('contactId') && $entity->getRelationParam('contact', 'entity') === 'Contact') {
|
||||
if ($entity->get('contactId') === $contactId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($entity->hasRelation('contacts') && $entity->getRelationParam('contacts', 'entity') === 'Contact') {
|
||||
$repository = $this->getEntityManager()->getRepository($entity->getEntityType());
|
||||
if ($repository->isRelated($entity, 'contacts', $contactId)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($entity->hasAttribute('parentId') && $entity->hasRelation('parent')) {
|
||||
if ($entity->get('parentType') === 'Contact') {
|
||||
if ($entity->get('parentId') === $contactId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
use Portal;
|
||||
}
|
||||
|
||||
216
application/Espo/Core/AclPortal/Portal.php
Normal file
216
application/Espo/Core/AclPortal/Portal.php
Normal file
@@ -0,0 +1,216 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2020 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\AclPortal;
|
||||
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
trait Portal
|
||||
{
|
||||
public function checkScope(User $user, $data, $action = null, Entity $entity = null, $entityAccessData = [])
|
||||
{
|
||||
if ($user->isAdmin()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_null($data)) {
|
||||
return false;
|
||||
}
|
||||
if ($data === false) {
|
||||
return false;
|
||||
}
|
||||
if ($data === true) {
|
||||
return true;
|
||||
}
|
||||
if (is_string($data)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$isOwner = null;
|
||||
if (isset($entityAccessData['isOwner'])) {
|
||||
$isOwner = $entityAccessData['isOwner'];
|
||||
}
|
||||
$inAccount = null;
|
||||
if (isset($entityAccessData['inAccount'])) {
|
||||
$inAccount = $entityAccessData['inAccount'];
|
||||
}
|
||||
$isOwnContact = null;
|
||||
if (isset($entityAccessData['isOwnContact'])) {
|
||||
$isOwnContact = $entityAccessData['isOwnContact'];
|
||||
}
|
||||
|
||||
if (is_null($action)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!isset($data->$action)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$value = $data->$action;
|
||||
|
||||
if ($value === 'all' || $value === 'yes' || $value === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!$value || $value === 'no') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_null($isOwner)) {
|
||||
if ($entity) {
|
||||
$isOwner = $this->checkIsOwner($user, $entity);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($isOwner) {
|
||||
if ($value === 'own' || $value === 'account' || $value === 'contact') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($value === 'account') {
|
||||
if (is_null($inAccount) && $entity) {
|
||||
$inAccount = $this->checkInAccount($user, $entity);
|
||||
}
|
||||
if ($inAccount) {
|
||||
return true;
|
||||
} else {
|
||||
if (is_null($isOwnContact) && $entity) {
|
||||
$isOwnContact = $this->checkIsOwnContact($user, $entity);
|
||||
}
|
||||
if ($isOwnContact) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($value === 'contact') {
|
||||
if (is_null($isOwnContact) && $entity) {
|
||||
$isOwnContact = $this->checkIsOwnContact($user, $entity);
|
||||
}
|
||||
if ($isOwnContact) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function checkReadOnlyAccount(User $user, $data)
|
||||
{
|
||||
if (empty($data) || !is_object($data) || !isset($data->read)) {
|
||||
return false;
|
||||
}
|
||||
return $data->read === 'account';
|
||||
}
|
||||
|
||||
public function checkReadOnlyContact(User $user, $data)
|
||||
{
|
||||
if (empty($data) || !is_object($data) || !isset($data->read)) {
|
||||
return false;
|
||||
}
|
||||
return $data->read === 'contact';
|
||||
}
|
||||
|
||||
public function checkIsOwner(User $user, Entity $entity)
|
||||
{
|
||||
if ($entity->hasAttribute('createdById')) {
|
||||
if ($entity->has('createdById')) {
|
||||
if ($user->id === $entity->get('createdById')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function checkInAccount(User $user, Entity $entity)
|
||||
{
|
||||
$accountIdList = $user->getLinkMultipleIdList('accounts');
|
||||
if (count($accountIdList)) {
|
||||
if ($entity->hasAttribute('accountId') && $entity->getRelationParam('account', 'entity') === 'Account') {
|
||||
if (in_array($entity->get('accountId'), $accountIdList)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($entity->hasRelation('accounts') && $entity->getRelationParam('accounts', 'entity') === 'Account') {
|
||||
$repository = $this->getEntityManager()->getRepository($entity->getEntityType());
|
||||
foreach ($accountIdList as $accountId) {
|
||||
if ($repository->isRelated($entity, 'accounts', $accountId)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($entity->hasAttribute('parentId') && $entity->hasRelation('parent')) {
|
||||
if ($entity->get('parentType') === 'Account') {
|
||||
if (in_array($entity->get('parentId'), $accountIdList)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function checkIsOwnContact(User $user, Entity $entity)
|
||||
{
|
||||
$contactId = $user->get('contactId');
|
||||
if ($contactId) {
|
||||
if ($entity->hasAttribute('contactId') && $entity->getRelationParam('contact', 'entity') === 'Contact') {
|
||||
if ($entity->get('contactId') === $contactId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($entity->hasRelation('contacts') && $entity->getRelationParam('contacts', 'entity') === 'Contact') {
|
||||
$repository = $this->getEntityManager()->getRepository($entity->getEntityType());
|
||||
if ($repository->isRelated($entity, 'contacts', $contactId)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($entity->hasAttribute('parentId') && $entity->hasRelation('parent')) {
|
||||
if ($entity->get('parentType') === 'Contact') {
|
||||
if ($entity->get('parentId') === $contactId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -27,10 +27,9 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Interfaces;
|
||||
namespace Espo\Core\AclPortal;
|
||||
|
||||
interface InjectableService
|
||||
interface PortalScopeAcl
|
||||
{
|
||||
public function inject(string $name, object $object);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -29,16 +29,24 @@
|
||||
|
||||
namespace Espo\Core\AclPortal;
|
||||
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Exceptions\Error;
|
||||
|
||||
use \Espo\ORM\Entity;
|
||||
use \Espo\Entities\User;
|
||||
use \Espo\Entities\Portal;
|
||||
use Espo\Entities\{
|
||||
User,
|
||||
Portal,
|
||||
};
|
||||
|
||||
use \Espo\Core\Utils\Config;
|
||||
use \Espo\Core\Utils\Metadata;
|
||||
use \Espo\Core\Utils\FieldManagerUtil;
|
||||
use \Espo\Core\Utils\File\Manager as FileManager;
|
||||
use Espo\Core\{
|
||||
ORM\EntityManager,
|
||||
ORM\Entity,
|
||||
Utils\Config,
|
||||
Utils\Metadata,
|
||||
Utils\FieldUtil,
|
||||
Utils\File\Manager as FileManager,
|
||||
Utils\DataCache,
|
||||
};
|
||||
|
||||
use Traversable;
|
||||
|
||||
class Table extends \Espo\Core\Acl\Table
|
||||
{
|
||||
@@ -52,13 +60,22 @@ class Table extends \Espo\Core\Acl\Table
|
||||
|
||||
protected $isStrictModeForced = true;
|
||||
|
||||
public function __construct(User $user, Portal $portal, Config $config = null, FileManager $fileManager = null, Metadata $metadata = null, FieldManagerUtil $fieldManager = null)
|
||||
{
|
||||
public function __construct(
|
||||
EntityManager $entityManager,
|
||||
User $user,
|
||||
Portal $portal,
|
||||
Config $config = null,
|
||||
Metadata $metadata = null,
|
||||
FieldUtil $fieldUtil = null,
|
||||
DataCache $dataCache
|
||||
) {
|
||||
if (empty($portal)) {
|
||||
throw new Error("No portal was passed to AclPortal\\Table constructor.");
|
||||
}
|
||||
|
||||
$this->portal = $portal;
|
||||
parent::__construct($user, $config, $fileManager, $metadata, $fieldManager);
|
||||
|
||||
parent::__construct($entityManager, $user, $config, $metadata, $fieldUtil, $dataCache);
|
||||
}
|
||||
|
||||
protected function getPortal()
|
||||
@@ -66,27 +83,37 @@ class Table extends \Espo\Core\Acl\Table
|
||||
return $this->portal;
|
||||
}
|
||||
|
||||
protected function initCacheFilePath()
|
||||
protected function initCacheKey()
|
||||
{
|
||||
$this->cacheFilePath = 'data/cache/application/acl-portal/'.$this->getPortal()->id.'/' . $this->getUser()->id . '.php';
|
||||
$this->cacheKey = 'aclPortal/' . $this->getPortal()->id.'/' . $this->getUser()->id;
|
||||
}
|
||||
|
||||
protected function getRoleList()
|
||||
{
|
||||
$roleList = [];
|
||||
|
||||
$userRoleList = $this->getUser()->get('portalRoles');
|
||||
if (!(is_array($userRoleList) || $userRoleList instanceof \Traversable)) {
|
||||
$userRoleList = $this->entityManager
|
||||
->getRepository('User')
|
||||
->getRelation($this->getUser(), 'portalRoles')
|
||||
->find();
|
||||
|
||||
if (! $userRoleList instanceof Traversable) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
foreach ($userRoleList as $role) {
|
||||
$roleList[] = $role;
|
||||
}
|
||||
|
||||
$portalRoleList = $this->getPortal()->get('portalRoles');
|
||||
if (!(is_array($portalRoleList) || $portalRoleList instanceof \Traversable)) {
|
||||
$portalRoleList = $this->entityManager
|
||||
->getRepository('Portal')
|
||||
->getRelation($this->getPortal(), 'portalRoles')
|
||||
->find();
|
||||
|
||||
if (! $portalRoleList instanceof Traversable) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
foreach ($portalRoleList as $role) {
|
||||
$roleList[] = $role;
|
||||
}
|
||||
@@ -132,4 +159,3 @@ class Table extends \Espo\Core\Acl\Table
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user