mirror of
https://github.com/espocrm/espocrm.git
synced 2026-03-05 19:37:01 +00:00
Compare commits
339 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f04ba5083 | ||
|
|
1ad265611f | ||
|
|
04906c5307 | ||
|
|
fefadc58bd | ||
|
|
1628d3b566 | ||
|
|
b456d86b22 | ||
|
|
c3a1236ca9 | ||
|
|
d76a6fa0ec | ||
|
|
b008270fe6 | ||
|
|
2deb826266 | ||
|
|
66815e3192 | ||
|
|
9e34de69f0 | ||
|
|
6346fae044 | ||
|
|
5ab64077e8 | ||
|
|
ae8221e208 | ||
|
|
84062f331f | ||
|
|
0a1cd5cb74 | ||
|
|
f63b7e04d6 | ||
|
|
c287137283 | ||
|
|
d3d8a5dc25 | ||
|
|
bf5468d4f4 | ||
|
|
7b93cf028a | ||
|
|
3012008b85 | ||
|
|
145312af2c | ||
|
|
0414ee6aa0 | ||
|
|
bf716b7953 | ||
|
|
d4657ff85d | ||
|
|
5023a58cc1 | ||
|
|
9ea75f1abf | ||
|
|
50a91836e8 | ||
|
|
12d05f7a6b | ||
|
|
bff4002cad | ||
|
|
29388a8a8d | ||
|
|
7f29a66ad3 | ||
|
|
e7067447b6 | ||
|
|
b807f32669 | ||
|
|
db596c886e | ||
|
|
0e37635373 | ||
|
|
58ac7de123 | ||
|
|
ab8421dbae | ||
|
|
915b8f288b | ||
|
|
52b7a59085 | ||
|
|
e75119645b | ||
|
|
a39cd8f114 | ||
|
|
4e8f771ef7 | ||
|
|
e0ce18407d | ||
|
|
fcdcf78629 | ||
|
|
821e346e7d | ||
|
|
0276fcf5ce | ||
|
|
7c729aa23f | ||
|
|
14d08204f1 | ||
|
|
2747fe2716 | ||
|
|
ff6957f577 | ||
|
|
39c0da248b | ||
|
|
4a29c14051 | ||
|
|
4f8dcf0447 | ||
|
|
081998c79f | ||
|
|
23f0f0bc6d | ||
|
|
4f7f40a245 | ||
|
|
624be85604 | ||
|
|
452eca267d | ||
|
|
0711781a1e | ||
|
|
65d3c25a9e | ||
|
|
3a94e1fb98 | ||
|
|
c621b84a4c | ||
|
|
df69375644 | ||
|
|
53ecb36705 | ||
|
|
2c5ba1b826 | ||
|
|
90ddfe3b43 | ||
|
|
a58d4be849 | ||
|
|
6f7b5e9973 | ||
|
|
a727cee3a6 | ||
|
|
562da819a6 | ||
|
|
7cc980d525 | ||
|
|
4af973015f | ||
|
|
210fac225d | ||
|
|
875377e0d2 | ||
|
|
eacd62f7fb | ||
|
|
4faaa2d082 | ||
|
|
8dda1321b5 | ||
|
|
bb59cda131 | ||
|
|
ed12c6f6bd | ||
|
|
324ded28de | ||
|
|
1262fdbf29 | ||
|
|
901c0ba24e | ||
|
|
8213f704ce | ||
|
|
17af6bf515 | ||
|
|
0d9f1a225f | ||
|
|
8518a44935 | ||
|
|
458fb47f43 | ||
|
|
f4db21af2c | ||
|
|
9d426f7feb | ||
|
|
52ae61b1a5 | ||
|
|
c3c76fab71 | ||
|
|
61db883330 | ||
|
|
447c236953 | ||
|
|
8bbfb743f0 | ||
|
|
3e18f488c6 | ||
|
|
ad6d001e9f | ||
|
|
faa11d603e | ||
|
|
ae2eff925b | ||
|
|
f180a8ff84 | ||
|
|
e6ad56d6d0 | ||
|
|
37f1948f9f | ||
|
|
1adea79d23 | ||
|
|
dd640c2fbe | ||
|
|
6c49e93cbf | ||
|
|
247858d295 | ||
|
|
797679ce7a | ||
|
|
ae17acce20 | ||
|
|
5974098482 | ||
|
|
0ae0a7e1f9 | ||
|
|
fb1e4acd60 | ||
|
|
e6407bf292 | ||
|
|
2ee1728776 | ||
|
|
5cab488a81 | ||
|
|
c623dfac75 | ||
|
|
3e94e18b73 | ||
|
|
1d00350fa1 | ||
|
|
9056ad493b | ||
|
|
b5a9007619 | ||
|
|
9221cbc361 | ||
|
|
9a6cfd4228 | ||
|
|
81ac053818 | ||
|
|
693bb804b6 | ||
|
|
6ec70d7792 | ||
|
|
0c6ddb725b | ||
|
|
fe7689297c | ||
|
|
95809454b8 | ||
|
|
9bc932d54f | ||
|
|
d57d5d1d4a | ||
|
|
b929aa07ec | ||
|
|
0c0e118ea2 | ||
|
|
d522eab8f5 | ||
|
|
21d5ea9fe6 | ||
|
|
9bb1cb3efc | ||
|
|
c681c3ba54 | ||
|
|
7960eb87cc | ||
|
|
f565f4cf04 | ||
|
|
5775ddcf6a | ||
|
|
a220bce2a4 | ||
|
|
184573946f | ||
|
|
e0a00f4d41 | ||
|
|
4fe1a035c6 | ||
|
|
5148f5e341 | ||
|
|
19fa50b6a7 | ||
|
|
19949f4217 | ||
|
|
bef906e421 | ||
|
|
0c68ecef37 | ||
|
|
4c5cd11ffb | ||
|
|
b4a5d28f53 | ||
|
|
06ffc8c6f6 | ||
|
|
eb32f23d38 | ||
|
|
5d61b049b5 | ||
|
|
2b667ffa03 | ||
|
|
fdb24513b5 | ||
|
|
a822535537 | ||
|
|
83b60e49c6 | ||
|
|
86c8fe9a83 | ||
|
|
d460805c8b | ||
|
|
a9d73a8501 | ||
|
|
a3cf76be92 | ||
|
|
23c9893362 | ||
|
|
7efbe56763 | ||
|
|
9759ab523c | ||
|
|
21f8ff0168 | ||
|
|
f865bd9282 | ||
|
|
8e0770b15d | ||
|
|
0b696f8388 | ||
|
|
6b6c166eca | ||
|
|
598422a822 | ||
|
|
15b3a89520 | ||
|
|
7d06d0f6a4 | ||
|
|
b975799231 | ||
|
|
96a8194775 | ||
|
|
600150c363 | ||
|
|
8a1242c312 | ||
|
|
b44b8721c5 | ||
|
|
6149f1a476 | ||
|
|
f4eb69c831 | ||
|
|
0e94a9708a | ||
|
|
97cc94d3d9 | ||
|
|
ec848524a6 | ||
|
|
ec20edde5e | ||
|
|
d6093e7764 | ||
|
|
b71bc36061 | ||
|
|
359fc94d99 | ||
|
|
5e59cbea8a | ||
|
|
843a7c1f1e | ||
|
|
aa5427d976 | ||
|
|
04218e0291 | ||
|
|
9a7c4da780 | ||
|
|
da777f2e40 | ||
|
|
7fc92a6a03 | ||
|
|
f93958ffea | ||
|
|
1f71856d59 | ||
|
|
097f5141d3 | ||
|
|
9547c45109 | ||
|
|
753158ae7a | ||
|
|
f50752df6f | ||
|
|
1525401105 | ||
|
|
d62d4265a8 | ||
|
|
4aae685c6e | ||
|
|
18cd794fe1 | ||
|
|
b20a4e768e | ||
|
|
c7c6cfe27f | ||
|
|
87077c7644 | ||
|
|
fd02e83cea | ||
|
|
8da8285c56 | ||
|
|
5c7f67a711 | ||
|
|
26634de090 | ||
|
|
1f2c89517e | ||
|
|
472a4a63d7 | ||
|
|
634315f9e4 | ||
|
|
064ccd31fc | ||
|
|
0c694447d6 | ||
|
|
3ae84a0e5b | ||
|
|
234de9b812 | ||
|
|
257e3621ea | ||
|
|
65e31f15a2 | ||
|
|
982201a1fa | ||
|
|
bb9c2f5e92 | ||
|
|
9dc04b2357 | ||
|
|
98d457dae6 | ||
|
|
d0f7798d55 | ||
|
|
5ee7b4ffad | ||
|
|
7b15b13e8b | ||
|
|
2b10be3464 | ||
|
|
4911d7caa1 | ||
|
|
a23886143a | ||
|
|
16e0ee4576 | ||
|
|
33dccd4ef7 | ||
|
|
7d03ae498b | ||
|
|
fda1dedbe4 | ||
|
|
07c2a2e49b | ||
|
|
4964056343 | ||
|
|
98e15c8f87 | ||
|
|
6c602610d4 | ||
|
|
08efd3f979 | ||
|
|
49a9cf9248 | ||
|
|
d56877c591 | ||
|
|
6fe123b3f9 | ||
|
|
427d2dcf52 | ||
|
|
755f3de523 | ||
|
|
fc72d847da | ||
|
|
6a963f39d4 | ||
|
|
5161fbb1d4 | ||
|
|
fe32d8d47b | ||
|
|
fffa608816 | ||
|
|
fb79de7903 | ||
|
|
007cf8c2aa | ||
|
|
67c86c9521 | ||
|
|
780d83f049 | ||
|
|
1e420ed6f7 | ||
|
|
e5a382b2ec | ||
|
|
b326142952 | ||
|
|
6870c47132 | ||
|
|
5251fcd4ec | ||
|
|
f9c6147396 | ||
|
|
937b2adf57 | ||
|
|
b4e4c875b9 | ||
|
|
1d52afb45a | ||
|
|
37165fc735 | ||
|
|
bed891289f | ||
|
|
3a007518e5 | ||
|
|
c2b856c3da | ||
|
|
5f944554cf | ||
|
|
db34791130 | ||
|
|
8b73912c3d | ||
|
|
1ac2f489ab | ||
|
|
4309d79c56 | ||
|
|
a802f740c5 | ||
|
|
9eb4fd7cf9 | ||
|
|
4dd5784838 | ||
|
|
87911fc3b6 | ||
|
|
d2a1737a9e | ||
|
|
352eefdb29 | ||
|
|
e3a2042990 | ||
|
|
a492a3bbb6 | ||
|
|
fac84830d3 | ||
|
|
3d743d306b | ||
|
|
6de9e82709 | ||
|
|
773f929b01 | ||
|
|
3a6a185548 | ||
|
|
32d1863411 | ||
|
|
23dd15e979 | ||
|
|
f3da3e0fb3 | ||
|
|
396d74df63 | ||
|
|
7293893fc7 | ||
|
|
9b796e7832 | ||
|
|
a8dab3a3b1 | ||
|
|
e432e14a95 | ||
|
|
ee865662b2 | ||
|
|
a24313c789 | ||
|
|
02fd5c430f | ||
|
|
4e0e223766 | ||
|
|
2df5017e94 | ||
|
|
6deb9b4ab7 | ||
|
|
0a57e538d5 | ||
|
|
e8dae0cfd1 | ||
|
|
e9c55bbd43 | ||
|
|
7a98325eae | ||
|
|
b0a811c51d | ||
|
|
da013d8aae | ||
|
|
a1d920f003 | ||
|
|
07443e54de | ||
|
|
717a678c9a | ||
|
|
ffaf999587 | ||
|
|
37fba10ba7 | ||
|
|
0a9de8cf56 | ||
|
|
a02be88c4a | ||
|
|
e8dc05ad99 | ||
|
|
6338a73eaa | ||
|
|
73533080f6 | ||
|
|
1c7a6d9825 | ||
|
|
c553bc8d0a | ||
|
|
9ff967249b | ||
|
|
3011cdf22c | ||
|
|
4f2ade5f7f | ||
|
|
b882aad4ab | ||
|
|
0d6f98319d | ||
|
|
8541c12441 | ||
|
|
57d9dbc33e | ||
|
|
81d92870e9 | ||
|
|
a86795ea48 | ||
|
|
cb6a320eb5 | ||
|
|
c9ab718666 | ||
|
|
46cace5ed2 | ||
|
|
2a967261b3 | ||
|
|
66ecf7794e | ||
|
|
7365007511 | ||
|
|
88870ccac8 | ||
|
|
12e40352fe | ||
|
|
2b0bde8c0b | ||
|
|
931e8b934c | ||
|
|
a07cb5ef38 | ||
|
|
66a6f70a30 | ||
|
|
d7a629c5a3 | ||
|
|
2010d16d4d |
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a bug report
|
||||
about: Create a bug report. Not to be used for help requests or server configuration issues. We appreciate if you prefer posting bug reports on weekdays rather than weekends.
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
3
.github/ISSUE_TEMPLATE/config.yml
vendored
3
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,4 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: EspoCRM forum
|
||||
url: https://forum.espocrm.com/
|
||||
about: Please use our forum to ask questions not related to product development
|
||||
about: "Use our forum for help requests and questions not related to product development. We don't provide support on GitHub."
|
||||
|
||||
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
|
||||
about: Freature requests are frozen til mid-June 2023. Please post on the forum instead. (Suggest an idea for EspoCRM).
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
1
.idea/.gitignore
generated
vendored
1
.idea/.gitignore
generated
vendored
@@ -4,3 +4,4 @@
|
||||
!/fileTemplates
|
||||
!/inspectionProfiles
|
||||
!misc.xml
|
||||
!jsonSchemas.xml
|
||||
|
||||
1351
.idea/jsonSchemas.xml
generated
Normal file
1351
.idea/jsonSchemas.xml
generated
Normal file
File diff suppressed because it is too large
Load Diff
3
.vscode/.gitignore
vendored
Normal file
3
.vscode/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
*
|
||||
!.gitignore
|
||||
!settings.json
|
||||
422
.vscode/settings.json
vendored
Normal file
422
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,422 @@
|
||||
{
|
||||
"json.schemas": [
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/routes.json"
|
||||
],
|
||||
"url": "./schema/routes.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/module.json"
|
||||
],
|
||||
"url": "./schema/routes.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/layouts/*/detail.json",
|
||||
"*/Resources/layouts/*/detailSmall.json",
|
||||
"*/Resources/layouts/*/detailConvert.json"
|
||||
],
|
||||
"url": "./schema/layouts/detail.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/layouts/*/list.json",
|
||||
"*/Resources/layouts/*/listSmall.json",
|
||||
"*/Resources/layouts/*/listFor*.json"
|
||||
],
|
||||
"url": "./schema/layouts/list.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/metadata/aclDefs/*.json"
|
||||
],
|
||||
"url": "./schema/metadata/aclDefs.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/metadata/authenticationMethods/*.json"
|
||||
],
|
||||
"url": "./schema/metadata/authenticationMethods.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/metadata/clientDefs/*.json"
|
||||
],
|
||||
"url": "./schema/metadata/clientDefs.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/metadata/dashlets/*.json"
|
||||
],
|
||||
"url": "./schema/metadata/dashlets.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/metadata//*.json"
|
||||
],
|
||||
"url": "./schema/metadata/.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/metadata/entityAcl/*.json"
|
||||
],
|
||||
"url": "./schema/metadata/entityAcl.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/metadata/entityDefs/*.json"
|
||||
],
|
||||
"url": "./schema/metadata/entityDefs.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/metadata/fields/*.json"
|
||||
],
|
||||
"url": "./schema/metadata/fields.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/metadata/integrations/*.json"
|
||||
],
|
||||
"url": "./schema/metadata/integrations.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/metadata/notificationDefs/*.json"
|
||||
],
|
||||
"url": "./schema/metadata/notificationDefs.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/metadata/pdfDefs/*.json"
|
||||
],
|
||||
"url": "./schema/metadata/pdfDefs.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/metadata/recordDefs/*.json"
|
||||
],
|
||||
"url": "./schema/metadata/recordDefs.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/metadata/scopes/*.json"
|
||||
],
|
||||
"url": "./schema/metadata/scopes.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/metadata/selectDefs/*.json"
|
||||
],
|
||||
"url": "./schema/metadata/selectDefs.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/metadata/streamDefs/*.json"
|
||||
],
|
||||
"url": "./schema/metadata/streamDefs.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/acl.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/acl.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/aclPortal.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/aclPortal.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/actions.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/actions.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/addressFormats.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/addressFormats.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/adminPanel.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/adminPanel.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/api.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/api.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/appParams.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/appParams.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/authentication.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/authentication.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/authentication2FAMethods.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/authentication2FAMethods.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/cleanup.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/cleanup.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/client.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/client.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/clientRoutes.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/clientRoutes.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/complexExpression.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/complexExpression.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/config.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/config.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/consoleCommands.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/consoleCommands.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/containerServices.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/containerServices.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/currency.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/currency.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/currencyConversion.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/currencyConversion.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/databasePlatforms.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/databasePlatforms.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/dateTime.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/dateTime.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/defaultDashboardLayouts.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/defaultDashboardLayouts.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/defaultDashboardOptions.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/defaultDashboardOptions.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/entityManagerParams.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/entityManagerParams.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/entityTemplateList.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/entityTemplateList.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/export.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/export.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/fieldProcessing.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/fieldProcessing.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/file.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/file.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/fileStorage.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/fileStorage.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/formula.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/formula.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/hook.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/hook.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/image.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/image.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/jsLibs.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/jsLibs.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/language.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/language.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/linkManager.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/linkManager.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/massActions.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/massActions.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/metadata.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/metadata.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/orm.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/orm.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/pdfEngines.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/pdfEngines.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/popupNotifications.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/popupNotifications.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/portalContainerServices.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/portalContainerServices.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/rebuild.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/rebuild.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/recordId.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/recordId.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/regExpPatterns.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/regExpPatterns.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/relationships.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/relationships.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/scheduledJobs.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/scheduledJobs.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/select.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/select.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/smsProviders.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/smsProviders.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/templateHelpers.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/templateHelpers.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/templates.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/templates.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"*/Resources/metadata/app/webSocket.json"
|
||||
],
|
||||
"url": "./schema/metadata/app/webSocket.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -90,7 +90,7 @@ module.exports = grunt => {
|
||||
mkdir: {
|
||||
tmp: {
|
||||
options: {
|
||||
mode: 0755,
|
||||
mode: 0o755,
|
||||
create: [
|
||||
'build/tmp',
|
||||
],
|
||||
@@ -110,9 +110,7 @@ module.exports = grunt => {
|
||||
beforeFinal: {
|
||||
src: [
|
||||
'build/tmp/custom/Espo/Custom/*',
|
||||
'build/tmp/custom/Espo/Modules/*',
|
||||
'!build/tmp/custom/Espo/Custom/.htaccess',
|
||||
'!build/tmp/custom/Espo/Modules/.htaccess',
|
||||
'build/tmp/install/config.php',
|
||||
'build/tmp/vendor/*/*/.git',
|
||||
'build/tmp/custom/Espo/Custom/*',
|
||||
@@ -236,7 +234,7 @@ module.exports = grunt => {
|
||||
options: {
|
||||
patterns: [
|
||||
{
|
||||
match: /\# \{\#dev\}(.*)\{\/dev\}/gs,
|
||||
match: /# \{#dev}(.*)\{\/dev}/gs,
|
||||
replacement: '',
|
||||
}
|
||||
]
|
||||
|
||||
33
README.md
33
README.md
@@ -27,36 +27,47 @@ For more information about server configuration see [this article](https://docs.
|
||||
|
||||
### Documentation
|
||||
|
||||
The documentation for administrators, users and developers is available [here](https://docs.espocrm.com).
|
||||
See the [documentation](https://docs.espocrm.com) for administrators, users and developers.
|
||||
|
||||
### Bug reporting
|
||||
|
||||
Create an issue [here](https://github.com/espocrm/espocrm/issues) or post on our [forum](https://forum.espocrm.com/forum/bug-reports).
|
||||
We'd appreciate if you prefer posting issues on weekdays rather than weekends.
|
||||
Create a [GitHub issue](https://github.com/espocrm/espocrm/issues/new/choose) or post on our [forum](https://forum.espocrm.com/forum/bug-reports).
|
||||
|
||||
### Installing the stable version
|
||||
### Installing stable version
|
||||
|
||||
See the [instructions](https://docs.espocrm.com/administration/installation/) on installation.
|
||||
See installation instructions:
|
||||
|
||||
* [Manual installation](https://docs.espocrm.com/administration/installation/)
|
||||
* [Installation by script](https://docs.espocrm.com/administration/installation-by-script/)
|
||||
* [Installation with Docker](https://docs.espocrm.com/administration/docker/installation/)
|
||||
* [Installation with Traefik](https://docs.espocrm.com/administration/docker/traefik/)
|
||||
|
||||
### Development
|
||||
|
||||
* [Getting started](https://docs.espocrm.com/development/how-to-start)
|
||||
* [Running tests](https://docs.espocrm.com/development/tests)
|
||||
* [Making translation](https://docs.espocrm.com/development/translation)
|
||||
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.
|
||||
|
||||
### 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.
|
||||
|
||||
Contribute translations to [POEditor](https://poeditor.com/join/project/gLDKZtUF4i). Changes
|
||||
are usually merged to the GitHub repository before minor releases.
|
||||
|
||||
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
|
||||
|
||||
If you want to improve existing translation or add a language that is not available yet, you can contribute on our [POEditor](https://poeditor.com/join/project/gLDKZtUF4i) project. See instructions [here](https://www.espocrm.com/blog/how-to-use-poeditor-to-translate-espocrm/).
|
||||
|
||||
Changes on POEditor are usually merged to the GitHub repository before minor releases.
|
||||
|
||||
### Community & Support
|
||||
|
||||
If you have a question regarding some features, need help or customizations, want to get in touch with other EspoCRM users, or add a feature request, please use our [community forum](https://forum.espocrm.com/). We believe that using the forum to ask for help and share experience allows everyone in the community to contribute and use this knowledge later.
|
||||
|
||||
### License
|
||||
|
||||
EspoCRM is published under the GNU GPLv3 [license](https://raw.githubusercontent.com/espocrm/espocrm/master/LICENSE.txt).
|
||||
|
||||
@@ -48,17 +48,12 @@ class AccessChecker implements AccessEntityCREDChecker
|
||||
{
|
||||
use DefaultAccessCheckerDependency;
|
||||
|
||||
private AclManager $aclManager;
|
||||
private EntityManager $entityManager;
|
||||
|
||||
public function __construct(
|
||||
DefaultAccessChecker $defaultAccessChecker,
|
||||
AclManager $aclManager,
|
||||
EntityManager $entityManager
|
||||
private AclManager $aclManager,
|
||||
private EntityManager $entityManager
|
||||
) {
|
||||
$this->defaultAccessChecker = $defaultAccessChecker;
|
||||
$this->aclManager = $aclManager;
|
||||
$this->entityManager = $entityManager;
|
||||
}
|
||||
|
||||
public function checkEntityRead(User $user, Entity $entity, ScopeData $data): bool
|
||||
@@ -126,8 +121,8 @@ class AccessChecker implements AccessEntityCREDChecker
|
||||
{
|
||||
if ($note->getTargetType() === Note::TARGET_TEAMS) {
|
||||
$intersect = array_intersect(
|
||||
$note->getLinkMultipleIdList('teams') ?? [],
|
||||
$user->getLinkMultipleIdList('teams') ?? []
|
||||
$note->getLinkMultipleIdList('teams'),
|
||||
$user->getLinkMultipleIdList('teams')
|
||||
);
|
||||
|
||||
if (count($intersect)) {
|
||||
@@ -150,6 +145,10 @@ class AccessChecker implements AccessEntityCREDChecker
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($note->getTargetType() === Note::TARGET_ALL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!$note->getParentId() || !$note->getParentType()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ class AccessChecker implements AccessEntityCREDChecker
|
||||
}
|
||||
|
||||
if ($entity->getTargetType() === Note::TARGET_TEAMS) {
|
||||
$targetTeamIdList = $entity->getLinkMultipleIdList('teams') ?? [];
|
||||
$targetTeamIdList = $entity->getLinkMultipleIdList('teams');
|
||||
|
||||
foreach ($user->getTeamIdList() as $teamId) {
|
||||
if (in_array($teamId, $targetTeamIdList)) {
|
||||
@@ -138,7 +138,7 @@ class AccessChecker implements AccessEntityCREDChecker
|
||||
}
|
||||
|
||||
if ($entity->getTargetType() === Note::TARGET_USERS) {
|
||||
return in_array($user->getId(), $entity->getLinkMultipleIdList('users') ?? []);
|
||||
return in_array($user->getId(), $entity->getLinkMultipleIdList('users'));
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -51,8 +51,8 @@ class OwnershipChecker implements OwnershipOwnChecker, OwnershipTeamChecker
|
||||
assert($entity instanceof CoreEntity);
|
||||
|
||||
$intersect = array_intersect(
|
||||
$user->getLinkMultipleIdList('teams') ?? [],
|
||||
$entity->getLinkMultipleIdList('teams') ?? []
|
||||
$user->getLinkMultipleIdList('teams'),
|
||||
$entity->getLinkMultipleIdList('teams')
|
||||
);
|
||||
|
||||
if (count($intersect)) {
|
||||
|
||||
@@ -131,8 +131,8 @@ class AccessChecker implements AccessEntityCREDChecker
|
||||
|
||||
if ($note->getTargetType() === Note::TARGET_PORTALS) {
|
||||
$intersect = array_intersect(
|
||||
$note->getLinkMultipleIdList('portals') ?? [],
|
||||
$user->getLinkMultipleIdList('portals') ?? []
|
||||
$note->getLinkMultipleIdList('portals'),
|
||||
$user->getLinkMultipleIdList('portals')
|
||||
);
|
||||
|
||||
if (count($intersect)) {
|
||||
|
||||
@@ -118,7 +118,7 @@ class AccessChecker implements AccessEntityCREDChecker
|
||||
}
|
||||
|
||||
if ($entity->getTargetType() === Note::TARGET_PORTALS) {
|
||||
return in_array($user->getPortalId(), $entity->getLinkMultipleIdList('portals') ?? []);
|
||||
return in_array($user->getPortalId(), $entity->getLinkMultipleIdList('portals'));
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -29,10 +29,8 @@
|
||||
|
||||
namespace Espo\Classes\AddressFormatters;
|
||||
|
||||
use Espo\Core\{
|
||||
Field\Address,
|
||||
Field\Address\AddressFormatter,
|
||||
};
|
||||
use Espo\Core\Field\Address;
|
||||
use Espo\Core\Field\Address\AddressFormatter;
|
||||
|
||||
class Formatter1 implements AddressFormatter
|
||||
{
|
||||
|
||||
@@ -29,10 +29,8 @@
|
||||
|
||||
namespace Espo\Classes\AddressFormatters;
|
||||
|
||||
use Espo\Core\{
|
||||
Field\Address,
|
||||
Field\Address\AddressFormatter,
|
||||
};
|
||||
use Espo\Core\Field\Address;
|
||||
use Espo\Core\Field\Address\AddressFormatter;
|
||||
|
||||
class Formatter2 implements AddressFormatter
|
||||
{
|
||||
|
||||
@@ -29,10 +29,8 @@
|
||||
|
||||
namespace Espo\Classes\AddressFormatters;
|
||||
|
||||
use Espo\Core\{
|
||||
Field\Address,
|
||||
Field\Address\AddressFormatter,
|
||||
};
|
||||
use Espo\Core\Field\Address;
|
||||
use Espo\Core\Field\Address\AddressFormatter;
|
||||
|
||||
class Formatter3 implements AddressFormatter
|
||||
{
|
||||
|
||||
@@ -29,10 +29,8 @@
|
||||
|
||||
namespace Espo\Classes\AddressFormatters;
|
||||
|
||||
use Espo\Core\{
|
||||
Field\Address,
|
||||
Field\Address\AddressFormatter,
|
||||
};
|
||||
use Espo\Core\Field\Address;
|
||||
use Espo\Core\Field\Address\AddressFormatter;
|
||||
|
||||
class Formatter4 implements AddressFormatter
|
||||
{
|
||||
|
||||
@@ -29,23 +29,14 @@
|
||||
|
||||
namespace Espo\Classes\AppInfo;
|
||||
|
||||
use Espo\Core\{
|
||||
Container as ContainerService,
|
||||
Utils\Metadata,
|
||||
Console\Command\Params,
|
||||
};
|
||||
use Espo\Core\Console\Command\Params;
|
||||
use Espo\Core\Container as ContainerService;
|
||||
use Espo\Core\Utils\Metadata;
|
||||
|
||||
class Container
|
||||
{
|
||||
private $container;
|
||||
|
||||
private $metadata;
|
||||
|
||||
public function __construct(ContainerService $container, Metadata $metadata)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->metadata = $metadata;
|
||||
}
|
||||
public function __construct(private ContainerService $container, private Metadata $metadata)
|
||||
{}
|
||||
|
||||
public function process(Params $params): string
|
||||
{
|
||||
|
||||
@@ -30,31 +30,27 @@
|
||||
namespace Espo\Classes\FieldProcessing\Email;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
use Espo\Core\{
|
||||
FieldProcessing\Loader,
|
||||
FieldProcessing\Loader\Params,
|
||||
ORM\EntityManager,
|
||||
};
|
||||
|
||||
use Espo\Core\FieldProcessing\Loader;
|
||||
use Espo\Core\FieldProcessing\Loader\Params;
|
||||
use Espo\Core\ORM\EntityManager;
|
||||
use Espo\Repositories\Email as EmailRepository;
|
||||
use Espo\Entities\Email;
|
||||
|
||||
/**
|
||||
* @implements Loader<\Espo\Entities\Email>
|
||||
* @implements Loader<Email>
|
||||
*/
|
||||
class AddressDataLoader implements Loader
|
||||
{
|
||||
private $entityManager;
|
||||
|
||||
public function __construct(EntityManager $entityManager)
|
||||
{
|
||||
$this->entityManager = $entityManager;
|
||||
}
|
||||
public function __construct(private EntityManager $entityManager)
|
||||
{}
|
||||
|
||||
/**
|
||||
* @param Email $entity
|
||||
*/
|
||||
public function process(Entity $entity, Params $params): void
|
||||
{
|
||||
/** @var EmailRepository $repository */
|
||||
$repository = $this->entityManager->getRepository('Email');
|
||||
$repository = $this->entityManager->getRepository(Email::ENTITY_TYPE);
|
||||
|
||||
$repository->loadFromField($entity);
|
||||
$repository->loadToField($entity);
|
||||
|
||||
@@ -29,27 +29,21 @@
|
||||
|
||||
namespace Espo\Classes\FieldProcessing\Import;
|
||||
|
||||
use Espo\Entities\Import;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
use Espo\Core\{
|
||||
FieldProcessing\Loader,
|
||||
FieldProcessing\Loader\Params,
|
||||
ORM\EntityManager,
|
||||
};
|
||||
use Espo\Core\FieldProcessing\Loader;
|
||||
use Espo\Core\FieldProcessing\Loader\Params;
|
||||
use Espo\Core\ORM\EntityManager;
|
||||
|
||||
use Espo\Repositories\Import as ImportRepository;
|
||||
|
||||
/**
|
||||
* @implements Loader<\Espo\Entities\Import>
|
||||
* @implements Loader<Import>
|
||||
*/
|
||||
class CountsLoader implements Loader
|
||||
{
|
||||
private $entityManager;
|
||||
|
||||
public function __construct(EntityManager $entityManager)
|
||||
{
|
||||
$this->entityManager = $entityManager;
|
||||
}
|
||||
public function __construct(private EntityManager $entityManager)
|
||||
{}
|
||||
|
||||
public function process(Entity $entity, Params $params): void
|
||||
{
|
||||
|
||||
@@ -30,16 +30,12 @@
|
||||
namespace Espo\Classes\FieldProcessing\Note;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
use Espo\Core\{
|
||||
FieldProcessing\Loader,
|
||||
FieldProcessing\Loader\Params,
|
||||
};
|
||||
|
||||
use Espo\Core\FieldProcessing\Loader;
|
||||
use Espo\Core\FieldProcessing\Loader\Params;
|
||||
use Espo\Entities\Note;
|
||||
|
||||
/**
|
||||
* @implements Loader<\Espo\Entities\Note>
|
||||
* @implements Loader<Note>
|
||||
*/
|
||||
class AttachmentsLoader implements Loader
|
||||
{
|
||||
|
||||
@@ -33,19 +33,16 @@ use Espo\ORM\Entity;
|
||||
|
||||
use Espo\Repositories\Portal as PortalRepository;
|
||||
use Espo\Entities\Portal;
|
||||
|
||||
use Espo\Core\{
|
||||
FieldProcessing\Loader,
|
||||
FieldProcessing\Loader\Params,
|
||||
ORM\EntityManager,
|
||||
};
|
||||
use Espo\Core\FieldProcessing\Loader;
|
||||
use Espo\Core\FieldProcessing\Loader\Params;
|
||||
use Espo\Core\ORM\EntityManager;
|
||||
|
||||
/**
|
||||
* @implements Loader<\Espo\Entities\Portal>
|
||||
* @implements Loader<Portal>
|
||||
*/
|
||||
class UrlLoader implements Loader
|
||||
{
|
||||
private $entityManager;
|
||||
private EntityManager $entityManager;
|
||||
|
||||
public function __construct(EntityManager $entityManager)
|
||||
{
|
||||
|
||||
@@ -40,7 +40,7 @@ class ArrayType
|
||||
{
|
||||
private const DEFAULT_MAX_ITEM_LENGTH = 100;
|
||||
|
||||
public function __construct(private Metadata $metadata, private Defs $defs)
|
||||
public function __construct(protected Metadata $metadata, private Defs $defs)
|
||||
{}
|
||||
|
||||
public function checkRequired(Entity $entity, string $field): bool
|
||||
@@ -128,6 +128,14 @@ class ArrayType
|
||||
|
||||
/** @var ?string $path */
|
||||
$path = $fieldDefs->getParam('optionsPath');
|
||||
/** @var ?string $path */
|
||||
$ref = $fieldDefs->getParam('optionsReference');
|
||||
|
||||
if (!$path && $ref && str_contains($ref, '.')) {
|
||||
[$refEntityType, $refField] = explode('.', $ref);
|
||||
|
||||
$path = "entityDefs.{$refEntityType}.fields.{$refField}.options";
|
||||
}
|
||||
|
||||
/** @var string[]|null|false $optionList */
|
||||
$optionList = $path ?
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2023 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\FieldValidators\Email\Addresses;
|
||||
|
||||
use Espo\Core\FieldValidation\Validator;
|
||||
use Espo\Core\FieldValidation\Validator\Data;
|
||||
use Espo\Core\FieldValidation\Validator\Failure;
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Entities\Email;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
use LogicException;
|
||||
|
||||
/**
|
||||
* @implements Validator<Email>
|
||||
*/
|
||||
class MaxCount implements Validator
|
||||
{
|
||||
private const MAX_COUNT = 100;
|
||||
|
||||
public function __construct(private Config $config) {}
|
||||
|
||||
/**
|
||||
* @param Email $entity
|
||||
*/
|
||||
public function validate(Entity $entity, string $field, Data $data): ?Failure
|
||||
{
|
||||
if ($field === 'to') {
|
||||
$addresses = $entity->getToAddressList();
|
||||
}
|
||||
else if ($field === 'cc') {
|
||||
$addresses = $entity->getCcAddressList();
|
||||
}
|
||||
else if ($field === 'bcc') {
|
||||
$addresses = $entity->getBccAddressList();
|
||||
}
|
||||
else {
|
||||
throw new LogicException();
|
||||
}
|
||||
|
||||
$maxCount = $this->config->get('emailRecipientAddressMaxCount') ?? self::MAX_COUNT;
|
||||
|
||||
if (count($addresses) > $maxCount) {
|
||||
return Failure::create();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2023 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\FieldValidators\Email\Addresses;
|
||||
|
||||
use Espo\Core\FieldValidation\Validator;
|
||||
use Espo\Core\FieldValidation\Validator\Data;
|
||||
use Espo\Core\FieldValidation\Validator\Failure;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Entities\Email;
|
||||
|
||||
use LogicException;
|
||||
|
||||
/**
|
||||
* @implements Validator<Email>
|
||||
*/
|
||||
class Valid implements Validator
|
||||
{
|
||||
/**
|
||||
* @param Email $entity
|
||||
*/
|
||||
public function validate(Entity $entity, string $field, Data $data): ?Failure
|
||||
{
|
||||
if ($field === 'to') {
|
||||
$addresses = $entity->getToAddressList();
|
||||
}
|
||||
else if ($field === 'cc') {
|
||||
$addresses = $entity->getCcAddressList();
|
||||
}
|
||||
else if ($field === 'bcc') {
|
||||
$addresses = $entity->getBccAddressList();
|
||||
}
|
||||
else {
|
||||
throw new LogicException();
|
||||
}
|
||||
|
||||
foreach ($addresses as $address) {
|
||||
if (!filter_var($address, FILTER_VALIDATE_EMAIL)) {
|
||||
return Failure::create();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -29,13 +29,17 @@
|
||||
|
||||
namespace Espo\Classes\FieldValidators\Email;
|
||||
|
||||
use Espo\Entities\Email;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
class EmailAddresses
|
||||
{
|
||||
/**
|
||||
* @param Email $entity
|
||||
*/
|
||||
public function checkRequired(Entity $entity, string $field): bool
|
||||
{
|
||||
if ($entity->get('status') === 'Draft') {
|
||||
if ($entity->getStatus() === Email::STATUS_DRAFT) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@ use Espo\ORM\Entity;
|
||||
class EnumType
|
||||
{
|
||||
private Metadata $metadata;
|
||||
|
||||
private Defs $defs;
|
||||
|
||||
private const DEFAULT_MAX_LENGTH = 255;
|
||||
@@ -65,6 +64,14 @@ class EnumType
|
||||
|
||||
/** @var ?string $path */
|
||||
$path = $fieldDefs->getParam('optionsPath');
|
||||
/** @var ?string $path */
|
||||
$ref = $fieldDefs->getParam('optionsReference');
|
||||
|
||||
if (!$path && $ref && str_contains($ref, '.')) {
|
||||
[$refEntityType, $refField] = explode('.', $ref);
|
||||
|
||||
$path = "entityDefs.{$refEntityType}.fields.{$refField}.options";
|
||||
}
|
||||
|
||||
/** @var string[]|null|false $optionList */
|
||||
$optionList = $path ?
|
||||
|
||||
@@ -117,7 +117,7 @@ class LinkMultipleType
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @var ?array<string,string> $columnsMap */
|
||||
/** @var ?array<string, string> $columnsMap */
|
||||
$columnsMap = $fieldDefs->getParam('columns');
|
||||
|
||||
if ($columnsMap === null || $columnsMap === []) {
|
||||
|
||||
@@ -30,19 +30,12 @@
|
||||
namespace Espo\Classes\FieldValidators;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
use Espo\Core\{
|
||||
Utils\FieldUtil,
|
||||
};
|
||||
use Espo\Core\Utils\FieldUtil;
|
||||
|
||||
class PersonNameType
|
||||
{
|
||||
private $fieldUtil;
|
||||
|
||||
public function __construct(FieldUtil $fieldUtil)
|
||||
{
|
||||
$this->fieldUtil = $fieldUtil;
|
||||
}
|
||||
public function __construct(private FieldUtil $fieldUtil)
|
||||
{}
|
||||
|
||||
public function checkRequired(Entity $entity, string $field): bool
|
||||
{
|
||||
|
||||
55
application/Espo/Classes/FieldValidators/UrlMultipleType.php
Normal file
55
application/Espo/Classes/FieldValidators/UrlMultipleType.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2023 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\FieldValidators;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
class UrlMultipleType extends ArrayType
|
||||
{
|
||||
private const MAX_ITEM_LENGTH = 255;
|
||||
|
||||
public function checkNoEmptyString(Entity $entity, string $field, ?bool $validationValue): bool
|
||||
{
|
||||
return parent::checkNoEmptyString($entity, $field, true);
|
||||
}
|
||||
|
||||
public function checkMaxItemLength(Entity $entity, string $field, ?int $validationValue): bool
|
||||
{
|
||||
return parent::checkMaxItemLength($entity, $field, self::MAX_ITEM_LENGTH);
|
||||
}
|
||||
|
||||
public function checkPattern(Entity $entity, string $field, ?string $validationValue): bool
|
||||
{
|
||||
/** @var string $pattern */
|
||||
$pattern = $this->metadata->get(['app', 'regExpPatterns', 'uriOptionalProtocol', 'pattern']);
|
||||
|
||||
return parent::checkPattern($entity, $field, $pattern);
|
||||
}
|
||||
}
|
||||
@@ -29,31 +29,28 @@
|
||||
|
||||
namespace Espo\Classes\JobPreparators;
|
||||
|
||||
use Espo\Core\Utils\DateTime;
|
||||
use Espo\Core\Job\Job\Status;
|
||||
use Espo\Core\Job\Preparator;
|
||||
use Espo\Core\Job\Preparator\Data;
|
||||
|
||||
use Espo\Entities\EmailAccount;
|
||||
use Espo\ORM\EntityManager;
|
||||
|
||||
use Espo\Entities\Job as JobEntity;
|
||||
use Espo\Entities\EmailAccount;
|
||||
use Espo\Core\Job\Preparator\CollectionHelper;
|
||||
|
||||
use DateTimeImmutable;
|
||||
|
||||
class CheckEmailAccounts implements Preparator
|
||||
{
|
||||
private EntityManager $entityManager;
|
||||
|
||||
public function __construct(EntityManager $entityManager)
|
||||
{
|
||||
$this->entityManager = $entityManager;
|
||||
}
|
||||
/**
|
||||
* @param CollectionHelper<EmailAccount> $helper
|
||||
*/
|
||||
public function __construct(
|
||||
private EntityManager $entityManager,
|
||||
private CollectionHelper $helper
|
||||
) {}
|
||||
|
||||
public function prepare(Data $data, DateTimeImmutable $executeTime): void
|
||||
{
|
||||
$collection = $this->entityManager
|
||||
->getRDBRepository(EmailAccount::ENTITY_TYPE)
|
||||
->getRDBRepositoryByClass(EmailAccount::class)
|
||||
->join('assignedUser', 'assignedUserAdditional')
|
||||
->where([
|
||||
'status' => EmailAccount::STATUS_ACTIVE,
|
||||
@@ -62,49 +59,6 @@ class CheckEmailAccounts implements Preparator
|
||||
])
|
||||
->find();
|
||||
|
||||
foreach ($collection as $entity) {
|
||||
$running = $this->entityManager
|
||||
->getRDBRepository(JobEntity::ENTITY_TYPE)
|
||||
->where([
|
||||
'scheduledJobId' => $data->getId(),
|
||||
'status' => [
|
||||
Status::RUNNING,
|
||||
Status::READY,
|
||||
],
|
||||
'targetType' => EmailAccount::ENTITY_TYPE,
|
||||
'targetId' => $entity->getId(),
|
||||
])
|
||||
->findOne();
|
||||
|
||||
if ($running) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$countPending = $this->entityManager
|
||||
->getRDBRepository(JobEntity::ENTITY_TYPE)
|
||||
->where([
|
||||
'scheduledJobId' => $data->getId(),
|
||||
'status' => Status::PENDING,
|
||||
'targetType' => EmailAccount::ENTITY_TYPE,
|
||||
'targetId' => $entity->getId(),
|
||||
])
|
||||
->count();
|
||||
|
||||
if ($countPending > 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$jobEntity = $this->entityManager->getNewEntity(JobEntity::ENTITY_TYPE);
|
||||
|
||||
$jobEntity->set([
|
||||
'name' => $data->getName(),
|
||||
'scheduledJobId' => $data->getId(),
|
||||
'executeTime' => $executeTime->format(DateTime::SYSTEM_DATE_TIME_FORMAT),
|
||||
'targetType' => EmailAccount::ENTITY_TYPE,
|
||||
'targetId' => $entity->getId(),
|
||||
]);
|
||||
|
||||
$this->entityManager->saveEntity($jobEntity);
|
||||
}
|
||||
$this->helper->prepare($collection, $data, $executeTime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,80 +29,34 @@
|
||||
|
||||
namespace Espo\Classes\JobPreparators;
|
||||
|
||||
use Espo\Core\Utils\DateTime;
|
||||
use Espo\Core\Job\Job\Status;
|
||||
use Espo\Core\Job\Preparator;
|
||||
use Espo\Core\Job\Preparator\Data;
|
||||
|
||||
use Espo\Entities\InboundEmail;
|
||||
use Espo\ORM\EntityManager;
|
||||
|
||||
use Espo\Entities\Job as JobEntity;
|
||||
use Espo\Entities\InboundEmail;
|
||||
use Espo\Core\Job\Preparator\CollectionHelper;
|
||||
|
||||
use DateTimeImmutable;
|
||||
|
||||
class CheckInboundEmails implements Preparator
|
||||
{
|
||||
private $entityManager;
|
||||
|
||||
public function __construct(EntityManager $entityManager)
|
||||
{
|
||||
$this->entityManager = $entityManager;
|
||||
}
|
||||
/**
|
||||
* @param CollectionHelper<InboundEmail> $helper
|
||||
*/
|
||||
public function __construct(
|
||||
private EntityManager $entityManager,
|
||||
private CollectionHelper $helper
|
||||
) {}
|
||||
|
||||
public function prepare(Data $data, DateTimeImmutable $executeTime): void
|
||||
{
|
||||
$collection = $this->entityManager
|
||||
->getRDBRepository(InboundEmail::ENTITY_TYPE)
|
||||
->getRDBRepositoryByClass(InboundEmail::class)
|
||||
->where([
|
||||
'status' => InboundEmail::STATUS_ACTIVE,
|
||||
'useImap' => true,
|
||||
])
|
||||
->find();
|
||||
|
||||
foreach ($collection as $entity) {
|
||||
$running = $this->entityManager
|
||||
->getRDBRepository(JobEntity::ENTITY_TYPE)
|
||||
->where([
|
||||
'scheduledJobId' => $data->getId(),
|
||||
'status' => [
|
||||
Status::RUNNING,
|
||||
Status::READY,
|
||||
],
|
||||
'targetType' => InboundEmail::ENTITY_TYPE,
|
||||
'targetId' => $entity->getId(),
|
||||
])
|
||||
->findOne();
|
||||
|
||||
if ($running) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$countPending = $this->entityManager
|
||||
->getRDBRepository(JobEntity::ENTITY_TYPE)
|
||||
->where([
|
||||
'scheduledJobId' => $data->getId(),
|
||||
'status' => Status::PENDING,
|
||||
'targetType' => InboundEmail::ENTITY_TYPE,
|
||||
'targetId' => $entity->getId(),
|
||||
])
|
||||
->count();
|
||||
|
||||
if ($countPending > 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$jobEntity = $this->entityManager->getNewEntity(JobEntity::ENTITY_TYPE);
|
||||
|
||||
$jobEntity->set([
|
||||
'name' => $data->getName(),
|
||||
'scheduledJobId' => $data->getId(),
|
||||
'executeTime' => $executeTime->format(DateTime::SYSTEM_DATE_TIME_FORMAT),
|
||||
'targetType' => InboundEmail::ENTITY_TYPE,
|
||||
'targetId' => $entity->getId(),
|
||||
]);
|
||||
|
||||
$this->entityManager->saveEntity($jobEntity);
|
||||
}
|
||||
$this->helper->prepare($collection, $data, $executeTime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -609,8 +609,6 @@ class Cleanup implements JobDataLess
|
||||
|
||||
$repository->deleteFromDb($entity->getId());
|
||||
|
||||
$query = $this->entityManager->getQueryComposer();
|
||||
|
||||
foreach ($entity->getRelationList() as $relation) {
|
||||
if ($entity->getRelationType($relation) !== Entity::MANY_MANY) {
|
||||
continue;
|
||||
@@ -763,6 +761,15 @@ class Cleanup implements JobDataLess
|
||||
'deleted' => true,
|
||||
];
|
||||
|
||||
if (
|
||||
!$this->entityManager
|
||||
->getDefs()
|
||||
->getEntity($scope)
|
||||
->hasAttribute('deleted')
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($this->metadata->get(['entityDefs', $scope, 'fields', 'modifiedAt'])) {
|
||||
$whereClause['modifiedAt<'] = $datetime->format(DateTimeUtil::SYSTEM_DATE_TIME_FORMAT);
|
||||
}
|
||||
|
||||
@@ -29,19 +29,13 @@
|
||||
|
||||
namespace Espo\Classes\Jobs;
|
||||
|
||||
use Espo\Core\{
|
||||
Job\JobDataLess,
|
||||
Webhook\Queue,
|
||||
};
|
||||
use Espo\Core\Job\JobDataLess;
|
||||
use Espo\Core\Webhook\Queue;
|
||||
|
||||
class ProcessWebhookQueue implements JobDataLess
|
||||
{
|
||||
private $queue;
|
||||
|
||||
public function __construct(Queue $queue)
|
||||
{
|
||||
$this->queue = $queue;
|
||||
}
|
||||
public function __construct(private Queue $queue)
|
||||
{}
|
||||
|
||||
public function run(): void
|
||||
{
|
||||
|
||||
@@ -29,20 +29,14 @@
|
||||
|
||||
namespace Espo\Classes\Select\ActionHistoryRecord\AccessControlFilters;
|
||||
|
||||
use Espo\{
|
||||
Core\Select\AccessControl\Filter,
|
||||
ORM\Query\SelectBuilder as QueryBuilder,
|
||||
Entities\User,
|
||||
};
|
||||
use Espo\Core\Select\AccessControl\Filter;
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\Query\SelectBuilder as QueryBuilder;
|
||||
|
||||
class OnlyOwn implements Filter
|
||||
{
|
||||
private $user;
|
||||
|
||||
public function __construct(User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
public function __construct(private User $user)
|
||||
{}
|
||||
|
||||
public function apply(QueryBuilder $queryBuilder): void
|
||||
{
|
||||
|
||||
@@ -29,22 +29,16 @@
|
||||
|
||||
namespace Espo\Classes\Select\ActionHistoryRecord\BoolFilters;
|
||||
|
||||
use Espo\{
|
||||
Core\Select\Bool\Filter,
|
||||
ORM\Query\SelectBuilder as QueryBuilder,
|
||||
ORM\Query\Part\WhereClause,
|
||||
ORM\Query\Part\Where\OrGroupBuilder,
|
||||
Entities\User,
|
||||
};
|
||||
use Espo\Core\Select\Bool\Filter;
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\Query\Part\Where\OrGroupBuilder;
|
||||
use Espo\ORM\Query\Part\WhereClause;
|
||||
use Espo\ORM\Query\SelectBuilder as QueryBuilder;
|
||||
|
||||
class OnlyMy implements Filter
|
||||
{
|
||||
private $user;
|
||||
|
||||
public function __construct(User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
public function __construct(private User $user)
|
||||
{}
|
||||
|
||||
public function apply(QueryBuilder $queryBuilder, OrGroupBuilder $orGroupBuilder): void
|
||||
{
|
||||
|
||||
@@ -29,27 +29,15 @@
|
||||
|
||||
namespace Espo\Classes\Select\Email\AccessControlFilters;
|
||||
|
||||
use Espo\Core\{
|
||||
Select\AccessControl\Filter,
|
||||
};
|
||||
|
||||
use Espo\{
|
||||
ORM\Query\SelectBuilder as QueryBuilder,
|
||||
Classes\Select\Email\Helpers\JoinHelper,
|
||||
Entities\User,
|
||||
};
|
||||
use Espo\Core\Select\AccessControl\Filter;
|
||||
use Espo\Classes\Select\Email\Helpers\JoinHelper;
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\Query\SelectBuilder as QueryBuilder;
|
||||
|
||||
class PortalOnlyAccount implements Filter
|
||||
{
|
||||
private $user;
|
||||
|
||||
private $joinHelper;
|
||||
|
||||
public function __construct(User $user, JoinHelper $joinHelper)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->joinHelper = $joinHelper;
|
||||
}
|
||||
public function __construct(private User $user, private JoinHelper $joinHelper)
|
||||
{}
|
||||
|
||||
public function apply(QueryBuilder $queryBuilder): void
|
||||
{
|
||||
|
||||
@@ -29,27 +29,15 @@
|
||||
|
||||
namespace Espo\Classes\Select\Email\AccessControlFilters;
|
||||
|
||||
use Espo\Core\{
|
||||
Select\AccessControl\Filter,
|
||||
};
|
||||
|
||||
use Espo\{
|
||||
ORM\Query\SelectBuilder as QueryBuilder,
|
||||
Classes\Select\Email\Helpers\JoinHelper,
|
||||
Entities\User,
|
||||
};
|
||||
use Espo\Core\Select\AccessControl\Filter;
|
||||
use Espo\Classes\Select\Email\Helpers\JoinHelper;
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\Query\SelectBuilder as QueryBuilder;
|
||||
|
||||
class PortalOnlyContact implements Filter
|
||||
{
|
||||
private $user;
|
||||
|
||||
private $joinHelper;
|
||||
|
||||
public function __construct(User $user, JoinHelper $joinHelper)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->joinHelper = $joinHelper;
|
||||
}
|
||||
public function __construct(private User $user, private JoinHelper $joinHelper)
|
||||
{}
|
||||
|
||||
public function apply(QueryBuilder $queryBuilder): void
|
||||
{
|
||||
|
||||
@@ -29,26 +29,17 @@
|
||||
|
||||
namespace Espo\Classes\Select\Email\BoolFilters;
|
||||
|
||||
use Espo\{
|
||||
Core\Select\Bool\Filter,
|
||||
ORM\Query\SelectBuilder as QueryBuilder,
|
||||
ORM\Query\Part\WhereClause,
|
||||
ORM\Query\Part\Where\OrGroupBuilder,
|
||||
Classes\Select\Email\Helpers\JoinHelper,
|
||||
Entities\User,
|
||||
};
|
||||
use Espo\Classes\Select\Email\Helpers\JoinHelper;
|
||||
use Espo\Core\Select\Bool\Filter;
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\Query\Part\Where\OrGroupBuilder;
|
||||
use Espo\ORM\Query\Part\WhereClause;
|
||||
use Espo\ORM\Query\SelectBuilder as QueryBuilder;
|
||||
|
||||
class OnlyMy implements Filter
|
||||
{
|
||||
private $user;
|
||||
|
||||
private $joinHelper;
|
||||
|
||||
public function __construct(User $user, JoinHelper $joinHelper)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->joinHelper = $joinHelper;
|
||||
}
|
||||
public function __construct(private User $user, private JoinHelper $joinHelper)
|
||||
{}
|
||||
|
||||
public function apply(QueryBuilder $queryBuilder, OrGroupBuilder $orGroupBuilder): void
|
||||
{
|
||||
|
||||
@@ -29,32 +29,20 @@
|
||||
|
||||
namespace Espo\Classes\Select\Email\Where\ItemConverters;
|
||||
|
||||
use Espo\Core\{
|
||||
Select\Where\ItemConverter,
|
||||
Select\Where\Item,
|
||||
Select\Helpers\RandomStringGenerator,
|
||||
};
|
||||
|
||||
use Espo\{
|
||||
ORM\Query\SelectBuilder as QueryBuilder,
|
||||
ORM\Query\Part\WhereItem as WhereClauseItem,
|
||||
ORM\Query\Part\WhereClause,
|
||||
Classes\Select\Email\Helpers\EmailAddressHelper,
|
||||
};
|
||||
use Espo\Core\Select\Helpers\RandomStringGenerator;
|
||||
use Espo\Core\Select\Where\Item;
|
||||
use Espo\Core\Select\Where\ItemConverter;
|
||||
use Espo\Classes\Select\Email\Helpers\EmailAddressHelper;
|
||||
use Espo\ORM\Query\Part\WhereClause;
|
||||
use Espo\ORM\Query\Part\WhereItem as WhereClauseItem;
|
||||
use Espo\ORM\Query\SelectBuilder as QueryBuilder;
|
||||
|
||||
class EmailAddressEquals implements ItemConverter
|
||||
{
|
||||
private $emailAddressHelper;
|
||||
|
||||
private $randomStringGenerator;
|
||||
|
||||
public function __construct(
|
||||
EmailAddressHelper $emailAddressHelper,
|
||||
RandomStringGenerator $randomStringGenerator
|
||||
) {
|
||||
$this->emailAddressHelper = $emailAddressHelper;
|
||||
$this->randomStringGenerator = $randomStringGenerator;
|
||||
}
|
||||
private EmailAddressHelper $emailAddressHelper,
|
||||
private RandomStringGenerator $randomStringGenerator
|
||||
) {}
|
||||
|
||||
public function convert(QueryBuilder $queryBuilder, Item $item): WhereClauseItem
|
||||
{
|
||||
|
||||
@@ -29,38 +29,18 @@
|
||||
|
||||
namespace Espo\Classes\Select\Email\Where\ItemConverters;
|
||||
|
||||
use Espo\Core\{
|
||||
Select\Where\ItemConverter,
|
||||
Select\Where\Item,
|
||||
};
|
||||
|
||||
use Espo\{
|
||||
ORM\Query\SelectBuilder as QueryBuilder,
|
||||
ORM\Query\Part\WhereItem as WhereClauseItem,
|
||||
ORM\Query\Part\WhereClause,
|
||||
ORM\EntityManager,
|
||||
Classes\Select\Email\Helpers\EmailAddressHelper,
|
||||
};
|
||||
use Espo\Core\Select\Where\Item;
|
||||
use Espo\Core\Select\Where\ItemConverter;
|
||||
use Espo\Classes\Select\Email\Helpers\EmailAddressHelper;
|
||||
use Espo\ORM\Query\Part\WhereClause;
|
||||
use Espo\ORM\Query\Part\WhereItem as WhereClauseItem;
|
||||
use Espo\ORM\Query\SelectBuilder as QueryBuilder;
|
||||
|
||||
class FromEquals implements ItemConverter
|
||||
{
|
||||
/**
|
||||
* @var EntityManager
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* @var EmailAddressHelper
|
||||
*/
|
||||
protected $emailAddressHelper;
|
||||
|
||||
public function __construct(
|
||||
EntityManager $entityManager,
|
||||
EmailAddressHelper $emailAddressHelper
|
||||
) {
|
||||
$this->entityManager = $entityManager;
|
||||
$this->emailAddressHelper = $emailAddressHelper;
|
||||
}
|
||||
private EmailAddressHelper $emailAddressHelper
|
||||
) {}
|
||||
|
||||
public function convert(QueryBuilder $queryBuilder, Item $item): WhereClauseItem
|
||||
{
|
||||
|
||||
@@ -29,30 +29,18 @@
|
||||
|
||||
namespace Espo\Classes\Select\Email\Where\ItemConverters;
|
||||
|
||||
use Espo\Core\{
|
||||
Select\Where\ItemConverter,
|
||||
Select\Where\Item,
|
||||
};
|
||||
|
||||
use Espo\{
|
||||
ORM\Query\SelectBuilder as QueryBuilder,
|
||||
ORM\Query\Part\WhereItem as WhereClauseItem,
|
||||
ORM\Query\Part\WhereClause,
|
||||
Entities\User,
|
||||
Classes\Select\Email\Helpers\JoinHelper,
|
||||
};
|
||||
use Espo\Core\Select\Where\Item;
|
||||
use Espo\Core\Select\Where\ItemConverter;
|
||||
use Espo\Classes\Select\Email\Helpers\JoinHelper;
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\Query\Part\WhereClause;
|
||||
use Espo\ORM\Query\Part\WhereItem as WhereClauseItem;
|
||||
use Espo\ORM\Query\SelectBuilder as QueryBuilder;
|
||||
|
||||
class IsImportantIsFalse implements ItemConverter
|
||||
{
|
||||
private $user;
|
||||
|
||||
private $joinHelper;
|
||||
|
||||
public function __construct(User $user, JoinHelper $joinHelper)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->joinHelper = $joinHelper;
|
||||
}
|
||||
public function __construct(private User $user, private JoinHelper $joinHelper)
|
||||
{}
|
||||
|
||||
public function convert(QueryBuilder $queryBuilder, Item $item): WhereClauseItem
|
||||
{
|
||||
|
||||
@@ -29,30 +29,18 @@
|
||||
|
||||
namespace Espo\Classes\Select\Email\Where\ItemConverters;
|
||||
|
||||
use Espo\Core\{
|
||||
Select\Where\ItemConverter,
|
||||
Select\Where\Item,
|
||||
};
|
||||
|
||||
use Espo\{
|
||||
ORM\Query\SelectBuilder as QueryBuilder,
|
||||
ORM\Query\Part\WhereItem as WhereClauseItem,
|
||||
ORM\Query\Part\WhereClause,
|
||||
Entities\User,
|
||||
Classes\Select\Email\Helpers\JoinHelper,
|
||||
};
|
||||
use Espo\Core\Select\Where\Item;
|
||||
use Espo\Core\Select\Where\ItemConverter;
|
||||
use Espo\Classes\Select\Email\Helpers\JoinHelper;
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\Query\Part\WhereClause;
|
||||
use Espo\ORM\Query\Part\WhereItem as WhereClauseItem;
|
||||
use Espo\ORM\Query\SelectBuilder as QueryBuilder;
|
||||
|
||||
class IsImportantIsTrue implements ItemConverter
|
||||
{
|
||||
private $user;
|
||||
|
||||
private $joinHelper;
|
||||
|
||||
public function __construct(User $user, JoinHelper $joinHelper)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->joinHelper = $joinHelper;
|
||||
}
|
||||
public function __construct(private User $user, private JoinHelper $joinHelper)
|
||||
{}
|
||||
|
||||
public function convert(QueryBuilder $queryBuilder, Item $item): WhereClauseItem
|
||||
{
|
||||
|
||||
@@ -29,30 +29,18 @@
|
||||
|
||||
namespace Espo\Classes\Select\Email\Where\ItemConverters;
|
||||
|
||||
use Espo\Core\{
|
||||
Select\Where\ItemConverter,
|
||||
Select\Where\Item,
|
||||
};
|
||||
|
||||
use Espo\{
|
||||
ORM\Query\SelectBuilder as QueryBuilder,
|
||||
ORM\Query\Part\WhereItem as WhereClauseItem,
|
||||
ORM\Query\Part\WhereClause,
|
||||
Entities\User,
|
||||
Classes\Select\Email\Helpers\JoinHelper,
|
||||
};
|
||||
use Espo\Core\Select\Where\Item;
|
||||
use Espo\Core\Select\Where\ItemConverter;
|
||||
use Espo\Classes\Select\Email\Helpers\JoinHelper;
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\Query\Part\WhereClause;
|
||||
use Espo\ORM\Query\Part\WhereItem as WhereClauseItem;
|
||||
use Espo\ORM\Query\SelectBuilder as QueryBuilder;
|
||||
|
||||
class IsNotReadIsFalse implements ItemConverter
|
||||
{
|
||||
private $user;
|
||||
|
||||
private $joinHelper;
|
||||
|
||||
public function __construct(User $user, JoinHelper $joinHelper)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->joinHelper = $joinHelper;
|
||||
}
|
||||
public function __construct(private User $user, private JoinHelper $joinHelper)
|
||||
{}
|
||||
|
||||
public function convert(QueryBuilder $queryBuilder, Item $item): WhereClauseItem
|
||||
{
|
||||
|
||||
@@ -29,30 +29,18 @@
|
||||
|
||||
namespace Espo\Classes\Select\Email\Where\ItemConverters;
|
||||
|
||||
use Espo\Core\{
|
||||
Select\Where\ItemConverter,
|
||||
Select\Where\Item,
|
||||
};
|
||||
|
||||
use Espo\{
|
||||
ORM\Query\SelectBuilder as QueryBuilder,
|
||||
ORM\Query\Part\WhereItem as WhereClauseItem,
|
||||
ORM\Query\Part\WhereClause,
|
||||
Entities\User,
|
||||
Classes\Select\Email\Helpers\JoinHelper,
|
||||
};
|
||||
use Espo\Core\Select\Where\Item;
|
||||
use Espo\Core\Select\Where\ItemConverter;
|
||||
use Espo\Classes\Select\Email\Helpers\JoinHelper;
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\Query\Part\WhereClause;
|
||||
use Espo\ORM\Query\Part\WhereItem as WhereClauseItem;
|
||||
use Espo\ORM\Query\SelectBuilder as QueryBuilder;
|
||||
|
||||
class IsNotReadIsTrue implements ItemConverter
|
||||
{
|
||||
private $user;
|
||||
|
||||
private $joinHelper;
|
||||
|
||||
public function __construct(User $user, JoinHelper $joinHelper)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->joinHelper = $joinHelper;
|
||||
}
|
||||
public function __construct(private User $user, private JoinHelper $joinHelper)
|
||||
{}
|
||||
|
||||
public function convert(QueryBuilder $queryBuilder, Item $item): WhereClauseItem
|
||||
{
|
||||
|
||||
@@ -29,16 +29,11 @@
|
||||
|
||||
namespace Espo\Classes\Select\Email\Where\ItemConverters;
|
||||
|
||||
use Espo\Core\{
|
||||
Select\Where\ItemConverter,
|
||||
Select\Where\Item,
|
||||
};
|
||||
|
||||
use Espo\{
|
||||
ORM\Query\SelectBuilder as QueryBuilder,
|
||||
ORM\Query\Part\WhereItem as WhereClauseItem,
|
||||
ORM\Query\Part\WhereClause,
|
||||
};
|
||||
use Espo\Core\Select\Where\Item;
|
||||
use Espo\Core\Select\Where\ItemConverter;
|
||||
use Espo\ORM\Query\Part\WhereClause;
|
||||
use Espo\ORM\Query\Part\WhereItem as WhereClauseItem;
|
||||
use Espo\ORM\Query\SelectBuilder as QueryBuilder;
|
||||
|
||||
class IsNotRepliedIsFalse implements ItemConverter
|
||||
{
|
||||
|
||||
@@ -29,16 +29,11 @@
|
||||
|
||||
namespace Espo\Classes\Select\Email\Where\ItemConverters;
|
||||
|
||||
use Espo\Core\{
|
||||
Select\Where\ItemConverter,
|
||||
Select\Where\Item,
|
||||
};
|
||||
|
||||
use Espo\{
|
||||
ORM\Query\SelectBuilder as QueryBuilder,
|
||||
ORM\Query\Part\WhereItem as WhereClauseItem,
|
||||
ORM\Query\Part\WhereClause,
|
||||
};
|
||||
use Espo\Core\Select\Where\Item;
|
||||
use Espo\Core\Select\Where\ItemConverter;
|
||||
use Espo\ORM\Query\Part\WhereClause;
|
||||
use Espo\ORM\Query\Part\WhereItem as WhereClauseItem;
|
||||
use Espo\ORM\Query\SelectBuilder as QueryBuilder;
|
||||
|
||||
class IsNotRepliedIsTrue implements ItemConverter
|
||||
{
|
||||
|
||||
@@ -29,32 +29,20 @@
|
||||
|
||||
namespace Espo\Classes\Select\Email\Where\ItemConverters;
|
||||
|
||||
use Espo\Core\{
|
||||
Select\Where\ItemConverter,
|
||||
Select\Where\Item,
|
||||
Select\Helpers\RandomStringGenerator,
|
||||
};
|
||||
|
||||
use Espo\{
|
||||
ORM\Query\SelectBuilder as QueryBuilder,
|
||||
ORM\Query\Part\WhereItem as WhereClauseItem,
|
||||
ORM\Query\Part\WhereClause,
|
||||
Classes\Select\Email\Helpers\EmailAddressHelper,
|
||||
};
|
||||
use Espo\Core\Select\Helpers\RandomStringGenerator;
|
||||
use Espo\Core\Select\Where\Item;
|
||||
use Espo\Core\Select\Where\ItemConverter;
|
||||
use Espo\Classes\Select\Email\Helpers\EmailAddressHelper;
|
||||
use Espo\ORM\Query\Part\WhereClause;
|
||||
use Espo\ORM\Query\Part\WhereItem as WhereClauseItem;
|
||||
use Espo\ORM\Query\SelectBuilder as QueryBuilder;
|
||||
|
||||
class ToEquals implements ItemConverter
|
||||
{
|
||||
private $emailAddressHelper;
|
||||
|
||||
private $randomStringGenerator;
|
||||
|
||||
public function __construct(
|
||||
EmailAddressHelper $emailAddressHelper,
|
||||
RandomStringGenerator $randomStringGenerator
|
||||
) {
|
||||
$this->emailAddressHelper = $emailAddressHelper;
|
||||
$this->randomStringGenerator = $randomStringGenerator;
|
||||
}
|
||||
private EmailAddressHelper $emailAddressHelper,
|
||||
private RandomStringGenerator $randomStringGenerator
|
||||
) {}
|
||||
|
||||
public function convert(QueryBuilder $queryBuilder, Item $item): WhereClauseItem
|
||||
{
|
||||
|
||||
@@ -29,20 +29,14 @@
|
||||
|
||||
namespace Espo\Classes\Select\EmailAccount\AccessControlFilters;
|
||||
|
||||
use Espo\{
|
||||
Core\Select\AccessControl\Filter,
|
||||
ORM\Query\SelectBuilder as QueryBuilder,
|
||||
Entities\User,
|
||||
};
|
||||
use Espo\Core\Select\AccessControl\Filter;
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\Query\SelectBuilder as QueryBuilder;
|
||||
|
||||
class Mandatory implements Filter
|
||||
{
|
||||
private $user;
|
||||
|
||||
public function __construct(User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
public function __construct(private User $user)
|
||||
{}
|
||||
|
||||
public function apply(QueryBuilder $queryBuilder): void
|
||||
{
|
||||
|
||||
@@ -29,13 +29,11 @@
|
||||
|
||||
namespace Espo\Classes\Select\EmailFilter\AccessControlFilters;
|
||||
|
||||
use Espo\{
|
||||
Core\Select\AccessControl\Filter,
|
||||
Entities\EmailAccount,
|
||||
ORM\Query\SelectBuilder as QueryBuilder,
|
||||
ORM\EntityManager,
|
||||
Entities\User,
|
||||
};
|
||||
use Espo\Core\Select\AccessControl\Filter;
|
||||
use Espo\Entities\EmailAccount;
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\EntityManager;
|
||||
use Espo\ORM\Query\SelectBuilder as QueryBuilder;
|
||||
|
||||
class OnlyOwn implements Filter
|
||||
{
|
||||
|
||||
@@ -29,26 +29,18 @@
|
||||
|
||||
namespace Espo\Classes\Select\EmailFilter\BoolFilters;
|
||||
|
||||
use Espo\{
|
||||
Core\Select\Bool\Filter,
|
||||
Entities\EmailAccount,
|
||||
ORM\Query\SelectBuilder as QueryBuilder,
|
||||
ORM\Query\Part\WhereClause,
|
||||
ORM\Query\Part\Where\OrGroupBuilder,
|
||||
ORM\EntityManager,
|
||||
Entities\User,
|
||||
};
|
||||
use Espo\Core\Select\Bool\Filter;
|
||||
use Espo\Entities\EmailAccount;
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\EntityManager;
|
||||
use Espo\ORM\Query\Part\Where\OrGroupBuilder;
|
||||
use Espo\ORM\Query\Part\WhereClause;
|
||||
use Espo\ORM\Query\SelectBuilder as QueryBuilder;
|
||||
|
||||
class OnlyMy implements Filter
|
||||
{
|
||||
private User $user;
|
||||
private EntityManager $entityManager;
|
||||
|
||||
public function __construct(User $user, EntityManager $entityManager)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->entityManager = $entityManager;
|
||||
}
|
||||
public function __construct(private User $user, private EntityManager $entityManager)
|
||||
{}
|
||||
|
||||
public function apply(QueryBuilder $queryBuilder, OrGroupBuilder $orGroupBuilder): void
|
||||
{
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2023 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\Select\Event\PrimaryFilters;
|
||||
|
||||
use Espo\Core\Select\Primary\Filter;
|
||||
use Espo\Core\Utils\Metadata;
|
||||
use Espo\ORM\Query\SelectBuilder;
|
||||
|
||||
class Held implements Filter
|
||||
{
|
||||
public function __construct(
|
||||
private string $entityType,
|
||||
private Metadata $metadata
|
||||
) {}
|
||||
|
||||
public function apply(SelectBuilder $queryBuilder): void
|
||||
{
|
||||
$statusList = $this->metadata->get(['scopes', $this->entityType, 'completedStatusList']) ?? [];
|
||||
|
||||
$queryBuilder->where(['status' => $statusList]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2023 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\Select\Event\PrimaryFilters;
|
||||
|
||||
use Espo\Core\Select\Primary\Filter;
|
||||
use Espo\Core\Utils\Metadata;
|
||||
use Espo\ORM\Query\SelectBuilder;
|
||||
|
||||
class Planned implements Filter
|
||||
{
|
||||
public function __construct(
|
||||
private string $entityType,
|
||||
private Metadata $metadata
|
||||
) {}
|
||||
|
||||
public function apply(SelectBuilder $queryBuilder): void
|
||||
{
|
||||
$statusList = $this->metadata->get(['scopes', $this->entityType, 'activityStatusList']) ?? [];
|
||||
|
||||
$queryBuilder->where(['status' => $statusList]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2023 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Classes\Select\Event\PrimaryFilters;
|
||||
|
||||
use Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Select\Primary\Filter;
|
||||
use Espo\Core\Select\Helpers\UserTimeZoneProvider;
|
||||
use Espo\Core\Select\Where\ConverterFactory;
|
||||
use Espo\Core\Select\Where\Item;
|
||||
use Espo\ORM\Query\SelectBuilder;
|
||||
use Espo\Entities\User;
|
||||
use LogicException;
|
||||
|
||||
class Todays implements Filter
|
||||
{
|
||||
public function __construct(
|
||||
private User $user,
|
||||
private UserTimeZoneProvider $userTimeZoneProvider,
|
||||
private ConverterFactory $converterFactory,
|
||||
private string $entityType
|
||||
) {}
|
||||
|
||||
public function apply(SelectBuilder $queryBuilder): void
|
||||
{
|
||||
$item = Item::fromRaw([
|
||||
'type' => Item\Type::TODAY,
|
||||
'attribute' => 'dateStart',
|
||||
'timeZone' => $this->userTimeZoneProvider->get(),
|
||||
'dateTime' => true,
|
||||
]);
|
||||
|
||||
try {
|
||||
$whereItem = $this->converterFactory
|
||||
->create($this->entityType, $this->user)
|
||||
->convert($queryBuilder, $item);
|
||||
}
|
||||
catch (Error $e) {
|
||||
throw new LogicException($e->getMessage());
|
||||
}
|
||||
|
||||
$queryBuilder->where($whereItem);
|
||||
}
|
||||
}
|
||||
@@ -29,33 +29,21 @@
|
||||
|
||||
namespace Espo\Classes\Select\Template\AccessControlFilters;
|
||||
|
||||
use Espo\ORM\{
|
||||
Query\SelectBuilder,
|
||||
Defs,
|
||||
};
|
||||
|
||||
use Espo\Core\{
|
||||
Select\AccessControl\Filter,
|
||||
AclManager,
|
||||
Acl\Exceptions\NotImplemented,
|
||||
};
|
||||
use Espo\ORM\Defs;
|
||||
use Espo\ORM\Query\SelectBuilder;
|
||||
use Espo\Core\Acl\Exceptions\NotImplemented;
|
||||
use Espo\Core\AclManager;
|
||||
use Espo\Core\Select\AccessControl\Filter;
|
||||
|
||||
use Espo\Entities\User;
|
||||
|
||||
class Mandatory implements Filter
|
||||
{
|
||||
private $user;
|
||||
|
||||
private $defs;
|
||||
|
||||
private $aclManager;
|
||||
|
||||
public function __construct(User $user, Defs $defs, AclManager $aclManager)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->defs = $defs;
|
||||
$this->aclManager = $aclManager;
|
||||
}
|
||||
public function __construct(
|
||||
private User $user,
|
||||
private Defs $defs,
|
||||
private AclManager $aclManager
|
||||
) {}
|
||||
|
||||
public function apply(SelectBuilder $queryBuilder): void
|
||||
{
|
||||
|
||||
@@ -30,26 +30,16 @@
|
||||
namespace Espo\Classes\Select\User\AccessControlFilters;
|
||||
|
||||
use Espo\ORM\Query\SelectBuilder;
|
||||
|
||||
use Espo\Core\{
|
||||
Select\AccessControl\Filter,
|
||||
AclManager,
|
||||
Acl\Table,
|
||||
};
|
||||
use Espo\Core\Acl\Table;
|
||||
use Espo\Core\AclManager;
|
||||
use Espo\Core\Select\AccessControl\Filter;
|
||||
|
||||
use Espo\Entities\User;
|
||||
|
||||
class OnlyOwn implements Filter
|
||||
{
|
||||
private $user;
|
||||
|
||||
private $aclManager;
|
||||
|
||||
public function __construct(User $user, AclManager $aclManager)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->aclManager = $aclManager;
|
||||
}
|
||||
public function __construct(private User $user, private AclManager $aclManager)
|
||||
{}
|
||||
|
||||
public function apply(SelectBuilder $queryBuilder): void
|
||||
{
|
||||
@@ -57,7 +47,7 @@ class OnlyOwn implements Filter
|
||||
$queryBuilder->where([
|
||||
'OR' => [
|
||||
'id' => $this->user->getId(),
|
||||
'type' => 'portal',
|
||||
'type' => User::TYPE_PORTAL,
|
||||
],
|
||||
]);
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ class Admin
|
||||
|
||||
|
||||
/**
|
||||
* @param array<string,mixed> $params
|
||||
* @param array<string, mixed> $params
|
||||
* @param string $data
|
||||
* @return array{
|
||||
* id: string,
|
||||
@@ -145,7 +145,7 @@ class Admin
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int,array{id:string,type:string,message:string}>
|
||||
* @return array<int, array{id: string, type: string, message: string}>
|
||||
*/
|
||||
public function actionAdminNotificationList(): array
|
||||
{
|
||||
@@ -154,9 +154,9 @@ class Admin
|
||||
|
||||
/**
|
||||
* @return array{
|
||||
* php: array<string,array<string,mixed>>,
|
||||
* database: array<string,array<string,mixed>>,
|
||||
* permission: array<string,array<string,mixed>>,
|
||||
* php: array<string, array<string, mixed>>,
|
||||
* database: array<string, array<string, mixed>>,
|
||||
* permission: array<string, array<string, mixed>>,
|
||||
* }
|
||||
*/
|
||||
public function actionSystemRequirementList(): array
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use Espo\Core\Exceptions\Conflict;
|
||||
use Espo\Core\Exceptions\Error;
|
||||
use Espo\Entities\User;
|
||||
use Espo\Tools\EntityManager\EntityManager as EntityManagerTool;
|
||||
|
||||
@@ -38,7 +40,9 @@ use Espo\Core\Exceptions\Forbidden;
|
||||
|
||||
class EntityManager
|
||||
{
|
||||
|
||||
/**
|
||||
* @throws Forbidden
|
||||
*/
|
||||
public function __construct(
|
||||
private User $user,
|
||||
private EntityManagerTool $entityManagerTool
|
||||
@@ -49,6 +53,11 @@ class EntityManager
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws BadRequest
|
||||
* @throws Error
|
||||
* @throws Conflict
|
||||
*/
|
||||
public function postActionCreateEntity(Request $request): bool
|
||||
{
|
||||
$data = $request->getParsedBody();
|
||||
@@ -130,6 +139,10 @@ class EntityManager
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws BadRequest
|
||||
* @throws Error
|
||||
*/
|
||||
public function postActionUpdateEntity(Request $request): bool
|
||||
{
|
||||
$data = $request->getParsedBody();
|
||||
@@ -153,6 +166,11 @@ class EntityManager
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws BadRequest
|
||||
* @throws Forbidden
|
||||
* @throws Error
|
||||
*/
|
||||
public function postActionRemoveEntity(Request $request): bool
|
||||
{
|
||||
$data = $request->getParsedBody();
|
||||
@@ -176,6 +194,11 @@ class EntityManager
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws BadRequest
|
||||
* @throws Error
|
||||
* @throws Conflict
|
||||
*/
|
||||
public function postActionCreateLink(Request $request): bool
|
||||
{
|
||||
$data = $request->getParsedBody();
|
||||
@@ -354,6 +377,10 @@ class EntityManager
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws BadRequest
|
||||
* @throws Error
|
||||
*/
|
||||
public function postActionFormula(Request $request): bool
|
||||
{
|
||||
$data = $request->getParsedBody();
|
||||
|
||||
@@ -62,7 +62,7 @@ class FieldManager
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string,mixed>
|
||||
* @return array<string, mixed>
|
||||
* @throws BadRequest
|
||||
* @throws Error
|
||||
*/
|
||||
|
||||
@@ -43,7 +43,7 @@ class I18n
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string,mixed>
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getActionRead(Request $request): array
|
||||
{
|
||||
|
||||
@@ -29,34 +29,26 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use Espo\Core\{
|
||||
Exceptions\Forbidden,
|
||||
Exceptions\BadRequest,
|
||||
Api\Request,
|
||||
DataManager,
|
||||
};
|
||||
|
||||
use Espo\{
|
||||
Tools\LabelManager\LabelManager as LabelManagerTool,
|
||||
Entities\User,
|
||||
};
|
||||
use Espo\Core\Api\Request;
|
||||
use Espo\Core\DataManager;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Entities\User;
|
||||
use Espo\Tools\LabelManager\LabelManager as LabelManagerTool;
|
||||
|
||||
use stdClass;
|
||||
|
||||
class LabelManager
|
||||
{
|
||||
private $user;
|
||||
|
||||
private $dataManager;
|
||||
|
||||
private $labelManagerTool;
|
||||
|
||||
public function __construct(User $user, DataManager $dataManager, LabelManagerTool $labelManagerTool)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->dataManager = $dataManager;
|
||||
$this->labelManagerTool = $labelManagerTool;
|
||||
|
||||
/**
|
||||
* @throws Forbidden
|
||||
*/
|
||||
public function __construct(
|
||||
private User $user,
|
||||
private DataManager $dataManager,
|
||||
private LabelManagerTool $labelManagerTool
|
||||
) {
|
||||
if (!$this->user->isAdmin()) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
@@ -29,25 +29,16 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use Espo\Core\{
|
||||
Api\Request,
|
||||
Record\SearchParamsFetcher,
|
||||
};
|
||||
|
||||
use Espo\Core\Api\Request;
|
||||
use Espo\Core\Record\SearchParamsFetcher;
|
||||
use Espo\Tools\ActionHistory\Service as Service;
|
||||
|
||||
use stdClass;
|
||||
|
||||
class LastViewed
|
||||
{
|
||||
private SearchParamsFetcher $searchParamsFetcher;
|
||||
private Service $service;
|
||||
|
||||
public function __construct(SearchParamsFetcher $searchParamsFetcher, Service $service)
|
||||
{
|
||||
$this->searchParamsFetcher = $searchParamsFetcher;
|
||||
$this->service = $service;
|
||||
}
|
||||
public function __construct(private SearchParamsFetcher $searchParamsFetcher, private Service $service)
|
||||
{}
|
||||
|
||||
public function getActionIndex(Request $request): stdClass
|
||||
{
|
||||
|
||||
@@ -31,15 +31,14 @@ namespace Espo\Controllers;
|
||||
|
||||
use Espo\Tools\Notification\RecordService as Service;
|
||||
|
||||
use Espo\Core\{
|
||||
Controllers\RecordBase,
|
||||
Api\Request,
|
||||
Api\Response,
|
||||
Exceptions\BadRequest,
|
||||
Exceptions\Error,
|
||||
Exceptions\Forbidden,
|
||||
Select\SearchParams,
|
||||
Select\Where\Item as WhereItem};
|
||||
use Espo\Core\Api\Request;
|
||||
use Espo\Core\Api\Response;
|
||||
use Espo\Core\Controllers\RecordBase;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Select\SearchParams;
|
||||
use Espo\Core\Select\Where\Item as WhereItem;
|
||||
|
||||
use stdClass;
|
||||
|
||||
|
||||
@@ -29,10 +29,8 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use Espo\Core\{
|
||||
Controllers\Record,
|
||||
Acl\Table,
|
||||
};
|
||||
use Espo\Core\Acl\Table;
|
||||
use Espo\Core\Controllers\Record;
|
||||
|
||||
class Portal extends Record
|
||||
{
|
||||
|
||||
@@ -31,33 +31,24 @@ namespace Espo\Controllers;
|
||||
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
|
||||
use Espo\Core\Utils\Metadata;
|
||||
use Espo\Core\Utils\TemplateFileManager;
|
||||
use Espo\Core\ApplicationState;
|
||||
|
||||
use Espo\Core\{
|
||||
Api\Request,
|
||||
};
|
||||
use Espo\Core\Api\Request;
|
||||
|
||||
use stdClass;
|
||||
|
||||
class TemplateManager
|
||||
{
|
||||
private $metadata;
|
||||
|
||||
private $templateFileManager;
|
||||
|
||||
private $applicationState;
|
||||
|
||||
/**
|
||||
* @throws Forbidden
|
||||
*/
|
||||
public function __construct(
|
||||
Metadata $metadata,
|
||||
TemplateFileManager $templateFileManager,
|
||||
ApplicationState $applicationState
|
||||
private Metadata $metadata,
|
||||
private TemplateFileManager $templateFileManager,
|
||||
private ApplicationState $applicationState
|
||||
) {
|
||||
$this->metadata = $metadata;
|
||||
$this->templateFileManager = $templateFileManager;
|
||||
$this->applicationState = $applicationState;
|
||||
|
||||
if (!$this->applicationState->isAdmin()) {
|
||||
throw new Forbidden();
|
||||
|
||||
@@ -29,11 +29,9 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use Espo\Core\{
|
||||
Controllers\RecordBase,
|
||||
Api\Request,
|
||||
Api\Response,
|
||||
};
|
||||
use Espo\Core\Api\Request;
|
||||
use Espo\Core\Api\Response;
|
||||
use Espo\Core\Controllers\RecordBase;
|
||||
|
||||
use stdClass;
|
||||
|
||||
|
||||
@@ -29,41 +29,27 @@
|
||||
|
||||
namespace Espo\Core\Acl\AccessChecker;
|
||||
|
||||
use Espo\Core\{
|
||||
Utils\ClassFinder,
|
||||
Utils\Metadata,
|
||||
InjectableFactory,
|
||||
Acl\Exceptions\NotImplemented,
|
||||
Acl\DefaultAccessChecker,
|
||||
Acl\AccessChecker,
|
||||
AclManager,
|
||||
Binding\BindingContainer,
|
||||
Binding\Binder,
|
||||
Binding\BindingData,
|
||||
};
|
||||
use Espo\Core\Acl\AccessChecker;
|
||||
use Espo\Core\Acl\DefaultAccessChecker;
|
||||
use Espo\Core\Acl\Exceptions\NotImplemented;
|
||||
use Espo\Core\AclManager;
|
||||
use Espo\Core\Binding\Binder;
|
||||
use Espo\Core\Binding\BindingContainer;
|
||||
use Espo\Core\Binding\BindingData;
|
||||
use Espo\Core\InjectableFactory;
|
||||
use Espo\Core\Utils\ClassFinder;
|
||||
use Espo\Core\Utils\Metadata;
|
||||
|
||||
class AccessCheckerFactory
|
||||
{
|
||||
/**
|
||||
* @var class-string<AccessChecker>
|
||||
*/
|
||||
/** @var class-string<AccessChecker> */
|
||||
private string $defaultClassName = DefaultAccessChecker::class;
|
||||
|
||||
private ClassFinder $classFinder;
|
||||
|
||||
private Metadata $metadata;
|
||||
|
||||
private InjectableFactory $injectableFactory;
|
||||
|
||||
public function __construct(
|
||||
ClassFinder $classFinder,
|
||||
Metadata $metadata,
|
||||
InjectableFactory $injectableFactory
|
||||
) {
|
||||
$this->classFinder = $classFinder;
|
||||
$this->metadata = $metadata;
|
||||
$this->injectableFactory = $injectableFactory;
|
||||
}
|
||||
private ClassFinder $classFinder,
|
||||
private Metadata $metadata,
|
||||
private InjectableFactory $injectableFactory
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Create an access checker.
|
||||
@@ -112,7 +98,6 @@ class AccessCheckerFactory
|
||||
$bindingData = new BindingData();
|
||||
|
||||
$binder = new Binder($bindingData);
|
||||
|
||||
$binder->bindInstance(AclManager::class, $aclManager);
|
||||
|
||||
return new BindingContainer($bindingData);
|
||||
|
||||
@@ -29,10 +29,8 @@
|
||||
|
||||
namespace Espo\Core\Acl\AccessChecker;
|
||||
|
||||
use Espo\Core\{
|
||||
Acl\ScopeData,
|
||||
Acl\Table,
|
||||
};
|
||||
use Espo\Core\Acl\ScopeData;
|
||||
use Espo\Core\Acl\Table;
|
||||
|
||||
/**
|
||||
* Checks scope access.
|
||||
@@ -40,8 +38,7 @@ use Espo\Core\{
|
||||
class ScopeChecker
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
{}
|
||||
|
||||
public function check(ScopeData $data, ?string $action = null, ?ScopeCheckerData $checkerData = null): bool
|
||||
{
|
||||
|
||||
@@ -29,20 +29,17 @@
|
||||
|
||||
namespace Espo\Core\Acl\AccessChecker;
|
||||
|
||||
use Closure;
|
||||
|
||||
/**
|
||||
* Scope checker data.
|
||||
*/
|
||||
class ScopeCheckerData
|
||||
{
|
||||
private $isOwnChecker;
|
||||
|
||||
private $inTeamChecker;
|
||||
|
||||
public function __construct(callable $isOwnChecker, callable $inTeamChecker)
|
||||
{
|
||||
$this->isOwnChecker = $isOwnChecker;
|
||||
$this->inTeamChecker = $inTeamChecker;
|
||||
}
|
||||
public function __construct(
|
||||
private Closure $isOwnChecker,
|
||||
private Closure $inTeamChecker
|
||||
) {}
|
||||
|
||||
public function isOwn(): bool
|
||||
{
|
||||
|
||||
@@ -36,9 +36,8 @@ use Closure;
|
||||
*/
|
||||
class ScopeCheckerDataBuilder
|
||||
{
|
||||
private $isOwnChecker;
|
||||
|
||||
private $inTeamChecker;
|
||||
private Closure $isOwnChecker;
|
||||
private Closure $inTeamChecker;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
|
||||
@@ -29,37 +29,28 @@
|
||||
|
||||
namespace Espo\Core\Acl\AssignmentChecker;
|
||||
|
||||
use Espo\Core\{
|
||||
Utils\Metadata,
|
||||
InjectableFactory,
|
||||
Acl\AssignmentChecker,
|
||||
Acl\DefaultAssignmentChecker,
|
||||
Acl\Exceptions\NotImplemented,
|
||||
};
|
||||
use Espo\Core\Acl\AssignmentChecker;
|
||||
use Espo\Core\Acl\DefaultAssignmentChecker;
|
||||
use Espo\Core\Acl\Exceptions\NotImplemented;
|
||||
use Espo\Core\InjectableFactory;
|
||||
use Espo\Core\ORM\Entity as CoreEntity;
|
||||
use Espo\Core\Utils\Metadata;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
class AssignmentCheckerFactory
|
||||
{
|
||||
/**
|
||||
* @var class-string<AssignmentChecker<\Espo\Core\ORM\Entity>>
|
||||
*/
|
||||
/** @var class-string<AssignmentChecker<CoreEntity>> */
|
||||
private string $defaultClassName = DefaultAssignmentChecker::class;
|
||||
|
||||
private Metadata $metadata;
|
||||
|
||||
private InjectableFactory $injectableFactory;
|
||||
|
||||
public function __construct(
|
||||
Metadata $metadata,
|
||||
InjectableFactory $injectableFactory
|
||||
) {
|
||||
$this->metadata = $metadata;
|
||||
$this->injectableFactory = $injectableFactory;
|
||||
}
|
||||
private Metadata $metadata,
|
||||
private InjectableFactory $injectableFactory
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Create an access checker.
|
||||
*
|
||||
* @return AssignmentChecker<\Espo\ORM\Entity>
|
||||
* @return AssignmentChecker<Entity>
|
||||
* @throws NotImplemented
|
||||
*/
|
||||
public function create(string $scope): AssignmentChecker
|
||||
@@ -70,12 +61,12 @@ class AssignmentCheckerFactory
|
||||
}
|
||||
|
||||
/**
|
||||
* @return class-string<AssignmentChecker<\Espo\ORM\Entity>>
|
||||
* @return class-string<AssignmentChecker<Entity>>
|
||||
* @throws NotImplemented
|
||||
*/
|
||||
private function getClassName(string $scope): string
|
||||
{
|
||||
/** @var ?class-string<AssignmentChecker<\Espo\ORM\Entity>> $className */
|
||||
/** @var ?class-string<AssignmentChecker<Entity>> $className */
|
||||
$className = $this->metadata->get(['aclDefs', $scope, 'assignmentCheckerClassName']);
|
||||
|
||||
if ($className) {
|
||||
@@ -86,7 +77,7 @@ class AssignmentCheckerFactory
|
||||
throw new NotImplemented();
|
||||
}
|
||||
|
||||
/** @var class-string<AssignmentChecker<\Espo\ORM\Entity>> */
|
||||
/** @var class-string<AssignmentChecker<Entity>> */
|
||||
return $this->defaultClassName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,24 +30,16 @@
|
||||
namespace Espo\Core\Acl\AssignmentChecker;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
use Espo\Entities\User;
|
||||
|
||||
use Espo\Core\Acl\AssignmentChecker;
|
||||
|
||||
class AssignmentCheckerManager
|
||||
{
|
||||
/**
|
||||
* @var array<string,AssignmentChecker<Entity>>
|
||||
*/
|
||||
/** @var array<string, AssignmentChecker<Entity>> */
|
||||
private $checkerCache = [];
|
||||
|
||||
private AssignmentCheckerFactory $factory;
|
||||
|
||||
public function __construct(AssignmentCheckerFactory $factory)
|
||||
{
|
||||
$this->factory = $factory;
|
||||
}
|
||||
public function __construct(private AssignmentCheckerFactory $factory)
|
||||
{}
|
||||
|
||||
public function check(User $user, Entity $entity): bool
|
||||
{
|
||||
|
||||
@@ -32,16 +32,12 @@ namespace Espo\Core\Acl;
|
||||
use Espo\Core\Interfaces\Injectable;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
use Espo\Entities\User;
|
||||
|
||||
use Espo\Core\{
|
||||
ORM\EntityManager,
|
||||
Acl\AccessChecker\ScopeCheckerData,
|
||||
Acl\AccessChecker\ScopeChecker,
|
||||
AclManager,
|
||||
Utils\Config,
|
||||
};
|
||||
use Espo\Core\Acl\AccessChecker\ScopeChecker;
|
||||
use Espo\Core\Acl\AccessChecker\ScopeCheckerData;
|
||||
use Espo\Core\AclManager;
|
||||
use Espo\Core\ORM\EntityManager;
|
||||
use Espo\Core\Utils\Config;
|
||||
|
||||
/**
|
||||
* @deprecated As of v6.0. Use AccessChecker interfaces instead.
|
||||
|
||||
@@ -39,14 +39,8 @@ use Espo\ORM\EntityManager;
|
||||
*/
|
||||
class Clearer
|
||||
{
|
||||
private FileManager $fileManager;
|
||||
private EntityManager $entityManager;
|
||||
|
||||
public function __construct(FileManager $fileManager, EntityManager $entityManager)
|
||||
{
|
||||
$this->fileManager = $fileManager;
|
||||
$this->entityManager = $entityManager;
|
||||
}
|
||||
public function __construct(private FileManager $fileManager, private EntityManager $entityManager)
|
||||
{}
|
||||
|
||||
public function clearForAllInternalUsers(): void
|
||||
{
|
||||
|
||||
@@ -29,18 +29,14 @@
|
||||
|
||||
namespace Espo\Core\Acl\Map;
|
||||
|
||||
use Espo\Core\{
|
||||
Acl\Table,
|
||||
Utils\FieldUtil,
|
||||
};
|
||||
use Espo\Core\Acl\Table;
|
||||
use Espo\Core\Utils\FieldUtil;
|
||||
|
||||
use stdClass;
|
||||
|
||||
class DataBuilder
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
/** @var string[] */
|
||||
private $actionList = [
|
||||
Table::ACTION_READ,
|
||||
Table::ACTION_STREAM,
|
||||
@@ -48,32 +44,19 @@ class DataBuilder
|
||||
Table::ACTION_DELETE,
|
||||
Table::ACTION_CREATE,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
/** @var string[] */
|
||||
private $fieldActionList = [
|
||||
Table::ACTION_READ,
|
||||
Table::ACTION_EDIT,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
/** @var string[] */
|
||||
private $fieldLevelList = [
|
||||
Table::LEVEL_YES,
|
||||
Table::LEVEL_NO,
|
||||
];
|
||||
|
||||
private MetadataProvider $metadataProvider;
|
||||
|
||||
private FieldUtil $fieldUtil;
|
||||
|
||||
public function __construct(MetadataProvider $metadataProvider, FieldUtil $fieldUtil)
|
||||
{
|
||||
$this->metadataProvider = $metadataProvider;
|
||||
$this->fieldUtil = $fieldUtil;
|
||||
}
|
||||
public function __construct(private MetadataProvider $metadataProvider, private FieldUtil $fieldUtil)
|
||||
{}
|
||||
|
||||
public function build(Table $table): stdClass
|
||||
{
|
||||
|
||||
@@ -33,12 +33,8 @@ use Espo\Entities\User;
|
||||
|
||||
class DefaultCacheKeyProvider implements CacheKeyProvider
|
||||
{
|
||||
private $user;
|
||||
|
||||
public function __construct(User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
public function __construct(private User $user)
|
||||
{}
|
||||
|
||||
public function get(): string
|
||||
{
|
||||
|
||||
@@ -29,14 +29,10 @@
|
||||
|
||||
namespace Espo\Core\Acl\Map;
|
||||
|
||||
use Espo\Entities\User;
|
||||
|
||||
use Espo\Core\{
|
||||
Acl\Table,
|
||||
Utils\Config,
|
||||
Utils\DataCache,
|
||||
Utils\ObjectUtil,
|
||||
};
|
||||
use Espo\Core\Acl\Table;
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Core\Utils\DataCache;
|
||||
use Espo\Core\Utils\ObjectUtil;
|
||||
|
||||
use stdClass;
|
||||
use RuntimeException;
|
||||
@@ -47,50 +43,24 @@ use RuntimeException;
|
||||
class Map
|
||||
{
|
||||
private stdClass $data;
|
||||
|
||||
private string $cacheKey;
|
||||
|
||||
/**
|
||||
* @var array<string,string[]>
|
||||
*/
|
||||
/** @var array<string, string[]> */
|
||||
private $forbiddenFieldsCache = [];
|
||||
|
||||
/**
|
||||
* @var array<string,string[]>
|
||||
*/
|
||||
/** @var array<string, string[]> */
|
||||
private $forbiddenAttributesCache;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
/** @var string[] */
|
||||
private $fieldLevelList = [
|
||||
Table::LEVEL_YES,
|
||||
Table::LEVEL_NO,
|
||||
];
|
||||
|
||||
private User $user;
|
||||
|
||||
private Table $table;
|
||||
|
||||
private Config $config;
|
||||
|
||||
private DataCache $dataCache;
|
||||
|
||||
private DataBuilder $dataBuilder;
|
||||
|
||||
public function __construct(
|
||||
User $user,
|
||||
Table $table,
|
||||
DataBuilder $dataBuilder,
|
||||
Config $config,
|
||||
DataCache $dataCache,
|
||||
private DataBuilder $dataBuilder,
|
||||
private Config $config,
|
||||
private DataCache $dataCache,
|
||||
CacheKeyProvider $cacheKeyProvider
|
||||
) {
|
||||
$this->user = $user;
|
||||
$this->table = $table;
|
||||
$this->dataBuilder = $dataBuilder;
|
||||
$this->config = $config;
|
||||
$this->dataCache = $dataCache;
|
||||
|
||||
$this->cacheKey = $cacheKeyProvider->get();
|
||||
|
||||
|
||||
@@ -31,23 +31,16 @@ namespace Espo\Core\Acl\Map;
|
||||
|
||||
use Espo\Entities\User;
|
||||
|
||||
use Espo\Core\{
|
||||
InjectableFactory,
|
||||
Acl\Table,
|
||||
Acl\Map\Map,
|
||||
Binding\BindingContainer,
|
||||
Binding\Binder,
|
||||
Binding\BindingData,
|
||||
};
|
||||
use Espo\Core\Acl\Table;
|
||||
use Espo\Core\Binding\Binder;
|
||||
use Espo\Core\Binding\BindingContainer;
|
||||
use Espo\Core\Binding\BindingData;
|
||||
use Espo\Core\InjectableFactory;
|
||||
|
||||
class MapFactory
|
||||
{
|
||||
private $injectableFactory;
|
||||
|
||||
public function __construct(InjectableFactory $injectableFactory)
|
||||
{
|
||||
$this->injectableFactory = $injectableFactory;
|
||||
}
|
||||
public function __construct(private InjectableFactory $injectableFactory)
|
||||
{}
|
||||
|
||||
public function create(User $user, Table $table): Map
|
||||
{
|
||||
@@ -61,7 +54,6 @@ class MapFactory
|
||||
$bindingData = new BindingData();
|
||||
|
||||
$binder = new Binder($bindingData);
|
||||
|
||||
$binder
|
||||
->bindInstance(User::class, $user)
|
||||
->bindInstance(Table::class, $table)
|
||||
|
||||
@@ -35,12 +35,8 @@ class MetadataProvider
|
||||
{
|
||||
protected string $type = 'acl';
|
||||
|
||||
private Metadata $metadata;
|
||||
|
||||
public function __construct(Metadata $metadata)
|
||||
{
|
||||
$this->metadata = $metadata;
|
||||
}
|
||||
public function __construct(private Metadata $metadata)
|
||||
{}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
@@ -66,7 +62,7 @@ class MetadataProvider
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int,string>
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function getPermissionList(): array
|
||||
{
|
||||
@@ -74,7 +70,7 @@ class MetadataProvider
|
||||
|
||||
return array_map(
|
||||
function (string $item): string {
|
||||
if (substr($item, -10) === 'Permission') {
|
||||
if (str_ends_with($item, 'Permission')) {
|
||||
return substr($item, 0, -10);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,36 +29,25 @@
|
||||
|
||||
namespace Espo\Core\Acl\OwnershipChecker;
|
||||
|
||||
use Espo\Core\{
|
||||
Utils\Metadata,
|
||||
InjectableFactory,
|
||||
Acl\Exceptions\NotImplemented,
|
||||
Acl\DefaultOwnershipChecker,
|
||||
Acl\OwnershipChecker,
|
||||
AclManager,
|
||||
Binding\BindingContainer,
|
||||
Binding\Binder,
|
||||
Binding\BindingData,
|
||||
};
|
||||
use Espo\Core\Acl\DefaultOwnershipChecker;
|
||||
use Espo\Core\Acl\Exceptions\NotImplemented;
|
||||
use Espo\Core\Acl\OwnershipChecker;
|
||||
use Espo\Core\AclManager;
|
||||
use Espo\Core\Binding\Binder;
|
||||
use Espo\Core\Binding\BindingContainer;
|
||||
use Espo\Core\Binding\BindingData;
|
||||
use Espo\Core\InjectableFactory;
|
||||
use Espo\Core\Utils\Metadata;
|
||||
|
||||
class OwnershipCheckerFactory
|
||||
{
|
||||
/**
|
||||
* @var class-string<OwnershipChecker>
|
||||
*/
|
||||
/** @var class-string<OwnershipChecker> */
|
||||
private string $defaultClassName = DefaultOwnershipChecker::class;
|
||||
|
||||
private Metadata $metadata;
|
||||
|
||||
private InjectableFactory $injectableFactory;
|
||||
|
||||
public function __construct(
|
||||
Metadata $metadata,
|
||||
InjectableFactory $injectableFactory
|
||||
) {
|
||||
$this->metadata = $metadata;
|
||||
$this->injectableFactory = $injectableFactory;
|
||||
}
|
||||
private Metadata $metadata,
|
||||
private InjectableFactory $injectableFactory
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Create an ownership checker.
|
||||
|
||||
@@ -40,7 +40,7 @@ class ScopeData
|
||||
{
|
||||
/** @var stdClass|bool */
|
||||
private $raw;
|
||||
/** @var array<string,string> */
|
||||
/** @var array<string, string> */
|
||||
private $actionData = [];
|
||||
private bool $isBoolean = false;
|
||||
|
||||
|
||||
@@ -30,28 +30,16 @@
|
||||
namespace Espo\Core\Acl\Table;
|
||||
|
||||
use Espo\Entities\User;
|
||||
|
||||
use Espo\Core\{
|
||||
InjectableFactory,
|
||||
Acl\Table,
|
||||
Acl\Table\DefaultTable,
|
||||
Acl\Table\CacheKeyProvider,
|
||||
Acl\Table\DefaultCacheKeyProvider,
|
||||
Acl\Table\RoleListProvider,
|
||||
Acl\Table\DefaultRoleListProvider,
|
||||
Binding\BindingContainer,
|
||||
Binding\Binder,
|
||||
Binding\BindingData,
|
||||
};
|
||||
use Espo\Core\Acl\Table;
|
||||
use Espo\Core\Binding\Binder;
|
||||
use Espo\Core\Binding\BindingContainer;
|
||||
use Espo\Core\Binding\BindingData;
|
||||
use Espo\Core\InjectableFactory;
|
||||
|
||||
class DefaultTableFactory implements TableFactory
|
||||
{
|
||||
private $injectableFactory;
|
||||
|
||||
public function __construct(InjectableFactory $injectableFactory)
|
||||
{
|
||||
$this->injectableFactory = $injectableFactory;
|
||||
}
|
||||
public function __construct(private InjectableFactory $injectableFactory)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Create a table.
|
||||
|
||||
@@ -30,24 +30,18 @@
|
||||
namespace Espo\Core\AclPortal;
|
||||
|
||||
use Espo\Core\Interfaces\Injectable;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
use Espo\Entities\User;
|
||||
|
||||
use Espo\Core\AclManager;
|
||||
|
||||
use Espo\Core\{
|
||||
ORM\EntityManager,
|
||||
Portal\AclManager as PortalAclManager,
|
||||
Utils\Config,
|
||||
Acl\AccessChecker,
|
||||
Acl\ScopeData,
|
||||
Portal\Acl\Table,
|
||||
Portal\Acl\AccessChecker\ScopeChecker,
|
||||
Portal\Acl\AccessChecker\ScopeCheckerData,
|
||||
Portal\Acl\DefaultAccessChecker,
|
||||
};
|
||||
use Espo\Core\Acl\AccessChecker;
|
||||
use Espo\Core\Acl\ScopeData;
|
||||
use Espo\Core\ORM\EntityManager;
|
||||
use Espo\Core\Portal\Acl\AccessChecker\ScopeChecker;
|
||||
use Espo\Core\Portal\Acl\AccessChecker\ScopeCheckerData;
|
||||
use Espo\Core\Portal\Acl\DefaultAccessChecker;
|
||||
use Espo\Core\Portal\Acl\Table;
|
||||
use Espo\Core\Portal\AclManager as PortalAclManager;
|
||||
use Espo\Core\Utils\Config;
|
||||
|
||||
/**
|
||||
* @deprecated Use AccessChecker interfaces instead.
|
||||
|
||||
@@ -30,30 +30,32 @@
|
||||
namespace Espo\Core\Action\Actions;
|
||||
|
||||
use Espo\Core\Acl;
|
||||
use Espo\Core\Acl\Table;
|
||||
use Espo\Core\Action\Action;
|
||||
use Espo\Core\Action\Data;
|
||||
use Espo\Core\Action\Params;
|
||||
use Espo\Core\Currency\ConfigDataProvider as CurrencyConfigDataProvider;
|
||||
use Espo\Core\Currency\Converter as CurrencyConverter;
|
||||
use Espo\Core\ORM\Entity as CoreEntity;
|
||||
use Espo\Core\Currency\Rates as CurrencyRates;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\NotFound;
|
||||
use Espo\Core\Field\Currency;
|
||||
use Espo\Core\ORM\EntityManager;
|
||||
use Espo\Core\Utils\FieldUtil;
|
||||
use Espo\Core\ORM\Repository\Option\SaveOption;
|
||||
use Espo\Core\Utils\Metadata;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Entities\User;
|
||||
use Espo\Tools\Currency\Conversion\EntityConverterFactory;
|
||||
use RuntimeException;
|
||||
|
||||
class ConvertCurrency implements Action
|
||||
{
|
||||
public function __construct(
|
||||
private EntityConverterFactory $converterFactory,
|
||||
private Acl $acl,
|
||||
private EntityManager $entityManager,
|
||||
private FieldUtil $fieldUtil,
|
||||
private Metadata $metadata,
|
||||
private CurrencyConfigDataProvider $configDataProvider,
|
||||
private CurrencyConverter $currencyConverter
|
||||
private User $user
|
||||
) {}
|
||||
|
||||
public function process(Params $params, Data $data): void
|
||||
@@ -65,22 +67,16 @@ class ConvertCurrency implements Action
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
$fieldList = $this->getFieldList($entityType, $data);
|
||||
|
||||
if (empty($fieldList)) {
|
||||
throw new Forbidden("No fields to convert.");
|
||||
}
|
||||
$this->checkFieldAccess($entityType);
|
||||
|
||||
$baseCurrency = $this->configDataProvider->getBaseCurrency();
|
||||
|
||||
$targetCurrency = $data->get('targetCurrency');
|
||||
|
||||
if (!$targetCurrency) {
|
||||
throw new BadRequest("No target currency.");
|
||||
}
|
||||
|
||||
$rates =
|
||||
$this->getRatesFromData($data) ??
|
||||
$rates = $this->getRatesFromData($data) ??
|
||||
$this->configDataProvider->getCurrencyRates();
|
||||
|
||||
if ($targetCurrency !== $baseCurrency && !$rates->hasRate($targetCurrency)) {
|
||||
@@ -94,56 +90,21 @@ class ConvertCurrency implements Action
|
||||
}
|
||||
|
||||
if (!$this->acl->checkEntityEdit($entity)) {
|
||||
throw new Forbidden();
|
||||
throw new Forbidden("No 'edit' access.");
|
||||
}
|
||||
|
||||
$this->convertEntity($entity, $fieldList, $targetCurrency, $rates);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $fieldList
|
||||
*/
|
||||
protected function convertEntity(
|
||||
Entity $entity,
|
||||
array $fieldList,
|
||||
string $targetCurrency,
|
||||
CurrencyRates $rates
|
||||
): void {
|
||||
|
||||
$entityDefs = $this->entityManager
|
||||
->getDefs()
|
||||
->getEntity($entity->getEntityType());
|
||||
|
||||
foreach ($fieldList as $field) {
|
||||
$disabled = $entityDefs->getField($field)->getParam('conversionDisabled');
|
||||
|
||||
if ($disabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$amount = $entity->get($field);
|
||||
$code = $entity->get($field . 'Currency');
|
||||
|
||||
if ($amount === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($targetCurrency === $code) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$value = new Currency($amount, $code);
|
||||
|
||||
$convertedValue = $this->currencyConverter->convertWithRates($value, $targetCurrency, $rates);
|
||||
|
||||
$entity->set($field, $convertedValue->getAmount());
|
||||
$entity->set($field . 'Currency', $convertedValue->getCode());
|
||||
if (!$entity instanceof CoreEntity) {
|
||||
throw new RuntimeException("Only Core-Entity allowed.");
|
||||
}
|
||||
|
||||
$this->entityManager->saveEntity($entity);
|
||||
$converter = $this->converterFactory->create($entityType);
|
||||
|
||||
$converter->convert($entity, $targetCurrency, $rates);
|
||||
|
||||
$this->entityManager->saveEntity($entity, [SaveOption::MODIFIED_BY_ID => $this->user->getId()]);
|
||||
}
|
||||
|
||||
protected function getRatesFromData(Data $data): ?CurrencyRates
|
||||
private function getRatesFromData(Data $data): ?CurrencyRates
|
||||
{
|
||||
if ($data->get('rates') === null) {
|
||||
return null;
|
||||
@@ -152,37 +113,27 @@ class ConvertCurrency implements Action
|
||||
$baseCurrency = $this->configDataProvider->getBaseCurrency();
|
||||
|
||||
$ratesArray = get_object_vars($data->get('rates'));
|
||||
|
||||
$ratesArray[$baseCurrency] = 1.0;
|
||||
|
||||
return CurrencyRates::fromAssoc($ratesArray, $baseCurrency);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
* @throws Forbidden
|
||||
*/
|
||||
protected function getFieldList(string $entityType, Data $data): array
|
||||
private function checkFieldAccess(string $entityType): void
|
||||
{
|
||||
$forbiddenFieldList = $this->acl->getScopeForbiddenFieldList($entityType, 'edit');
|
||||
/** @var string[] $requiredFieldList */
|
||||
$requiredFieldList = $this->metadata->get(['scopes', $entityType, 'currencyConversionAccessRequiredFieldList']);
|
||||
|
||||
$resultList = [];
|
||||
|
||||
$fieldList = $data->get('fieldList') ?? $this->fieldUtil->getEntityTypeFieldList($entityType);
|
||||
|
||||
foreach ($fieldList as $field) {
|
||||
$type = $this->metadata->get(['entityDefs', $entityType, 'fields', $field, 'type']);
|
||||
|
||||
if ($type !== 'currency') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (in_array($field, $forbiddenFieldList)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$resultList[] = $field;
|
||||
if ($requiredFieldList === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
return $resultList;
|
||||
foreach ($requiredFieldList as $field) {
|
||||
if (!$this->acl->checkField($entityType, $field, Table::ACTION_EDIT)) {
|
||||
throw new Forbidden("No edit access to field `$field`.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,9 +38,8 @@ use Espo\Core\ORM\EntityManager;
|
||||
use Espo\Core\Record\ServiceContainer;
|
||||
use Espo\Core\Utils\Metadata;
|
||||
use Espo\Core\Utils\ObjectUtil;
|
||||
|
||||
use Espo\Entities\ActionHistoryRecord;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
use Espo\Entities\EmailAddress;
|
||||
use Espo\Entities\PhoneNumber;
|
||||
|
||||
@@ -136,7 +135,7 @@ class Merger
|
||||
foreach ($sourceEntityList as $sourceEntity) {
|
||||
$this->entityManager->removeEntity($sourceEntity);
|
||||
|
||||
$service->processActionHistoryRecord('delete', $sourceEntity);
|
||||
$service->processActionHistoryRecord(ActionHistoryRecord::ACTION_DELETE, $sourceEntity);
|
||||
}
|
||||
|
||||
if ($hasPhoneNumber) {
|
||||
@@ -151,7 +150,7 @@ class Merger
|
||||
|
||||
$this->entityManager->saveEntity($entity);
|
||||
|
||||
$service->processActionHistoryRecord('update', $entity);
|
||||
$service->processActionHistoryRecord(ActionHistoryRecord::ACTION_UPDATE, $entity);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,10 +29,14 @@
|
||||
|
||||
namespace Espo\Core\Api;
|
||||
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Exceptions\Conflict;
|
||||
use Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\HasBody;
|
||||
|
||||
use Espo\Core\Exceptions\HasLogLevel;
|
||||
use Espo\Core\Exceptions\HasLogMessage;
|
||||
use Espo\Core\Exceptions\NotFound;
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Core\Utils\Log;
|
||||
|
||||
@@ -67,11 +71,13 @@ class ErrorOutput
|
||||
503,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var class-string[]
|
||||
*/
|
||||
private $ignorePrintXStatusReasonExceptionClassNameList = [
|
||||
'PDOException',
|
||||
/** @var class-string<Throwable>[] */
|
||||
private array $printStatusReasonExceptionClassNameList = [
|
||||
Error::class,
|
||||
Forbidden::class,
|
||||
Conflict::class,
|
||||
BadRequest::class,
|
||||
NotFound::class,
|
||||
];
|
||||
|
||||
public function __construct(private Log $log, private Config $config)
|
||||
@@ -141,18 +147,13 @@ class ErrorOutput
|
||||
|
||||
$this->log->log($logLevel, $logMessage);
|
||||
|
||||
$toPrintBodyXStatusReason = !in_array(
|
||||
get_class($exception),
|
||||
$this->ignorePrintXStatusReasonExceptionClassNameList
|
||||
);
|
||||
|
||||
if (!in_array($statusCode, $this->allowedStatusCodeList)) {
|
||||
$statusCode = 500;
|
||||
}
|
||||
|
||||
$response->setStatus($statusCode);
|
||||
|
||||
if ($toPrintBodyXStatusReason) {
|
||||
if ($this->toPrintExceptionStatusReason($exception)) {
|
||||
$response->setHeader('X-Status-Reason', $this->stripInvalidCharactersFromHeaderValue($message));
|
||||
}
|
||||
|
||||
@@ -256,4 +257,16 @@ class ErrorOutput
|
||||
{
|
||||
return (bool) $this->config->get('logger.printTrace');
|
||||
}
|
||||
|
||||
private function toPrintExceptionStatusReason(Throwable $exception): bool
|
||||
{
|
||||
foreach ($this->printStatusReasonExceptionClassNameList as $clasName) {
|
||||
|
||||
if ($exception instanceof ($clasName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ interface Request
|
||||
/**
|
||||
* Get all query parameters.
|
||||
*
|
||||
* @return array<string,string|array<scalar,mixed>>
|
||||
* @return array<string, string|array<scalar, mixed>>
|
||||
*/
|
||||
public function getQueryParams(): array;
|
||||
|
||||
@@ -68,7 +68,7 @@ interface Request
|
||||
/**
|
||||
* Get all route parameters.
|
||||
*
|
||||
* @return array<string,string>
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function getRouteParams(): array;
|
||||
|
||||
|
||||
@@ -29,10 +29,13 @@
|
||||
|
||||
namespace Espo\Core\Api;
|
||||
|
||||
use Espo\Core\Utils\Config;
|
||||
use stdClass;
|
||||
|
||||
class Util
|
||||
{
|
||||
public function __construct(private Config $config) {}
|
||||
|
||||
public static function cloneObject(stdClass $source): stdClass
|
||||
{
|
||||
$cloned = (object) [];
|
||||
@@ -70,4 +73,11 @@ class Util
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
public function obtainIpFromRequest(Request $request): ?string
|
||||
{
|
||||
$param = $this->config->get('ipAddressServerParam') ?? 'REMOTE_ADDR';
|
||||
|
||||
return $request->getServerParam($param);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,17 +34,15 @@ namespace Espo\Core\Application\Runner;
|
||||
*/
|
||||
class Params
|
||||
{
|
||||
/** @var array<string,mixed> */
|
||||
/** @var array<string, mixed> */
|
||||
private $data = [];
|
||||
|
||||
public function __construct() {}
|
||||
|
||||
/**
|
||||
* Get a parameter value.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get(string $name)
|
||||
public function get(string $name): mixed
|
||||
{
|
||||
return $this->data[$name] ?? null;
|
||||
}
|
||||
@@ -59,13 +57,10 @@ class Params
|
||||
|
||||
/**
|
||||
* Clone with a parameter value.
|
||||
*
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function with(string $name, $value): self
|
||||
public function with(string $name, mixed $value): self
|
||||
{
|
||||
$obj = clone $this;
|
||||
|
||||
$obj->data[$name] = $value;
|
||||
|
||||
return $obj;
|
||||
@@ -74,12 +69,11 @@ class Params
|
||||
/**
|
||||
* Create from an associative array.
|
||||
*
|
||||
* @param array<string,mixed> $data
|
||||
* @param array<string, mixed> $data
|
||||
*/
|
||||
public static function fromArray(array $data): self
|
||||
{
|
||||
$obj = new self();
|
||||
|
||||
$obj->data = $data;
|
||||
|
||||
return $obj;
|
||||
|
||||
@@ -42,19 +42,11 @@ use ReflectionClass;
|
||||
*/
|
||||
class RunnerRunner
|
||||
{
|
||||
private Log $log;
|
||||
private ApplicationUser $applicationUser;
|
||||
private InjectableFactory $injectableFactory;
|
||||
|
||||
public function __construct(
|
||||
Log $log,
|
||||
ApplicationUser $applicationUser,
|
||||
InjectableFactory $injectableFactory
|
||||
) {
|
||||
$this->log = $log;
|
||||
$this->applicationUser = $applicationUser;
|
||||
$this->injectableFactory = $injectableFactory;
|
||||
}
|
||||
private Log $log,
|
||||
private ApplicationUser $applicationUser,
|
||||
private InjectableFactory $injectableFactory
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @param class-string<Runner|RunnerParameterized> $className
|
||||
@@ -72,7 +64,7 @@ class RunnerRunner
|
||||
|
||||
if (
|
||||
$class->getStaticPropertyValue('cli', false) &&
|
||||
substr(php_sapi_name() ?: '', 0, 3) !== 'cli'
|
||||
!str_starts_with(php_sapi_name() ?: '', 'cli')
|
||||
) {
|
||||
throw new RunnerException("Can be run only via CLI.");
|
||||
}
|
||||
@@ -83,19 +75,12 @@ class RunnerRunner
|
||||
|
||||
$runner = $this->injectableFactory->create($className);
|
||||
|
||||
if ($runner instanceof Runner) {
|
||||
$runner->run();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($runner instanceof RunnerParameterized) {
|
||||
$runner->run($params ?? Params::create());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/** @phpstan-ignore-next-line */
|
||||
throw new RunnerException("Class should implement Runner or RunnerParameterized interface.");
|
||||
$runner->run();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,11 +29,9 @@
|
||||
|
||||
namespace Espo\Core\ApplicationRunners;
|
||||
|
||||
use Espo\Core\{
|
||||
Application\Runner,
|
||||
DataManager,
|
||||
Exceptions\Error,
|
||||
};
|
||||
use Espo\Core\Application\Runner;
|
||||
use Espo\Core\DataManager;
|
||||
use Espo\Core\Exceptions\Error;
|
||||
|
||||
/**
|
||||
* Clears an application cache.
|
||||
@@ -42,12 +40,8 @@ class ClearCache implements Runner
|
||||
{
|
||||
use Cli;
|
||||
|
||||
private DataManager $dataManager;
|
||||
|
||||
public function __construct(DataManager $dataManager)
|
||||
{
|
||||
$this->dataManager = $dataManager;
|
||||
}
|
||||
public function __construct(private DataManager $dataManager)
|
||||
{}
|
||||
|
||||
/**
|
||||
* @throws Error
|
||||
|
||||
@@ -47,53 +47,71 @@ class Data
|
||||
private function __construct()
|
||||
{}
|
||||
|
||||
/**
|
||||
* A user ID.
|
||||
*/
|
||||
public function getUserId(): string
|
||||
{
|
||||
return $this->userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* A portal ID.
|
||||
*/
|
||||
public function getPortalId(): ?string
|
||||
{
|
||||
return $this->portalId;
|
||||
}
|
||||
|
||||
/**
|
||||
* A hash.
|
||||
*/
|
||||
public function getHash(): ?string
|
||||
{
|
||||
return $this->hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* An ID address.
|
||||
*/
|
||||
public function getIpAddress(): ?string
|
||||
{
|
||||
return $this->ipAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* To create a secret.
|
||||
*/
|
||||
public function toCreateSecret(): bool
|
||||
{
|
||||
return $this->createSecret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
* @param array{
|
||||
* userId: string,
|
||||
* portalId?: ?string,
|
||||
* hash?: ?string,
|
||||
* ipAddress?: ?string,
|
||||
* createSecret?: ?bool,
|
||||
* } $data
|
||||
*/
|
||||
public static function create(array $data): self
|
||||
{
|
||||
$object = new self();
|
||||
$obj = new self();
|
||||
|
||||
$object->userId = $data['userId'] ?? null;
|
||||
$object->portalId = $data['portalId'] ?? null;
|
||||
$object->hash = $data['hash'] ?? null;
|
||||
$object->ipAddress = $data['ipAddress'] ?? null;
|
||||
$object->createSecret = $data['createSecret'] ?? false;
|
||||
$userId = $data['userId'] ?? null;
|
||||
|
||||
$object->validate();
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
private function validate(): void
|
||||
{
|
||||
if (!$this->userId) {
|
||||
if (!$userId) {
|
||||
throw new RuntimeException("No user ID.");
|
||||
}
|
||||
|
||||
$obj->userId = $userId;
|
||||
$obj->portalId = $data['portalId'] ?? null;
|
||||
$obj->hash = $data['hash'] ?? null;
|
||||
$obj->ipAddress = $data['ipAddress'] ?? null;
|
||||
$obj->createSecret = $data['createSecret'] ?? false;
|
||||
|
||||
return $obj;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
namespace Espo\Core\Authentication;
|
||||
|
||||
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\NotFound;
|
||||
use Espo\Repositories\UserData as UserDataRepository;
|
||||
@@ -37,7 +38,6 @@ use Espo\Entities\User;
|
||||
use Espo\Entities\AuthLogRecord;
|
||||
use Espo\Entities\AuthToken as AuthTokenEntity;
|
||||
use Espo\Entities\UserData;
|
||||
|
||||
use Espo\Core\Exceptions\Error\Body;
|
||||
use Espo\Core\Authentication\Logout\Params as LogoutParams;
|
||||
use Espo\Core\Authentication\Util\MethodProvider;
|
||||
@@ -52,6 +52,7 @@ use Espo\Core\ApplicationUser;
|
||||
use Espo\Core\ApplicationState;
|
||||
use Espo\Core\Api\Request;
|
||||
use Espo\Core\Api\Response;
|
||||
use Espo\Core\Api\Util;
|
||||
use Espo\Core\Utils\Log;
|
||||
use Espo\Core\ORM\EntityManagerProxy;
|
||||
use Espo\Core\Exceptions\ServiceUnavailable;
|
||||
@@ -85,7 +86,8 @@ class Authentication
|
||||
private HookManager $hookManager,
|
||||
private Log $log,
|
||||
private LogoutFactory $logoutFactory,
|
||||
private MethodProvider $methodProvider
|
||||
private MethodProvider $methodProvider,
|
||||
private Util $util
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -219,7 +221,7 @@ class Authentication
|
||||
$user->loadLinkMultipleField('teams');
|
||||
}
|
||||
|
||||
$user->set('ipAddress', $request->getServerParam('REMOTE_ADDR') ?? null);
|
||||
$user->set('ipAddress', $this->util->obtainIpFromRequest($request));
|
||||
|
||||
[$loggedUser, $anotherUserFailReason] = $this->getLoggedUser($request, $user);
|
||||
|
||||
@@ -464,15 +466,19 @@ class Authentication
|
||||
$request->getHeader(self::HEADER_CREATE_TOKEN_SECRET) === 'true' &&
|
||||
!$this->configDataProvider->isAuthTokenSecretDisabled();
|
||||
|
||||
$arrayData = [
|
||||
'hash' => $user->get('password'),
|
||||
'ipAddress' => $request->getServerParam('REMOTE_ADDR'),
|
||||
'userId' => $user->hasId() ? $user->getId() : null,
|
||||
/** @var ?string $password */
|
||||
$password = $user->get('password');
|
||||
$ipAddress = $this->util->obtainIpFromRequest($request);
|
||||
|
||||
$authTokenData = AuthTokenData::create([
|
||||
'hash' => $password,
|
||||
'ipAddress' => $ipAddress,
|
||||
'userId' => $user->getId(),
|
||||
'portalId' => $this->isPortal() ? $this->getPortal()->getId() : null,
|
||||
'createSecret' => $createSecret,
|
||||
];
|
||||
]);
|
||||
|
||||
$authToken = $this->authTokenManager->create(AuthTokenData::create($arrayData));
|
||||
$authToken = $this->authTokenManager->create($authTokenData);
|
||||
|
||||
if ($createSecret) {
|
||||
$this->setSecretInCookie($authToken->getSecret(), $response, $request);
|
||||
@@ -590,7 +596,7 @@ class Authentication
|
||||
|
||||
$authLogRecord
|
||||
->setUsername($username)
|
||||
->setIpAddress($request->getServerParam('REMOTE_ADDR'))
|
||||
->setIpAddress($this->util->obtainIpFromRequest($request))
|
||||
->setRequestTime($request->getServerParam('REQUEST_TIME_FLOAT'))
|
||||
->setRequestMethod($request->getMethod())
|
||||
->setRequestUrl($requestUrl)
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
namespace Espo\Core\Authentication\Hook\Hooks;
|
||||
|
||||
use Espo\Core\Api\Util;
|
||||
use Espo\Core\Authentication\Hook\BeforeLogin;
|
||||
use Espo\Core\Authentication\AuthenticationData;
|
||||
use Espo\Core\Api\Request;
|
||||
@@ -45,7 +46,8 @@ class FailedAttemptsLimit implements BeforeLogin
|
||||
public function __construct(
|
||||
private ConfigDataProvider $configDataProvider,
|
||||
private EntityManager $entityManager,
|
||||
private Log $log
|
||||
private Log $log,
|
||||
private Util $util
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -70,7 +72,7 @@ class FailedAttemptsLimit implements BeforeLogin
|
||||
|
||||
$requestTimeFrom = (new DateTime('@' . $requestTime))->modify('-' . $failedAttemptsPeriod);
|
||||
|
||||
$ip = $request->getServerParam('REMOTE_ADDR');
|
||||
$ip = $this->util->obtainIpFromRequest($request);
|
||||
|
||||
$where = [
|
||||
'requestTime>' => $requestTimeFrom->format('U'),
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
namespace Espo\Core\Authentication\Ldap;
|
||||
|
||||
use Espo\Core\Api\Util;
|
||||
use Espo\Core\FieldProcessing\Relation\LinkMultipleSaver;
|
||||
use Espo\Core\FieldProcessing\EmailAddress\Saver as EmailAddressSaver;
|
||||
use Espo\Core\FieldProcessing\PhoneNumber\Saver as PhoneNumberSaver;
|
||||
@@ -56,50 +57,30 @@ class LdapLogin implements Login
|
||||
{
|
||||
private LDAPUtils $utils;
|
||||
private ?Client $client = null;
|
||||
private bool $isPortal;
|
||||
|
||||
private Config $config;
|
||||
private EntityManager $entityManager;
|
||||
private PasswordHash $passwordHash;
|
||||
private Language $language;
|
||||
private Log $log;
|
||||
private Espo $baseLogin;
|
||||
private ClientFactory $clientFactory;
|
||||
private LinkMultipleSaver $linkMultipleSaver;
|
||||
private EmailAddressSaver $emailAddressSaver;
|
||||
private PhoneNumberSaver $phoneNumberSaver;
|
||||
|
||||
public function __construct(
|
||||
Config $config,
|
||||
EntityManager $entityManager,
|
||||
PasswordHash $passwordHash,
|
||||
private Config $config,
|
||||
private EntityManager $entityManager,
|
||||
private PasswordHash $passwordHash,
|
||||
Language $defaultLanguage,
|
||||
Log $log,
|
||||
Espo $baseLogin,
|
||||
ClientFactory $clientFactory,
|
||||
LinkMultipleSaver $linkMultipleSaver,
|
||||
EmailAddressSaver $emailAddressSaver,
|
||||
PhoneNumberSaver $phoneNumberSaver,
|
||||
bool $isPortal = false
|
||||
private Log $log,
|
||||
private Espo $baseLogin,
|
||||
private ClientFactory $clientFactory,
|
||||
private LinkMultipleSaver $linkMultipleSaver,
|
||||
private EmailAddressSaver $emailAddressSaver,
|
||||
private PhoneNumberSaver $phoneNumberSaver,
|
||||
private Util $util,
|
||||
private bool $isPortal = false
|
||||
) {
|
||||
$this->config = $config;
|
||||
$this->entityManager = $entityManager;
|
||||
$this->passwordHash = $passwordHash;
|
||||
$this->language = $defaultLanguage;
|
||||
$this->log = $log;
|
||||
$this->baseLogin = $baseLogin;
|
||||
$this->clientFactory = $clientFactory;
|
||||
$this->linkMultipleSaver = $linkMultipleSaver;
|
||||
$this->emailAddressSaver = $emailAddressSaver;
|
||||
$this->phoneNumberSaver = $phoneNumberSaver;
|
||||
|
||||
$this->isPortal = $isPortal;
|
||||
|
||||
$this->utils = new LDAPUtils($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @var array<string,string>
|
||||
* @var array<string, string>
|
||||
*/
|
||||
private $ldapFieldMap = [
|
||||
'userName' => 'userNameAttribute',
|
||||
@@ -111,7 +92,7 @@ class LdapLogin implements Login
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array<string,string>
|
||||
* @var array<string, string>
|
||||
*/
|
||||
private $userFieldMap = [
|
||||
'teamsIds' => 'userTeamsIds',
|
||||
@@ -119,7 +100,7 @@ class LdapLogin implements Login
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array<string,string>
|
||||
* @var array<string, string>
|
||||
*/
|
||||
private $portalUserFieldMap = [
|
||||
'portalsIds' => 'portalUserPortalsIds',
|
||||
@@ -135,7 +116,7 @@ class LdapLogin implements Login
|
||||
$isPortal = $this->isPortal;
|
||||
|
||||
if ($authToken) {
|
||||
$user = $this->loginByToken($username, $authToken);
|
||||
$user = $this->loginByToken($username, $authToken, $request);
|
||||
|
||||
if ($user) {
|
||||
return Result::success($user);
|
||||
@@ -278,9 +259,9 @@ class LdapLogin implements Login
|
||||
/**
|
||||
* Login by authorization token.
|
||||
*/
|
||||
private function loginByToken(?string $username, AuthToken $authToken = null): ?User
|
||||
private function loginByToken(?string $username, AuthToken $authToken, Request $request): ?User
|
||||
{
|
||||
if (!isset($authToken) || $username === null) {
|
||||
if ($username === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -296,11 +277,9 @@ class LdapLogin implements Login
|
||||
$tokenUsername = $user->getUserName() ?? '';
|
||||
|
||||
if (strtolower($username) !== strtolower($tokenUsername)) {
|
||||
$ip = $_SERVER['REMOTE_ADDR'] ?? '';
|
||||
$ip = $this->util->obtainIpFromRequest($request);
|
||||
|
||||
$this->log->alert(
|
||||
'Unauthorized access attempt for user [' . $username . '] from IP [' . $ip . ']'
|
||||
);
|
||||
$this->log->alert('Unauthorized access attempt for user [' . $username . '] from IP [' . $ip . ']');
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -331,7 +310,7 @@ class LdapLogin implements Login
|
||||
/**
|
||||
* Create Espo user with data gets from LDAP server.
|
||||
*
|
||||
* @param array<string,mixed> $userData
|
||||
* @param array<string, mixed> $userData
|
||||
*/
|
||||
private function createUser(array $userData, bool $isPortal = false): ?User
|
||||
{
|
||||
@@ -415,7 +394,7 @@ class LdapLogin implements Login
|
||||
'(' . $options['userNameAttribute'] . '=' . $username . ')' .
|
||||
$loginFilterString . ')';
|
||||
|
||||
/** @var array<int,array{dn: string}> $result */
|
||||
/** @var array<int, array{dn: string}> $result */
|
||||
$result = $ldapClient->search($searchString, null, Client::SEARCH_SCOPE_SUB);
|
||||
|
||||
$this->log->debug('LDAP: user search string: "' . $searchString . '"');
|
||||
@@ -448,7 +427,7 @@ class LdapLogin implements Login
|
||||
/**
|
||||
* Load fields for a user.
|
||||
*
|
||||
* @return array<string,mixed>
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function loadFields(string $type): array
|
||||
{
|
||||
|
||||
@@ -36,12 +36,12 @@ class Utils
|
||||
private Config $config;
|
||||
|
||||
/**
|
||||
* @var ?array<string,mixed>
|
||||
* @var ?array<string, mixed>
|
||||
*/
|
||||
private ?array $options = null;
|
||||
|
||||
/**
|
||||
* @var array<string,string>
|
||||
* @var array<string, string>
|
||||
*/
|
||||
private $fieldMap = [
|
||||
'host' => 'ldapHost',
|
||||
@@ -76,7 +76,7 @@ class Utils
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array<int,string>
|
||||
* @var array<int, string>
|
||||
*/
|
||||
private $permittedEspoOptions = [
|
||||
'createEspoUser',
|
||||
@@ -98,7 +98,7 @@ class Utils
|
||||
/**
|
||||
* AccountCanonicalForm Map between Espo and Laminas value.
|
||||
*
|
||||
* @var array<string,int>
|
||||
* @var array<string, int>
|
||||
*/
|
||||
private $accountCanonicalFormMap = [
|
||||
'Dn' => 1,
|
||||
@@ -117,7 +117,7 @@ class Utils
|
||||
/**
|
||||
* Get Options from espo config according to $this->fieldMap.
|
||||
*
|
||||
* @return array<string,mixed>
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getOptions(): array
|
||||
{
|
||||
@@ -143,9 +143,9 @@ class Utils
|
||||
/**
|
||||
* Normalize options to LDAP client format
|
||||
*
|
||||
* @param array<string,mixed> $options
|
||||
* @param array<string, mixed> $options
|
||||
*
|
||||
* @return array<string,mixed>
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function normalizeOptions(array $options): array
|
||||
{
|
||||
@@ -179,7 +179,7 @@ class Utils
|
||||
/**
|
||||
* Get Laminas options for using Laminas\Ldap.
|
||||
*
|
||||
* @return array<string,mixed>
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getLdapClientOptions(): array
|
||||
{
|
||||
|
||||
@@ -114,14 +114,32 @@ class BindingContainer
|
||||
$key = '$' . $param->getName();
|
||||
}
|
||||
|
||||
if ($className && $key && $this->data->hasContext($className, $key)) {
|
||||
$type = $param->getType();
|
||||
|
||||
if (
|
||||
$className &&
|
||||
$key &&
|
||||
$this->data->hasContext($className, $key)
|
||||
) {
|
||||
// @todo For v7.6. Uncomment, then remove the return statement below.
|
||||
/*$binding = $this->data->getContext($className, $key);
|
||||
|
||||
$notMatching =
|
||||
$type &&
|
||||
$type instanceof ReflectionNamedType &&
|
||||
!$type->isBuiltin() &&
|
||||
$binding->getType() === Binding::VALUE &&
|
||||
is_scalar($binding->getValue());
|
||||
|
||||
if (!$notMatching) {
|
||||
return $binding;
|
||||
}*/
|
||||
|
||||
return $this->data->getContext($className, $key);
|
||||
}
|
||||
|
||||
$dependencyClassName = null;
|
||||
|
||||
$type = $param->getType();
|
||||
|
||||
if (
|
||||
$type &&
|
||||
$type instanceof ReflectionNamedType &&
|
||||
|
||||
@@ -38,7 +38,7 @@ use Espo\Core\Utils\Util;
|
||||
*/
|
||||
class Params
|
||||
{
|
||||
/** @var array<string,string> */
|
||||
/** @var array<string, string> */
|
||||
private $options;
|
||||
/** @var string[] */
|
||||
private $flagList;
|
||||
@@ -46,7 +46,7 @@ class Params
|
||||
private $argumentList;
|
||||
|
||||
/**
|
||||
* @param array<string,string>|null $options
|
||||
* @param array<string, string>|null $options
|
||||
* @param string[]|null $flagList
|
||||
* @param string[]|null $argumentList
|
||||
*/
|
||||
@@ -58,7 +58,7 @@ class Params
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string,string>
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function getOptions(): array
|
||||
{
|
||||
@@ -114,7 +114,7 @@ class Params
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int,string> $args
|
||||
* @param array<int, string> $args
|
||||
*/
|
||||
public static function fromArgs(array $args): self
|
||||
{
|
||||
@@ -123,17 +123,17 @@ class Params
|
||||
$flagList = [];
|
||||
|
||||
foreach ($args as $i => $item) {
|
||||
if (strpos($item, '--') === 0 && strpos($item, '=') > 2) {
|
||||
list($name, $value) = explode('=', substr($item, 2));
|
||||
if (str_starts_with($item, '--') && strpos($item, '=') > 2) {
|
||||
[$name, $value] = explode('=', substr($item, 2));
|
||||
|
||||
$name = Util::hyphenToCamelCase($name);
|
||||
|
||||
$options[$name] = $value;
|
||||
}
|
||||
else if (strpos($item, '--') === 0) {
|
||||
else if (str_starts_with($item, '--')) {
|
||||
$flagList[] = Util::hyphenToCamelCase(substr($item, 2));
|
||||
}
|
||||
else if (strpos($item, '-') === 0) {
|
||||
else if (str_starts_with($item, '-')) {
|
||||
$flagList[] = substr($item, 1);
|
||||
}
|
||||
else if ($i > 0) {
|
||||
|
||||
@@ -54,7 +54,7 @@ class CommandManager
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int,string> $argv
|
||||
* @param array<int, string> $argv
|
||||
*
|
||||
* @return int<0, 255> Exit-status.
|
||||
*/
|
||||
@@ -98,7 +98,7 @@ class CommandManager
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int,string> $argv
|
||||
* @param array<int, string> $argv
|
||||
*/
|
||||
private function getCommandNameFromArgv(array $argv): ?string
|
||||
{
|
||||
@@ -146,7 +146,7 @@ class CommandManager
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int,string> $argv
|
||||
* @param array<int, string> $argv
|
||||
*/
|
||||
private function createParamsFromArgv(array $argv): Params
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user