mirror of
https://github.com/espocrm/espocrm.git
synced 2026-03-09 14:57:01 +00:00
Compare commits
1694 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a64537da1 | ||
|
|
6d3c765ef6 | ||
|
|
8f96c8c3e1 | ||
|
|
43749cbabb | ||
|
|
803185b747 | ||
|
|
4f5886fbd7 | ||
|
|
c28ba84f03 | ||
|
|
7f08df77f6 | ||
|
|
48116f8dcf | ||
|
|
0784fd589a | ||
|
|
8b8d3ee158 | ||
|
|
628e7e8131 | ||
|
|
408a161595 | ||
|
|
b46c6d76a5 | ||
|
|
d78f7d499a | ||
|
|
5662fed5eb | ||
|
|
3f4cdf629f | ||
|
|
bedab9c780 | ||
|
|
291bcf1684 | ||
|
|
bd9e1bf353 | ||
|
|
7d507460be | ||
|
|
99b4205181 | ||
|
|
5ae56454fc | ||
|
|
1e2369bba1 | ||
|
|
c42bfa3aab | ||
|
|
2b01cefb66 | ||
|
|
0b72848e33 | ||
|
|
ecc7f80c80 | ||
|
|
be5fff011b | ||
|
|
d7a29fdc71 | ||
|
|
e1c2203750 | ||
|
|
66cde8c86c | ||
|
|
10fb006fd8 | ||
|
|
b94c5ff869 | ||
|
|
39898bbbe8 | ||
|
|
62a6c2761f | ||
|
|
6fe6a52f47 | ||
|
|
c5d7fa5b0b | ||
|
|
ceaac6c3dc | ||
|
|
490843e371 | ||
|
|
b290fbfeb2 | ||
|
|
ca1e649b11 | ||
|
|
5e74cd8d06 | ||
|
|
63508423cc | ||
|
|
a5961811b4 | ||
|
|
fdb1595cd5 | ||
|
|
4bfedf8db3 | ||
|
|
325429eb52 | ||
|
|
e558a3139c | ||
|
|
e424c26963 | ||
|
|
b2cc1f97c4 | ||
|
|
d48ce0fc58 | ||
|
|
b61462a1cd | ||
|
|
1bb82151da | ||
|
|
9852e25c6e | ||
|
|
90fe358753 | ||
|
|
bd65e62fce | ||
|
|
fff9d7a781 | ||
|
|
8441fa05d2 | ||
|
|
9e86649b1c | ||
|
|
3c50a0e058 | ||
|
|
e571a63a30 | ||
|
|
14d405542b | ||
|
|
429d03504c | ||
|
|
fc24840a7b | ||
|
|
603ca985df | ||
|
|
a12940e36c | ||
|
|
dcab0ad7d0 | ||
|
|
89124b354b | ||
|
|
03c275f495 | ||
|
|
da89e0a321 | ||
|
|
90af51b8b9 | ||
|
|
92b82b9d4c | ||
|
|
c0aabe65d7 | ||
|
|
6e6478bb6a | ||
|
|
d457b461e7 | ||
|
|
2f6673f8d9 | ||
|
|
ee94408394 | ||
|
|
6d343cb564 | ||
|
|
f5c07c9d6b | ||
|
|
f9f2eaeb1f | ||
|
|
16d8015d92 | ||
|
|
2fa330d6f3 | ||
|
|
3bd2c8a813 | ||
|
|
12ba23e8ab | ||
|
|
e7c3314285 | ||
|
|
13ec8dd49f | ||
|
|
80146e8bca | ||
|
|
b974c673d2 | ||
|
|
c7ae23fd01 | ||
|
|
ccd21ebf76 | ||
|
|
f5c1fdebf3 | ||
|
|
863cd03eb3 | ||
|
|
d3eb08b171 | ||
|
|
5a39450e6d | ||
|
|
dd40786b6f | ||
|
|
35e894a391 | ||
|
|
8bd2b32908 | ||
|
|
ce58cc262f | ||
|
|
2dad119c3c | ||
|
|
7e872e845c | ||
|
|
678e39b0e5 | ||
|
|
ce10665bc3 | ||
|
|
d091ebbd23 | ||
|
|
68e495566d | ||
|
|
9943e413c1 | ||
|
|
8f4f4b4ac1 | ||
|
|
4a7f967d4f | ||
|
|
abf5690239 | ||
|
|
bc6946e6be | ||
|
|
06001a4b4c | ||
|
|
d5d913cf12 | ||
|
|
37f7fa8464 | ||
|
|
27c98bdf0f | ||
|
|
2c223a0739 | ||
|
|
3c756e6252 | ||
|
|
e7331efcbe | ||
|
|
62ae30aa8f | ||
|
|
c49f6c8045 | ||
|
|
16ce6eb0b9 | ||
|
|
701422892a | ||
|
|
abd2016444 | ||
|
|
cc68bd640e | ||
|
|
c247a24db4 | ||
|
|
b8a484cb7e | ||
|
|
61d546a649 | ||
|
|
23b9c08752 | ||
|
|
191f834e0f | ||
|
|
9fa9198963 | ||
|
|
f89e8acb79 | ||
|
|
33fbe125fa | ||
|
|
d93dd9f6df | ||
|
|
08841f7da5 | ||
|
|
725222e4c2 | ||
|
|
003f7864b3 | ||
|
|
48efcdb7a8 | ||
|
|
7bc8c2f161 | ||
|
|
4e3cb38477 | ||
|
|
20ac2dd2d7 | ||
|
|
0cabbaab01 | ||
|
|
8cc7adee3f | ||
|
|
9d297d5587 | ||
|
|
6e2409d969 | ||
|
|
b8cdbfb585 | ||
|
|
5c06345424 | ||
|
|
142cbfa4b8 | ||
|
|
04018bba93 | ||
|
|
e77143640b | ||
|
|
2964fff2cd | ||
|
|
0c8cdb61dc | ||
|
|
a9e0dad0dd | ||
|
|
b34d8ebca8 | ||
|
|
ed795d0d49 | ||
|
|
2673f60831 | ||
|
|
09b56fd8c0 | ||
|
|
44175614d4 | ||
|
|
1b7657bf0c | ||
|
|
469fcdb8bc | ||
|
|
fde53deae6 | ||
|
|
e470bf4eb1 | ||
|
|
5011f1e197 | ||
|
|
bc37a83982 | ||
|
|
d97b9be4c9 | ||
|
|
e51a9621f3 | ||
|
|
6dde915b7e | ||
|
|
a109e1353d | ||
|
|
8018478b13 | ||
|
|
9edb8bd59a | ||
|
|
3b3b05286a | ||
|
|
bb534e6c46 | ||
|
|
fce05fd5d2 | ||
|
|
875be06d28 | ||
|
|
0125ad0db9 | ||
|
|
4634c5d30f | ||
|
|
6bd717fe5d | ||
|
|
be56156516 | ||
|
|
3da90da35b | ||
|
|
6b9ebdc731 | ||
|
|
1d0d12649f | ||
|
|
e2aa06f755 | ||
|
|
19e4943410 | ||
|
|
668233a978 | ||
|
|
5a3bfe5f68 | ||
|
|
90ff998e5c | ||
|
|
71b7c1af2b | ||
|
|
997e5cc44c | ||
|
|
3f8e6b2854 | ||
|
|
3bf7ec7e9d | ||
|
|
1d7c47f005 | ||
|
|
b1d77bdca0 | ||
|
|
36a9563e69 | ||
|
|
67e1917426 | ||
|
|
344714fda0 | ||
|
|
836de2d624 | ||
|
|
2c5e548c07 | ||
|
|
f0ac0dc03e | ||
|
|
2bb53c5495 | ||
|
|
e0256018de | ||
|
|
019cea2641 | ||
|
|
d8286a2de6 | ||
|
|
685f034e15 | ||
|
|
3210733bc7 | ||
|
|
38ad6def8c | ||
|
|
b2d31d0230 | ||
|
|
ddd9a1dc88 | ||
|
|
3355ee7192 | ||
|
|
a4e694412c | ||
|
|
7222a5a546 | ||
|
|
423f26cb29 | ||
|
|
fe4679cbc5 | ||
|
|
df0c56bd9b | ||
|
|
269f38e07c | ||
|
|
48c89aec97 | ||
|
|
2cca4fe429 | ||
|
|
8672fd1dff | ||
|
|
755d3b863a | ||
|
|
453e2bd1a0 | ||
|
|
9ab35f457c | ||
|
|
0a60ffefa5 | ||
|
|
47fad3eb07 | ||
|
|
7cca2353d8 | ||
|
|
089c8d56f8 | ||
|
|
17c9379c15 | ||
|
|
d6d83a209f | ||
|
|
86b77266bb | ||
|
|
9339082f9b | ||
|
|
bac385545c | ||
|
|
3e4b4f2df8 | ||
|
|
990406889b | ||
|
|
487d8dc909 | ||
|
|
4945e19fdf | ||
|
|
0eecaf3d5a | ||
|
|
337dd67c36 | ||
|
|
e8f0d38554 | ||
|
|
e7904b976b | ||
|
|
2360b75f97 | ||
|
|
785c3a8545 | ||
|
|
1d1fccaed9 | ||
|
|
fb921bb023 | ||
|
|
0ffe39cec8 | ||
|
|
21034bfeb2 | ||
|
|
94188f4256 | ||
|
|
20f6d67f40 | ||
|
|
a3c289aee2 | ||
|
|
e56121bc18 | ||
|
|
5b90d4c3f3 | ||
|
|
f69eed63d7 | ||
|
|
06c173486e | ||
|
|
32f1bfb1c6 | ||
|
|
68fee2ec9f | ||
|
|
d15ab2df31 | ||
|
|
cb7f87a3f1 | ||
|
|
6783331aab | ||
|
|
88d4b6f27c | ||
|
|
3347b7fba8 | ||
|
|
cff703db05 | ||
|
|
16ba6ce7cf | ||
|
|
d4bda1fa9d | ||
|
|
06b5100e87 | ||
|
|
f1a3cd397a | ||
|
|
2481e1a652 | ||
|
|
e32ad76590 | ||
|
|
81f1374f55 | ||
|
|
d3fd314e35 | ||
|
|
20b1b06d5f | ||
|
|
a364ae1923 | ||
|
|
bb2ce37a38 | ||
|
|
fd092a3eb1 | ||
|
|
fb3bdde2c3 | ||
|
|
66623c02e2 | ||
|
|
22fded93fb | ||
|
|
6da20c6f86 | ||
|
|
34834b2d5c | ||
|
|
c971304b03 | ||
|
|
6c7d424349 | ||
|
|
f8e43a3694 | ||
|
|
17dddbc248 | ||
|
|
2246ab0cfd | ||
|
|
a42f2cbe3a | ||
|
|
ad4c039b4a | ||
|
|
2638fdb884 | ||
|
|
c0cfe8a36f | ||
|
|
b9ab872ccd | ||
|
|
018b0b46aa | ||
|
|
7bcd347f2d | ||
|
|
a6db71957a | ||
|
|
1a5df659cf | ||
|
|
2219f52140 | ||
|
|
78dde9c7a1 | ||
|
|
3f0e2a242d | ||
|
|
64aebdde6b | ||
|
|
24f6db674f | ||
|
|
bfc4fb7ca8 | ||
|
|
d46945dd2e | ||
|
|
51130d1aef | ||
|
|
6eb71a789e | ||
|
|
9c77169e6c | ||
|
|
26f1218240 | ||
|
|
8b09a81237 | ||
|
|
f3ee5c654b | ||
|
|
19c8fe9ac5 | ||
|
|
81f45d5679 | ||
|
|
1a413cb54e | ||
|
|
3ab2ffee3c | ||
|
|
53a481622a | ||
|
|
7d72a7ff71 | ||
|
|
314cc7f4c2 | ||
|
|
df1228f720 | ||
|
|
bbd071221a | ||
|
|
9235a3cf79 | ||
|
|
75c966e4bb | ||
|
|
c298f5ec9a | ||
|
|
94986d8835 | ||
|
|
ece8405b33 | ||
|
|
0d2d708ba8 | ||
|
|
ef31f7ba08 | ||
|
|
41bcaf50c4 | ||
|
|
dcd3fa0fc8 | ||
|
|
b48b9683ab | ||
|
|
2548f396ef | ||
|
|
0122b99a4b | ||
|
|
fb747d3f65 | ||
|
|
41cb0de44e | ||
|
|
f7442be97b | ||
|
|
20ccacddf3 | ||
|
|
1ce56bb522 | ||
|
|
f2330b9a51 | ||
|
|
0346a0023e | ||
|
|
bc7d9443b1 | ||
|
|
8188dc065b | ||
|
|
a2025d0a89 | ||
|
|
c46933f427 | ||
|
|
1d31637c2e | ||
|
|
e26026c9ad | ||
|
|
f9e4d1a953 | ||
|
|
6f00a6b2e7 | ||
|
|
a89860dc52 | ||
|
|
626c23b1b5 | ||
|
|
7513d05451 | ||
|
|
5e7aabc46c | ||
|
|
740baffeb4 | ||
|
|
2ba808c371 | ||
|
|
8c7f9f43e4 | ||
|
|
2df98585f9 | ||
|
|
9d4266bed0 | ||
|
|
cb313cd7ef | ||
|
|
ce54d516e9 | ||
|
|
6bce395daf | ||
|
|
b634daca6e | ||
|
|
338e0bb9d4 | ||
|
|
cc576a6af8 | ||
|
|
cbb0159d27 | ||
|
|
515b43614b | ||
|
|
736d23fa6d | ||
|
|
7a096cdde4 | ||
|
|
5b0787474e | ||
|
|
8c87f20374 | ||
|
|
ba35115a48 | ||
|
|
069010d0fe | ||
|
|
fe5878fd99 | ||
|
|
ee37960259 | ||
|
|
3ce8fae228 | ||
|
|
13ebc558e9 | ||
|
|
3ae342c275 | ||
|
|
a283305c7b | ||
|
|
839ceea142 | ||
|
|
a6c698588c | ||
|
|
3007976506 | ||
|
|
8189f0eb2f | ||
|
|
248063f327 | ||
|
|
3a76807a2b | ||
|
|
6e6d3e15ea | ||
|
|
eee086ef52 | ||
|
|
9cce9d7347 | ||
|
|
69d0dbbf1c | ||
|
|
92695c0c39 | ||
|
|
44346e962c | ||
|
|
68ef9ce4ac | ||
|
|
83a415cb33 | ||
|
|
163cf047e5 | ||
|
|
02efdf11f5 | ||
|
|
cc574afd3d | ||
|
|
26a0c4b108 | ||
|
|
77d76fe0ee | ||
|
|
caeaf46403 | ||
|
|
c254f5cc0d | ||
|
|
e7ab75ec5a | ||
|
|
53c3f4b4c3 | ||
|
|
53a12e17f8 | ||
|
|
2ab5489bec | ||
|
|
a5c87272b1 | ||
|
|
130bae6a88 | ||
|
|
c520a02ca5 | ||
|
|
b8d5612d32 | ||
|
|
6ecf5fcdd0 | ||
|
|
5a93722232 | ||
|
|
d6035523e2 | ||
|
|
ff6983c9f3 | ||
|
|
e2673473c5 | ||
|
|
e8bf70ab8e | ||
|
|
f459d5811d | ||
|
|
8b49e72f2b | ||
|
|
53bf9b024a | ||
|
|
a1292cf933 | ||
|
|
5ccfd77669 | ||
|
|
67a27ec21b | ||
|
|
25aed1a1c4 | ||
|
|
b1de15339a | ||
|
|
3f819500d3 | ||
|
|
435be717c3 | ||
|
|
d2f4fbc59d | ||
|
|
2589801993 | ||
|
|
9e0a77588d | ||
|
|
5f954c22da | ||
|
|
423e2ca544 | ||
|
|
d033b26e57 | ||
|
|
6176b8770f | ||
|
|
cbced73f6e | ||
|
|
9168dd69f2 | ||
|
|
c69fc7f2c9 | ||
|
|
faebc13757 | ||
|
|
8ab7452859 | ||
|
|
bd6e0023c3 | ||
|
|
5f00c85882 | ||
|
|
6c8cffeb2a | ||
|
|
683eb5a491 | ||
|
|
07035bf8bf | ||
|
|
9e8df41174 | ||
|
|
64736349f0 | ||
|
|
71f389703d | ||
|
|
669701c6fd | ||
|
|
02917943b6 | ||
|
|
7221963990 | ||
|
|
062043d5e1 | ||
|
|
acf1833d9f | ||
|
|
9c45902213 | ||
|
|
f9674bd60c | ||
|
|
831d840cc5 | ||
|
|
adc3df5144 | ||
|
|
3f6f718cd9 | ||
|
|
c35b5c5aa5 | ||
|
|
0050f44a8a | ||
|
|
f86479afdc | ||
|
|
f4cbb5e56a | ||
|
|
6c8e9b129a | ||
|
|
c033cb171e | ||
|
|
bdf7d56e0c | ||
|
|
3e7326f605 | ||
|
|
61e51bfb31 | ||
|
|
c35d934ba7 | ||
|
|
80fa391daa | ||
|
|
1092b17a13 | ||
|
|
1de6568918 | ||
|
|
ee7c7046ac | ||
|
|
19638dd649 | ||
|
|
df30678484 | ||
|
|
b05697d874 | ||
|
|
91177fc0d2 | ||
|
|
e56c64dba4 | ||
|
|
7ecc0dc6a9 | ||
|
|
808e2f8788 | ||
|
|
7682539114 | ||
|
|
890649e46b | ||
|
|
33d710d265 | ||
|
|
0558739e67 | ||
|
|
552983ca33 | ||
|
|
d328872479 | ||
|
|
6cee542972 | ||
|
|
afb77424d4 | ||
|
|
c7465970bd | ||
|
|
4f6ae321b9 | ||
|
|
b92970992f | ||
|
|
4923984e2d | ||
|
|
071fb20f7b | ||
|
|
3e7d0f23c8 | ||
|
|
268009e5f6 | ||
|
|
573d810d37 | ||
|
|
b3f3226f55 | ||
|
|
511b7b0d9d | ||
|
|
3e54b58c8c | ||
|
|
92467afad3 | ||
|
|
a1c6004c4f | ||
|
|
0d8943d86a | ||
|
|
07ca2b6ceb | ||
|
|
e019de24cc | ||
|
|
7b417313a2 | ||
|
|
cb6777d19b | ||
|
|
2790834c62 | ||
|
|
9162b61a3f | ||
|
|
8488afb6f6 | ||
|
|
1b518f4b6f | ||
|
|
92bad5240b | ||
|
|
5f70d1b408 | ||
|
|
0941bd440e | ||
|
|
2fabf4bfe1 | ||
|
|
f9e13e8093 | ||
|
|
d1f0fd4fb8 | ||
|
|
7c9ae7caa4 | ||
|
|
2adcab4417 | ||
|
|
2fd367cd7d | ||
|
|
82c74c23f9 | ||
|
|
9de57ba4b2 | ||
|
|
53d544a6d5 | ||
|
|
5f550f38f9 | ||
|
|
63a1c0be66 | ||
|
|
a43903dfd2 | ||
|
|
ce369c774b | ||
|
|
971543ab02 | ||
|
|
bf8b9a7e16 | ||
|
|
d727cea9ee | ||
|
|
373e05d219 | ||
|
|
8fa5cf4af1 | ||
|
|
776ae3ac9d | ||
|
|
f3d9f93564 | ||
|
|
d32995df9b | ||
|
|
6c8e5b2c92 | ||
|
|
b44e536e35 | ||
|
|
8e9045352d | ||
|
|
97499626db | ||
|
|
65cf99faad | ||
|
|
5719f29262 | ||
|
|
06811d9fcc | ||
|
|
9000dba6fd | ||
|
|
b8734f68da | ||
|
|
a6eb683770 | ||
|
|
00a8c7326d | ||
|
|
8faa5b91e2 | ||
|
|
afeeba80f7 | ||
|
|
b6daa5accb | ||
|
|
7bf5c9ba11 | ||
|
|
2433da0db7 | ||
|
|
b820c880a7 | ||
|
|
c7c31ee73a | ||
|
|
18ec05f63e | ||
|
|
cf42c312cf | ||
|
|
d76bb5677b | ||
|
|
9afaf54dea | ||
|
|
99490fdf4c | ||
|
|
244edbbcac | ||
|
|
2fab584c60 | ||
|
|
9d797ae17a | ||
|
|
15860096d3 | ||
|
|
c0ac579b7f | ||
|
|
50d5280d2c | ||
|
|
b1c5526286 | ||
|
|
384902f349 | ||
|
|
86e75a4d47 | ||
|
|
0401f0cce6 | ||
|
|
5b54f1b579 | ||
|
|
62370b6c8d | ||
|
|
edc9d00be3 | ||
|
|
73d4250daa | ||
|
|
aa052560bf | ||
|
|
b35c2ca97f | ||
|
|
d2946cacfa | ||
|
|
eaaceb74bd | ||
|
|
2138591b72 | ||
|
|
3583b586c2 | ||
|
|
965e5149f3 | ||
|
|
be899bc0e2 | ||
|
|
e23e8854d7 | ||
|
|
2b977e5456 | ||
|
|
1c7f65db1a | ||
|
|
cdda1e0514 | ||
|
|
9fc955d7b5 | ||
|
|
0372374e1f | ||
|
|
c615f0d8db | ||
|
|
f4b9b3fd81 | ||
|
|
da06eb9426 | ||
|
|
2a3f3f2d04 | ||
|
|
19a8535dbe | ||
|
|
363a1c1b06 | ||
|
|
5c2b3f707b | ||
|
|
73f88bc097 | ||
|
|
7ebcc6c71a | ||
|
|
af3baef0cb | ||
|
|
14ea8886f6 | ||
|
|
852977d5db | ||
|
|
4178406453 | ||
|
|
149649be09 | ||
|
|
2a17155f96 | ||
|
|
d9488ba71c | ||
|
|
275c3aa492 | ||
|
|
9f5975eb5b | ||
|
|
a1d5174372 | ||
|
|
547b77423a | ||
|
|
a2c81e5891 | ||
|
|
f1070612b4 | ||
|
|
c5828a9feb | ||
|
|
62b2314ff3 | ||
|
|
dce9c437bf | ||
|
|
e5a012cdc7 | ||
|
|
17f18e36fd | ||
|
|
e99fca0252 | ||
|
|
316a865d02 | ||
|
|
0f6049b36f | ||
|
|
63771d37b3 | ||
|
|
9e50367303 | ||
|
|
71b0dfec04 | ||
|
|
90c692648d | ||
|
|
e968424b9d | ||
|
|
ed77c8758c | ||
|
|
caa536f17a | ||
|
|
7463caf8ee | ||
|
|
d4096b8dfb | ||
|
|
f73e4f2b47 | ||
|
|
fc93a3d029 | ||
|
|
0bcbfdd223 | ||
|
|
6b1f8de16a | ||
|
|
64741be18b | ||
|
|
8ad8a42ecd | ||
|
|
ead2d3829b | ||
|
|
55be65d48c | ||
|
|
ecee6e7477 | ||
|
|
c037870e7e | ||
|
|
e86c4a9b6b | ||
|
|
586e28bd26 | ||
|
|
3ca7cdffcc | ||
|
|
d36dc4bc1c | ||
|
|
ede53970bf | ||
|
|
0348778bc1 | ||
|
|
3e2a3597ad | ||
|
|
1c5afb52b3 | ||
|
|
8601c8fc24 | ||
|
|
5b3b7b583d | ||
|
|
9f0827e478 | ||
|
|
a0858acae3 | ||
|
|
49a49d6103 | ||
|
|
7d9d248ab7 | ||
|
|
acc0b71a7a | ||
|
|
1f3a0bb5dd | ||
|
|
c5611a691f | ||
|
|
72927e34e2 | ||
|
|
6c2a114c47 | ||
|
|
edb5f30ddd | ||
|
|
94881de082 | ||
|
|
ce0baf450e | ||
|
|
3854141292 | ||
|
|
152bc76e57 | ||
|
|
6d01717f7e | ||
|
|
b026ff8066 | ||
|
|
f9270a20d4 | ||
|
|
5196343479 | ||
|
|
3606051b91 | ||
|
|
3fc4b487b3 | ||
|
|
692e0b43e3 | ||
|
|
c273e26cf9 | ||
|
|
a26a48fac7 | ||
|
|
2a42ab8db5 | ||
|
|
16d66aa642 | ||
|
|
85d167aad4 | ||
|
|
cbec1cbbe5 | ||
|
|
1509f8d18f | ||
|
|
e6d618f142 | ||
|
|
455c51e806 | ||
|
|
d9f553d47f | ||
|
|
5dc08ecf05 | ||
|
|
2d791010dc | ||
|
|
cc8e2511e0 | ||
|
|
92b0af358e | ||
|
|
9526614a3d | ||
|
|
9fca726329 | ||
|
|
83a9f53fe3 | ||
|
|
da27b68312 | ||
|
|
d2e90f7853 | ||
|
|
0f7cd11ffe | ||
|
|
b006349528 | ||
|
|
f03637ac64 | ||
|
|
e0909af3fe | ||
|
|
1ed38e28cd | ||
|
|
567db6b204 | ||
|
|
04689b8ed0 | ||
|
|
c007010a73 | ||
|
|
498dd5478b | ||
|
|
4e9e71b1bb | ||
|
|
06771f2f3e | ||
|
|
bafe2dd471 | ||
|
|
4591d8f634 | ||
|
|
f1e42f931b | ||
|
|
8cf31f17cb | ||
|
|
40c8d0063d | ||
|
|
157f03b38f | ||
|
|
ffa5cf44ea | ||
|
|
cc88ab9290 | ||
|
|
1a179c2373 | ||
|
|
066f815d0c | ||
|
|
8078043f3c | ||
|
|
06de056bb5 | ||
|
|
630bdd7885 | ||
|
|
847440ad6c | ||
|
|
0271deddde | ||
|
|
65a385dfcf | ||
|
|
71dd872618 | ||
|
|
8025c1e101 | ||
|
|
fe15332531 | ||
|
|
508699d0aa | ||
|
|
0465f826f1 | ||
|
|
f78d38d592 | ||
|
|
db7142e4f9 | ||
|
|
e1b4fd6bdb | ||
|
|
483ffb91fa | ||
|
|
76a5721c72 | ||
|
|
27c5bec755 | ||
|
|
8e3016e301 | ||
|
|
d93f08b18e | ||
|
|
701abe129b | ||
|
|
e585ef0a0d | ||
|
|
beaafccd2a | ||
|
|
cad2e0078a | ||
|
|
3dc35d0f16 | ||
|
|
2d73c62936 | ||
|
|
dad5c3724e | ||
|
|
d0f3373515 | ||
|
|
bbe578ce0a | ||
|
|
51a8a3a302 | ||
|
|
e3d3cae647 | ||
|
|
bfed154feb | ||
|
|
bbe9281cb0 | ||
|
|
b2a6ababd2 | ||
|
|
343ad33a78 | ||
|
|
0a51e27d57 | ||
|
|
dc1dc5de9b | ||
|
|
868aa64e51 | ||
|
|
0a9c0dcb20 | ||
|
|
c1855e84a9 | ||
|
|
4aa4b17c9f | ||
|
|
689d59df2e | ||
|
|
6fd55a6c2d | ||
|
|
ae141c2415 | ||
|
|
ab88abfb5e | ||
|
|
90ab1de10f | ||
|
|
e3bf337a07 | ||
|
|
69329a87db | ||
|
|
663eacb962 | ||
|
|
38ce78fced | ||
|
|
ddf904fa0f | ||
|
|
eba3138d16 | ||
|
|
05227281e7 | ||
|
|
16672722d4 | ||
|
|
7fcc80025c | ||
|
|
da264fcc71 | ||
|
|
2b6bf419a1 | ||
|
|
310f83e9c2 | ||
|
|
45cdae005a | ||
|
|
15b4ce9657 | ||
|
|
665dd2f242 | ||
|
|
ecde690c3a | ||
|
|
90728dfc18 | ||
|
|
d519318804 | ||
|
|
0d8a5ce5a1 | ||
|
|
3dc29b1bae | ||
|
|
0819b69d4a | ||
|
|
8ac3d4e90b | ||
|
|
a577950cc0 | ||
|
|
3cf6edd051 | ||
|
|
5a19b90b13 | ||
|
|
daa3980122 | ||
|
|
bab460c521 | ||
|
|
64b3f55d7a | ||
|
|
ca053b57c5 | ||
|
|
a07f6b032a | ||
|
|
ad0fbb577f | ||
|
|
df55323a04 | ||
|
|
a081237b2e | ||
|
|
b604501eba | ||
|
|
b4368862e0 | ||
|
|
81043740dc | ||
|
|
b6beb39192 | ||
|
|
687f66908d | ||
|
|
a5ef17e06c | ||
|
|
cb8a43da52 | ||
|
|
a575a69704 | ||
|
|
7d6b1d7bcf | ||
|
|
31b875bbdd | ||
|
|
33b0ca8824 | ||
|
|
e7d388f55f | ||
|
|
559ef609e0 | ||
|
|
c382cb0e7b | ||
|
|
3912f937bd | ||
|
|
7222cd6436 | ||
|
|
540c58a564 | ||
|
|
6f7c1f72f6 | ||
|
|
5bf4ab368d | ||
|
|
42645a9bc1 | ||
|
|
5be0f5c71e | ||
|
|
f029675856 | ||
|
|
3a1f5bec0f | ||
|
|
1356e7b9f8 | ||
|
|
ab10b6c036 | ||
|
|
ed0e5112a3 | ||
|
|
46da1bb8a0 | ||
|
|
cd216007af | ||
|
|
bd96cf9e73 | ||
|
|
67c0be5699 | ||
|
|
695dc2eb42 | ||
|
|
516bd037e7 | ||
|
|
f122d3d3bb | ||
|
|
e7b64bcc6f | ||
|
|
3df7349d2b | ||
|
|
47b6471717 | ||
|
|
95280e8f7b | ||
|
|
296f73a407 | ||
|
|
918faa340b | ||
|
|
4526224ae4 | ||
|
|
e35b8e799f | ||
|
|
06fffdf1cf | ||
|
|
ac133fb257 | ||
|
|
2ab3274768 | ||
|
|
b7f22352ed | ||
|
|
7873340616 | ||
|
|
546a0440cd | ||
|
|
9783271a1d | ||
|
|
35ad3177d3 | ||
|
|
06b6be0c9e | ||
|
|
a390d71ef0 | ||
|
|
39dafddb4b | ||
|
|
2706f8681f | ||
|
|
44babbc240 | ||
|
|
2fe1403cdb | ||
|
|
665bfc3649 | ||
|
|
dd3746d435 | ||
|
|
b881c11672 | ||
|
|
2c0370f546 | ||
|
|
6e1b3c64ec | ||
|
|
17c3fe5699 | ||
|
|
8d20ba1ffd | ||
|
|
0d124269f0 | ||
|
|
1fcf326ecc | ||
|
|
76397085a3 | ||
|
|
1b4bdfaa12 | ||
|
|
040e6ddeb7 | ||
|
|
d148b53d52 | ||
|
|
90f125567c | ||
|
|
8cc72d1c7b | ||
|
|
747c1bbf12 | ||
|
|
22b5f89991 | ||
|
|
d19de50f7f | ||
|
|
d1dd39bf5a | ||
|
|
d07f5b8e18 | ||
|
|
9fa9440d7b | ||
|
|
042b3ae66e | ||
|
|
c07004faf4 | ||
|
|
b3905eedf4 | ||
|
|
9cffccbe73 | ||
|
|
43322186b5 | ||
|
|
a0ab01a77f | ||
|
|
d7b9b65990 | ||
|
|
e23a22d259 | ||
|
|
5d946a44f6 | ||
|
|
437f12245e | ||
|
|
b1cefdfbbe | ||
|
|
857ee7fb98 | ||
|
|
3b68d3dd13 | ||
|
|
4125b4f9a0 | ||
|
|
913cad075a | ||
|
|
8a542adb7e | ||
|
|
481f352f5a | ||
|
|
8ce2c7a40b | ||
|
|
8dda7cf2ea | ||
|
|
01eacf875d | ||
|
|
6da9ac6af1 | ||
|
|
418ad2776d | ||
|
|
2a2c07af87 | ||
|
|
ca898cf240 | ||
|
|
05b6f83ba1 | ||
|
|
68ccaf8818 | ||
|
|
162e0ab546 | ||
|
|
4c9df4da1b | ||
|
|
d1c2e82abc | ||
|
|
c054cde199 | ||
|
|
f113905edd | ||
|
|
b4e2b91c31 | ||
|
|
3568345343 | ||
|
|
4f1223e9a0 | ||
|
|
c6a172a6d0 | ||
|
|
1da70019fb | ||
|
|
a84962d96d | ||
|
|
98253fb0a8 | ||
|
|
7887e4c7e3 | ||
|
|
abe383d532 | ||
|
|
a8078c3fc6 | ||
|
|
9fd07d2fab | ||
|
|
f42fbd0098 | ||
|
|
d431699dbe | ||
|
|
9b883d3e59 | ||
|
|
955e53304f | ||
|
|
ed7085e952 | ||
|
|
e274907260 | ||
|
|
96520162d3 | ||
|
|
bb091a0301 | ||
|
|
7253082497 | ||
|
|
0e446c6f08 | ||
|
|
7af208c2a0 | ||
|
|
60e0c03a1e | ||
|
|
89f75bfb37 | ||
|
|
b83a66cf49 | ||
|
|
3cd6deb732 | ||
|
|
59fff4cc80 | ||
|
|
35e7b5c5ee | ||
|
|
e7415a2317 | ||
|
|
69c5af4d97 | ||
|
|
dca1d34685 | ||
|
|
105922b026 | ||
|
|
1dd6ea1bac | ||
|
|
54f1fb27a1 | ||
|
|
27353b1fbf | ||
|
|
afc34a9730 | ||
|
|
5b4e3cbc66 | ||
|
|
5b763b7519 | ||
|
|
fb07cec466 | ||
|
|
04ea13d8e2 | ||
|
|
9208775f34 | ||
|
|
308c480317 | ||
|
|
85af141f9f | ||
|
|
d7a88b382d | ||
|
|
8133682288 | ||
|
|
0b035b89c0 | ||
|
|
d5930542d9 | ||
|
|
831dfc5bfd | ||
|
|
078808604c | ||
|
|
35f8c5652a | ||
|
|
14d8f88985 | ||
|
|
2ae0a118b6 | ||
|
|
1a51759b0f | ||
|
|
acc2eb1a55 | ||
|
|
e513a72504 | ||
|
|
a8fa8bfa00 | ||
|
|
e8473e5ce0 | ||
|
|
5a6583b93b | ||
|
|
977b9adeff | ||
|
|
382ba22cd7 | ||
|
|
03e16b81e5 | ||
|
|
455bb2a298 | ||
|
|
0b19bd4bb5 | ||
|
|
48b0063c69 | ||
|
|
9395fa886e | ||
|
|
a6c95ff0d5 | ||
|
|
0a115a3603 | ||
|
|
fcb3158a06 | ||
|
|
77f60734f6 | ||
|
|
1c10ddd7b8 | ||
|
|
d8dd048a89 | ||
|
|
42812f8bb2 | ||
|
|
b5a9690b63 | ||
|
|
268354f3c6 | ||
|
|
3ca68f2539 | ||
|
|
08f2c5ea72 | ||
|
|
4d1f3d214d | ||
|
|
fab13b3140 | ||
|
|
b741898ce9 | ||
|
|
45ac0f7fee | ||
|
|
406487d50c | ||
|
|
d73608e16b | ||
|
|
8328e8c644 | ||
|
|
b89c28fe96 | ||
|
|
19a96d2e3d | ||
|
|
1c68c805aa | ||
|
|
a823e06f13 | ||
|
|
cef7a919b8 | ||
|
|
3117db022f | ||
|
|
7b642e40b0 | ||
|
|
89ff3dcf15 | ||
|
|
0336b76a96 | ||
|
|
76cac35c54 | ||
|
|
79c8d25a80 | ||
|
|
58d926be82 | ||
|
|
3044f83690 | ||
|
|
f2d5b2685e | ||
|
|
17abe18d01 | ||
|
|
09880ff8f1 | ||
|
|
23f4686577 | ||
|
|
088edbf708 | ||
|
|
e2f6c8abe7 | ||
|
|
f2735e5fbc | ||
|
|
f8106a81c7 | ||
|
|
1d7bce5343 | ||
|
|
224e0e8625 | ||
|
|
d5b3a7d2ea | ||
|
|
bd0be4542e | ||
|
|
e78052ce33 | ||
|
|
24f79b9206 | ||
|
|
df9633503b | ||
|
|
5e1c12f4b1 | ||
|
|
5699c3d15e | ||
|
|
cd88e8ac7a | ||
|
|
3c83e1dcd3 | ||
|
|
d232838676 | ||
|
|
2ae0f48619 | ||
|
|
170782d17f | ||
|
|
2cbdbbf6fb | ||
|
|
0a750f373f | ||
|
|
92ff5e1859 | ||
|
|
cb3257a890 | ||
|
|
9377cd150d | ||
|
|
e1f2fd8094 | ||
|
|
70dcf6ed69 | ||
|
|
6c62dbc604 | ||
|
|
b6807091a5 | ||
|
|
33f3784b3a | ||
|
|
0e9b552796 | ||
|
|
111e1a278c | ||
|
|
34ecdd7533 | ||
|
|
7ec9c1c5ae | ||
|
|
1055fd8a79 | ||
|
|
03102da373 | ||
|
|
23ea8b418a | ||
|
|
9269fa1933 | ||
|
|
5a1a9f17a0 | ||
|
|
c4ca71a7f6 | ||
|
|
991d111ac3 | ||
|
|
7f18fb0cba | ||
|
|
c4d54ffa71 | ||
|
|
1ee2d5c58b | ||
|
|
94d9d0fc38 | ||
|
|
9252905103 | ||
|
|
e9b49a5317 | ||
|
|
9e50337c99 | ||
|
|
07b68d9b7d | ||
|
|
c7d1fc7c35 | ||
|
|
18b5b99854 | ||
|
|
e60d4ba19b | ||
|
|
dcf2b49b19 | ||
|
|
4aecb4a255 | ||
|
|
7cf7f2ffd8 | ||
|
|
a78161799c | ||
|
|
6a37eee973 | ||
|
|
bb5be010f2 | ||
|
|
dcd4e56aa7 | ||
|
|
12f928c192 | ||
|
|
1b47b94154 | ||
|
|
f64f4ea1b1 | ||
|
|
faa33769f1 | ||
|
|
1b0487eeea | ||
|
|
7ffc06065f | ||
|
|
515720afda | ||
|
|
3f1e6a6628 | ||
|
|
a94deb9f33 | ||
|
|
b2d91b3fe9 | ||
|
|
1f7e23af28 | ||
|
|
0275b7fe4d | ||
|
|
d4b25090aa | ||
|
|
7b14e7fb79 | ||
|
|
1d156e6af6 | ||
|
|
b91e40b0e7 | ||
|
|
fab908c313 | ||
|
|
24e5cd67dd | ||
|
|
7297e0eb83 | ||
|
|
81cce0ddb7 | ||
|
|
8232ef3dd2 | ||
|
|
b930180907 | ||
|
|
ef6866773c | ||
|
|
415dc7607e | ||
|
|
726f2abd96 | ||
|
|
894acad7ec | ||
|
|
d9d7de8804 | ||
|
|
dcd0469977 | ||
|
|
434112eff3 | ||
|
|
ace9186691 | ||
|
|
29cc42e2e8 | ||
|
|
3ae6120067 | ||
|
|
377f51a962 | ||
|
|
dc61e630b4 | ||
|
|
d2ec8eda86 | ||
|
|
376de3b6d9 | ||
|
|
82ad0cb5cd | ||
|
|
df9431670a | ||
|
|
e96f4fcedd | ||
|
|
1e96b90e28 | ||
|
|
46897ec3dd | ||
|
|
ffa8248ac1 | ||
|
|
7d90a0c59b | ||
|
|
e33e77cb9e | ||
|
|
f7a7c3bc72 | ||
|
|
c8371bef3f | ||
|
|
7c3f285342 | ||
|
|
ed3a7d64c6 | ||
|
|
00b4569f8e | ||
|
|
41e0b90850 | ||
|
|
4d35bce189 | ||
|
|
e6631ba1d1 | ||
|
|
6bae952947 | ||
|
|
2e550af196 | ||
|
|
95ecc416f1 | ||
|
|
ee3b0cb53d | ||
|
|
bfa0ea53a4 | ||
|
|
2f338d6eda | ||
|
|
6e59165786 | ||
|
|
a5a6c8a0a1 | ||
|
|
cd0c397b07 | ||
|
|
7ba7f8aa14 | ||
|
|
91d0660cba | ||
|
|
e8bbcb2ae4 | ||
|
|
834d1808a1 | ||
|
|
2d264eccb7 | ||
|
|
3ec0fe1e03 | ||
|
|
2f85c5cb3c | ||
|
|
69cd4386f1 | ||
|
|
effb81837f | ||
|
|
d0139ada06 | ||
|
|
2920793e40 | ||
|
|
570f886a1f | ||
|
|
21dab40b43 | ||
|
|
801ccf94c7 | ||
|
|
7114b99cbc | ||
|
|
94c99f298e | ||
|
|
ac37a4c232 | ||
|
|
5ad4f30505 | ||
|
|
b4773ade9b | ||
|
|
63f422f93a | ||
|
|
1134f89a5a | ||
|
|
fe0b1b62d1 | ||
|
|
d549ccb300 | ||
|
|
06d596b5cf | ||
|
|
1091b21306 | ||
|
|
780b66d5b4 | ||
|
|
10982e1b19 | ||
|
|
068375022e | ||
|
|
d6bd701492 | ||
|
|
223b4ab8aa | ||
|
|
6aff357473 | ||
|
|
3ef24bb5ad | ||
|
|
9e60ea2209 | ||
|
|
86904380bb | ||
|
|
9cdef865fe | ||
|
|
1d611720c6 | ||
|
|
7285e058cd | ||
|
|
e44617e276 | ||
|
|
81972e26ff | ||
|
|
5a7a4608c9 | ||
|
|
9161e64932 | ||
|
|
cfd514c5a5 | ||
|
|
b42f4ee256 | ||
|
|
a5a4fbca2f | ||
|
|
a706811338 | ||
|
|
ec120dfd75 | ||
|
|
fe7b19b96d | ||
|
|
ff5e1dac43 | ||
|
|
e73f7e9807 | ||
|
|
15e99e3063 | ||
|
|
4edc909f2a | ||
|
|
d1fe29adbe | ||
|
|
980226c4a2 | ||
|
|
c4c4306ad3 | ||
|
|
10732c0a90 | ||
|
|
ca2c4b3ee4 | ||
|
|
03671efe11 | ||
|
|
748529afac | ||
|
|
1a003e5905 | ||
|
|
d6c66b8b43 | ||
|
|
813bc05e74 | ||
|
|
ef447b400c | ||
|
|
aca823df64 | ||
|
|
0832faa2f2 | ||
|
|
7573bd1f92 | ||
|
|
35eb013c97 | ||
|
|
110f769384 | ||
|
|
6ce7c6ccda | ||
|
|
350141727b | ||
|
|
c63fed50d5 | ||
|
|
24a22328c9 | ||
|
|
0504410ee9 | ||
|
|
3a1bc73692 | ||
|
|
f02ee87344 | ||
|
|
7cdcb375b3 | ||
|
|
52c07987c6 | ||
|
|
11dee6e984 | ||
|
|
036cf4330f | ||
|
|
4fec73307b | ||
|
|
a58047433b | ||
|
|
e004884352 | ||
|
|
8aa7236dd6 | ||
|
|
1f297aa42e | ||
|
|
b5a0848513 | ||
|
|
c2ea9781ee | ||
|
|
93255071ac | ||
|
|
313293b57b | ||
|
|
687ceca26e | ||
|
|
55b1c1511a | ||
|
|
04ccf36966 | ||
|
|
3eba144998 | ||
|
|
e2bd509aeb | ||
|
|
9be5342220 | ||
|
|
e96c1deffe | ||
|
|
aa53bc89b4 | ||
|
|
e57bd13f73 | ||
|
|
00f30e01d5 | ||
|
|
fcb73fb3a8 | ||
|
|
fba82f8697 | ||
|
|
13c8fc15f4 | ||
|
|
3a3cac54a2 | ||
|
|
303860d509 | ||
|
|
ba2a740d38 | ||
|
|
87f4508d1c | ||
|
|
fea79ba8c9 | ||
|
|
734ca9b488 | ||
|
|
c695e64652 | ||
|
|
77218b788a | ||
|
|
e5063c2016 | ||
|
|
a6cfb38433 | ||
|
|
ae657ada61 | ||
|
|
ee8963ace8 | ||
|
|
aca76ae3a6 | ||
|
|
5e3cdc594d | ||
|
|
959fdbab94 | ||
|
|
f19952af12 | ||
|
|
9b3c59bfa4 | ||
|
|
55dd4ecf74 | ||
|
|
bbff632fbc | ||
|
|
4127be7f2f | ||
|
|
3a77ea83a3 | ||
|
|
2bd70eee4b | ||
|
|
0dd34df7d7 | ||
|
|
41c0567a4f | ||
|
|
dcaa1e209e | ||
|
|
f1e67d943d | ||
|
|
70afd19f7f | ||
|
|
7f6ce95fd6 | ||
|
|
035e1ef9eb | ||
|
|
4964fbb1b2 | ||
|
|
2ca3aade8c | ||
|
|
878f33929b | ||
|
|
ae340b3279 | ||
|
|
0da0b8974c | ||
|
|
d7804bfa79 | ||
|
|
e451126af7 | ||
|
|
38049d0ef4 | ||
|
|
31e25047cc | ||
|
|
8d105465a3 | ||
|
|
d8c021def2 | ||
|
|
ba08b8a8af | ||
|
|
6974c00d02 | ||
|
|
5446914131 | ||
|
|
68add0bbd7 | ||
|
|
28d0c4dd6e | ||
|
|
a201f61eeb | ||
|
|
0ec428b1ed | ||
|
|
a088ca0875 | ||
|
|
d412766794 | ||
|
|
3cd2a6b74e | ||
|
|
136ae8ae24 | ||
|
|
d80b8ce76b | ||
|
|
392616bdd3 | ||
|
|
4d6387e69d | ||
|
|
eebe244247 | ||
|
|
dbfb1c696f | ||
|
|
2ed620335f | ||
|
|
09efbd175d | ||
|
|
e4c67a4a6f | ||
|
|
e8dd049baf | ||
|
|
4c0f3413f3 | ||
|
|
a052c65b89 | ||
|
|
1bcc81018b | ||
|
|
81ba1b8790 | ||
|
|
ed946a532e | ||
|
|
db34e75d1f | ||
|
|
bcb588b968 | ||
|
|
474e787234 | ||
|
|
691c62f65d | ||
|
|
a3a4d7bf36 | ||
|
|
0bcb9acb02 | ||
|
|
e148b16882 | ||
|
|
3a764fae00 | ||
|
|
f46c2d6079 | ||
|
|
1847132ded | ||
|
|
5765247f9e | ||
|
|
0ef6c7ed55 | ||
|
|
e2e6398026 | ||
|
|
9cd19228a3 | ||
|
|
4268794990 | ||
|
|
de4dcaaecb | ||
|
|
b7b1e3056a | ||
|
|
458a5250f4 | ||
|
|
5cf3856b6a | ||
|
|
8ddd139111 | ||
|
|
952b94d6fe | ||
|
|
42e68ecd63 | ||
|
|
41fac7ef9d | ||
|
|
253736870e | ||
|
|
569ff9a81b | ||
|
|
3b17174431 | ||
|
|
52436afda9 | ||
|
|
531b4cccca | ||
|
|
e06544cc0f | ||
|
|
c48f4c065c | ||
|
|
c4d7a794ea | ||
|
|
e7c777281d | ||
|
|
f687b1543d | ||
|
|
869bee454d | ||
|
|
83bcb9176b | ||
|
|
f72c902b6a | ||
|
|
e3f68e1e98 | ||
|
|
b002846f7c | ||
|
|
f55cccd7c6 | ||
|
|
929badd208 | ||
|
|
d347fe66ca | ||
|
|
42921ac205 | ||
|
|
58304ceded | ||
|
|
0a7d2055be | ||
|
|
1edc3ead8d | ||
|
|
231bd7699a | ||
|
|
4dee2ebb15 | ||
|
|
16f956bdf8 | ||
|
|
a20ab17236 | ||
|
|
68c5fe94bf | ||
|
|
78f536e7f1 | ||
|
|
ee408300fd | ||
|
|
0f146959e5 | ||
|
|
449d6a6fcd | ||
|
|
63afa92fc1 | ||
|
|
2b786af35b | ||
|
|
231f498eca | ||
|
|
046aafd97e | ||
|
|
d376847d12 | ||
|
|
befd48d053 | ||
|
|
ee294f889c | ||
|
|
5fddac8c6b | ||
|
|
a3d73848a9 | ||
|
|
b829e25f5a | ||
|
|
58a6cc1658 | ||
|
|
f7eef2d3c7 | ||
|
|
b5d007c61c | ||
|
|
8570b6f768 | ||
|
|
b02e2f72b6 | ||
|
|
e546509f53 | ||
|
|
244864c984 | ||
|
|
33af4be469 | ||
|
|
29e3470587 | ||
|
|
2b8e764deb | ||
|
|
79f7194429 | ||
|
|
ca0625f15e | ||
|
|
88c9fbd153 | ||
|
|
7f51b3a31e | ||
|
|
ac0b08fdb7 | ||
|
|
de0e5ea72d | ||
|
|
e06d60a3c3 | ||
|
|
128bfb09ea | ||
|
|
7f0283caf8 | ||
|
|
a00cf5e696 | ||
|
|
65a9a0db41 | ||
|
|
8ac98067de | ||
|
|
f725dc395e | ||
|
|
6e8368c7c5 | ||
|
|
ddd9425463 | ||
|
|
4739cd1510 | ||
|
|
5a3c977759 | ||
|
|
85889ab99d | ||
|
|
a9dc3a62fb | ||
|
|
c2ddf89747 | ||
|
|
9f743b718a | ||
|
|
0db4a9c672 | ||
|
|
2dbdb0da60 | ||
|
|
ba59cb1010 | ||
|
|
ee36f73ec2 | ||
|
|
abcbabfbd8 | ||
|
|
5be76d2657 | ||
|
|
ddb92f66e7 | ||
|
|
f52759cd00 | ||
|
|
b0d9d7779f | ||
|
|
8cd6912fee | ||
|
|
bc58ca647d | ||
|
|
1456569cf6 | ||
|
|
e1e7d3d093 | ||
|
|
495cd0f259 | ||
|
|
f4371efa1c | ||
|
|
d83b05e030 | ||
|
|
bf8267c2a8 | ||
|
|
3bad29ed6d | ||
|
|
cbae4d89e4 | ||
|
|
18f89aebbb | ||
|
|
c2b4caf723 | ||
|
|
c5f3a9e366 | ||
|
|
54de942e7f | ||
|
|
0093bf9b79 | ||
|
|
8e9dcb5d62 | ||
|
|
38e909dfca | ||
|
|
6b0276dc22 | ||
|
|
0d19dfa7b8 | ||
|
|
13052b2969 | ||
|
|
1f9de30ff0 | ||
|
|
5b708c1d8d | ||
|
|
ed92d68317 | ||
|
|
330fccad25 | ||
|
|
14243b5ef7 | ||
|
|
9c702814b2 | ||
|
|
ba7acc5443 | ||
|
|
10557ca1ec | ||
|
|
2085701516 | ||
|
|
88ba56618e | ||
|
|
c35545c209 | ||
|
|
585a859340 | ||
|
|
cf214de06c | ||
|
|
2f14c49215 | ||
|
|
72e68e1a6f | ||
|
|
5ff535cab1 | ||
|
|
90e6acf8b9 | ||
|
|
e5e29101fd | ||
|
|
47735aaa0b | ||
|
|
43eb2f8d36 | ||
|
|
1d3c0074d4 | ||
|
|
e8f07e0765 | ||
|
|
54216709b2 | ||
|
|
9f6f034f97 | ||
|
|
e0c74aa2ad | ||
|
|
c083e1b97d | ||
|
|
c4078ed8f1 | ||
|
|
4001e40613 | ||
|
|
a19deac279 | ||
|
|
f655e543b1 | ||
|
|
5031e7f430 | ||
|
|
7f75ee6ed8 | ||
|
|
bde2e0284a | ||
|
|
026826fc84 | ||
|
|
c5f5d88f75 | ||
|
|
3ae4478a70 | ||
|
|
246ece8e3d | ||
|
|
2681acebc6 | ||
|
|
a611301c5a | ||
|
|
3f2ffb851f | ||
|
|
daa37961e4 | ||
|
|
66b336279a | ||
|
|
c99804621d | ||
|
|
2ebbc942d3 | ||
|
|
b81d6962ae | ||
|
|
dc89f75678 | ||
|
|
1ccd70b3fc | ||
|
|
0b3002979e | ||
|
|
25dfa76807 | ||
|
|
46760ccfba | ||
|
|
214a32c472 | ||
|
|
ee6a4607d8 | ||
|
|
5a3b3ab2e8 | ||
|
|
5d5000fbbd | ||
|
|
554668f1bd | ||
|
|
7ff8b1ecee | ||
|
|
0858b845fb | ||
|
|
d480113b90 | ||
|
|
8f8370b5bc | ||
|
|
cfcc1b06f9 | ||
|
|
7dbb842085 | ||
|
|
76bb7d3e49 | ||
|
|
3d79861ffb | ||
|
|
236942c902 | ||
|
|
3efe0a0fe0 | ||
|
|
9d933a5d95 | ||
|
|
24eb879352 | ||
|
|
f0141cd725 | ||
|
|
9a82bc7c5a | ||
|
|
b42bfefef9 | ||
|
|
47e881af60 | ||
|
|
442284fee2 | ||
|
|
49a6454999 | ||
|
|
a95e52a10c | ||
|
|
b2f1f00875 | ||
|
|
91b50cdcc4 | ||
|
|
078bd7c80e | ||
|
|
7641918224 | ||
|
|
a90198d50e | ||
|
|
dcff1748b7 | ||
|
|
5f903d8ce2 | ||
|
|
92fdea37d1 | ||
|
|
7e59fafb3f | ||
|
|
ae58224f95 | ||
|
|
857c5eee6f | ||
|
|
adb7f876aa | ||
|
|
6b282c378a | ||
|
|
aaca0fc47b | ||
|
|
4681493b58 | ||
|
|
37b0c15c1a | ||
|
|
f7c29ef4e6 | ||
|
|
15c08c347d | ||
|
|
4bf5f85644 | ||
|
|
dfe09dac0e | ||
|
|
ce26ecc8c1 | ||
|
|
356971cc66 | ||
|
|
82413cd3fd | ||
|
|
f07f3de1bd | ||
|
|
33072072c4 | ||
|
|
94351646b1 | ||
|
|
c766465527 | ||
|
|
b982ab9daf | ||
|
|
114682b49f | ||
|
|
0e5dcb0d40 | ||
|
|
3f491f1a9a | ||
|
|
62494e9c12 | ||
|
|
c88e34fe8f | ||
|
|
2f3c775d4f | ||
|
|
7078efcc20 | ||
|
|
e73eb87374 | ||
|
|
471a209d86 | ||
|
|
fabb88d611 | ||
|
|
d7d7752868 | ||
|
|
0fe9041272 | ||
|
|
860680aaa1 | ||
|
|
1640bdf172 | ||
|
|
9a1495199b | ||
|
|
15d264acce | ||
|
|
959e8d3acc | ||
|
|
b2edf702ce | ||
|
|
4837474bb0 | ||
|
|
b052eacba7 | ||
|
|
0129305c5f | ||
|
|
5a725b79c9 | ||
|
|
c4f706e918 | ||
|
|
9673f09c9f | ||
|
|
6a84ccbd4e | ||
|
|
14587ee65c | ||
|
|
ef2d129699 | ||
|
|
580c0bef0a | ||
|
|
eef7fef69b | ||
|
|
4d1776f9ff | ||
|
|
f92c21c9c0 | ||
|
|
fac8730ddd | ||
|
|
a3fe58f61b | ||
|
|
696a25fe82 | ||
|
|
04b28dc88f | ||
|
|
abbffb9b15 | ||
|
|
1cd914e5a7 | ||
|
|
6b7bf55acb | ||
|
|
eddcb797e4 | ||
|
|
3014e9253b | ||
|
|
016489ffba | ||
|
|
96ea9e225c | ||
|
|
c4cfc204e7 | ||
|
|
00f5433518 | ||
|
|
3077589cff | ||
|
|
3e3258a4b7 | ||
|
|
dee0f9937e | ||
|
|
1138a4deb1 | ||
|
|
720b14b804 | ||
|
|
196f328312 | ||
|
|
1eb7215162 | ||
|
|
52762cc738 | ||
|
|
c3c38e3510 | ||
|
|
5dcb112621 | ||
|
|
3a9e5fc0cd | ||
|
|
a789fabe2e | ||
|
|
6c8f5e8a16 | ||
|
|
cf08bbdd00 | ||
|
|
80a7765fdd | ||
|
|
119948938e | ||
|
|
b9563cfdc0 | ||
|
|
97e9786fa6 | ||
|
|
60a4541e24 | ||
|
|
52ae19b896 | ||
|
|
8973e976ca | ||
|
|
5ebee4bec7 | ||
|
|
c34b2acf9b | ||
|
|
cd400e5090 | ||
|
|
b433538ab2 | ||
|
|
0a8cc398fe | ||
|
|
a8e37d9486 | ||
|
|
288b017355 | ||
|
|
91745580d8 | ||
|
|
d0d652dba9 | ||
|
|
96f9f89b7b | ||
|
|
2f093011a2 | ||
|
|
cfe55c1975 | ||
|
|
42dc0d754f | ||
|
|
1623257b17 | ||
|
|
3f27256589 | ||
|
|
52c34b6d35 | ||
|
|
485bcfc039 | ||
|
|
1cbcf7048c | ||
|
|
4d90aec5a6 | ||
|
|
85b2a72624 | ||
|
|
acd5d78d30 | ||
|
|
c81e4a8194 | ||
|
|
81fcd57e3a | ||
|
|
6dd940cfab | ||
|
|
a1bdb6c308 | ||
|
|
525397e64d | ||
|
|
e4af67aa68 | ||
|
|
2d7d8812fd | ||
|
|
726ecefd55 | ||
|
|
69fbf9d1ad | ||
|
|
38e89f4e4d | ||
|
|
b3ff273b7c | ||
|
|
fb0be72b56 | ||
|
|
339fbffbb7 | ||
|
|
5c308bb60c | ||
|
|
514fe6e98d | ||
|
|
a80d7fddba | ||
|
|
ec11a89496 | ||
|
|
61aa5f907d | ||
|
|
ac7ba173cd | ||
|
|
fe7a764935 | ||
|
|
c1190d348b | ||
|
|
a81c8518b8 | ||
|
|
d7f533118a | ||
|
|
252fdc4208 | ||
|
|
c1d28655da | ||
|
|
77c2e8abc4 | ||
|
|
b6179463b1 | ||
|
|
99bd7ae437 | ||
|
|
e968388ba7 | ||
|
|
60be576f9d | ||
|
|
47249776a0 | ||
|
|
c8f4fa437d | ||
|
|
09c1b575a7 | ||
|
|
2437b0d901 | ||
|
|
0a33ad6542 | ||
|
|
7ee626edf8 | ||
|
|
600b58be75 | ||
|
|
5601704ac1 | ||
|
|
4eb6386b6e | ||
|
|
9c78970712 | ||
|
|
e465e25adb | ||
|
|
4d735e9e26 | ||
|
|
28bc720bbc | ||
|
|
0fc170e1c3 | ||
|
|
012d98d303 | ||
|
|
180bc99726 | ||
|
|
e537d64c5b | ||
|
|
03de09c836 | ||
|
|
9dc309390a | ||
|
|
8280f31180 | ||
|
|
61a1227f87 | ||
|
|
02c37924aa | ||
|
|
ddc4baf5e2 | ||
|
|
63f975516a | ||
|
|
9160c8319d | ||
|
|
d268b0335e | ||
|
|
6b355c645c | ||
|
|
951e981d8e | ||
|
|
c4c301d363 | ||
|
|
50821924f2 | ||
|
|
3c01011c28 | ||
|
|
d674176356 | ||
|
|
8c60396197 | ||
|
|
4a5b442f35 | ||
|
|
80ea398660 | ||
|
|
69aae1abfa | ||
|
|
c528a98820 | ||
|
|
89fa12c6db | ||
|
|
34598d73a3 | ||
|
|
c507aeae4a | ||
|
|
356ce3cc42 | ||
|
|
560f145324 | ||
|
|
4308fb3f9b | ||
|
|
a17b66c6dc | ||
|
|
f52a3ba773 | ||
|
|
f865338ad0 | ||
|
|
7221207fe5 | ||
|
|
cbc61b533f | ||
|
|
bdff8767f7 | ||
|
|
26f3a9215d | ||
|
|
15e34647fb | ||
|
|
d121aa5a9f | ||
|
|
7d77f754c5 | ||
|
|
2012c4d161 | ||
|
|
fed9ede878 | ||
|
|
4b12716961 | ||
|
|
db2f387a77 | ||
|
|
cb972fdf17 | ||
|
|
5c34d57012 | ||
|
|
3906aa245c | ||
|
|
c86daf3a68 | ||
|
|
9bafd81093 | ||
|
|
5305a27c60 | ||
|
|
6b30ca05db | ||
|
|
b99c17c7cd | ||
|
|
fabaccd3da | ||
|
|
48c0ae93f4 | ||
|
|
30768071bf | ||
|
|
68b619e276 | ||
|
|
d5a441b0e8 | ||
|
|
a5ed0864be | ||
|
|
7db9fe46ff | ||
|
|
99df1bfbaa | ||
|
|
067a4a95e0 | ||
|
|
2154a51831 | ||
|
|
f0bc58e289 | ||
|
|
5fd2f15a5d | ||
|
|
baeef22f4c | ||
|
|
2f6fef0817 | ||
|
|
9cc97c44d5 | ||
|
|
6350e881da | ||
|
|
ec5a799b31 | ||
|
|
026238b125 | ||
|
|
46712cd967 | ||
|
|
273ca15c17 | ||
|
|
94d8a6039e | ||
|
|
61fde5ed42 | ||
|
|
27a0127556 | ||
|
|
585fedd824 | ||
|
|
49ea990519 | ||
|
|
31e06538a2 | ||
|
|
22657f030c | ||
|
|
3805ecca02 | ||
|
|
31ddef5ddb | ||
|
|
6b4d3e5bbd | ||
|
|
acaeb46d5f | ||
|
|
ce70c3b62a | ||
|
|
55ff63ffeb |
31
.github/CONTRIBUTING.md
vendored
31
.github/CONTRIBUTING.md
vendored
@@ -1,25 +1,28 @@
|
||||
## Issues
|
||||
|
||||
When reporting a possible bug, provide detail steps so that we will be able
|
||||
to reproduce the issue. Steps to reproduce should be clear and unambiguous. Try not to use phrases like "very big bug",
|
||||
"huge issue", "useless feature", etc. No need to use exclamation marks as well.
|
||||
|
||||
Note that we don't provide developer help or any kind of support on GitHub.
|
||||
For this, please use our [forum](https://forum.espocrm.com).
|
||||
|
||||
If you are very new to EspoCRM, it's probable that an issue you ran into is not a bug.
|
||||
Consider creating a topic on our [forum](https://forum.espocrm.com/forum/general) instead.
|
||||
|
||||
The issue tracker is for the benefit of the EspoCRM project. The project maintainers are going to handle issues in the project's best interest.
|
||||
The maintainers have right to close issues without explanation.
|
||||
|
||||
## Pull Requests
|
||||
|
||||
We are open for contributions that are bug fixes and small improvements. If you would like to contribute something that is not a small fix, please reach out to maintainers before submitting your PR (by creating a GitHub issue).
|
||||
|
||||
Before we can merge your pull request, you need to accept our CLA [here](https://github.com/espocrm/cla).
|
||||
|
||||
It's desirable that one PR solves one specific problem. Do not include code style changes to PRs
|
||||
(unless the main purpose of the PR is a code style fix).
|
||||
|
||||
If you would like to contribute something that is not a small fix, it's reasonable to create an issue first
|
||||
(a bug report or feature request).
|
||||
|
||||
Branches:
|
||||
|
||||
* *master* – the develop branch; new features should be pushed to here;
|
||||
* *fix* – the upcoming maintenance release; small fixes should be pushed to here.
|
||||
|
||||
## Issues
|
||||
|
||||
We'd appreciate if you prefer posting issues on weekdays rather than weekends.
|
||||
|
||||
When reporting a possible bug, please provide detail steps so that we will be able
|
||||
to reproduce the issue. Please try not to use phrases like "very big bug",
|
||||
"huge issue", etc. No need to use exclamation marks as well.
|
||||
|
||||
Note that we don't provide developer help or any kind of support on GitHub.
|
||||
For this, please use our [forum](https://forum.espocrm.com).
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for EspoCRM. For high-level features, consider creating feature requests on the forum. For low-level (framework) – here on GitHub.
|
||||
about: For high-level features, create feature requests on our forum. For low-level (framework) – here on GitHub.
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -5,6 +5,8 @@
|
||||
/data/.backup/*
|
||||
/data/config.php
|
||||
/data/config-internal.php
|
||||
/data/config-override.php
|
||||
/data/config-internal-override.php
|
||||
/data/tmp/*
|
||||
/build
|
||||
/node_modules
|
||||
|
||||
2
.idea/codeStyles/Project.xml
generated
2
.idea/codeStyles/Project.xml
generated
@@ -17,8 +17,6 @@
|
||||
<codeStyleSettings language="PHP">
|
||||
<option name="KEEP_FIRST_COLUMN_COMMENT" value="false" />
|
||||
<option name="KEEP_CONTROL_STATEMENT_IN_ONE_LINE" value="false" />
|
||||
<option name="CATCH_ON_NEW_LINE" value="true" />
|
||||
<option name="FINALLY_ON_NEW_LINE" value="true" />
|
||||
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
|
||||
<option name="ALIGN_MULTILINE_FOR" value="false" />
|
||||
<option name="METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" />
|
||||
|
||||
1
.idea/inspectionProfiles/Project_Default.xml
generated
1
.idea/inspectionProfiles/Project_Default.xml
generated
@@ -3,6 +3,7 @@
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="DuplicatedCode" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="ES6ConvertLetToConst" enabled="true" level="WEAK WARNING" enabled_by_default="true" editorAttributes="INFO_ATTRIBUTES" />
|
||||
<inspection_tool class="HtmlUnknownAnchorTarget" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="JSIgnoredPromiseFromCall" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PhpDocMissingThrowsInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PhpDocSignatureIsNotCompleteInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
|
||||
1
.idea/misc.xml
generated
1
.idea/misc.xml
generated
@@ -3,5 +3,6 @@
|
||||
<component name="PhpEntryPointsManager">
|
||||
<pattern value="\Espo\Controllers\*" member="*Action*" />
|
||||
<pattern value="\Espo\Modules\*\Controllers\*" member="*Action*" />
|
||||
<suppressed_annotations>@implements</suppressed_annotations>
|
||||
</component>
|
||||
</project>
|
||||
8
.vscode/settings.json
vendored
8
.vscode/settings.json
vendored
@@ -16,7 +16,7 @@
|
||||
"fileMatch": [
|
||||
"*/Resources/module.json"
|
||||
],
|
||||
"url": "./schema/routes.json"
|
||||
"url": "./schema/module.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
@@ -58,12 +58,6 @@
|
||||
],
|
||||
"url": "./schema/metadata/dashlets.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/metadata//*.json"
|
||||
],
|
||||
"url": "./schema/metadata/.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/metadata/entityAcl/*.json"
|
||||
|
||||
@@ -134,9 +134,11 @@ module.exports = grunt => {
|
||||
src: [
|
||||
'build/tmp/custom/Espo/Custom/*',
|
||||
'!build/tmp/custom/Espo/Custom/.htaccess',
|
||||
'!build/tmp/custom/Espo/Modules',
|
||||
'build/tmp/custom/Espo/Modules/*',
|
||||
'!build/tmp/custom/Espo/Modules/.htaccess',
|
||||
'build/tmp/install/config.php',
|
||||
'build/tmp/vendor/*/*/.git',
|
||||
'build/tmp/custom/Espo/Custom/*',
|
||||
'build/tmp/client/custom/*',
|
||||
'!build/tmp/client/custom/modules',
|
||||
'build/tmp/client/custom/modules/*',
|
||||
|
||||
78
README.md
78
README.md
@@ -2,37 +2,41 @@
|
||||
|
||||
[](#espocrm)
|
||||
|
||||
[EspoCRM is an Open Source CRM](https://www.espocrm.com) (Customer Relationship Management)
|
||||
software that allows you to see, enter and evaluate all your company relationships regardless
|
||||
of the type. People, companies or opportunities – all in an easy and intuitive interface.
|
||||
|
||||
It's a web application with a frontend designed as a single page application and a REST API
|
||||
backend written in PHP.
|
||||
|
||||
[Download](https://www.espocrm.com/download/) the latest release from our website. Release notes
|
||||
and release packages are available at [Releases](https://github.com/espocrm/espocrm/releases) on GitHub.
|
||||
[EspoCRM](https://www.espocrm.com) is a free, open-source CRM platform designed to help organizations build and maintain strong customer relationships.
|
||||
It provides a wide range of tools to store, organize, and manage leads, contacts, sales opportunities, marketing campaigns,
|
||||
support cases, and more – all business information in a simple and intuitive interface.
|
||||
|
||||

|
||||
|
||||
### Architecture
|
||||
|
||||
EspoCRM is a web application with a frontend designed as a single-page application and a REST API
|
||||
backend written in PHP.
|
||||
|
||||
### Demo
|
||||
|
||||
You can try the CRM on the online [demo](https://www.espocrm.com/demo/).
|
||||
You can try the CRM on an online [demo](https://www.espocrm.com/demo/).
|
||||
|
||||
### Requirements
|
||||
|
||||
* PHP 8.1 - 8.3;
|
||||
* MySQL 5.7 (and later), or MariaDB 10.2 (and later).
|
||||
* PostgreSQL 15 (and later) (yet experimental, officially supported soon)
|
||||
* MySQL 5.7 (and later), or MariaDB 10.2 (and later);
|
||||
* PostgreSQL 15 (and later) (beta, official support soon).
|
||||
|
||||
For more information about server configuration see [this article](https://docs.espocrm.com/administration/server-configuration/).
|
||||
For more information about server configuration, see [this article](https://docs.espocrm.com/administration/server-configuration/).
|
||||
|
||||
### Documentation
|
||||
### Why EspoCRM?
|
||||
|
||||
See the [documentation](https://docs.espocrm.com) for administrators, users and developers.
|
||||
* **Open-source transparency**. EspoCRM’s source code is open and accessible, so anyone can inspect it and see how data is being managed within the CRM.
|
||||
* **Customization freedom**. You can develop features, create custom entities, fields, relationships, buttons to make the CRM fit your specific needs.
|
||||
* **Clean user interface**. EspoCRM has a uncluttered, minimalist and very fast user inteface that is easy to navigate and has a short learning curve.
|
||||
* **Straightforward REST API**. It can be easily integrated with other applications using a REST API.
|
||||
|
||||
### Bug reporting
|
||||
### Who is EspoCRM for?
|
||||
|
||||
Create a [GitHub issue](https://github.com/espocrm/espocrm/issues/new/choose) or post on our [forum](https://forum.espocrm.com/forum/bug-reports).
|
||||
* **Startups, small & medium-sized businesses**. It’s an affordable solution that is flexible and fully customizable.
|
||||
* **Developers & tech enthusiasts**. You can extend functionalities, build extensions, and create custom integrations.
|
||||
* **Anyone seeking a free CRM**. If you're looking for a user-friendly and secure CRM platform, it can be a good option.
|
||||
|
||||
### Installing stable version
|
||||
|
||||
@@ -43,21 +47,29 @@ See installation instructions:
|
||||
* [Installation with Docker](https://docs.espocrm.com/administration/docker/installation/)
|
||||
* [Installation with Traefik](https://docs.espocrm.com/administration/docker/traefik/)
|
||||
|
||||
### Download
|
||||
|
||||
[Download](https://www.espocrm.com/download/) the latest release from our website. You can also download the latest and previous release packages from GitHub [releases](https://github.com/espocrm/espocrm/releases).
|
||||
|
||||
### Release notes
|
||||
|
||||
Release notes are available at GitHub [releases](https://github.com/espocrm/espocrm/releases).
|
||||
|
||||
### Documentation
|
||||
|
||||
See the [documentation](https://docs.espocrm.com) for administrators, users and developers.
|
||||
|
||||
### Bug reporting
|
||||
|
||||
Create a [GitHub issue](https://github.com/espocrm/espocrm/issues/new/choose) or post on our [forum](https://forum.espocrm.com/forum/bug-reports).
|
||||
|
||||
### Development
|
||||
|
||||
See the [developer documentation](https://docs.espocrm.com/development/).
|
||||
|
||||
We highly recommend using IDE for development. The backend codebase follows SOLID principles, utilizes interfaces, static typing and generics. We recommend to start learning EspoCRM from the Dependency Injection article in the documentation.
|
||||
We highly recommend using an IDE for development. The backend codebase follows SOLID principles, utilizes interfaces, static typing and generics. We recommend to start learning EspoCRM from the Dependency Injection article in the documentation.
|
||||
|
||||
### Contributing
|
||||
|
||||
Before we can merge your pull request, you need to accept our CLA [here](https://github.com/espocrm/cla). It's very simple to do.
|
||||
|
||||
Branches:
|
||||
|
||||
* *fix* – upcoming maintenance release; minor fixes should be pushed to this branch;
|
||||
* *master* – develop branch; new features should be pushed to this branch;
|
||||
* *stable* – last stable release.
|
||||
Metadata plays an integral role in the EspoCRM application. All possible parameters are described with a JSON Schema, meaning you will have autocompletion in the IDE. You can also find the full metadata reference in the documentation.
|
||||
|
||||
### Community & Support
|
||||
|
||||
@@ -65,7 +77,17 @@ If you have a question regarding some features, need help or customizations, wan
|
||||
|
||||
### License
|
||||
|
||||
EspoCRM is published under the GNU AGPLv3 [license](https://raw.githubusercontent.com/espocrm/espocrm/master/LICENSE.txt).
|
||||
EspoCRM is an open-source project licensed under [GNU AGPLv3](https://raw.githubusercontent.com/espocrm/espocrm/master/LICENSE.txt).
|
||||
|
||||
### Contributing
|
||||
|
||||
Before we can merge your pull request, you need to accept our CLA [here](https://github.com/espocrm/cla). See the [contributing guidelines](https://github.com/espocrm/espocrm/blob/master/.github/CONTRIBUTING.md).
|
||||
|
||||
Branches:
|
||||
|
||||
* *fix* – upcoming maintenance release; minor fixes should be pushed to this branch;
|
||||
* *master* – develop branch; new features should be pushed to this branch;
|
||||
* *stable* – last stable release.
|
||||
|
||||
### Language
|
||||
|
||||
|
||||
@@ -63,11 +63,6 @@ class Binding implements BindingProcessor
|
||||
'container'
|
||||
);
|
||||
|
||||
$binder->bindService(
|
||||
'Espo\\Core\\Container',
|
||||
'container'
|
||||
);
|
||||
|
||||
$binder->bindService(
|
||||
'Psr\\Container\\ContainerInterface',
|
||||
'container'
|
||||
@@ -148,11 +143,6 @@ class Binding implements BindingProcessor
|
||||
'recordServiceContainer'
|
||||
);
|
||||
|
||||
$binder->bindService(
|
||||
'Espo\\Core\\Record\\HookManager',
|
||||
'recordHookManager'
|
||||
);
|
||||
|
||||
$binder->bindService(
|
||||
'Espo\\Core\\HookManager',
|
||||
'hookManager'
|
||||
|
||||
@@ -99,10 +99,7 @@ class AccessChecker implements AccessEntityCREDChecker
|
||||
else if ($this->aclManager->checkEntity($user, $parent)) {
|
||||
if (
|
||||
$entity->getTargetField() &&
|
||||
in_array(
|
||||
$entity->getTargetField(),
|
||||
$this->aclManager->getScopeForbiddenFieldList($user, $parent->getEntityType())
|
||||
)
|
||||
!$this->aclManager->checkField($user, $parent->getEntityType(), $entity->getTargetField())
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
47
application/Espo/Classes/Acl/ImportEml/AccessChecker.php
Normal file
47
application/Espo/Classes/Acl/ImportEml/AccessChecker.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\Acl\ImportEml;
|
||||
|
||||
use Espo\Core\Acl\AccessCreateChecker;
|
||||
use Espo\Core\Acl\ScopeData;
|
||||
use Espo\Entities\User;
|
||||
|
||||
class AccessChecker implements AccessCreateChecker
|
||||
{
|
||||
public function check(User $user, ScopeData $data): bool
|
||||
{
|
||||
return $data->isTrue();
|
||||
}
|
||||
|
||||
public function checkCreate(User $user, ScopeData $data): bool
|
||||
{
|
||||
return $data->isTrue();
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,8 @@
|
||||
|
||||
namespace Espo\Classes\Acl\Note;
|
||||
|
||||
use Espo\Core\Acl\Permission;
|
||||
use Espo\Core\Acl\Table;
|
||||
use Espo\Entities\Note;
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\Entity;
|
||||
@@ -141,6 +143,10 @@ class AccessChecker implements AccessEntityCREDChecker
|
||||
return in_array($user->getId(), $entity->getLinkMultipleIdList('users'));
|
||||
}
|
||||
|
||||
if ($entity->getTargetType() === Note::TARGET_PORTALS) {
|
||||
return $this->aclManager->getPermissionLevel($user, Permission::PORTAL) === Table::LEVEL_YES;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
namespace Espo\Classes\Acl\Portal;
|
||||
|
||||
use Espo\Core\Acl\Permission;
|
||||
use Espo\Entities\Portal;
|
||||
use Espo\Entities\User;
|
||||
use Espo\Core\Acl\AccessEntityCREDChecker;
|
||||
@@ -45,18 +46,12 @@ class AccessChecker implements AccessEntityCREDChecker
|
||||
{
|
||||
use DefaultAccessCheckerDependency;
|
||||
|
||||
private DefaultAccessChecker $defaultAccessChecker;
|
||||
private AclManager $aclManager;
|
||||
|
||||
public function __construct(DefaultAccessChecker $defaultAccessChecker, AclManager $aclManager)
|
||||
{
|
||||
$this->defaultAccessChecker = $defaultAccessChecker;
|
||||
$this->aclManager = $aclManager;
|
||||
}
|
||||
public function __construct(private DefaultAccessChecker $defaultAccessChecker, private AclManager $aclManager)
|
||||
{}
|
||||
|
||||
public function check(User $user, ScopeData $data): bool
|
||||
{
|
||||
$level = $this->aclManager->getPermissionLevel($user, 'portal');
|
||||
$level = $this->aclManager->getPermissionLevel($user, Permission::PORTAL);
|
||||
|
||||
return $level === Table::LEVEL_YES;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
namespace Espo\Classes\Acl\User;
|
||||
|
||||
use Espo\Core\Acl\Permission;
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Core\Acl\AccessEntityCREDSChecker;
|
||||
@@ -60,8 +61,6 @@ class AccessChecker implements AccessEntityCREDSChecker
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var User $entity */
|
||||
|
||||
if ($entity->isSuperAdmin() && !$user->isSuperAdmin()) {
|
||||
return false;
|
||||
}
|
||||
@@ -71,10 +70,8 @@ class AccessChecker implements AccessEntityCREDSChecker
|
||||
|
||||
public function checkEntityRead(User $user, Entity $entity, ScopeData $data): bool
|
||||
{
|
||||
/** @var User $entity */
|
||||
|
||||
if ($entity->isPortal()) {
|
||||
if ($this->aclManager->getPermissionLevel($user, 'portal') === Table::LEVEL_YES) {
|
||||
if ($this->aclManager->getPermissionLevel($user, Permission::PORTAL) === Table::LEVEL_YES) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -90,8 +87,6 @@ class AccessChecker implements AccessEntityCREDSChecker
|
||||
|
||||
public function checkEntityEdit(User $user, Entity $entity, ScopeData $data): bool
|
||||
{
|
||||
/** @var User $entity */
|
||||
|
||||
if ($entity->isSystem()) {
|
||||
return false;
|
||||
}
|
||||
@@ -111,8 +106,6 @@ class AccessChecker implements AccessEntityCREDSChecker
|
||||
|
||||
public function checkEntityDelete(User $user, Entity $entity, ScopeData $data): bool
|
||||
{
|
||||
/** @var User $entity */
|
||||
|
||||
if (!$user->isAdmin()) {
|
||||
return false;
|
||||
}
|
||||
@@ -130,8 +123,7 @@ class AccessChecker implements AccessEntityCREDSChecker
|
||||
|
||||
public function checkEntityStream(User $user, Entity $entity, ScopeData $data): bool
|
||||
{
|
||||
/** @var User $entity */
|
||||
|
||||
return $this->aclManager->checkUserPermission($user, $entity, 'user');
|
||||
/** @noinspection PhpRedundantOptionalArgumentInspection */
|
||||
return $this->aclManager->checkUserPermission($user, $entity, Permission::USER);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,10 +105,7 @@ class AccessChecker implements AccessEntityCREDChecker
|
||||
else if ($this->aclManager->checkEntity($user, $parent)) {
|
||||
if (
|
||||
$entity->getTargetField() &&
|
||||
in_array(
|
||||
$entity->getTargetField(),
|
||||
$this->aclManager->getScopeForbiddenFieldList($user, $parent->getEntityType())
|
||||
)
|
||||
!$this->aclManager->checkField($user, $parent->getEntityType(), $entity->getTargetField())
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
48
application/Espo/Classes/AppParams/AddressCountryData.php
Normal file
48
application/Espo/Classes/AppParams/AddressCountryData.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\AppParams;
|
||||
|
||||
use Espo\Core\Utils\Address\CountryDataProvider;
|
||||
use Espo\Tools\App\AppParam;
|
||||
|
||||
class AddressCountryData implements AppParam
|
||||
{
|
||||
public function __construct(
|
||||
private CountryDataProvider $provider
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @return array{list: string[], preferredList: string[]}
|
||||
*/
|
||||
public function get(): array
|
||||
{
|
||||
return $this->provider->get();
|
||||
}
|
||||
}
|
||||
65
application/Espo/Classes/Cleanup/AppLog.php
Normal file
65
application/Espo/Classes/Cleanup/AppLog.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\Cleanup;
|
||||
|
||||
use Espo\Core\Cleanup\Cleanup;
|
||||
use Espo\Core\Field\DateTime;
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Entities\AppLogRecord;
|
||||
use Espo\ORM\EntityManager;
|
||||
use Espo\ORM\Query\DeleteBuilder;
|
||||
|
||||
class AppLog implements Cleanup
|
||||
{
|
||||
private const PERIOD = '30 days';
|
||||
|
||||
public function __construct(
|
||||
private EntityManager $entityManager,
|
||||
private Config $config
|
||||
) {}
|
||||
|
||||
public function process(): void
|
||||
{
|
||||
$query = DeleteBuilder::create()
|
||||
->from(AppLogRecord::ENTITY_TYPE)
|
||||
->where(['createdAt<' => $this->getBefore()->toString()])
|
||||
->build();
|
||||
|
||||
$this->entityManager->getQueryExecutor()->execute($query);
|
||||
}
|
||||
|
||||
private function getBefore(): DateTime
|
||||
{
|
||||
/** @var string $period */
|
||||
$period = $this->config->get('cleanupAppLogPeriod') ?? self::PERIOD;
|
||||
|
||||
return DateTime::createNow()->modify('-' . $period);
|
||||
}
|
||||
}
|
||||
104
application/Espo/Classes/Cleanup/Audit.php
Normal file
104
application/Espo/Classes/Cleanup/Audit.php
Normal file
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\Cleanup;
|
||||
|
||||
use Espo\Core\Cleanup\Cleanup;
|
||||
use Espo\Core\Field\DateTime;
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Core\Utils\Metadata;
|
||||
use Espo\Entities\Note;
|
||||
use Espo\ORM\EntityManager;
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class Audit implements Cleanup
|
||||
{
|
||||
private const PERIOD = '3 months';
|
||||
|
||||
public function __construct(
|
||||
private Metadata $metadata,
|
||||
private EntityManager $entityManager,
|
||||
private Config $config
|
||||
) {}
|
||||
|
||||
public function process(): void
|
||||
{
|
||||
if (!$this->config->get('cleanupAudit')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$entityTypeList = $this->getEntityTypeList();
|
||||
|
||||
foreach ($entityTypeList as $scope) {
|
||||
$this->processEntityType($scope);
|
||||
}
|
||||
}
|
||||
|
||||
private function processEntityType(string $entityType): void
|
||||
{
|
||||
$query = $this->entityManager
|
||||
->getQueryBuilder()
|
||||
->delete()
|
||||
->from(Note::ENTITY_TYPE)
|
||||
->where([
|
||||
'parentType' => $entityType,
|
||||
'createdAt<' => $this->getBefore()->toString(),
|
||||
'type' => [Note::TYPE_UPDATE, Note::TYPE_STATUS],
|
||||
])
|
||||
->build();
|
||||
|
||||
$this->entityManager->getQueryExecutor()->execute($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private function getEntityTypeList(): array
|
||||
{
|
||||
/** @var string[] $scopeList */
|
||||
$scopeList = array_keys($this->metadata->get(['scopes']) ?? []);
|
||||
|
||||
$scopeList = array_filter($scopeList, function ($item) {
|
||||
return $this->metadata->get(['scopes', $item, 'entity']) &&
|
||||
!$this->metadata->get(['scopes', $item, 'stream']);
|
||||
});
|
||||
|
||||
return array_values($scopeList);
|
||||
}
|
||||
|
||||
private function getBefore(): DateTime
|
||||
{
|
||||
/** @var string $period */
|
||||
$period = $this->config->get('cleanupAuditPeriod') ?? self::PERIOD;
|
||||
|
||||
return DateTime::createNow()->modify('-' . $period);
|
||||
}
|
||||
}
|
||||
137
application/Espo/Classes/Cleanup/Stars.php
Normal file
137
application/Espo/Classes/Cleanup/Stars.php
Normal file
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\Cleanup;
|
||||
|
||||
use Espo\Core\Cleanup\Cleanup;
|
||||
use Espo\Core\Utils\Acl\UserAclManagerProvider;
|
||||
use Espo\Entities\StarSubscription;
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\EntityManager;
|
||||
use Espo\ORM\Query\DeleteBuilder;
|
||||
use Espo\Tools\Stars\StarService;
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class Stars implements Cleanup
|
||||
{
|
||||
public function __construct(
|
||||
private EntityManager $entityManager,
|
||||
private UserAclManagerProvider $userAclManagerProvider,
|
||||
private StarService $service
|
||||
) {}
|
||||
|
||||
public function process(): void
|
||||
{
|
||||
foreach ($this->getEntityTypeList() as $entityType) {
|
||||
$this->processEntityType($entityType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private function getEntityTypeList(): array
|
||||
{
|
||||
$groups = $this->entityManager->getRDBRepositoryByClass(StarSubscription::class)
|
||||
->group('entityType')
|
||||
->select('entityType')
|
||||
->find();
|
||||
|
||||
$list = [];
|
||||
|
||||
foreach ($groups as $group) {
|
||||
$list[] = $group->get('entityType');
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
private function processEntityType(string $entityType): void
|
||||
{
|
||||
if (
|
||||
!$this->service->isEnabled($entityType) ||
|
||||
!$this->entityManager->hasRepository($entityType)
|
||||
) {
|
||||
$deleteQuery = DeleteBuilder::create()
|
||||
->from(StarSubscription::ENTITY_TYPE)
|
||||
->where(['entityType' => $entityType])
|
||||
->build();
|
||||
|
||||
$this->entityManager->getQueryExecutor()->execute($deleteQuery);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$stars = $this->entityManager
|
||||
->getRDBRepositoryByClass(StarSubscription::class)
|
||||
->where(['entityType' => $entityType])
|
||||
->sth()
|
||||
->find();
|
||||
|
||||
foreach ($stars as $star) {
|
||||
$entityId = $star->get('entityId');
|
||||
$userId = $star->get('userId');
|
||||
|
||||
if ($userId === null || $entityId === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$entity = $this->entityManager->getEntityById($entityType, $entityId);
|
||||
$user = $this->entityManager->getRDBRepositoryByClass(User::class)->getById($userId);
|
||||
|
||||
if (!$entity || !$user) {
|
||||
$this->unstar($userId, $entityType, $entityId);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$aclManager = $this->userAclManagerProvider->get($user);
|
||||
|
||||
if (!$aclManager->checkEntityRead($user, $entity)) {
|
||||
$this->unstar($userId, $entityType, $entityId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function unstar(string $userId, string $entityType, string $entityId): void
|
||||
{
|
||||
$deleteQuery = DeleteBuilder::create()
|
||||
->from(StarSubscription::ENTITY_TYPE)
|
||||
->where([
|
||||
'userId' => $userId,
|
||||
'entityType' => $entityType,
|
||||
'entityId' => $entityId,
|
||||
])
|
||||
->build();
|
||||
|
||||
$this->entityManager->getQueryExecutor()->execute($deleteQuery);
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@ use Espo\Core\Cleanup\Cleanup;
|
||||
use Espo\Core\Field\DateTime;
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Core\Utils\Metadata;
|
||||
use Espo\Entities\Subscription;
|
||||
use Espo\Entities\StreamSubscription;
|
||||
use Espo\ORM\EntityManager;
|
||||
use Espo\ORM\Query\Part\Condition as Cond;
|
||||
|
||||
@@ -41,19 +41,11 @@ class Subscribers implements Cleanup
|
||||
{
|
||||
private const PERIOD = '2 months';
|
||||
|
||||
private Metadata $metadata;
|
||||
private EntityManager $entityManager;
|
||||
private Config $config;
|
||||
|
||||
public function __construct(
|
||||
Metadata $metadata,
|
||||
EntityManager $entityManager,
|
||||
Config $config
|
||||
) {
|
||||
$this->metadata = $metadata;
|
||||
$this->entityManager = $entityManager;
|
||||
$this->config = $config;
|
||||
}
|
||||
private Metadata $metadata,
|
||||
private EntityManager $entityManager,
|
||||
private Config $config
|
||||
) {}
|
||||
|
||||
public function process(): void
|
||||
{
|
||||
@@ -105,7 +97,7 @@ class Subscribers implements Cleanup
|
||||
$query = $this->entityManager
|
||||
->getQueryBuilder()
|
||||
->delete()
|
||||
->from(Subscription::ENTITY_TYPE, 'subscription')
|
||||
->from(StreamSubscription::ENTITY_TYPE, 'subscription')
|
||||
->join(
|
||||
$entityType,
|
||||
'entity',
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\ConsoleCommands;
|
||||
|
||||
use Espo\Core\Console\Command;
|
||||
use Espo\Core\Console\Command\Params;
|
||||
use Espo\Core\Console\IO;
|
||||
use Espo\Core\Utils\File\Manager as FileManager;
|
||||
use Espo\Core\Utils\System;
|
||||
use Espo\Core\Utils\Util;
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class CheckFilePermissions implements Command
|
||||
{
|
||||
public function __construct(
|
||||
private FileManager $fileManager,
|
||||
private System $system
|
||||
) {}
|
||||
|
||||
public function run(Params $params, IO $io): void
|
||||
{
|
||||
$io->writeLine("\nNote: Run this command under the web server user.\n");
|
||||
|
||||
$io->writeLine('Writable:');
|
||||
$io->writeLine('');
|
||||
|
||||
foreach ($this->fileManager->getPermissionUtils()->getWritableList() as $path) {
|
||||
$fullPath = Util::concatPath($this->system->getRootDir(), $path);
|
||||
|
||||
$isWritable = $this->fileManager->isWritable($fullPath);
|
||||
|
||||
$msg = " " . ($isWritable ? "OK" : "FAIL") . " : $path";
|
||||
|
||||
$io->writeLine($msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\FieldProcessing\Email;
|
||||
|
||||
use Espo\Core\FieldProcessing\Loader;
|
||||
use Espo\Core\FieldProcessing\Loader\Params;
|
||||
use Espo\Entities\Email;
|
||||
use Espo\Entities\EmailFolder;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\ORM\EntityManager;
|
||||
|
||||
/**
|
||||
* @implements Loader<Email>
|
||||
*/
|
||||
class FolderDataLoader implements Loader
|
||||
{
|
||||
public function __construct(private EntityManager $entityManager) {}
|
||||
|
||||
public function process(Entity $entity, Params $params): void
|
||||
{
|
||||
$folderId = $entity->get(Email::USERS_COLUMN_FOLDER_ID);
|
||||
|
||||
if (!$folderId) {
|
||||
return;
|
||||
}
|
||||
|
||||
$folder = $this->entityManager
|
||||
->getRDBRepositoryByClass(EmailFolder::class)
|
||||
->select(['id', 'name'])
|
||||
->where(['id' => $folderId])
|
||||
->findOne();
|
||||
|
||||
if (!$folder) {
|
||||
return;
|
||||
}
|
||||
|
||||
$entity->set('folderName', $folder->getName());
|
||||
}
|
||||
}
|
||||
@@ -41,14 +41,10 @@ use Espo\Entities\User;
|
||||
*/
|
||||
class UserColumnsLoader implements Loader
|
||||
{
|
||||
private EntityManager $entityManager;
|
||||
private User $user;
|
||||
|
||||
public function __construct(EntityManager $entityManager, User $user)
|
||||
{
|
||||
$this->entityManager = $entityManager;
|
||||
$this->user = $user;
|
||||
}
|
||||
public function __construct(
|
||||
private EntityManager $entityManager,
|
||||
private User $user
|
||||
) {}
|
||||
|
||||
public function process(Entity $entity, Params $params): void
|
||||
{
|
||||
@@ -58,6 +54,7 @@ class UserColumnsLoader implements Loader
|
||||
Email::USERS_COLUMN_IS_READ,
|
||||
Email::USERS_COLUMN_IS_IMPORTANT,
|
||||
Email::USERS_COLUMN_IN_TRASH,
|
||||
Email::USERS_COLUMN_IN_ARCHIVE,
|
||||
])
|
||||
->where([
|
||||
'deleted' => false,
|
||||
@@ -70,6 +67,7 @@ class UserColumnsLoader implements Loader
|
||||
$entity->set(Email::USERS_COLUMN_IS_READ, null);
|
||||
$entity->clear(Email::USERS_COLUMN_IS_IMPORTANT);
|
||||
$entity->clear(Email::USERS_COLUMN_IN_TRASH);
|
||||
$entity->clear(Email::USERS_COLUMN_IN_ARCHIVE);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -78,6 +76,8 @@ class UserColumnsLoader implements Loader
|
||||
Email::USERS_COLUMN_IS_READ => $emailUser->get(Email::USERS_COLUMN_IS_READ),
|
||||
Email::USERS_COLUMN_IS_IMPORTANT => $emailUser->get(Email::USERS_COLUMN_IS_IMPORTANT),
|
||||
Email::USERS_COLUMN_IN_TRASH => $emailUser->get(Email::USERS_COLUMN_IN_TRASH),
|
||||
Email::USERS_COLUMN_IN_ARCHIVE => $emailUser->get(Email::USERS_COLUMN_IN_ARCHIVE),
|
||||
'isUsersSent' => $entity->getSentBy()?->getId() === $this->user->getId(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
/**LICENSE**/
|
||||
|
||||
namespace Espo\Classes\FieldProcessing\InboundEmail;
|
||||
|
||||
use Espo\Core\FieldProcessing\Loader;
|
||||
use Espo\Core\FieldProcessing\Loader\Params;
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Entities\InboundEmail;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
/**
|
||||
* @implements Loader<InboundEmail>
|
||||
*/
|
||||
class IsSystemLoader implements Loader
|
||||
{
|
||||
public function __construct(
|
||||
private Config $config,
|
||||
) {}
|
||||
|
||||
public function process(Entity $entity, Params $params): void
|
||||
{
|
||||
$isSystem = $entity->getEmailAddress() === $this->config->get('outboundEmailFromAddress');
|
||||
|
||||
$entity->set('isSystem', $isSystem);
|
||||
}
|
||||
}
|
||||
@@ -27,46 +27,58 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Services;
|
||||
namespace Espo\Classes\FieldProcessing\LeadCapture;
|
||||
|
||||
use Espo\Entities\LeadCapture as LeadCaptureEntity;
|
||||
use Espo\Core\FieldProcessing\Loader;
|
||||
use Espo\Core\FieldProcessing\Loader\Params;
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Core\Utils\FieldUtil;
|
||||
use Espo\Core\Utils\Util;
|
||||
use Espo\Entities\LeadCapture;
|
||||
use Espo\Modules\Crm\Entities\Lead;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Tools\LeadCapture\Service as LeadCaptureService;
|
||||
use Espo\Core\Utils\Util;
|
||||
use Espo\ORM\EntityManager;
|
||||
|
||||
/**
|
||||
* @extends Record<LeadCaptureEntity>
|
||||
* @implements Loader<LeadCapture>
|
||||
*/
|
||||
class LeadCapture extends Record
|
||||
class ExampleLoader implements Loader
|
||||
{
|
||||
/** @var string[] */
|
||||
protected $readOnlyAttributeList = ['apiKey'];
|
||||
public function __construct(
|
||||
private FieldUtil $fieldUtil,
|
||||
private Config $config,
|
||||
private EntityManager $entityManager
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @param LeadCaptureEntity $entity
|
||||
*/
|
||||
public function prepareEntityForOutput(Entity $entity)
|
||||
public function process(Entity $entity, Params $params): void
|
||||
{
|
||||
parent::prepareEntityForOutput($entity);
|
||||
|
||||
$entity->set('exampleRequestMethod', 'POST');
|
||||
|
||||
$entity->set('exampleRequestHeaders', [
|
||||
'Content-Type: application/json',
|
||||
]);
|
||||
|
||||
$this->processRequestUrl($entity);
|
||||
$this->processRequestPayload($entity);
|
||||
}
|
||||
|
||||
private function processRequestUrl(LeadCapture $entity): void
|
||||
{
|
||||
$apiKey = $entity->getApiKey();
|
||||
$siteUrl = $this->config->get('siteUrl');
|
||||
|
||||
if ($apiKey) {
|
||||
$requestUrl = $this->config->getSiteUrl() . '/api/v1/LeadCapture/' . $apiKey;
|
||||
|
||||
$entity->set('exampleRequestUrl', $requestUrl);
|
||||
if (!$apiKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fieldUtil = $this->fieldUtil;
|
||||
$requestUrl = "$siteUrl/api/v1/LeadCapture/$apiKey";
|
||||
|
||||
$requestPayload = "```{\n";
|
||||
$entity->set('exampleRequestUrl', $requestUrl);
|
||||
}
|
||||
|
||||
private function processRequestPayload(LeadCapture $entity): void
|
||||
{
|
||||
$requestPayload = "```\n{\n";
|
||||
|
||||
$attributeList = [];
|
||||
|
||||
@@ -80,7 +92,7 @@ class LeadCapture extends Record
|
||||
];
|
||||
|
||||
foreach ($entity->getFieldList() as $field) {
|
||||
foreach ($fieldUtil->getActualAttributeList(Lead::ENTITY_TYPE, $field) as $attribute) {
|
||||
foreach ($this->fieldUtil->getActualAttributeList(Lead::ENTITY_TYPE, $field) as $attribute) {
|
||||
if (!in_array($attribute, $attributeIgnoreList)) {
|
||||
$attributeList[] = $attribute;
|
||||
}
|
||||
@@ -105,20 +117,8 @@ class LeadCapture extends Record
|
||||
$requestPayload .= "\n";
|
||||
}
|
||||
|
||||
$requestPayload .= '}```';
|
||||
$requestPayload .= "}\n```";
|
||||
|
||||
$entity->set('exampleRequestPayload', $requestPayload);
|
||||
}
|
||||
|
||||
protected function beforeCreateEntity(Entity $entity, $data)
|
||||
{
|
||||
$apiKey = $this->createLeadCaptureService()->generateApiKey();
|
||||
|
||||
$entity->set('apiKey', $apiKey);
|
||||
}
|
||||
|
||||
protected function createLeadCaptureService(): LeadCaptureService
|
||||
{
|
||||
return $this->injectableFactory->create(LeadCaptureService::class);
|
||||
}
|
||||
}
|
||||
@@ -29,19 +29,18 @@
|
||||
|
||||
namespace Espo\Classes\FieldProcessing\Note;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Core\FieldProcessing\Loader;
|
||||
use Espo\Core\FieldProcessing\Loader\Params;
|
||||
use Espo\Entities\Note;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
/**
|
||||
* @implements Loader<Note>
|
||||
*/
|
||||
class AttachmentsLoader implements Loader
|
||||
class AdditionalFieldsLoader implements Loader
|
||||
{
|
||||
public function process(Entity $entity, Params $params): void
|
||||
{
|
||||
/** @var Note $entity */
|
||||
$entity->loadAttachments();
|
||||
$entity->loadAdditionalFields();
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,6 @@ use Espo\Entities\AuthToken;
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Core\Acl;
|
||||
use Espo\Core\Acl\Table;
|
||||
use Espo\Core\FieldProcessing\Loader;
|
||||
use Espo\Core\FieldProcessing\Loader\Params;
|
||||
use Espo\Core\ORM\EntityManager;
|
||||
@@ -44,6 +43,7 @@ use Exception;
|
||||
|
||||
/**
|
||||
* @implements Loader<User>
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class LastAccessLoader implements Loader
|
||||
{
|
||||
@@ -58,10 +58,7 @@ class LastAccessLoader implements Loader
|
||||
|
||||
public function process(Entity $entity, Params $params): void
|
||||
{
|
||||
$forbiddenFieldList = $this->acl
|
||||
->getScopeForbiddenFieldList($entity->getEntityType(), Table::ACTION_READ);
|
||||
|
||||
if (in_array('lastAccess', $forbiddenFieldList)) {
|
||||
if (!$this->acl->checkField($entity->getEntityType(), 'lastAccess')) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
54
application/Espo/Classes/FieldSanitizers/ArrayFromNull.php
Normal file
54
application/Espo/Classes/FieldSanitizers/ArrayFromNull.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\FieldSanitizers;
|
||||
|
||||
use Espo\Core\FieldSanitize\Sanitizer;
|
||||
use Espo\Core\FieldSanitize\Sanitizer\Data;
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class ArrayFromNull implements Sanitizer
|
||||
{
|
||||
public function sanitize(Data $data, string $field): void
|
||||
{
|
||||
if (!$data->has($field)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$value = $data->get($field);
|
||||
|
||||
if ($value !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$data->set($field, []);
|
||||
}
|
||||
}
|
||||
62
application/Espo/Classes/FieldSanitizers/ArrayStringTrim.php
Normal file
62
application/Espo/Classes/FieldSanitizers/ArrayStringTrim.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\FieldSanitizers;
|
||||
|
||||
use Espo\Core\FieldSanitize\Sanitizer;
|
||||
use Espo\Core\FieldSanitize\Sanitizer\Data;
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class ArrayStringTrim implements Sanitizer
|
||||
{
|
||||
public function sanitize(Data $data, string $field): void
|
||||
{
|
||||
if (!$data->has($field)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$value = $data->get($field);
|
||||
|
||||
if (!is_array($value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($value as $i => $item) {
|
||||
if (!is_string($item)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$value[$i] = trim($item);
|
||||
}
|
||||
|
||||
$data->set($field, $value);
|
||||
}
|
||||
}
|
||||
70
application/Espo/Classes/FieldSanitizers/Date.php
Normal file
70
application/Espo/Classes/FieldSanitizers/Date.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\FieldSanitizers;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use DateTimeInterface;
|
||||
use Espo\Core\Field\Date as DateValue;
|
||||
use Espo\Core\FieldSanitize\Sanitizer;
|
||||
use Espo\Core\FieldSanitize\Sanitizer\Data;
|
||||
use Espo\Core\Utils\DateTime as DateTimeUtil;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class Date implements Sanitizer
|
||||
{
|
||||
public function sanitize(Data $data, string $field): void
|
||||
{
|
||||
$value = $data->get($field);
|
||||
|
||||
if ($value === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
DateValue::fromString($value);
|
||||
|
||||
return;
|
||||
}
|
||||
catch (Exception) {}
|
||||
|
||||
$dateTime = DateTimeImmutable::createFromFormat(DateTimeInterface::ATOM, $value);
|
||||
|
||||
if ($dateTime === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$value = $dateTime->format(DateTimeUtil::SYSTEM_DATE_FORMAT);
|
||||
|
||||
$data->set($field, $value);
|
||||
}
|
||||
}
|
||||
73
application/Espo/Classes/FieldSanitizers/Datetime.php
Normal file
73
application/Espo/Classes/FieldSanitizers/Datetime.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\FieldSanitizers;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use DateTimeInterface;
|
||||
use DateTimeZone;
|
||||
use Espo\Core\Field\DateTime as DateTimeValue;
|
||||
use Espo\Core\FieldSanitize\Sanitizer;
|
||||
use Espo\Core\FieldSanitize\Sanitizer\Data;
|
||||
use Espo\Core\Utils\DateTime as DateTimeUtil;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class Datetime implements Sanitizer
|
||||
{
|
||||
public function sanitize(Data $data, string $field): void
|
||||
{
|
||||
$value = $data->get($field);
|
||||
|
||||
if ($value === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
DateTimeValue::fromString($value);
|
||||
|
||||
return;
|
||||
}
|
||||
catch (Exception) {}
|
||||
|
||||
$dateTime = DateTimeImmutable::createFromFormat(DateTimeInterface::ATOM, $value);
|
||||
|
||||
if ($dateTime === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$value = $dateTime
|
||||
->setTimezone(new DateTimeZone('UTC'))
|
||||
->format(DateTimeUtil::SYSTEM_DATE_TIME_FORMAT);
|
||||
|
||||
$data->set($field, $value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\FieldSanitizers;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use DateTimeInterface;
|
||||
use Espo\Core\Field\Date;
|
||||
use Espo\Core\FieldSanitize\Sanitizer;
|
||||
use Espo\Core\FieldSanitize\Sanitizer\Data;
|
||||
use Espo\Core\Utils\DateTime as DateTimeUtil;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class DatetimeOptionalDate implements Sanitizer
|
||||
{
|
||||
public function sanitize(Data $data, string $field): void
|
||||
{
|
||||
$attribute = $field . 'Date';
|
||||
|
||||
$value = $data->get($attribute);
|
||||
|
||||
if ($value === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Date::fromString($value);
|
||||
|
||||
return;
|
||||
}
|
||||
catch (Exception) {}
|
||||
|
||||
$dateTime = DateTimeImmutable::createFromFormat(DateTimeInterface::ATOM, $value);
|
||||
|
||||
if ($dateTime === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$value = $dateTime->format(DateTimeUtil::SYSTEM_DATE_FORMAT);
|
||||
|
||||
$data->set($attribute, $value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\FieldSanitizers;
|
||||
|
||||
use Espo\Core\FieldSanitize\Sanitizer;
|
||||
use Espo\Core\FieldSanitize\Sanitizer\Data;
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class EmptyStringToNull implements Sanitizer
|
||||
{
|
||||
public function sanitize(Data $data, string $field): void
|
||||
{
|
||||
if (!$data->has($field)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$value = $data->get($field);
|
||||
|
||||
if (!is_string($value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($value === '') {
|
||||
$value = null;
|
||||
}
|
||||
|
||||
$data->set($field, $value);
|
||||
}
|
||||
}
|
||||
56
application/Espo/Classes/FieldSanitizers/StringLowerCase.php
Normal file
56
application/Espo/Classes/FieldSanitizers/StringLowerCase.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\FieldSanitizers;
|
||||
|
||||
use Espo\Core\FieldSanitize\Sanitizer;
|
||||
use Espo\Core\FieldSanitize\Sanitizer\Data;
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class StringLowerCase implements Sanitizer
|
||||
{
|
||||
public function sanitize(Data $data, string $field): void
|
||||
{
|
||||
if (!$data->has($field)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$value = $data->get($field);
|
||||
|
||||
if (!is_string($value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$value = mb_strtolower($value);
|
||||
|
||||
$data->set($field, $value);
|
||||
}
|
||||
}
|
||||
60
application/Espo/Classes/FieldSanitizers/StringTrim.php
Normal file
60
application/Espo/Classes/FieldSanitizers/StringTrim.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\FieldSanitizers;
|
||||
|
||||
use Espo\Core\FieldSanitize\Sanitizer;
|
||||
use Espo\Core\FieldSanitize\Sanitizer\Data;
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class StringTrim implements Sanitizer
|
||||
{
|
||||
public function sanitize(Data $data, string $field): void
|
||||
{
|
||||
if (!$data->has($field)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$value = $data->get($field);
|
||||
|
||||
if (!is_string($value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$value = trim($value);
|
||||
|
||||
if ($value === '') {
|
||||
$value = null;
|
||||
}
|
||||
|
||||
$data->set($field, $value);
|
||||
}
|
||||
}
|
||||
56
application/Espo/Classes/FieldSanitizers/StringUpperCase.php
Normal file
56
application/Espo/Classes/FieldSanitizers/StringUpperCase.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\FieldSanitizers;
|
||||
|
||||
use Espo\Core\FieldSanitize\Sanitizer;
|
||||
use Espo\Core\FieldSanitize\Sanitizer\Data;
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class StringUpperCase implements Sanitizer
|
||||
{
|
||||
public function sanitize(Data $data, string $field): void
|
||||
{
|
||||
if (!$data->has($field)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$value = $data->get($field);
|
||||
|
||||
if (!is_string($value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$value = mb_strtoupper($value);
|
||||
|
||||
$data->set($field, $value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\FieldValidators\InboundEmail\FetchSince;
|
||||
|
||||
use Espo\Core\FieldValidation\Validator;
|
||||
use Espo\Core\FieldValidation\Validator\Data;
|
||||
use Espo\Core\FieldValidation\Validator\Failure;
|
||||
use Espo\Entities\EmailAccount;
|
||||
use Espo\Entities\InboundEmail;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
/**
|
||||
* @implements Validator<InboundEmail|EmailAccount>
|
||||
*/
|
||||
class Required implements Validator
|
||||
{
|
||||
public function validate(Entity $entity, string $field, Data $data): ?Failure
|
||||
{
|
||||
if (!$entity->isAvailableForFetching()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$entity->get('fetchSince')) {
|
||||
return Failure::create();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -29,16 +29,54 @@
|
||||
|
||||
namespace Espo\Classes\FieldValidators;
|
||||
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Espo\ORM\Defs;
|
||||
use Espo\ORM\Entity;
|
||||
use stdClass;
|
||||
|
||||
class IntType
|
||||
{
|
||||
public function __construct(
|
||||
private Defs $defs,
|
||||
) {}
|
||||
|
||||
public function checkRequired(Entity $entity, string $field): bool
|
||||
{
|
||||
return $this->isNotEmpty($entity, $field);
|
||||
}
|
||||
|
||||
/** @noinspection PhpUnused */
|
||||
public function checkRangeInternal(Entity $entity, string $field): bool
|
||||
{
|
||||
$value = $entity->get($field);
|
||||
|
||||
if ($value === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$dbType = $this->defs
|
||||
->getEntity($entity->getEntityType())
|
||||
->tryGetAttribute($field)
|
||||
?->getParam('dbType') ?? Types::INTEGER;
|
||||
|
||||
$ranges = [
|
||||
Types::INTEGER => [-2147483648, 2147483647],
|
||||
Types::SMALLINT => [-32768, 32767],
|
||||
];
|
||||
|
||||
$range = $ranges[$dbType] ?? null;
|
||||
|
||||
if (!$range) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($value < $range[0] || $value > $range[1]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $validationValue
|
||||
* @noinspection PhpUnused
|
||||
|
||||
@@ -36,20 +36,17 @@ use Espo\Core\ORM\Entity as CoreEntity;
|
||||
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class LinkMultipleType
|
||||
{
|
||||
private Metadata $metadata;
|
||||
private Defs $defs;
|
||||
|
||||
private const COLUMN_TYPE_ENUM = 'enum';
|
||||
private const COLUMN_TYPE_VARCHAR = 'varchar';
|
||||
private const COLUMN_TYPE_BOOL = 'bool';
|
||||
|
||||
public function __construct(Metadata $metadata, Defs $defs)
|
||||
{
|
||||
$this->metadata = $metadata;
|
||||
$this->defs = $defs;
|
||||
}
|
||||
public function __construct(private Metadata $metadata, private Defs $defs)
|
||||
{}
|
||||
|
||||
public function checkRequired(Entity $entity, string $field): bool
|
||||
{
|
||||
@@ -63,6 +60,7 @@ class LinkMultipleType
|
||||
return count($idList) > 0;
|
||||
}
|
||||
|
||||
/** @noinspection PhpUnused */
|
||||
public function checkPattern(Entity $entity, string $field): bool
|
||||
{
|
||||
/** @var ?mixed[] $idList */
|
||||
@@ -93,6 +91,27 @@ class LinkMultipleType
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @noinspection PhpUnused */
|
||||
public function checkMaxCount(Entity $entity, string $field, ?int $maxCount): bool
|
||||
{
|
||||
if ($maxCount === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$list = $entity->get($field . 'Ids');
|
||||
|
||||
if (!is_array($list)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (count($list) > $maxCount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @noinspection PhpUnused */
|
||||
public function checkColumnsValid(Entity $entity, string $field): bool
|
||||
{
|
||||
if (!$entity instanceof CoreEntity) {
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace Espo\Classes\FieldValidators;
|
||||
|
||||
use Brick\PhoneNumber\PhoneNumber;
|
||||
use Brick\PhoneNumber\PhoneNumberParseException;
|
||||
use Espo\Core\PhoneNumber\Util;
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Core\Utils\Metadata;
|
||||
use Espo\ORM\Defs;
|
||||
@@ -203,6 +204,22 @@ class PhoneType
|
||||
return true;
|
||||
}
|
||||
|
||||
$ext = null;
|
||||
|
||||
if ($this->config->get('phoneNumberExtensions')) {
|
||||
[$number, $ext] = Util::splitExtension($number);
|
||||
}
|
||||
|
||||
if ($ext) {
|
||||
if (!preg_match('/[0-9]+/', $ext)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strlen($ext) > 6) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$numberObj = PhoneNumber::parse($number);
|
||||
}
|
||||
|
||||
@@ -27,38 +27,36 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Services;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
namespace Espo\Classes\FieldValidators\ScheduledJob\Scheduling;
|
||||
|
||||
use Cron\CronExpression;
|
||||
|
||||
use Espo\Core\FieldValidation\Validator;
|
||||
use Espo\Core\FieldValidation\Validator\Data;
|
||||
use Espo\Core\FieldValidation\Validator\Failure;
|
||||
use Espo\Entities\ScheduledJob;
|
||||
use Espo\ORM\Entity;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* @extends Record<\Espo\Entities\ScheduledJob>
|
||||
* @implements Validator<ScheduledJob>
|
||||
*/
|
||||
class ScheduledJob extends Record
|
||||
class Valid implements Validator
|
||||
{
|
||||
/** Should not be removed. */
|
||||
protected bool $findLinkedLogCountQueryDisabled = true;
|
||||
|
||||
public function processValidation(Entity $entity, $data)
|
||||
public function validate(Entity $entity, string $field, Data $data): ?Failure
|
||||
{
|
||||
parent::processValidation($entity, $data);
|
||||
$scheduling = $entity->getScheduling();
|
||||
|
||||
$scheduling = $entity->get('scheduling');
|
||||
if ($scheduling === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
$cronExpression = CronExpression::factory($scheduling);
|
||||
new CronExpression($scheduling);
|
||||
}
|
||||
catch (Exception) {
|
||||
return Failure::create();
|
||||
}
|
||||
|
||||
/** @phpstan-ignore-next-line*/
|
||||
$cronExpression->getNextRunDate()->format('Y-m-d H:i:s');
|
||||
}
|
||||
catch (Exception $e) {
|
||||
throw new BadRequest("Not valid scheduling expression.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\FieldValidators\Settings\AuthIpAddressWhitelist;
|
||||
|
||||
use Espo\Core\FieldValidation\Validator;
|
||||
use Espo\Core\FieldValidation\Validator\Data;
|
||||
use Espo\Core\FieldValidation\Validator\Failure;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
/**
|
||||
* @implements Validator<Entity>
|
||||
*/
|
||||
class Valid implements Validator
|
||||
{
|
||||
public function validate(Entity $entity, string $field, Data $data): ?Failure
|
||||
{
|
||||
$list = $entity->get($field);
|
||||
|
||||
if (!is_array($list)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach ($list as $item) {
|
||||
if (!is_string($item)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$this->isValid($item)) {
|
||||
return Failure::create();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function isValid(string $item): bool
|
||||
{
|
||||
$address = $item;
|
||||
|
||||
if (count(explode('/', $item)) > 1) {
|
||||
[$address, $mask] = explode('/', $item, 2);
|
||||
|
||||
if (!is_numeric($mask)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$mask = (int) $mask;
|
||||
|
||||
if ($mask < 0 || $mask > 128) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false &&
|
||||
filter_var($address, FILTER_VALIDATE_IP) === false
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\FieldValidators\Settings\ThousandSeparator;
|
||||
|
||||
use Espo\Core\FieldValidation\Validator;
|
||||
use Espo\Core\FieldValidation\Validator\Data;
|
||||
use Espo\Core\FieldValidation\Validator\Failure;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
/**
|
||||
* @implements Validator<Entity>
|
||||
*/
|
||||
class Valid implements Validator
|
||||
{
|
||||
public function validate(Entity $entity, string $field, Data $data): ?Failure
|
||||
{
|
||||
$value = $entity->get($field);
|
||||
|
||||
if (!$value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!is_string($value)) {
|
||||
return Failure::create();
|
||||
}
|
||||
|
||||
if (preg_match('/^[0-9]$/', $value)) {
|
||||
return Failure::create();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\FieldValidators\User\DefaultTeam;
|
||||
|
||||
use Espo\Core\FieldValidation\Validator;
|
||||
use Espo\Core\FieldValidation\Validator\Data;
|
||||
use Espo\Core\FieldValidation\Validator\Failure;
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
/**
|
||||
* @implements Validator<User>
|
||||
*/
|
||||
class IsUserTeam implements Validator
|
||||
{
|
||||
public function validate(Entity $entity, string $field, Data $data): ?Failure
|
||||
{
|
||||
if (!$entity->getDefaultTeam()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (in_array($entity->getDefaultTeam()->getId(), $entity->getTeamIdList())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Failure::create();
|
||||
}
|
||||
}
|
||||
@@ -30,61 +30,99 @@
|
||||
namespace Espo\Classes\Jobs;
|
||||
|
||||
use Espo\Entities\AuthToken;
|
||||
use Espo\Entities\Portal;
|
||||
use Espo\Core\Job\JobDataLess;
|
||||
use Espo\Core\ORM\EntityManager;
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Core\Utils\DateTime as DateTimeUtil;
|
||||
|
||||
use DateTime;
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class AuthTokenControl implements JobDataLess
|
||||
{
|
||||
private Config $config;
|
||||
private EntityManager $entityManager;
|
||||
private const LIMIT = 500;
|
||||
|
||||
public function __construct(Config $config, EntityManager $entityManager)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->entityManager = $entityManager;
|
||||
}
|
||||
public function __construct(
|
||||
private Config $config,
|
||||
private EntityManager $entityManager
|
||||
) {}
|
||||
|
||||
public function run(): void
|
||||
{
|
||||
$authTokenLifetime = $this->config->get('authTokenLifetime');
|
||||
$authTokenMaxIdleTime = $this->config->get('authTokenMaxIdleTime');
|
||||
$lifetime = (int) ($this->config->get('authTokenLifetime', 0) * 60);
|
||||
$maxIdleTime = (int) ($this->config->get('authTokenMaxIdleTime', 0) * 60);
|
||||
|
||||
if (!$authTokenLifetime && !$authTokenMaxIdleTime) {
|
||||
$portalIds = [];
|
||||
|
||||
/** @var iterable<Portal> $portals */
|
||||
$portals = $this->entityManager
|
||||
->getRDBRepositoryByClass(Portal::class)
|
||||
->find();
|
||||
|
||||
foreach ($portals as $portal) {
|
||||
$portalIds[] = $portal->getId();
|
||||
}
|
||||
|
||||
$this->process(null, $lifetime, $maxIdleTime, $portalIds);
|
||||
|
||||
foreach ($portals as $portal) {
|
||||
$itemLifetime = $portal->get('authTokenLifetime') !== null ?
|
||||
(int) ($portal->get('authTokenLifetime') * 60) :
|
||||
$lifetime;
|
||||
|
||||
$itemMaxIdleTime = $portal->get('authTokenMaxIdleTime') !== null ?
|
||||
(int) ($portal->get('authTokenMaxIdleTime') * 60) :
|
||||
$maxIdleTime;
|
||||
|
||||
$this->process($portal->getId(), $itemLifetime, $itemMaxIdleTime);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $ignorePortalIds
|
||||
*/
|
||||
private function process(?string $portalId, int $lifetime, int $maxIdleTime, array $ignorePortalIds = []): void
|
||||
{
|
||||
if (!$lifetime && !$maxIdleTime) {
|
||||
return;
|
||||
}
|
||||
|
||||
$whereClause = [
|
||||
'isActive' => true,
|
||||
];
|
||||
$whereClause = ['isActive' => true];
|
||||
|
||||
if ($authTokenLifetime) {
|
||||
$dt = new DateTime();
|
||||
|
||||
$dt->modify('-' . $authTokenLifetime . ' hours');
|
||||
|
||||
$authTokenLifetimeThreshold = $dt->format(DateTimeUtil::SYSTEM_DATE_TIME_FORMAT);
|
||||
|
||||
$whereClause['createdAt<'] = $authTokenLifetimeThreshold;
|
||||
if ($portalId) {
|
||||
$whereClause['portalId'] = $portalId;
|
||||
}
|
||||
|
||||
if ($authTokenMaxIdleTime) {
|
||||
if (!$portalId && $ignorePortalIds !== []) {
|
||||
$whereClause[] = [
|
||||
'OR' => [
|
||||
['portalId' => null],
|
||||
['portalId!=' => $ignorePortalIds],
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
if ($lifetime) {
|
||||
$dt = new DateTime();
|
||||
$dt->modify("-$lifetime minutes");
|
||||
|
||||
$dt->modify('-' . $authTokenMaxIdleTime . ' hours');
|
||||
$whereClause['createdAt<'] = $dt->format(DateTimeUtil::SYSTEM_DATE_TIME_FORMAT);
|
||||
}
|
||||
|
||||
$authTokenMaxIdleTimeThreshold = $dt->format(DateTimeUtil::SYSTEM_DATE_TIME_FORMAT);
|
||||
if ($maxIdleTime) {
|
||||
$dt = new DateTime();
|
||||
$dt->modify("-$maxIdleTime minutes");
|
||||
|
||||
$whereClause['lastAccess<'] = $authTokenMaxIdleTimeThreshold;
|
||||
$whereClause['lastAccess<'] = $dt->format(DateTimeUtil::SYSTEM_DATE_TIME_FORMAT);
|
||||
}
|
||||
|
||||
$tokenList = $this->entityManager
|
||||
->getRDBRepository(AuthToken::ENTITY_TYPE)
|
||||
->sth()
|
||||
->where($whereClause)
|
||||
->limit(0, 500)
|
||||
->limit(0, self::LIMIT)
|
||||
->find();
|
||||
|
||||
foreach ($tokenList as $token) {
|
||||
|
||||
@@ -29,39 +29,31 @@
|
||||
|
||||
namespace Espo\Classes\Jobs;
|
||||
|
||||
use Espo\Core\Exceptions\Error;
|
||||
|
||||
use Espo\Core\Mail\Account\PersonalAccount\Service;
|
||||
use Espo\Core\Job\Job;
|
||||
use Espo\Core\Job\Job\Data;
|
||||
|
||||
use RuntimeException;
|
||||
use Throwable;
|
||||
|
||||
class CheckEmailAccounts implements Job
|
||||
{
|
||||
private $service;
|
||||
|
||||
public function __construct(Service $service)
|
||||
{
|
||||
$this->service = $service;
|
||||
}
|
||||
public function __construct(private Service $service)
|
||||
{}
|
||||
|
||||
public function run(Data $data): void
|
||||
{
|
||||
$targetId = $data->getTargetId();
|
||||
|
||||
if (!$targetId) {
|
||||
throw new Error("No target.");
|
||||
throw new RuntimeException("No target.");
|
||||
}
|
||||
|
||||
try {
|
||||
$this->service->fetch($targetId);
|
||||
}
|
||||
catch (Throwable $e) {
|
||||
throw new Error(
|
||||
'Job CheckEmailAccounts ' . $targetId . ': [' . $e->getCode() . '] ' . $e->getMessage() . ' ' .
|
||||
$e->getFile() . ':' . $e->getLine()
|
||||
);
|
||||
throw new RuntimeException("CheckInboundEmails job failed, $targetId; {$e->getMessage()}", 0, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,37 +29,31 @@
|
||||
|
||||
namespace Espo\Classes\Jobs;
|
||||
|
||||
use Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Mail\Account\GroupAccount\Service;
|
||||
use Espo\Core\Job\Job;
|
||||
use Espo\Core\Job\Job\Data;
|
||||
|
||||
use RuntimeException;
|
||||
use Throwable;
|
||||
|
||||
class CheckInboundEmails implements Job
|
||||
{
|
||||
private $service;
|
||||
|
||||
public function __construct(Service $service)
|
||||
{
|
||||
$this->service = $service;
|
||||
}
|
||||
public function __construct(private Service $service)
|
||||
{}
|
||||
|
||||
public function run(Data $data): void
|
||||
{
|
||||
$targetId = $data->getTargetId();
|
||||
|
||||
if (!$targetId) {
|
||||
throw new Error("No target.");
|
||||
throw new RuntimeException("No target.");
|
||||
}
|
||||
|
||||
try {
|
||||
$this->service->fetch($targetId);
|
||||
}
|
||||
catch (Throwable $e) {
|
||||
throw new Error(
|
||||
'Job CheckInboundEmails ' . $targetId . ': [' . $e->getCode() . '] ' .$e->getMessage()
|
||||
);
|
||||
throw new RuntimeException("CheckInboundEmails job failed, $targetId; {$e->getMessage()}", 0, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
namespace Espo\Classes\MassAction\Email;
|
||||
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\MassAction\Data;
|
||||
use Espo\Core\MassAction\MassAction;
|
||||
@@ -44,11 +45,10 @@ use Espo\ORM\EntityManager;
|
||||
use Espo\Tools\Email\Folder;
|
||||
use Espo\Tools\Email\InboxService as EmailService;
|
||||
use Exception;
|
||||
use RuntimeException;
|
||||
|
||||
class MoveToFolder implements MassAction
|
||||
{
|
||||
private const FOLDER_INBOX = Folder::INBOX;
|
||||
|
||||
public function __construct(
|
||||
private QueryBuilder $queryBuilder,
|
||||
private EntityManager $entityManager,
|
||||
@@ -68,7 +68,11 @@ class MoveToFolder implements MassAction
|
||||
throw new BadRequest("No folder ID.");
|
||||
}
|
||||
|
||||
if ($folderId !== self::FOLDER_INBOX && !str_starts_with($folderId, 'group:')) {
|
||||
if (
|
||||
$folderId !== Folder::INBOX &&
|
||||
$folderId !== Folder::ARCHIVE &&
|
||||
!str_starts_with($folderId, 'group:')
|
||||
) {
|
||||
$folder = $this->entityManager
|
||||
->getRDBRepositoryByClass(EmailFolder::class)
|
||||
->where([
|
||||
@@ -93,7 +97,12 @@ class MoveToFolder implements MassAction
|
||||
}
|
||||
}
|
||||
|
||||
$query = $this->queryBuilder->build($params);
|
||||
try {
|
||||
$query = $this->queryBuilder->build($params);
|
||||
}
|
||||
catch (BadRequest|Forbidden $e) {
|
||||
throw new RuntimeException($e->getMessage());
|
||||
}
|
||||
|
||||
$collection = $this->entityManager
|
||||
->getRDBRepositoryByClass(Email::class)
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace Espo\Classes\MassAction\User;
|
||||
|
||||
use Espo\Core\Acl;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\MassAction\Actions\MassDelete as MassDeleteOriginal;
|
||||
use Espo\Core\MassAction\Data;
|
||||
@@ -59,18 +60,19 @@ class MassDelete implements MassAction
|
||||
/**
|
||||
* @throws Forbidden
|
||||
* @throws BadRequest
|
||||
* @throws Error
|
||||
*/
|
||||
public function process(Params $params, Data $data): Result
|
||||
{
|
||||
$entityType = $params->getEntityType();
|
||||
|
||||
if (!$this->acl->check($entityType, Acl\Table::ACTION_DELETE)) {
|
||||
throw new Forbidden("No delete access for '{$entityType}'.");
|
||||
throw new Forbidden("No delete access for '$entityType'.");
|
||||
}
|
||||
|
||||
if (
|
||||
!$params->hasIds() &&
|
||||
$this->acl->getPermissionLevel('massUpdatePermission') !== Acl\Table::LEVEL_YES
|
||||
$this->acl->getPermissionLevel(Acl\Permission::MASS_UPDATE) !== Acl\Table::LEVEL_YES
|
||||
) {
|
||||
throw new Forbidden("No mass-update permission.");
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ use Espo\Tools\MassUpdate\Data as MassUpdateData;
|
||||
|
||||
class MassUpdate implements MassAction
|
||||
{
|
||||
private const PERMISSION = 'massUpdatePermission';
|
||||
private const PERMISSION = Acl\Permission::MASS_UPDATE;
|
||||
|
||||
/** @var string[] */
|
||||
private array $notAllowedAttributeList = [
|
||||
|
||||
156
application/Espo/Classes/Record/Attachment/CreateInputFilter.php
Normal file
156
application/Espo/Classes/Record/Attachment/CreateInputFilter.php
Normal file
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\Record\Attachment;
|
||||
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Record\Input\Data;
|
||||
use Espo\Core\Record\Input\Filter;
|
||||
use Espo\Entities\Attachment;
|
||||
use Espo\ORM\EntityManager;
|
||||
use Espo\Tools\Attachment\AccessChecker;
|
||||
use Espo\Tools\Attachment\DetailsObtainer;
|
||||
use Espo\Tools\Attachment\FieldData;
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class CreateInputFilter implements Filter
|
||||
{
|
||||
public function __construct(
|
||||
private EntityManager $entityManager,
|
||||
private AccessChecker $accessChecker,
|
||||
private DetailsObtainer $detailsObtainer
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @throws BadRequest
|
||||
* @throws Error
|
||||
* @throws Forbidden
|
||||
*/
|
||||
public function filter(Data $data): void
|
||||
{
|
||||
$data->clear('parentId');
|
||||
$data->clear('relatedId');
|
||||
|
||||
$contents = $this->handleContents($data);
|
||||
|
||||
$relatedEntityType = $this->getRelatedEntityType($data);
|
||||
|
||||
$field = $data->get('field');
|
||||
$role = $data->get('role') ?? Attachment::ROLE_ATTACHMENT;
|
||||
|
||||
if (!$relatedEntityType || !$field) {
|
||||
throw new BadRequest("No `field` and `parentType`.");
|
||||
}
|
||||
|
||||
$fieldData = new FieldData($field, $data->get('parentType'), $data->get('relatedType'));
|
||||
|
||||
$this->accessChecker->check($fieldData, $role);
|
||||
$this->checkMaxSize($contents, $data, $field, $role);
|
||||
}
|
||||
|
||||
private function getRelatedEntityType(Data $data): ?string
|
||||
{
|
||||
if ($data->get('parentType') !== null) {
|
||||
$data->clear('relatedType');
|
||||
|
||||
return $data->get('parentType');
|
||||
}
|
||||
|
||||
if ($data->get('relatedType') !== null) {
|
||||
return $data->get('relatedType');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws BadRequest
|
||||
*/
|
||||
private function handleContents(Data $data): string
|
||||
{
|
||||
$isBeingUploaded = $data->get('isBeingUploaded') ?? false;
|
||||
|
||||
$contents = '';
|
||||
|
||||
if (!$isBeingUploaded) {
|
||||
if (!$data->has('file')) {
|
||||
throw new BadRequest("No file contents.");
|
||||
}
|
||||
|
||||
$file = $data->get('file');
|
||||
|
||||
if (!is_string($file)) {
|
||||
throw new BadRequest("Non-string file contents.");
|
||||
}
|
||||
|
||||
$arr = explode(',', $file);
|
||||
|
||||
if (count($arr) < 2) {
|
||||
throw new BadRequest("Bad file contents.");
|
||||
}
|
||||
|
||||
$contents = base64_decode($arr[1]);
|
||||
|
||||
if ($contents === false) {
|
||||
throw new BadRequest("Could not decode file contents.");
|
||||
}
|
||||
}
|
||||
|
||||
$data->set('contents', $contents);
|
||||
|
||||
return $contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws BadRequest
|
||||
*/
|
||||
private function checkMaxSize(string $contents, Data $data, mixed $field, mixed $role): void
|
||||
{
|
||||
$size = mb_strlen($contents, '8bit');
|
||||
|
||||
$dummy = $this->entityManager->getRepositoryByClass(Attachment::class)->getNew();
|
||||
|
||||
$dummy->set([
|
||||
'parentType' => $data->get('parentType'),
|
||||
'relatedType' => $data->get('relatedType'),
|
||||
'field' => $field,
|
||||
'role' => $role,
|
||||
]);
|
||||
|
||||
$maxSize = $this->detailsObtainer->getUploadMaxSize($dummy);
|
||||
|
||||
if ($maxSize && $size > $maxSize * 1024 * 1024) {
|
||||
throw new BadRequest("File size should not exceed $maxSize Mb.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\Record\Attachment;
|
||||
|
||||
use Espo\Core\Record\Input\Data;
|
||||
use Espo\Core\Record\Input\Filter;
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class UpdateInputFilter implements Filter
|
||||
{
|
||||
public function filter(Data $data): void
|
||||
{
|
||||
$data->clear('parentId');
|
||||
$data->clear('parentType');
|
||||
$data->clear('relatedId');
|
||||
$data->clear('relatedType');
|
||||
$data->clear('isBeingUploaded');
|
||||
$data->clear('storage');
|
||||
}
|
||||
}
|
||||
@@ -27,33 +27,26 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Services;
|
||||
namespace Espo\Classes\Record\AuthToken;
|
||||
|
||||
use stdClass;
|
||||
use Espo\Core\Record\Input\Data;
|
||||
use Espo\Core\Record\Input\Filter;
|
||||
|
||||
/**
|
||||
* @extends Record<\Espo\Entities\AuthToken>
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class AuthToken extends Record
|
||||
class UpdateInputFilter implements Filter
|
||||
{
|
||||
protected $actionHistoryDisabled = true;
|
||||
|
||||
public function filterUpdateInput(stdClass $data): void
|
||||
public function filter(Data $data): void
|
||||
{
|
||||
parent::filterUpdateInput($data);
|
||||
|
||||
$dataArray = get_object_vars($data);
|
||||
|
||||
foreach (array_keys($dataArray) as $attribute) {
|
||||
foreach ($data->getAttributeList() as $attribute) {
|
||||
if ($attribute !== 'isActive') {
|
||||
unset($data->$attribute);
|
||||
|
||||
continue;
|
||||
$data->clear($attribute);
|
||||
}
|
||||
}
|
||||
|
||||
if ($data->isActive ?? false) {
|
||||
unset($data->isActive);
|
||||
if ($data->get('isActive')) {
|
||||
$data->clear('isActive');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\Record\InboundEmail;
|
||||
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Record\Input\Data;
|
||||
use Espo\Core\Record\Input\Filter;
|
||||
use Espo\Core\Utils\Crypt;
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class PasswordsInputFilter implements Filter
|
||||
{
|
||||
public function __construct(
|
||||
private Crypt $crypt
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @throws BadRequest
|
||||
*/
|
||||
public function filter(Data $data): void
|
||||
{
|
||||
$password = $data->get('password');
|
||||
|
||||
if ($password !== null) {
|
||||
if (!is_string($password)) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
$data->set('password', $this->crypt->encrypt($password));
|
||||
}
|
||||
|
||||
$smtpPassword = $data->get('smtpPassword');
|
||||
|
||||
if ($smtpPassword !== null) {
|
||||
if (!is_string($smtpPassword)) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
$data->set('smtpPassword', $this->crypt->encrypt($smtpPassword));
|
||||
}
|
||||
}
|
||||
}
|
||||
50
application/Espo/Classes/Record/Note/UpdateInputFilter.php
Normal file
50
application/Espo/Classes/Record/Note/UpdateInputFilter.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\Record\Note;
|
||||
|
||||
use Espo\Core\Record\Input\Data;
|
||||
use Espo\Core\Record\Input\Filter;
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class UpdateInputFilter implements Filter
|
||||
{
|
||||
public function filter(Data $data): void
|
||||
{
|
||||
$data->clear('parentId');
|
||||
$data->clear('parentType');
|
||||
$data->clear('targetType');
|
||||
$data->clear('usersIds');
|
||||
$data->clear('teamsIds');
|
||||
$data->clear('portalsIds');
|
||||
$data->clear('isGlobal');
|
||||
}
|
||||
}
|
||||
59
application/Espo/Classes/Record/Portal/InputFilter.php
Normal file
59
application/Espo/Classes/Record/Portal/InputFilter.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\Record\Portal;
|
||||
|
||||
use Espo\Core\Record\Input\Data;
|
||||
use Espo\Core\Record\Input\Filter;
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Entities\User;
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class InputFilter implements Filter
|
||||
{
|
||||
public function __construct(
|
||||
private User $user,
|
||||
private Config $config
|
||||
) {}
|
||||
|
||||
public function filter(Data $data): void
|
||||
{
|
||||
if (!$this->config->get('restrictedMode')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->user->isSuperAdmin()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$data->clear('customUrl');
|
||||
}
|
||||
}
|
||||
74
application/Espo/Classes/Record/User/OutputFilter.php
Normal file
74
application/Espo/Classes/Record/User/OutputFilter.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\Record\User;
|
||||
|
||||
use Espo\Core\Authentication\Logins\Hmac;
|
||||
use Espo\Core\Record\Output\Filter;
|
||||
use Espo\Core\Utils\ApiKey;
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
/**
|
||||
* @implements Filter<User>
|
||||
*/
|
||||
class OutputFilter implements Filter
|
||||
{
|
||||
public function __construct(
|
||||
private User $user,
|
||||
private ApiKey $apiKey
|
||||
) {}
|
||||
|
||||
public function filter(Entity $entity): void
|
||||
{
|
||||
$entity->clear('sendAccessInfo');
|
||||
|
||||
$this->filterApiUser($entity);
|
||||
}
|
||||
|
||||
private function filterApiUser(User $entity): void
|
||||
{
|
||||
if (!$entity->isApi()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->user->isAdmin()) {
|
||||
if ($entity->getAuthMethod() === Hmac::NAME) {
|
||||
$secretKey = $this->apiKey->getSecretKeyForUserId($entity->getId());
|
||||
|
||||
$entity->set('secretKey', $secretKey);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$entity->clear('apiKey');
|
||||
$entity->clear('secretKey');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\Record\Webhook;
|
||||
|
||||
use Espo\Core\Record\Defaults\DefaultPopulator;
|
||||
use Espo\Core\Record\Defaults\Populator;
|
||||
use Espo\Entities\User;
|
||||
use Espo\Entities\Webhook;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
/**
|
||||
* @implements Populator<Webhook>
|
||||
*/
|
||||
class DefaultsPopulator implements Populator
|
||||
{
|
||||
public function __construct(
|
||||
private DefaultPopulator $defaultsDefaultsPopulator,
|
||||
private User $user
|
||||
) {}
|
||||
|
||||
public function populate(Entity $entity): void
|
||||
{
|
||||
$this->defaultsDefaultsPopulator->populate($entity);
|
||||
|
||||
if ($this->user->isApi()) {
|
||||
$entity->set('userId', $this->user->getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\Record\Webhook;
|
||||
|
||||
use Espo\Core\Record\Input\Data;
|
||||
use Espo\Core\Record\Input\Filter;
|
||||
use Espo\Entities\User;
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class UpdateInputFilter implements Filter
|
||||
{
|
||||
public function __construct(
|
||||
private User $user
|
||||
) {}
|
||||
|
||||
public function filter(Data $data): void
|
||||
{
|
||||
if (!$this->user->isAdmin()) {
|
||||
$data->clear('event');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\RecordHooks\AddressCountry;
|
||||
|
||||
use Espo\Core\Exceptions\ConflictSilent;
|
||||
use Espo\Core\Exceptions\Error\Body;
|
||||
use Espo\Core\Record\Hook\SaveHook;
|
||||
use Espo\Entities\AddressCountry;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\ORM\EntityManager;
|
||||
|
||||
/**
|
||||
* @implements SaveHook<AddressCountry>
|
||||
*/
|
||||
class BeforeSave implements SaveHook
|
||||
{
|
||||
public function __construct(
|
||||
private EntityManager $entityManager,
|
||||
) {}
|
||||
|
||||
public function process(Entity $entity): void
|
||||
{
|
||||
$where = ['name' => $entity->getName()];
|
||||
|
||||
if (!$entity->isNew()) {
|
||||
$where['id!='] = $entity->getId();
|
||||
}
|
||||
|
||||
$one = $this->entityManager
|
||||
->getRDBRepositoryByClass(AddressCountry::class)
|
||||
->where($where)
|
||||
->findOne();
|
||||
|
||||
if (!$one) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw ConflictSilent::createWithBody(
|
||||
'duplicateError',
|
||||
Body::create()->withMessageTranslation('duplicateConflict')
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -27,18 +27,19 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Modules\Crm\Services;
|
||||
namespace Espo\Classes\RecordHooks\Attachment;
|
||||
|
||||
use Espo\Core\Record\Hook\SaveHook;
|
||||
use Espo\Entities\Attachment;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
class Call extends Meeting
|
||||
/**
|
||||
* @implements SaveHook<Attachment>
|
||||
*/
|
||||
class AfterCreate implements SaveHook
|
||||
{
|
||||
protected function afterUpdateEntity(Entity $entity, $data)
|
||||
public function process(Entity $entity): void
|
||||
{
|
||||
parent::afterUpdateEntity($entity, $data);
|
||||
|
||||
if (isset($data->contactsIds) || isset($data->leadsIds)) {
|
||||
$this->loadAdditionalFields($entity);
|
||||
}
|
||||
$entity->clear('contents');
|
||||
}
|
||||
}
|
||||
100
application/Espo/Classes/RecordHooks/Attachment/BeforeCreate.php
Normal file
100
application/Espo/Classes/RecordHooks/Attachment/BeforeCreate.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\RecordHooks\Attachment;
|
||||
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Record\Hook\SaveHook;
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Core\Utils\Metadata;
|
||||
use Espo\Entities\Attachment;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Tools\Attachment\Checker;
|
||||
use Espo\Tools\Attachment\DetailsObtainer;
|
||||
|
||||
/**
|
||||
* @implements SaveHook<Attachment>
|
||||
*/
|
||||
class BeforeCreate implements SaveHook
|
||||
{
|
||||
public function __construct(
|
||||
private Config $config,
|
||||
private Metadata $metadata,
|
||||
private DetailsObtainer $detailsObtainer,
|
||||
private Checker $checker
|
||||
) {}
|
||||
|
||||
public function process(Entity $entity): void
|
||||
{
|
||||
$this->processStorage($entity);
|
||||
$this->processRole($entity);
|
||||
$this->processSize($entity);
|
||||
|
||||
$this->checker->checkType($entity);
|
||||
}
|
||||
|
||||
private function processStorage(Attachment $entity): void
|
||||
{
|
||||
$storage = $entity->getStorage();
|
||||
|
||||
$availableStorageList = $this->config->get('attachmentAvailableStorageList') ?? [];
|
||||
|
||||
if (
|
||||
$storage &&
|
||||
(
|
||||
!in_array($storage, $availableStorageList) ||
|
||||
!$this->metadata->get(['app', 'fileStorage', 'implementationClassNameMap', $storage])
|
||||
)
|
||||
) {
|
||||
$entity->clear('storage');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Forbidden
|
||||
*/
|
||||
private function processSize(Attachment $entity): void
|
||||
{
|
||||
$size = $entity->getSize();
|
||||
|
||||
$maxSize = $this->detailsObtainer->getUploadMaxSize($entity);
|
||||
|
||||
// Checking not actual file size but a set value.
|
||||
if ($size && $size > $maxSize) {
|
||||
throw new Forbidden("Attachment size exceeds `attachmentUploadMaxSize`.");
|
||||
}
|
||||
}
|
||||
|
||||
private function processRole(Attachment $entity): void
|
||||
{
|
||||
if (!$entity->getRole()) {
|
||||
$entity->setRole(Attachment::ROLE_ATTACHMENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
64
application/Espo/Classes/RecordHooks/Email/AfterUpdate.php
Normal file
64
application/Espo/Classes/RecordHooks/Email/AfterUpdate.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\RecordHooks\Email;
|
||||
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Mail\Exceptions\NoSmtp;
|
||||
use Espo\Core\Mail\Exceptions\SendingError;
|
||||
use Espo\Core\Record\Hook\SaveHook;
|
||||
use Espo\Entities\Email;
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Tools\Email\SendService;
|
||||
|
||||
/**
|
||||
* @implements SaveHook<Email>
|
||||
*/
|
||||
class AfterUpdate implements SaveHook
|
||||
{
|
||||
public function __construct(
|
||||
private User $user,
|
||||
private SendService $sendService
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @throws BadRequest
|
||||
* @throws Error
|
||||
* @throws NoSmtp
|
||||
* @throws SendingError
|
||||
*/
|
||||
public function process(Entity $entity): void
|
||||
{
|
||||
if ($entity->getStatus() === Email::STATUS_SENDING) {
|
||||
$this->sendService->send($entity, $this->user);
|
||||
}
|
||||
}
|
||||
}
|
||||
50
application/Espo/Classes/RecordHooks/Email/BeforeCreate.php
Normal file
50
application/Espo/Classes/RecordHooks/Email/BeforeCreate.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\RecordHooks\Email;
|
||||
|
||||
use Espo\Core\Mail\Sender;
|
||||
use Espo\Core\Record\Hook\SaveHook;
|
||||
use Espo\Entities\Email;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
/**
|
||||
* @implements SaveHook<Email>
|
||||
*/
|
||||
class BeforeCreate implements SaveHook
|
||||
{
|
||||
public function process(Entity $entity): void
|
||||
{
|
||||
if ($entity->getStatus() === Email::STATUS_SENDING) {
|
||||
$messageId = Sender::generateMessageId($entity);
|
||||
|
||||
$entity->setMessageId('<' . $messageId . '>');
|
||||
}
|
||||
}
|
||||
}
|
||||
151
application/Espo/Classes/RecordHooks/Email/BeforeUpdate.php
Normal file
151
application/Espo/Classes/RecordHooks/Email/BeforeUpdate.php
Normal file
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\RecordHooks\Email;
|
||||
|
||||
use Espo\Core\Mail\Sender;
|
||||
use Espo\Core\Record\Hook\SaveHook;
|
||||
use Espo\Core\Utils\FieldUtil;
|
||||
use Espo\Core\Utils\SystemUser;
|
||||
use Espo\Entities\Email;
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\ORM\EntityManager;
|
||||
|
||||
/**
|
||||
* @implements SaveHook<Email>
|
||||
*/
|
||||
class BeforeUpdate implements SaveHook
|
||||
{
|
||||
/** @var string[] */
|
||||
private $allowedForUpdateFieldList = [
|
||||
'parent',
|
||||
'teams',
|
||||
'assignedUser',
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
private User $user,
|
||||
private EntityManager $entityManager,
|
||||
private FieldUtil $fieldUtil
|
||||
) {}
|
||||
|
||||
public function process(Entity $entity): void
|
||||
{
|
||||
$skipFilter = false;
|
||||
|
||||
if ($this->user->isAdmin()) {
|
||||
$skipFilter = true;
|
||||
}
|
||||
|
||||
if ($this->isEmailManuallyArchived($entity)) {
|
||||
$skipFilter = true;
|
||||
}
|
||||
else if ($entity->isAttributeChanged('dateSent')) {
|
||||
$entity->set('dateSent', $entity->getFetched('dateSent'));
|
||||
}
|
||||
|
||||
if ($entity->getStatus() === Email::STATUS_DRAFT) {
|
||||
$skipFilter = true;
|
||||
}
|
||||
|
||||
if (
|
||||
$entity->getStatus() === Email::STATUS_SENDING &&
|
||||
$entity->getFetched('status') === Email::STATUS_DRAFT
|
||||
) {
|
||||
$skipFilter = true;
|
||||
}
|
||||
|
||||
if (
|
||||
$entity->isAttributeChanged('status') &&
|
||||
$entity->getFetched('status') === Email::STATUS_ARCHIVED
|
||||
) {
|
||||
$entity->setStatus(Email::STATUS_ARCHIVED);
|
||||
}
|
||||
|
||||
if (!$skipFilter) {
|
||||
$this->clearEntityForUpdate($entity);
|
||||
}
|
||||
|
||||
if ($entity->getStatus() == Email::STATUS_SENDING) {
|
||||
$messageId = Sender::generateMessageId($entity);
|
||||
|
||||
$entity->setMessageId('<' . $messageId . '>');
|
||||
}
|
||||
}
|
||||
|
||||
private function isEmailManuallyArchived(Email $email): bool
|
||||
{
|
||||
if ($email->getStatus() !== Email::STATUS_ARCHIVED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$userId = $email->getCreatedBy()?->getId();
|
||||
|
||||
if (!$userId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$user = $this->entityManager
|
||||
->getRDBRepositoryByClass(User::class)
|
||||
->getById($userId);
|
||||
|
||||
if (!$user) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $user->getUserName() !== SystemUser::NAME;
|
||||
}
|
||||
|
||||
private function clearEntityForUpdate(Email $email): void
|
||||
{
|
||||
$fieldDefsList = $this->entityManager
|
||||
->getDefs()
|
||||
->getEntity(Email::ENTITY_TYPE)
|
||||
->getFieldList();
|
||||
|
||||
foreach ($fieldDefsList as $fieldDefs) {
|
||||
$field = $fieldDefs->getName();
|
||||
|
||||
if ($fieldDefs->getParam('isCustom')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (in_array($field, $this->allowedForUpdateFieldList)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$attributeList = $this->fieldUtil->getAttributeList(Email::ENTITY_TYPE, $field);
|
||||
|
||||
foreach ($attributeList as $attribute) {
|
||||
$email->clear($attribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
101
application/Espo/Classes/RecordHooks/Email/CheckFromAddress.php
Normal file
101
application/Espo/Classes/RecordHooks/Email/CheckFromAddress.php
Normal file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\RecordHooks\Email;
|
||||
|
||||
use Espo\Core\Acl;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Mail\Account\SendingAccountProvider;
|
||||
use Espo\Core\Record\Hook\SaveHook;
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Entities\Email;
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
/**
|
||||
* @implements SaveHook<Email>
|
||||
*/
|
||||
class CheckFromAddress implements SaveHook
|
||||
{
|
||||
public function __construct(
|
||||
private User $user,
|
||||
private SendingAccountProvider $sendingAccountProvider,
|
||||
private Config $config,
|
||||
private Acl $acl,
|
||||
) {}
|
||||
|
||||
public function process(Entity $entity): void
|
||||
{
|
||||
if ($this->user->isAdmin()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fromAddress = $entity->getFromAddress();
|
||||
|
||||
// Should be after 'getFromAddress'.
|
||||
if (!$entity->isAttributeChanged('from')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$fromAddress) {
|
||||
throw new BadRequest("No 'from' address");
|
||||
}
|
||||
|
||||
if ($this->acl->checkScope('Import')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fromAddress = strtolower($fromAddress);
|
||||
|
||||
foreach ($this->user->getEmailAddressGroup()->getAddressList() as $address) {
|
||||
if ($fromAddress === strtolower($address)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->sendingAccountProvider->getShared($this->user, $fromAddress)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$system = $this->sendingAccountProvider->getSystem();
|
||||
|
||||
if (
|
||||
$system &&
|
||||
$this->config->get('outboundEmailIsShared') &&
|
||||
$system->getEmailAddress()
|
||||
) {
|
||||
if ($fromAddress === strtolower($system->getEmailAddress())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Forbidden("Not allowed 'from' address.");
|
||||
}
|
||||
}
|
||||
55
application/Espo/Classes/RecordHooks/Email/MarkAsRead.php
Normal file
55
application/Espo/Classes/RecordHooks/Email/MarkAsRead.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\RecordHooks\Email;
|
||||
|
||||
use Espo\Core\Record\Hook\ReadHook;
|
||||
use Espo\Core\Record\ReadParams;
|
||||
use Espo\Entities\Email;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Tools\Email\InboxService;
|
||||
|
||||
/**
|
||||
* @implements ReadHook<Email>
|
||||
*/
|
||||
class MarkAsRead implements ReadHook
|
||||
{
|
||||
public function __construct(
|
||||
private InboxService $inboxService
|
||||
) {}
|
||||
|
||||
public function process(Entity $entity, ReadParams $params): void
|
||||
{
|
||||
if ($entity->isRead()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->inboxService->markAsRead($entity->getId());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\RecordHooks\Email;
|
||||
|
||||
use Espo\Core\Record\Hook\SaveHook;
|
||||
use Espo\Entities\Email;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Tools\Email\InboxService;
|
||||
|
||||
/**
|
||||
* @implements SaveHook<Email>
|
||||
*/
|
||||
class MarkAsReadBeforeUpdate implements SaveHook
|
||||
{
|
||||
public function __construct(
|
||||
private InboxService $inboxService
|
||||
) {}
|
||||
|
||||
public function process(Entity $entity): void
|
||||
{
|
||||
if ($entity->isRead()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->inboxService->markAsRead($entity->getId());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\RecordHooks\EmailAccount;
|
||||
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Record\Hook\SaveHook;
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Entities\EmailAccount;
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\ORM\EntityManager;
|
||||
use const PHP_INT_MAX;
|
||||
|
||||
/**
|
||||
* @implements SaveHook<EmailAccount>
|
||||
*/
|
||||
class BeforeCreate implements SaveHook
|
||||
{
|
||||
public function __construct(
|
||||
private User $user,
|
||||
private Config $config,
|
||||
private EntityManager $entityManager
|
||||
) {}
|
||||
|
||||
public function process(Entity $entity): void
|
||||
{
|
||||
if ($this->user->isAdmin()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$entity->set('assignedUserId', $this->user->getId());
|
||||
|
||||
$count = $this->entityManager
|
||||
->getRDBRepository(EmailAccount::ENTITY_TYPE)
|
||||
->where(['assignedUserId' => $this->user->getId()])
|
||||
->count();
|
||||
|
||||
if ($count >= $this->config->get('maxEmailAccountCount', PHP_INT_MAX)) {
|
||||
throw new Forbidden("Email Account number for user limit exceeded.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,32 +27,33 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Services;
|
||||
namespace Espo\Classes\RecordHooks\EmailFilter;
|
||||
|
||||
use Espo\Core\Acl;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Record\Hook\SaveHook;
|
||||
use Espo\Entities\EmailAccount as EmailAccountEntity;
|
||||
use Espo\Entities\EmailFilter as EmailFilterEntity;
|
||||
use Espo\Entities\EmailFilter;
|
||||
use Espo\Entities\InboundEmail as InboundEmailEntity;
|
||||
use Espo\Entities\User as UserEntity;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* @extends Record<EmailFilterEntity>
|
||||
* @implements SaveHook<EmailFilter>
|
||||
*/
|
||||
class EmailFilter extends Record
|
||||
class BeforeSave implements SaveHook
|
||||
{
|
||||
/**
|
||||
* @param EmailFilterEntity $entity
|
||||
* @throws Forbidden
|
||||
*/
|
||||
protected function beforeCreateEntity(Entity $entity, $data)
|
||||
{
|
||||
parent::beforeCreateEntity($entity, $data);
|
||||
public function __construct(
|
||||
private Acl $acl
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function process(Entity $entity): void
|
||||
{
|
||||
// Check if own.
|
||||
if (!$this->acl->checkEntityEdit($entity)) {
|
||||
if ($entity->isNew() && !$this->acl->checkEntityEdit($entity)) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
@@ -60,26 +61,17 @@ class EmailFilter extends Record
|
||||
}
|
||||
|
||||
/**
|
||||
* @param EmailFilterEntity $entity
|
||||
* @throws Forbidden
|
||||
*/
|
||||
protected function beforeUpdateEntity(Entity $entity, $data)
|
||||
{
|
||||
parent::beforeUpdateEntity($entity, $data);
|
||||
|
||||
$this->controlEntityValues($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Forbidden
|
||||
*/
|
||||
private function controlEntityValues(EmailFilterEntity $entity): void
|
||||
private function controlEntityValues(EmailFilter $entity): void
|
||||
{
|
||||
if ($entity->isGlobal()) {
|
||||
$entity->set('parentId', null);
|
||||
$entity->set('parentType', null);
|
||||
$entity->setMultiple([
|
||||
'parentType' => null,
|
||||
'parentId' => null,
|
||||
]);
|
||||
|
||||
if ($entity->getAction() !== EmailFilterEntity::ACTION_SKIP) {
|
||||
if ($entity->getAction() !== EmailFilter::ACTION_SKIP) {
|
||||
throw new Forbidden("Not allowed `action`.");
|
||||
}
|
||||
}
|
||||
@@ -93,9 +85,9 @@ class EmailFilter extends Record
|
||||
!in_array(
|
||||
$entity->getAction(),
|
||||
[
|
||||
EmailFilterEntity::ACTION_NONE,
|
||||
EmailFilterEntity::ACTION_SKIP,
|
||||
EmailFilterEntity::ACTION_MOVE_TO_FOLDER,
|
||||
EmailFilter::ACTION_NONE,
|
||||
EmailFilter::ACTION_SKIP,
|
||||
EmailFilter::ACTION_MOVE_TO_FOLDER,
|
||||
]
|
||||
)
|
||||
) {
|
||||
@@ -107,8 +99,8 @@ class EmailFilter extends Record
|
||||
!in_array(
|
||||
$entity->getAction(),
|
||||
[
|
||||
EmailFilterEntity::ACTION_SKIP,
|
||||
EmailFilterEntity::ACTION_MOVE_TO_GROUP_FOLDER,
|
||||
EmailFilter::ACTION_SKIP,
|
||||
EmailFilter::ACTION_MOVE_TO_GROUP_FOLDER,
|
||||
]
|
||||
)
|
||||
) {
|
||||
@@ -117,26 +109,19 @@ class EmailFilter extends Record
|
||||
|
||||
if (
|
||||
$entity->getParentType() === EmailAccountEntity::ENTITY_TYPE &&
|
||||
$entity->getAction() !== EmailFilterEntity::ACTION_SKIP
|
||||
$entity->getAction() !== EmailFilter::ACTION_SKIP
|
||||
) {
|
||||
throw new Forbidden("Not allowed `action`.");
|
||||
}
|
||||
|
||||
if ($entity->getAction() !== EmailFilterEntity::ACTION_MOVE_TO_FOLDER) {
|
||||
if ($entity->getAction() !== EmailFilter::ACTION_MOVE_TO_FOLDER) {
|
||||
/** @noinspection PhpRedundantOptionalArgumentInspection */
|
||||
$entity->set('emailFolderId', null);
|
||||
}
|
||||
|
||||
if ($entity->getAction() !== EmailFilterEntity::ACTION_MOVE_TO_GROUP_FOLDER) {
|
||||
if ($entity->getAction() !== EmailFilter::ACTION_MOVE_TO_GROUP_FOLDER) {
|
||||
/** @noinspection PhpRedundantOptionalArgumentInspection */
|
||||
$entity->set('groupEmailFolderId', null);
|
||||
}
|
||||
}
|
||||
|
||||
public function filterUpdateInput(stdClass $data): void
|
||||
{
|
||||
parent::filterUpdateInput($data);
|
||||
|
||||
unset($data->isGlobal);
|
||||
unset($data->parentId);
|
||||
unset($data->parentType);
|
||||
}
|
||||
}
|
||||
@@ -27,20 +27,27 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Services;
|
||||
namespace Espo\Classes\RecordHooks\EmailFolder;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Core\Acl;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Record\Hook\SaveHook;
|
||||
use Espo\Entities\EmailFolder;
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
/**
|
||||
* @extends Record<\Espo\Entities\EmailFolder>
|
||||
* @implements SaveHook<EmailFolder>
|
||||
*/
|
||||
class EmailFolder extends Record
|
||||
class BeforeCreate implements SaveHook
|
||||
{
|
||||
protected function beforeCreateEntity(Entity $entity, $data)
|
||||
{
|
||||
parent::beforeCreateEntity($entity, $data);
|
||||
public function __construct(
|
||||
private User $user,
|
||||
private Acl $acl
|
||||
) {}
|
||||
|
||||
public function process(Entity $entity): void
|
||||
{
|
||||
if (!$this->user->isAdmin() || !$entity->get('assignedUserId')) {
|
||||
$entity->set('assignedUserId', $this->user->getId());
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\RecordHooks\LeadCapture;
|
||||
|
||||
use Espo\Core\Record\Hook\SaveHook;
|
||||
use Espo\Entities\LeadCapture;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Tools\LeadCapture\Service;
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
* @implements SaveHook<LeadCapture>
|
||||
*/
|
||||
class BeforeCreate implements SaveHook
|
||||
{
|
||||
public function __construct(
|
||||
private Service $service
|
||||
) {}
|
||||
|
||||
public function process(Entity $entity): void
|
||||
{
|
||||
$apiKey = $this->service->generateApiKey();
|
||||
|
||||
$entity->setApiKey($apiKey);
|
||||
}
|
||||
}
|
||||
90
application/Espo/Classes/RecordHooks/Note/AfterCreate.php
Normal file
90
application/Espo/Classes/RecordHooks/Note/AfterCreate.php
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\RecordHooks\Note;
|
||||
|
||||
use Espo\Core\Record\Hook\SaveHook;
|
||||
use Espo\Core\Utils\Metadata;
|
||||
use Espo\Entities\Note;
|
||||
use Espo\Entities\Note as NoteEntity;
|
||||
use Espo\Entities\Preferences;
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\ORM\EntityManager;
|
||||
use Espo\Tools\Stream\Service;
|
||||
|
||||
/**
|
||||
* @implements SaveHook<Note>
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class AfterCreate implements SaveHook
|
||||
{
|
||||
public function __construct(
|
||||
private EntityManager $entityManager,
|
||||
private User $user,
|
||||
private Metadata $metadata,
|
||||
private Service $streamService
|
||||
) {}
|
||||
|
||||
public function process(Entity $entity): void
|
||||
{
|
||||
$parentType = $entity->getParentType();
|
||||
$parentId = $entity->getParentId();
|
||||
|
||||
if (
|
||||
$entity->getType() !== NoteEntity::TYPE_POST ||
|
||||
!$parentType ||
|
||||
!$parentId
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$this->metadata->get(['scopes', $parentType, 'stream'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$preferences = $this->entityManager->getEntityById(Preferences::ENTITY_TYPE, $this->user->getId());
|
||||
|
||||
if (!$preferences) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$preferences->get('followEntityOnStreamPost')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$parent = $this->entityManager->getEntityById($parentType, $parentId);
|
||||
|
||||
if (!$parent || $this->user->isSystem() || $this->user->isApi()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->streamService->followEntity($parent, $this->user->getId());
|
||||
}
|
||||
}
|
||||
196
application/Espo/Classes/RecordHooks/Note/AssignmentCheck.php
Normal file
196
application/Espo/Classes/RecordHooks/Note/AssignmentCheck.php
Normal file
@@ -0,0 +1,196 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\RecordHooks\Note;
|
||||
|
||||
use Espo\Core\Acl;
|
||||
use Espo\Core\Acl\Permission;
|
||||
use Espo\Core\Acl\Table as AclTable;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Record\Hook\SaveHook;
|
||||
use Espo\Entities\Note;
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\ORM\EntityManager;
|
||||
use Espo\Repositories\User as UserRepository;
|
||||
|
||||
/**
|
||||
* @implements SaveHook<Note>
|
||||
*/
|
||||
class AssignmentCheck implements SaveHook
|
||||
{
|
||||
public function __construct(
|
||||
private User $user,
|
||||
private Acl $acl,
|
||||
private EntityManager $entityManager
|
||||
) {}
|
||||
|
||||
public function process(Entity $entity): void
|
||||
{
|
||||
$targetType = $entity->getTargetType();
|
||||
|
||||
if (!$targetType) {
|
||||
return;
|
||||
}
|
||||
|
||||
$userTeamIdList = $this->user->getTeamIdList();
|
||||
|
||||
$userIdList = $entity->getLinkMultipleIdList('users');
|
||||
$portalIdList = $entity->getLinkMultipleIdList('portals');
|
||||
$teamIdList = $entity->getLinkMultipleIdList('teams');
|
||||
|
||||
/** @var iterable<User> $targetUserList */
|
||||
$targetUserList = [];
|
||||
|
||||
if ($targetType === Note::TARGET_USERS) {
|
||||
/** @var iterable<User> $targetUserList */
|
||||
$targetUserList = $this->entityManager
|
||||
->getRDBRepository(User::ENTITY_TYPE)
|
||||
->select(['id', 'type'])
|
||||
->where(['id' => $userIdList])
|
||||
->find();
|
||||
}
|
||||
|
||||
$hasPortalTargetUser = false;
|
||||
$allTargetUsersArePortal = true;
|
||||
|
||||
foreach ($targetUserList as $user) {
|
||||
if (!$user->isPortal()) {
|
||||
$allTargetUsersArePortal = false;
|
||||
}
|
||||
|
||||
if ($user->isPortal()) {
|
||||
$hasPortalTargetUser = true;
|
||||
}
|
||||
}
|
||||
|
||||
$messagePermission = $this->acl->getPermissionLevel(Permission::MESSAGE);
|
||||
|
||||
if ($messagePermission === AclTable::LEVEL_NO) {
|
||||
if (
|
||||
$targetType !== Note::TARGET_SELF &&
|
||||
$targetType !== Note::TARGET_PORTALS &&
|
||||
!(
|
||||
$targetType === Note::TARGET_USERS &&
|
||||
count($userIdList) === 1 &&
|
||||
$userIdList[0] === $this->user->getId()
|
||||
) &&
|
||||
!(
|
||||
$targetType === Note::TARGET_USERS && $allTargetUsersArePortal
|
||||
)
|
||||
) {
|
||||
throw new Forbidden('Not permitted to post to anybody except self.');
|
||||
}
|
||||
}
|
||||
|
||||
if ($targetType === Note::TARGET_TEAMS) {
|
||||
if (empty($teamIdList)) {
|
||||
throw new BadRequest("No team IDS.");
|
||||
}
|
||||
}
|
||||
|
||||
if ($targetType === Note::TARGET_USERS) {
|
||||
if (empty($userIdList)) {
|
||||
throw new BadRequest("No user IDs.");
|
||||
}
|
||||
}
|
||||
|
||||
if ($targetType === Note::TARGET_PORTALS) {
|
||||
if (empty($portalIdList)) {
|
||||
throw new BadRequest("No portal IDs.");
|
||||
}
|
||||
|
||||
if ($this->acl->getPermissionLevel(Permission::PORTAL) !== AclTable::LEVEL_YES) {
|
||||
throw new Forbidden('Not permitted to post to portal users.');
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
$targetType === Note::TARGET_USERS &&
|
||||
$this->acl->getPermissionLevel(Permission::PORTAL) !== AclTable::LEVEL_YES
|
||||
) {
|
||||
if ($hasPortalTargetUser) {
|
||||
throw new Forbidden('Not permitted to post to portal users.');
|
||||
}
|
||||
}
|
||||
|
||||
if ($messagePermission === AclTable::LEVEL_TEAM) {
|
||||
if ($targetType === Note::TARGET_ALL) {
|
||||
throw new Forbidden('Not permitted to post to all.');
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
$messagePermission === AclTable::LEVEL_TEAM &&
|
||||
$targetType === Note::TARGET_TEAMS
|
||||
) {
|
||||
if (empty($userTeamIdList)) {
|
||||
throw new Forbidden('Not permitted to post to foreign teams.');
|
||||
}
|
||||
|
||||
foreach ($teamIdList as $teamId) {
|
||||
if (!in_array($teamId, $userTeamIdList)) {
|
||||
throw new Forbidden("Not permitted to post to foreign teams.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
$messagePermission === AclTable::LEVEL_TEAM &&
|
||||
$targetType === Note::TARGET_USERS
|
||||
) {
|
||||
if (empty($userTeamIdList)) {
|
||||
throw new Forbidden('Not permitted to post to users from foreign teams.');
|
||||
}
|
||||
|
||||
foreach ($targetUserList as $user) {
|
||||
if ($user->getId() === $this->user->getId()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($user->isPortal()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$inTeam = $this->getUserRepository()->checkBelongsToAnyOfTeams($user->getId(), $userTeamIdList);
|
||||
|
||||
if (!$inTeam) {
|
||||
throw new Forbidden('Not permitted to post to users from foreign teams.');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function getUserRepository(): UserRepository
|
||||
{
|
||||
/** @var UserRepository */
|
||||
return $this->entityManager->getRepository(User::ENTITY_TYPE);
|
||||
}
|
||||
}
|
||||
136
application/Espo/Classes/RecordHooks/Note/BeforeCreate.php
Normal file
136
application/Espo/Classes/RecordHooks/Note/BeforeCreate.php
Normal file
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\RecordHooks\Note;
|
||||
|
||||
use Espo\Core\Acl;
|
||||
use Espo\Core\Acl\Table as AclTable;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Record\Hook\SaveHook;
|
||||
use Espo\Entities\Note;
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\ORM\EntityManager;
|
||||
use Espo\Tools\Stream\NoteUtil;
|
||||
|
||||
/**
|
||||
* @implements SaveHook<Note>
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class BeforeCreate implements SaveHook
|
||||
{
|
||||
public function __construct(
|
||||
private EntityManager $entityManager,
|
||||
private Acl $acl,
|
||||
private User $user,
|
||||
private NoteUtil $noteUtil
|
||||
) {}
|
||||
|
||||
public function process(Entity $entity): void
|
||||
{
|
||||
$this->checkParent($entity);
|
||||
|
||||
if (!$entity->isPost() && !$this->user->isAdmin()) {
|
||||
throw new Forbidden("Only 'Post' type allowed.");
|
||||
}
|
||||
|
||||
if ($this->user->isPortal()) {
|
||||
$entity->set('isInternal', false);
|
||||
}
|
||||
|
||||
if ($entity->isPost()) {
|
||||
$this->noteUtil->handlePostText($entity);
|
||||
}
|
||||
|
||||
$targetType = $entity->getTargetType();
|
||||
|
||||
$entity->clear('isPinned');
|
||||
$entity->clear('isGlobal');
|
||||
|
||||
switch ($targetType) {
|
||||
case Note::TARGET_ALL:
|
||||
|
||||
$entity->clear('usersIds');
|
||||
$entity->clear('teamsIds');
|
||||
$entity->clear('portalsIds');
|
||||
$entity->set('isGlobal', true);
|
||||
|
||||
break;
|
||||
|
||||
case Note::TARGET_SELF:
|
||||
|
||||
$entity->clear('usersIds');
|
||||
$entity->clear('teamsIds');
|
||||
$entity->clear('portalsIds');
|
||||
$entity->setUsersIds([$this->user->getId()]);
|
||||
$entity->set('isForSelf', true);
|
||||
|
||||
break;
|
||||
|
||||
case Note::TARGET_USERS:
|
||||
|
||||
$entity->clear('teamsIds');
|
||||
$entity->clear('portalsIds');
|
||||
|
||||
break;
|
||||
|
||||
case Note::TARGET_TEAMS:
|
||||
|
||||
$entity->clear('usersIds');
|
||||
$entity->clear('portalsIds');
|
||||
|
||||
break;
|
||||
|
||||
case Note::TARGET_PORTALS:
|
||||
|
||||
$entity->clear('usersIds');
|
||||
$entity->clear('teamsIds');
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Forbidden
|
||||
*/
|
||||
private function checkParent(Note $entity): void
|
||||
{
|
||||
if (!$entity->getParentType() || !$entity->getParentId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$parent = $this->entityManager->getEntityById($entity->getParentType(), $entity->getParentId());
|
||||
|
||||
if ($parent && $this->acl->check($parent, AclTable::ACTION_READ)) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Forbidden("No access to parent.");
|
||||
}
|
||||
}
|
||||
68
application/Espo/Classes/RecordHooks/Note/BeforeUpdate.php
Normal file
68
application/Espo/Classes/RecordHooks/Note/BeforeUpdate.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\RecordHooks\Note;
|
||||
|
||||
use Espo\Core\Exceptions\ForbiddenSilent;
|
||||
use Espo\Core\Record\Hook\SaveHook;
|
||||
use Espo\Entities\Note;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Tools\Stream\NoteUtil;
|
||||
|
||||
/**
|
||||
* @implements SaveHook<Note>
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class BeforeUpdate implements SaveHook
|
||||
{
|
||||
public function __construct(
|
||||
private NoteUtil $noteUtil,
|
||||
) {}
|
||||
|
||||
public function process(Entity $entity): void
|
||||
{
|
||||
if (!$this->isEditableType($entity)) {
|
||||
throw new ForbiddenSilent("Note is not editable.");
|
||||
}
|
||||
|
||||
if ($entity->isPost()) {
|
||||
$this->noteUtil->handlePostText($entity);
|
||||
}
|
||||
|
||||
if (!$entity->isPost()) {
|
||||
$entity->clear('post');
|
||||
$entity->clear('attachmentsIds');
|
||||
}
|
||||
}
|
||||
|
||||
private function isEditableType(Note $entity): bool
|
||||
{
|
||||
return $entity->getType() == Note::TYPE_POST;
|
||||
}
|
||||
}
|
||||
@@ -27,45 +27,42 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Services;
|
||||
namespace Espo\Classes\RecordHooks\Portal;
|
||||
|
||||
use Espo\Core\Acl\Cache\Clearer as AclCacheClearer;
|
||||
use Espo\Core\Acl\Cache\Clearer;
|
||||
use Espo\Core\DataManager;
|
||||
use Espo\Core\Record\Hook\SaveHook;
|
||||
use Espo\Entities\Portal;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
use Espo\Core\Di;
|
||||
use Espo\ORM\EntityManager;
|
||||
use Espo\Repositories\Portal as PortalRepository;
|
||||
|
||||
/**
|
||||
* @extends Record<\Espo\Entities\PortalRole>
|
||||
* @implements SaveHook<Portal>
|
||||
*/
|
||||
class PortalRole extends Record implements
|
||||
|
||||
Di\DataManagerAware
|
||||
class AfterUpdate implements SaveHook
|
||||
{
|
||||
use Di\DataManagerSetter;
|
||||
public function __construct(
|
||||
private Clearer $clearer,
|
||||
private DataManager $dataManager,
|
||||
private EntityManager $entityManager
|
||||
) {}
|
||||
|
||||
protected $forceSelectAllAttributes = true;
|
||||
|
||||
public function afterCreateEntity(Entity $entity, $data)
|
||||
public function process(Entity $entity): void
|
||||
{
|
||||
parent::afterCreateEntity($entity, $data);
|
||||
$this->clearRolesCache();
|
||||
}
|
||||
$this->getPortalRepository()->loadUrlField($entity);
|
||||
|
||||
public function afterUpdateEntity(Entity $entity, $data)
|
||||
{
|
||||
parent::afterUpdateEntity($entity, $data);
|
||||
$this->clearRolesCache();
|
||||
}
|
||||
|
||||
protected function clearRolesCache(): void
|
||||
{
|
||||
$this->createAclCacheClearer()->clearForAllPortalUsers();
|
||||
if (!$entity->isAttributeChanged('portalRolesIds')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->clearer->clearForAllPortalUsers();
|
||||
$this->dataManager->updateCacheTimestamp();
|
||||
}
|
||||
|
||||
private function createAclCacheClearer(): AclCacheClearer
|
||||
private function getPortalRepository(): PortalRepository
|
||||
{
|
||||
return $this->injectableFactory->create(AclCacheClearer::class);
|
||||
/** @var PortalRepository */
|
||||
return $this->entityManager->getRDBRepositoryByClass(Portal::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\RecordHooks\PortalRole;
|
||||
|
||||
use Espo\Core\Acl\Cache\Clearer;
|
||||
use Espo\Core\DataManager;
|
||||
use Espo\Core\Record\Hook\SaveHook;
|
||||
use Espo\Entities\PortalRole;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
/**
|
||||
* @implements SaveHook<PortalRole>
|
||||
*/
|
||||
class AfterSave implements SaveHook
|
||||
{
|
||||
public function __construct(
|
||||
private Clearer $clearer,
|
||||
private DataManager $dataManager
|
||||
) {}
|
||||
|
||||
public function process(Entity $entity): void
|
||||
{
|
||||
$this->clearer->clearForAllInternalUsers();
|
||||
$this->dataManager->updateCacheTimestamp();
|
||||
}
|
||||
}
|
||||
53
application/Espo/Classes/RecordHooks/Role/AfterSave.php
Normal file
53
application/Espo/Classes/RecordHooks/Role/AfterSave.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\RecordHooks\Role;
|
||||
|
||||
use Espo\Core\Acl\Cache\Clearer;
|
||||
use Espo\Core\DataManager;
|
||||
use Espo\Core\Record\Hook\SaveHook;
|
||||
use Espo\Entities\Role;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
/**
|
||||
* @implements SaveHook<Role>
|
||||
*/
|
||||
class AfterSave implements SaveHook
|
||||
{
|
||||
public function __construct(
|
||||
private Clearer $clearer,
|
||||
private DataManager $dataManager
|
||||
) {}
|
||||
|
||||
public function process(Entity $entity): void
|
||||
{
|
||||
$this->clearer->clearForAllInternalUsers();
|
||||
$this->dataManager->updateCacheTimestamp();
|
||||
}
|
||||
}
|
||||
232
application/Espo/Classes/RecordHooks/Role/BeforeSaveValidate.php
Normal file
232
application/Espo/Classes/RecordHooks/Role/BeforeSaveValidate.php
Normal file
@@ -0,0 +1,232 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\RecordHooks\Role;
|
||||
|
||||
use Espo\Core\Acl\Table;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Portal\Acl\Table as TablePortal;
|
||||
use Espo\Core\Record\Hook\SaveHook;
|
||||
use Espo\Core\Utils\Metadata;
|
||||
use Espo\Entities\PortalRole;
|
||||
use Espo\Entities\Role;
|
||||
use Espo\ORM\Entity;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
* @implements SaveHook<Role|PortalRole>
|
||||
*/
|
||||
class BeforeSaveValidate implements SaveHook
|
||||
{
|
||||
/** @var string[] */
|
||||
private array $levelList = [
|
||||
Table::LEVEL_YES,
|
||||
Table::LEVEL_ALL,
|
||||
Table::LEVEL_TEAM,
|
||||
Table::LEVEL_OWN,
|
||||
Table::LEVEL_NO,
|
||||
];
|
||||
|
||||
/** @var string[] */
|
||||
private array $portalLevelList = [
|
||||
Table::LEVEL_YES,
|
||||
Table::LEVEL_ALL,
|
||||
TablePortal::LEVEL_ACCOUNT,
|
||||
TablePortal::LEVEL_CONTACT,
|
||||
Table::LEVEL_OWN,
|
||||
Table::LEVEL_NO,
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
private Metadata $metadata
|
||||
) {}
|
||||
|
||||
public function process(Entity $entity): void
|
||||
{
|
||||
$this->validateData($entity);
|
||||
$this->validateFieldData($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws BadRequest
|
||||
*/
|
||||
private function validateData(Role|PortalRole $entity): void
|
||||
{
|
||||
if ($entity->get('data') === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var array<string, mixed> $data */
|
||||
$data = get_object_vars($entity->get('data'));
|
||||
|
||||
foreach ($data as $scope => $item) {
|
||||
if (!is_bool($item) && !$item instanceof stdClass) {
|
||||
throw new BadRequest("Bad data. Should be bool or object.");
|
||||
}
|
||||
|
||||
$this->validateDataItem($scope, $entity, $item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws BadRequest
|
||||
*/
|
||||
private function validateDataItem(string $scope, Role|PortalRole $entity, bool|stdClass $item): void
|
||||
{
|
||||
$key = $entity instanceof PortalRole ?
|
||||
'aclPortal' : 'acl';
|
||||
|
||||
$type = $this->metadata->get("scopes.$scope.$key");
|
||||
|
||||
if ($type === 'boolean') {
|
||||
if (!is_bool($item)) {
|
||||
throw new BadRequest("Bad data. Value for *$scope* should be be bool.");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($type === null) {
|
||||
throw new BadRequest("Bad data. Scope *$scope* is not allowed.");
|
||||
}
|
||||
|
||||
if ($item === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_bool($item)) {
|
||||
throw new BadRequest("Bad data. Value for *$scope* should be be false or object.");
|
||||
}
|
||||
|
||||
$actions = [
|
||||
Table::ACTION_CREATE,
|
||||
Table::ACTION_READ,
|
||||
Table::ACTION_EDIT,
|
||||
Table::ACTION_DELETE,
|
||||
Table::ACTION_STREAM,
|
||||
];
|
||||
|
||||
$levels = $entity instanceof PortalRole ?
|
||||
$this->portalLevelList : $this->levelList;
|
||||
|
||||
foreach ($actions as $action) {
|
||||
if (!property_exists($item, $action)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$level = $item->$action;
|
||||
|
||||
if (!in_array($level, $levels)) {
|
||||
throw new BadRequest("Level `$level` is not allowed for action *$action* for *$scope*.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws BadRequest
|
||||
*/
|
||||
private function validateFieldData(Role|PortalRole $entity): void
|
||||
{
|
||||
if ($entity->get('fieldData') === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var array<string, mixed> $data */
|
||||
$data = get_object_vars($entity->get('fieldData'));
|
||||
|
||||
foreach ($data as $scope => $item) {
|
||||
if (!$item instanceof stdClass) {
|
||||
throw new BadRequest("Bad field-level data. Should be object.");
|
||||
}
|
||||
|
||||
$this->validateFieldDataItem($scope, $entity, $item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws BadRequest
|
||||
*/
|
||||
private function validateFieldDataItem(string $scope, PortalRole|Role $entity, stdClass $item): void
|
||||
{
|
||||
$disabledKey = $entity instanceof PortalRole ? 'aclPortalFieldLevelDisabled' : 'aclFieldLevelDisabled';
|
||||
$key = $entity instanceof PortalRole ? 'aclPortal' : 'acl';
|
||||
|
||||
if (
|
||||
!$this->metadata->get("scopes.$scope.entity") ||
|
||||
!$this->metadata->get("scopes.$scope.$key") ||
|
||||
$this->metadata->get("scopes.$scope.$disabledKey")
|
||||
) {
|
||||
throw new BadRequest("Bad field-level data. Scope *$scope* is not allowed.");
|
||||
}
|
||||
|
||||
/** @var array<string, mixed> $data */
|
||||
$data = get_object_vars($item);
|
||||
|
||||
foreach ($data as $field => $fieldItem) {
|
||||
if (!$fieldItem instanceof stdClass) {
|
||||
throw new BadRequest("Data for field *$field*, scope *$scope* should be object.");
|
||||
}
|
||||
|
||||
$this->validateFieldDataItemItem($scope, $field, $fieldItem);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws BadRequest
|
||||
*/
|
||||
private function validateFieldDataItemItem(string $scope, string $field, stdClass $item): void
|
||||
{
|
||||
if (!$this->metadata->get("entityDefs.$scope.fields.$field")) {
|
||||
throw new BadRequest("Field *$field* does not exist in *$scope*.");
|
||||
}
|
||||
|
||||
$actions = [
|
||||
Table::ACTION_READ,
|
||||
Table::ACTION_EDIT,
|
||||
];
|
||||
|
||||
$levels = [
|
||||
Table::LEVEL_YES,
|
||||
Table::LEVEL_NO,
|
||||
];
|
||||
|
||||
foreach ($actions as $action) {
|
||||
if (!property_exists($item, $action)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$level = $item->$action;
|
||||
|
||||
if (!in_array($level, $levels)) {
|
||||
throw new BadRequest("Level `$level` is not allowed for *$scope*, field *$field*.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
58
application/Espo/Classes/RecordHooks/Team/AfterUpdate.php
Normal file
58
application/Espo/Classes/RecordHooks/Team/AfterUpdate.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\RecordHooks\Team;
|
||||
|
||||
use Espo\Core\Acl\Cache\Clearer;
|
||||
use Espo\Core\DataManager;
|
||||
use Espo\Core\Record\Hook\SaveHook;
|
||||
use Espo\Entities\Team;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
/**
|
||||
* @implements SaveHook<Team>
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class AfterUpdate implements SaveHook
|
||||
{
|
||||
public function __construct(
|
||||
private Clearer $clearer,
|
||||
private DataManager $dataManager
|
||||
) {}
|
||||
|
||||
public function process(Entity $entity): void
|
||||
{
|
||||
if (!$entity->isAttributeChanged('rolesIds')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->clearer->clearForAllInternalUsers();
|
||||
$this->dataManager->updateCacheTimestamp();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\RecordHooks\Team;
|
||||
|
||||
use Espo\Core\Acl\Cache\Clearer;
|
||||
use Espo\Core\DataManager;
|
||||
use Espo\Core\Record\Hook\LinkHook;
|
||||
use Espo\Entities\Team;
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
/**
|
||||
* @implements LinkHook<Team>
|
||||
*/
|
||||
class ClearCacheAfterLink implements LinkHook
|
||||
{
|
||||
public function __construct(
|
||||
private Clearer $clearer,
|
||||
private DataManager $dataManager
|
||||
) {}
|
||||
|
||||
public function process(Entity $entity, string $link, Entity $foreignEntity): void
|
||||
{
|
||||
if ($link !== 'users' || !$foreignEntity instanceof User) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->clearer->clearForUser($foreignEntity);
|
||||
$this->dataManager->updateCacheTimestamp();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\RecordHooks\Team;
|
||||
|
||||
use Espo\Core\Acl\Cache\Clearer;
|
||||
use Espo\Core\DataManager;
|
||||
use Espo\Core\Record\Hook\UnlinkHook;
|
||||
use Espo\Entities\Team;
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
/**
|
||||
* @implements UnlinkHook<Team>
|
||||
*/
|
||||
class ClearCacheAfterUnlink implements UnlinkHook
|
||||
{
|
||||
public function __construct(
|
||||
private Clearer $clearer,
|
||||
private DataManager $dataManager
|
||||
) {}
|
||||
|
||||
public function process(Entity $entity, string $link, Entity $foreignEntity): void
|
||||
{
|
||||
if ($link !== 'users' || !$foreignEntity instanceof User) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->clearer->clearForUser($foreignEntity);
|
||||
$this->dataManager->updateCacheTimestamp();
|
||||
}
|
||||
}
|
||||
114
application/Espo/Classes/RecordHooks/User/AfterUpdate.php
Normal file
114
application/Espo/Classes/RecordHooks/User/AfterUpdate.php
Normal file
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\RecordHooks\User;
|
||||
|
||||
use Espo\Core\Acl\Cache\Clearer;
|
||||
use Espo\Core\DataManager;
|
||||
use Espo\Core\Record\Hook\SaveHook;
|
||||
use Espo\Modules\Crm\Entities\Contact;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\EntityManager;
|
||||
|
||||
/**
|
||||
* @implements SaveHook<User>
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class AfterUpdate implements SaveHook
|
||||
{
|
||||
public function __construct(
|
||||
private EntityManager $entityManager,
|
||||
private Clearer $clearer,
|
||||
private DataManager $dataManager
|
||||
) {}
|
||||
|
||||
public function process(Entity $entity): void
|
||||
{
|
||||
$this->processCache($entity);
|
||||
$this->processContactName($entity);
|
||||
}
|
||||
|
||||
private function processCache(User $entity): void
|
||||
{
|
||||
if (
|
||||
$entity->isAttributeChanged('rolesIds') ||
|
||||
$entity->isAttributeChanged('teamsIds') ||
|
||||
$entity->isAttributeChanged('type') ||
|
||||
$entity->isAttributeChanged('portalRolesIds') ||
|
||||
$entity->isAttributeChanged('portalsIds')
|
||||
) {
|
||||
$this->clearer->clearForUser($entity);
|
||||
$this->dataManager->updateCacheTimestamp();
|
||||
}
|
||||
|
||||
if (
|
||||
$entity->isAttributeChanged('portalRolesIds') ||
|
||||
$entity->isAttributeChanged('portalsIds') ||
|
||||
$entity->isAttributeChanged('contactId') ||
|
||||
$entity->isAttributeChanged('accountsIds')
|
||||
) {
|
||||
$this->clearer->clearForAllPortalUsers();
|
||||
$this->dataManager->updateCacheTimestamp();
|
||||
}
|
||||
}
|
||||
|
||||
private function processContactName(User $entity): void
|
||||
{
|
||||
if (
|
||||
!$entity->isPortal() ||
|
||||
!$entity->getContactId() ||
|
||||
!$entity->isAttributeChanged('firstName') &&
|
||||
!$entity->isAttributeChanged('lastName') &&
|
||||
!$entity->isAttributeChanged('salutationName')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$contact = $this->entityManager->getEntityById(Contact::ENTITY_TYPE, $entity->getContactId());
|
||||
|
||||
if (!$contact) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($entity->isAttributeChanged('firstName')) {
|
||||
$contact->set('firstName', $entity->get('firstName'));
|
||||
}
|
||||
|
||||
if ($entity->isAttributeChanged('lastName')) {
|
||||
$contact->set('lastName', $entity->get('lastName'));
|
||||
}
|
||||
|
||||
if ($entity->isAttributeChanged('salutationName')) {
|
||||
$contact->set('salutationName', $entity->get('salutationName'));
|
||||
}
|
||||
|
||||
$this->entityManager->saveEntity($contact);
|
||||
}
|
||||
}
|
||||
135
application/Espo/Classes/RecordHooks/User/BeforeCreate.php
Normal file
135
application/Espo/Classes/RecordHooks/User/BeforeCreate.php
Normal file
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\RecordHooks\User;
|
||||
|
||||
use Espo\Core\Authentication\Logins\Hmac;
|
||||
use Espo\Core\Exceptions\Conflict;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Record\Hook\SaveHook;
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Core\Utils\Util;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Entities\User;
|
||||
use Espo\Tools\User\UserUtil;
|
||||
|
||||
/**
|
||||
* @implements SaveHook<User>
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class BeforeCreate implements SaveHook
|
||||
{
|
||||
public function __construct(
|
||||
private Config $config,
|
||||
private User $user,
|
||||
private UserUtil $util
|
||||
) {}
|
||||
|
||||
public function process(Entity $entity): void
|
||||
{
|
||||
$this->processLimitChecking($entity);
|
||||
$this->processUserExistsChecking($entity);
|
||||
$this->processApi($entity);
|
||||
$this->processTypeChecking($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Conflict
|
||||
*/
|
||||
private function processUserExistsChecking(User $entity): void
|
||||
{
|
||||
if ($this->util->checkExists($entity)) {
|
||||
throw new Conflict('userNameExists');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Forbidden
|
||||
*/
|
||||
private function processLimitChecking(User $entity): void
|
||||
{
|
||||
$userLimit = $this->config->get('userLimit');
|
||||
$portalUserLimit = $this->config->get('portalUserLimit');
|
||||
|
||||
if (
|
||||
$userLimit &&
|
||||
!$this->user->isSuperAdmin() &&
|
||||
!$entity->isPortal() && !$entity->isApi()
|
||||
) {
|
||||
$userCount = $this->util->getInternalCount();
|
||||
|
||||
if ($userCount >= $userLimit) {
|
||||
throw new Forbidden("User limit $userLimit is reached.");
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
$portalUserLimit &&
|
||||
!$this->user->isSuperAdmin() &&
|
||||
$entity->isPortal()
|
||||
) {
|
||||
$portalUserCount = $this->util->getPortalCount();
|
||||
|
||||
if ($portalUserCount >= $portalUserLimit) {
|
||||
throw new Forbidden("Portal user limit $portalUserLimit is reached.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function processApi(User $entity): void
|
||||
{
|
||||
if (!$entity->isApi()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$entity->set('apiKey', Util::generateApiKey());
|
||||
|
||||
if ($entity->getAuthMethod() === Hmac::NAME) {
|
||||
$secretKey = Util::generateSecretKey();
|
||||
|
||||
$entity->set('secretKey', $secretKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Forbidden
|
||||
*/
|
||||
private function processTypeChecking(User $entity): void
|
||||
{
|
||||
if (
|
||||
$entity->isSuperAdmin() ||
|
||||
!$entity->getType() ||
|
||||
in_array($entity->getType(), $this->util->getAllowedUserTypeList())
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Forbidden("Not allowed 'type'.");
|
||||
}
|
||||
}
|
||||
171
application/Espo/Classes/RecordHooks/User/BeforeUpdate.php
Normal file
171
application/Espo/Classes/RecordHooks/User/BeforeUpdate.php
Normal file
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\RecordHooks\User;
|
||||
|
||||
use Espo\Core\Authentication\Logins\Hmac;
|
||||
use Espo\Core\Exceptions\Conflict;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Record\Hook\SaveHook;
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Core\Utils\Util;
|
||||
use Espo\Entities\User as UserEntity;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Entities\User;
|
||||
use Espo\Tools\User\UserUtil;
|
||||
|
||||
/**
|
||||
* @implements SaveHook<User>
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class BeforeUpdate implements SaveHook
|
||||
{
|
||||
public function __construct(
|
||||
private Config $config,
|
||||
private User $user,
|
||||
private UserUtil $util
|
||||
) {}
|
||||
|
||||
public function process(Entity $entity): void
|
||||
{
|
||||
$this->processLimitChecking($entity);
|
||||
$this->processUserExistsChecking($entity);
|
||||
$this->processApi($entity);
|
||||
$this->processTypeChecking($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Conflict
|
||||
*/
|
||||
private function processUserExistsChecking(User $entity): void
|
||||
{
|
||||
if (!$entity->isAttributeChanged('userName')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->util->checkExists($entity)) {
|
||||
throw new Conflict('userNameExists');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Forbidden
|
||||
*/
|
||||
private function processLimitChecking(User $entity): void
|
||||
{
|
||||
$userLimit = $this->config->get('userLimit');
|
||||
$portalUserLimit = $this->config->get('portalUserLimit');
|
||||
|
||||
if (
|
||||
$userLimit &&
|
||||
!$this->user->isSuperAdmin() &&
|
||||
(
|
||||
(
|
||||
$entity->isActive() &&
|
||||
$entity->isAttributeChanged('isActive') &&
|
||||
!$entity->isPortal() &&
|
||||
!$entity->isApi()
|
||||
) ||
|
||||
(
|
||||
!$entity->isPortal() &&
|
||||
!$entity->isApi() &&
|
||||
$entity->isAttributeChanged('type') &&
|
||||
(
|
||||
$entity->isRegular() ||
|
||||
$entity->isAdmin()
|
||||
) &&
|
||||
(
|
||||
$entity->getFetched('type') == UserEntity::TYPE_PORTAL ||
|
||||
$entity->getFetched('type') == UserEntity::TYPE_API
|
||||
)
|
||||
)
|
||||
)
|
||||
) {
|
||||
$userCount = $this->util->getInternalCount();
|
||||
|
||||
if ($userCount >= $userLimit) {
|
||||
throw new Forbidden("User limit $userLimit is reached.");
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
$portalUserLimit &&
|
||||
!$this->user->isSuperAdmin() &&
|
||||
(
|
||||
(
|
||||
$entity->isActive() &&
|
||||
$entity->isAttributeChanged('isActive') &&
|
||||
$entity->isPortal()
|
||||
) ||
|
||||
(
|
||||
$entity->isPortal() &&
|
||||
$entity->isAttributeChanged('type')
|
||||
)
|
||||
)
|
||||
) {
|
||||
$portalUserCount = $this->util->getPortalCount();
|
||||
|
||||
if ($portalUserCount >= $portalUserLimit) {
|
||||
throw new Forbidden("Portal user limit $portalUserLimit is reached.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function processApi(User $entity): void
|
||||
{
|
||||
if (
|
||||
!$entity->isApi() ||
|
||||
!$entity->isAttributeChanged('authMethod') ||
|
||||
$entity->getAuthMethod() !== Hmac::NAME
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$secretKey = Util::generateSecretKey();
|
||||
|
||||
$entity->set('secretKey', $secretKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Forbidden
|
||||
*/
|
||||
private function processTypeChecking(User $entity): void
|
||||
{
|
||||
if (
|
||||
$entity->isSuperAdmin() ||
|
||||
!$entity->isAttributeChanged('type') ||
|
||||
!$entity->getType() ||
|
||||
in_array($entity->getType(), $this->util->getAllowedUserTypeList())
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Forbidden("Can't change type.");
|
||||
}
|
||||
}
|
||||
62
application/Espo/Classes/RecordHooks/Webhook/AfterDelete.php
Normal file
62
application/Espo/Classes/RecordHooks/Webhook/AfterDelete.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\RecordHooks\Webhook;
|
||||
|
||||
use Espo\Core\Record\DeleteParams;
|
||||
use Espo\Core\Record\Hook\DeleteHook;
|
||||
use Espo\Core\Webhook\Manager;
|
||||
use Espo\Entities\Webhook;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
/**
|
||||
* @implements DeleteHook<Webhook>
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class AfterDelete implements DeleteHook
|
||||
{
|
||||
public function __construct(
|
||||
private Manager $webhookManager
|
||||
) {}
|
||||
|
||||
public function process(Entity $entity, DeleteParams $params): void
|
||||
{
|
||||
$event = $entity->getEvent();
|
||||
|
||||
if (!$event) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$entity->isActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->webhookManager->removeEvent($event);
|
||||
}
|
||||
}
|
||||
75
application/Espo/Classes/RecordHooks/Webhook/AfterSave.php
Normal file
75
application/Espo/Classes/RecordHooks/Webhook/AfterSave.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\RecordHooks\Webhook;
|
||||
|
||||
use Espo\Core\Record\Hook\SaveHook;
|
||||
use Espo\Core\Webhook\Manager;
|
||||
use Espo\Entities\Webhook;
|
||||
use Espo\ORM\Entity;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* @implements SaveHook<Webhook>
|
||||
*/
|
||||
class AfterSave implements SaveHook
|
||||
{
|
||||
public function __construct(
|
||||
private Manager $webhookManager
|
||||
) {}
|
||||
|
||||
public function process(Entity $entity): void
|
||||
{
|
||||
$event = $entity->getEvent();
|
||||
|
||||
if (!$event) {
|
||||
throw new RuntimeException("No 'event'.");
|
||||
}
|
||||
|
||||
if ($entity->isNew()) {
|
||||
if ($entity->isActive()) {
|
||||
$this->webhookManager->addEvent($event);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$entity->isAttributeChanged('isActive')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($entity->isActive()) {
|
||||
$this->webhookManager->addEvent($event);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->webhookManager->removeEvent($event);
|
||||
}
|
||||
}
|
||||
@@ -27,124 +27,75 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Services;
|
||||
namespace Espo\Classes\RecordHooks\Webhook;
|
||||
|
||||
use Espo\Entities\Webhook as WebhookEntity;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Core\Acl;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Di;
|
||||
|
||||
use Espo\Core\Record\Hook\SaveHook;
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Core\Utils\Metadata;
|
||||
use Espo\Entities\User;
|
||||
|
||||
use stdClass;
|
||||
use Espo\Entities\Webhook;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\ORM\EntityManager;
|
||||
|
||||
/**
|
||||
* @extends Record<WebhookEntity>
|
||||
* @implements SaveHook<Webhook>
|
||||
*/
|
||||
class Webhook extends Record implements
|
||||
Di\WebhookManagerAware
|
||||
|
||||
class BeforeSave implements SaveHook
|
||||
{
|
||||
use Di\WebhookManagerSetter;
|
||||
private const WEBHOOK_MAX_COUNT_PER_USER = 50;
|
||||
|
||||
const WEBHOOK_MAX_COUNT_PER_USER = 50;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected $eventTypeList = [
|
||||
/** @var string[] */
|
||||
private $eventTypeList = [
|
||||
'create',
|
||||
'update',
|
||||
'delete',
|
||||
'fieldUpdate',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected $onlyAdminAttributeList = ['userId', 'userName'];
|
||||
public function __construct(
|
||||
private User $user,
|
||||
private EntityManager $entityManager,
|
||||
private Acl $acl,
|
||||
private Metadata $metadata,
|
||||
private Config $config
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected $readOnlyAttributeList = ['secretKey'];
|
||||
|
||||
public function populateDefaults(Entity $entity, stdClass $data): void
|
||||
{
|
||||
parent::populateDefaults($entity, $data);
|
||||
|
||||
if ($this->user->isApi()) {
|
||||
$entity->set('userId', $this->user->getId());
|
||||
}
|
||||
}
|
||||
|
||||
protected function filterInput($data)
|
||||
{
|
||||
parent::filterInput($data);
|
||||
|
||||
unset($data->entityType);
|
||||
unset($data->field);
|
||||
unset($data->type);
|
||||
}
|
||||
|
||||
public function filterUpdateInput(stdClass $data): void
|
||||
{
|
||||
if (!$this->user->isAdmin()) {
|
||||
unset($data->event);
|
||||
}
|
||||
|
||||
parent::filterUpdateInput($data);
|
||||
}
|
||||
|
||||
protected function beforeCreateEntity(Entity $entity, $data)
|
||||
public function process(Entity $entity): void
|
||||
{
|
||||
$this->checkEntityUserIsApi($entity);
|
||||
$this->processEntityEventData($entity);
|
||||
|
||||
if (!$this->user->isAdmin()) {
|
||||
if ($entity->isNew() && !$this->user->isAdmin()) {
|
||||
$this->checkMaxCount();
|
||||
}
|
||||
}
|
||||
|
||||
protected function checkMaxCount(): void
|
||||
/**
|
||||
* @throws Forbidden
|
||||
*/
|
||||
private function checkEntityUserIsApi(Webhook $entity): void
|
||||
{
|
||||
$maxCount = $this->config->get('webhookMaxCountPerUser', self::WEBHOOK_MAX_COUNT_PER_USER);
|
||||
|
||||
$count = $this->entityManager
|
||||
->getRDBRepository(WebhookEntity::ENTITY_TYPE)
|
||||
->where([
|
||||
'userId' => $this->user->getId(),
|
||||
])
|
||||
->count();
|
||||
|
||||
if ($maxCount && $count >= $maxCount) {
|
||||
throw new Forbidden("Webhook number per user exceeded the limit.");
|
||||
}
|
||||
}
|
||||
|
||||
protected function beforeUpdateEntity(Entity $entity, $data)
|
||||
{
|
||||
$this->checkEntityUserIsApi($entity);
|
||||
$this->processEntityEventData($entity);
|
||||
}
|
||||
|
||||
protected function checkEntityUserIsApi(Entity $entity): void
|
||||
{
|
||||
$userId = $entity->get('userId');
|
||||
$userId = $entity->getUserId();
|
||||
|
||||
if (!$userId) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var User|null $user */
|
||||
$user = $this->entityManager->getEntity(User::ENTITY_TYPE, $userId);
|
||||
$user = $this->entityManager->getRDBRepositoryByClass(User::class)->getById($userId);
|
||||
|
||||
if (!$user || !$user->isApi()) {
|
||||
throw new Forbidden("User must be an API User.");
|
||||
if ($user && $user->isApi()) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Forbidden("User must be an API User.");
|
||||
}
|
||||
|
||||
protected function processEntityEventData(Entity $entity): void
|
||||
/**
|
||||
* @throws Forbidden
|
||||
*/
|
||||
private function processEntityEventData(Webhook $entity): void
|
||||
{
|
||||
$event = $entity->get('event');
|
||||
|
||||
@@ -152,10 +103,8 @@ class Webhook extends Record implements
|
||||
throw new Forbidden("Event is empty.");
|
||||
}
|
||||
|
||||
if (!$entity->isNew()) {
|
||||
if ($entity->isAttributeChanged('event')) {
|
||||
throw new Forbidden("Event can't be changed.");
|
||||
}
|
||||
if (!$entity->isNew() && $entity->isAttributeChanged('event')) {
|
||||
throw new Forbidden("Event can't be changed.");
|
||||
}
|
||||
|
||||
$arr = explode('.', $event);
|
||||
@@ -164,7 +113,6 @@ class Webhook extends Record implements
|
||||
throw new Forbidden("Not supported event.");
|
||||
}
|
||||
|
||||
$arr = explode('.', $event);
|
||||
$entityType = $arr[0];
|
||||
$type = $arr[1];
|
||||
|
||||
@@ -185,7 +133,7 @@ class Webhook extends Record implements
|
||||
throw new Forbidden("Not existing Entity Type.");
|
||||
}
|
||||
|
||||
if (!$this->acl->checkScope($entityType, 'read')) {
|
||||
if (!$this->acl->checkScope($entityType, Acl\Table::ACTION_READ)) {
|
||||
throw new Forbidden("Entity type is forbidden.");
|
||||
}
|
||||
|
||||
@@ -204,43 +152,35 @@ class Webhook extends Record implements
|
||||
throw new Forbidden("Field is empty.");
|
||||
}
|
||||
|
||||
$forbiddenFieldList = $this->getAcl()->getScopeForbiddenFieldList($entityType);
|
||||
|
||||
if (in_array($field, $forbiddenFieldList)) {
|
||||
if (!$this->acl->checkField($entityType, $field)) {
|
||||
throw new Forbidden("Field is forbidden.");
|
||||
}
|
||||
|
||||
if (!$this->metadata->get(['entityDefs', $entityType, 'fields', $field])) {
|
||||
throw new Forbidden("Field does not exist.");
|
||||
}
|
||||
} else {
|
||||
$entity->set('field', null);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/** @noinspection PhpRedundantOptionalArgumentInspection */
|
||||
$entity->set('field', null);
|
||||
}
|
||||
|
||||
protected function afterCreateEntity(Entity $entity, $data)
|
||||
/**
|
||||
* @throws Forbidden
|
||||
*/
|
||||
private function checkMaxCount(): void
|
||||
{
|
||||
if ($entity->get('isActive')) {
|
||||
$this->webhookManager->addEvent($entity->get('event'));
|
||||
}
|
||||
}
|
||||
$maxCount = $this->config->get('webhookMaxCountPerUser', self::WEBHOOK_MAX_COUNT_PER_USER);
|
||||
|
||||
protected function afterDeleteEntity(Entity $entity)
|
||||
{
|
||||
if ($entity->get('isActive')) {
|
||||
$this->webhookManager->removeEvent($entity->get('event'));
|
||||
}
|
||||
}
|
||||
$count = $this->entityManager
|
||||
->getRDBRepositoryByClass(Webhook::class)
|
||||
->where(['userId' => $this->user->getId()])
|
||||
->count();
|
||||
|
||||
protected function afterUpdateEntity(Entity $entity, $data)
|
||||
{
|
||||
if (isset($data->isActive)) {
|
||||
if ($entity->get('isActive')) {
|
||||
$this->webhookManager->addEvent($entity->get('event'));
|
||||
}
|
||||
else {
|
||||
$this->webhookManager->removeEvent($entity->get('event'));
|
||||
}
|
||||
if ($maxCount && $count >= $maxCount) {
|
||||
throw new Forbidden("Webhook number per user exceeded the limit.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\Select\AddressCountry;
|
||||
|
||||
use Espo\Core\Select\Order\Item;
|
||||
use Espo\Core\Select\Order\Orderer;
|
||||
use Espo\ORM\Query\Part\Order;
|
||||
use Espo\ORM\Query\SelectBuilder;
|
||||
|
||||
class PreferredNameOrderer implements Orderer
|
||||
{
|
||||
public function apply(SelectBuilder $queryBuilder, Item $item): void
|
||||
{
|
||||
$queryBuilder
|
||||
->order('isPreferred', $item->getOrder() === Order::ASC ? Order::DESC : Order::ASC)
|
||||
->order('name', $item->getOrder());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\Select\AppLogRecord\PrimaryFilters;
|
||||
|
||||
use Espo\Core\Select\Primary\Filter;
|
||||
use Espo\ORM\Query\SelectBuilder as QueryBuilder;
|
||||
use Psr\Log\LogLevel;
|
||||
|
||||
class Errors implements Filter
|
||||
{
|
||||
public function apply(QueryBuilder $queryBuilder): void
|
||||
{
|
||||
$queryBuilder->where([
|
||||
'level' => [
|
||||
ucfirst(LogLevel::ERROR),
|
||||
ucfirst(LogLevel::EMERGENCY),
|
||||
ucfirst(LogLevel::CRITICAL),
|
||||
ucfirst(LogLevel::ALERT),
|
||||
]
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,8 @@
|
||||
namespace Espo\Classes\Select\Attachment\PrimaryFilters;
|
||||
|
||||
use Espo\Core\Select\Primary\Filter;
|
||||
use Espo\Entities\Attachment;
|
||||
use Espo\Entities\Settings;
|
||||
use Espo\ORM\Query\SelectBuilder;
|
||||
|
||||
class Orphan implements Filter
|
||||
@@ -37,32 +39,35 @@ class Orphan implements Filter
|
||||
public function apply(SelectBuilder $queryBuilder): void
|
||||
{
|
||||
$queryBuilder->where([
|
||||
'role' => ['Attachment', 'Inline Attachment'],
|
||||
'role' => [
|
||||
Attachment::ROLE_ATTACHMENT,
|
||||
Attachment::ROLE_INLINE_ATTACHMENT,
|
||||
],
|
||||
[
|
||||
'OR' => [
|
||||
[
|
||||
'parentId' => null,
|
||||
'parentType!=' => null,
|
||||
'relatedType=' => null,
|
||||
'parentId' => null,
|
||||
'relatedType' => null,
|
||||
],
|
||||
[
|
||||
'parentType' => null,
|
||||
'relatedId' => null,
|
||||
'relatedType!=' => null,
|
||||
'relatedId' => null,
|
||||
'parentType' => null,
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'OR' => [
|
||||
'relatedType!=' => 'Settings',
|
||||
'relatedType=' => null,
|
||||
'relatedType!=' => Settings::ENTITY_TYPE,
|
||||
'relatedType' => null,
|
||||
],
|
||||
],
|
||||
'attachmentChild.id' => null,
|
||||
]);
|
||||
|
||||
$queryBuilder->leftJoin(
|
||||
'Attachment',
|
||||
Attachment::ENTITY_TYPE,
|
||||
'attachmentChild',
|
||||
[
|
||||
'attachmentChild.sourceId:' => 'attachment.id',
|
||||
|
||||
@@ -32,6 +32,7 @@ namespace Espo\Classes\Select\Email\AccessControlFilters;
|
||||
use Espo\Core\Select\AccessControl\Filter;
|
||||
|
||||
use Espo\Classes\Select\Email\Helpers\JoinHelper;
|
||||
use Espo\Entities\Email;
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\Query\SelectBuilder as QueryBuilder;
|
||||
|
||||
@@ -46,6 +47,6 @@ class OnlyOwn implements Filter
|
||||
{
|
||||
$this->joinHelper->joinEmailUser($queryBuilder, $this->user->getId());
|
||||
|
||||
$queryBuilder->where(['emailUser.userId' => $this->user->getId()]);
|
||||
$queryBuilder->where([Email::ALIAS_INBOX . '.userId' => $this->user->getId()]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,15 +52,15 @@ class OnlyTeam implements Filter
|
||||
'entityTeam.entityType' => Email::ENTITY_TYPE,
|
||||
'entityTeam.deleted' => false,
|
||||
])
|
||||
->leftJoin(Email::RELATIONSHIP_EMAIL_USER, 'emailUser', [
|
||||
'emailUser.emailId:' => 'id',
|
||||
'emailUser.deleted' => false,
|
||||
'emailUser.userId' => $this->user->getId(),
|
||||
->leftJoin(Email::RELATIONSHIP_EMAIL_USER, Email::ALIAS_INBOX, [
|
||||
Email::ALIAS_INBOX . '.emailId:' => 'id',
|
||||
Email::ALIAS_INBOX . '.deleted' => false,
|
||||
Email::ALIAS_INBOX . '.userId' => $this->user->getId(),
|
||||
])
|
||||
->where([
|
||||
'OR' => [
|
||||
'entityTeam.teamId' => $this->user->getTeamIdList(),
|
||||
'emailUser.userId' => $this->user->getId(),
|
||||
Email::ALIAS_INBOX . '.userId' => $this->user->getId(),
|
||||
]
|
||||
])
|
||||
->build();
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace Espo\Classes\Select\Email\AccessControlFilters;
|
||||
|
||||
use Espo\Core\Select\AccessControl\Filter;
|
||||
use Espo\Classes\Select\Email\Helpers\JoinHelper;
|
||||
use Espo\Entities\Email;
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\Query\SelectBuilder as QueryBuilder;
|
||||
|
||||
@@ -46,10 +47,9 @@ class PortalOnlyAccount implements Filter
|
||||
$queryBuilder->distinct();
|
||||
|
||||
$orGroup = [
|
||||
'emailUser.userId' => $this->user->getId(),
|
||||
Email::ALIAS_INBOX . '.userId' => $this->user->getId(),
|
||||
];
|
||||
|
||||
/** @var string[] $accountIdList */
|
||||
$accountIdList = $this->user->getLinkMultipleIdList('accounts');
|
||||
|
||||
if (count($accountIdList)) {
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace Espo\Classes\Select\Email\AccessControlFilters;
|
||||
|
||||
use Espo\Core\Select\AccessControl\Filter;
|
||||
use Espo\Classes\Select\Email\Helpers\JoinHelper;
|
||||
use Espo\Entities\Email;
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\Query\SelectBuilder as QueryBuilder;
|
||||
|
||||
@@ -46,7 +47,7 @@ class PortalOnlyContact implements Filter
|
||||
$queryBuilder->distinct();
|
||||
|
||||
$orGroup = [
|
||||
'emailUser.userId' => $this->user->getId(),
|
||||
Email::ALIAS_INBOX . '.userId' => $this->user->getId(),
|
||||
];
|
||||
|
||||
$contactId = $this->user->get('contactId');
|
||||
|
||||
@@ -76,9 +76,9 @@ class Main implements AdditionalApplier
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->checkApplyDateSentIndex($queryBuilder, $searchParams)) {
|
||||
/*if ($this->checkApplyDateSentIndex($queryBuilder, $searchParams)) {
|
||||
$queryBuilder->useIndex('dateSent');
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
private function joinEmailUser(SelectBuilder $queryBuilder): void
|
||||
@@ -93,11 +93,12 @@ class Main implements AdditionalApplier
|
||||
Email::USERS_COLUMN_IS_READ,
|
||||
Email::USERS_COLUMN_IS_IMPORTANT,
|
||||
Email::USERS_COLUMN_IN_TRASH,
|
||||
Email::USERS_COLUMN_IN_ARCHIVE,
|
||||
Email::USERS_COLUMN_FOLDER_ID,
|
||||
];
|
||||
|
||||
foreach ($itemList as $item) {
|
||||
$queryBuilder->select('emailUser.' . $item, $item);
|
||||
$queryBuilder->select(Email::ALIAS_INBOX . '.' . $item, $item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,7 +117,7 @@ class Main implements AdditionalApplier
|
||||
return null;
|
||||
}
|
||||
|
||||
private function checkApplyDateSentIndex(SelectBuilder $queryBuilder, SearchParams $searchParams): bool
|
||||
/*private function checkApplyDateSentIndex(SelectBuilder $queryBuilder, SearchParams $searchParams): bool
|
||||
{
|
||||
if ($searchParams->getTextFilter()) {
|
||||
return false;
|
||||
@@ -149,5 +150,5 @@ class Main implements AdditionalApplier
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace Espo\Classes\Select\Email\BoolFilters;
|
||||
|
||||
use Espo\Classes\Select\Email\Helpers\JoinHelper;
|
||||
use Espo\Core\Select\Bool\Filter;
|
||||
use Espo\Entities\Email;
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\Query\Part\Where\OrGroupBuilder;
|
||||
use Espo\ORM\Query\Part\WhereClause;
|
||||
@@ -46,7 +47,7 @@ class OnlyMy implements Filter
|
||||
$this->joinHelper->joinEmailUser($queryBuilder, $this->user->getId());
|
||||
|
||||
$item = WhereClause::fromRaw([
|
||||
'emailUser.userId' => $this->user->getId(),
|
||||
Email::ALIAS_INBOX . '.userId' => $this->user->getId(),
|
||||
]);
|
||||
|
||||
$orGroupBuilder->add($item);
|
||||
|
||||
@@ -36,14 +36,14 @@ class JoinHelper
|
||||
{
|
||||
public function joinEmailUser(QueryBuilder $queryBuilder, string $userId): void
|
||||
{
|
||||
if ($queryBuilder->hasLeftJoinAlias('emailUser')) {
|
||||
if ($queryBuilder->hasLeftJoinAlias(Email::ALIAS_INBOX)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$queryBuilder->leftJoin(Email::RELATIONSHIP_EMAIL_USER, 'emailUser', [
|
||||
'emailUser.emailId:' => 'id',
|
||||
'emailUser.deleted' => false,
|
||||
'emailUser.userId' => $userId,
|
||||
$queryBuilder->leftJoin(Email::RELATIONSHIP_EMAIL_USER, Email::ALIAS_INBOX, [
|
||||
Email::ALIAS_INBOX . '.emailId:' => 'id',
|
||||
Email::ALIAS_INBOX . '.deleted' => false,
|
||||
Email::ALIAS_INBOX . '.userId' => $userId,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user