mirror of
https://github.com/Combodo/iTop.git
synced 2026-03-19 16:04:15 +01:00
Compare commits
1009 Commits
3.0.0-beta
...
3.0.0-beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f9fc85e763 | ||
|
|
1a1439946b | ||
|
|
dade45308c | ||
|
|
142699c3b1 | ||
|
|
4c6a7ca30b | ||
|
|
57b08b5b24 | ||
|
|
548d08b1ec | ||
|
|
47fcddc9a6 | ||
|
|
f67f3eaf74 | ||
|
|
0d51dc61f9 | ||
|
|
9949b1b11a | ||
|
|
8be141f692 | ||
|
|
a11bc9ad33 | ||
|
|
3214ae91c7 | ||
|
|
21545da062 | ||
|
|
7476b6d059 | ||
|
|
b15c8e30bc | ||
|
|
39d71c9c43 | ||
|
|
75d913a003 | ||
|
|
2b38c98183 | ||
|
|
ca7f6362bf | ||
|
|
4d8ac5fee5 | ||
|
|
938de4c71c | ||
|
|
3ff117596d | ||
|
|
194459e0de | ||
|
|
95179b0c18 | ||
|
|
d76c9cee6f | ||
|
|
6e45b74665 | ||
|
|
3d8259a083 | ||
|
|
6d8a36e061 | ||
|
|
814038f5fd | ||
|
|
f237b4dd30 | ||
|
|
4cf4c0e4c3 | ||
|
|
86538cf88e | ||
|
|
722cae240e | ||
|
|
f0d3149a1c | ||
|
|
92e315e2c7 | ||
|
|
6150881154 | ||
|
|
cf223b583e | ||
|
|
eb5e5591d7 | ||
|
|
dcf4053c30 | ||
|
|
fedc3d4d76 | ||
|
|
e93c0123aa | ||
|
|
0004586779 | ||
|
|
7ae4fe06ed | ||
|
|
7404599721 | ||
|
|
eb8aed19c2 | ||
|
|
3a05e9159d | ||
|
|
99026cec1f | ||
|
|
029d2ad526 | ||
|
|
1cb100b010 | ||
|
|
197864ff83 | ||
|
|
1e73ee8ccd | ||
|
|
e2b73995e1 | ||
|
|
24cedbdebd | ||
|
|
0dc95f93a9 | ||
|
|
b6bd7fe400 | ||
|
|
03a19ab3f4 | ||
|
|
50849ae4ea | ||
|
|
70d7f576f3 | ||
|
|
bf491b7298 | ||
|
|
40ec7e35fd | ||
|
|
ffe5541361 | ||
|
|
73c55748d7 | ||
|
|
0754dda1d9 | ||
|
|
057bea196e | ||
|
|
e97a266c44 | ||
|
|
b4278a6987 | ||
|
|
50f860a0e8 | ||
|
|
198c9ed479 | ||
|
|
6e076ae1fa | ||
|
|
9d44f0982c | ||
|
|
fd933ce49a | ||
|
|
ae0c43a099 | ||
|
|
7c7386afc7 | ||
|
|
c306c6e30d | ||
|
|
d9ccac3aea | ||
|
|
d8316734e2 | ||
|
|
7ac5c1bbbb | ||
|
|
37585614ba | ||
|
|
03b728b394 | ||
|
|
ae2072f4d5 | ||
|
|
2f6ed8f8af | ||
|
|
1f5dabf8f6 | ||
|
|
eb5cdb053e | ||
|
|
25ee577eba | ||
|
|
f0aaf21a79 | ||
|
|
a906086751 | ||
|
|
ab17eaad27 | ||
|
|
469c1553bf | ||
|
|
4f72f8be0c | ||
|
|
5f2323f10b | ||
|
|
98013a68a4 | ||
|
|
f55193604c | ||
|
|
586b8c5c71 | ||
|
|
a771d35197 | ||
|
|
96e4ef68df | ||
|
|
df40b53b6b | ||
|
|
6d4f919519 | ||
|
|
8af116275c | ||
|
|
e930d34963 | ||
|
|
4de79afe56 | ||
|
|
daf24d8cb3 | ||
|
|
6e0d570d41 | ||
|
|
ce4379920d | ||
|
|
291bbdf3da | ||
|
|
61f6c1fe33 | ||
|
|
a1d6a705ca | ||
|
|
b861d45b08 | ||
|
|
fc7d2551cd | ||
|
|
b15267d8db | ||
|
|
94ce62e424 | ||
|
|
6271a33fdb | ||
|
|
4b6db53e84 | ||
|
|
540b9f8164 | ||
|
|
5839bab042 | ||
|
|
adc7666dd0 | ||
|
|
e3687b3cbb | ||
|
|
e81c02b25a | ||
|
|
28a8a4457e | ||
|
|
eee0f453da | ||
|
|
164a5501c0 | ||
|
|
ab3c1ad4af | ||
|
|
83a3530880 | ||
|
|
0ad23e272b | ||
|
|
b28ef803a3 | ||
|
|
b2155c042e | ||
|
|
b1b1d25186 | ||
|
|
61ee4d6807 | ||
|
|
443292e1f6 | ||
|
|
c025a7aa3b | ||
|
|
86ebb93149 | ||
|
|
120670240c | ||
|
|
5bd8a25440 | ||
|
|
754f755141 | ||
|
|
74b7223d44 | ||
|
|
1fb2ff355d | ||
|
|
61f3d3aeda | ||
|
|
99860e9e20 | ||
|
|
eb164b47e2 | ||
|
|
dbd197d9bc | ||
|
|
e638c65685 | ||
|
|
42b779d301 | ||
|
|
da3bab9647 | ||
|
|
94d53575ae | ||
|
|
c26cfad062 | ||
|
|
9cd9087cc7 | ||
|
|
df7991adeb | ||
|
|
2dcfe0e281 | ||
|
|
bac584a6b4 | ||
|
|
e4d7bf7dbb | ||
|
|
2d907f31dd | ||
|
|
e2001f4585 | ||
|
|
9810f666fd | ||
|
|
de5f47d43e | ||
|
|
d2662e27e1 | ||
|
|
9ac0100d3a | ||
|
|
1ea5983464 | ||
|
|
15081ba82c | ||
|
|
df189bd1f2 | ||
|
|
06f469faf2 | ||
|
|
b27aa70a1e | ||
|
|
0e35b8e4fa | ||
|
|
c0fc62bcb8 | ||
|
|
d1721b0834 | ||
|
|
66e4f369f7 | ||
|
|
c5e1dbce88 | ||
|
|
5d23a250ae | ||
|
|
b46357a1bd | ||
|
|
4433cdb21b | ||
|
|
45dc79f5af | ||
|
|
2ab0fab051 | ||
|
|
be81d1f6c0 | ||
|
|
ddedce1589 | ||
|
|
b557f16cfa | ||
|
|
30bf2015cb | ||
|
|
14b6e903cb | ||
|
|
4f6e040346 | ||
|
|
3e12799d1d | ||
|
|
7d0550879f | ||
|
|
6a765fad50 | ||
|
|
cbb70c94e5 | ||
|
|
b120488085 | ||
|
|
191891ac35 | ||
|
|
f171380396 | ||
|
|
f9e54e1dde | ||
|
|
15e99a898e | ||
|
|
27c397cc1a | ||
|
|
616229140e | ||
|
|
04e016c919 | ||
|
|
a4e43d3f17 | ||
|
|
ed6969be2c | ||
|
|
8105c4d6cd | ||
|
|
3b7073ad4e | ||
|
|
dbb84fa4e6 | ||
|
|
91c7ed9f4d | ||
|
|
e4c818cacb | ||
|
|
5b6b07af48 | ||
|
|
1331bc2139 | ||
|
|
08946066fb | ||
|
|
0de4e62fcd | ||
|
|
8edd155351 | ||
|
|
3980ba81e7 | ||
|
|
0193868581 | ||
|
|
d2b3de9734 | ||
|
|
d1c39c5e10 | ||
|
|
e29ae488a1 | ||
|
|
ad3e3195a8 | ||
|
|
738411005b | ||
|
|
40b095e407 | ||
|
|
3b7cb9a554 | ||
|
|
f474b3de06 | ||
|
|
7890cbd701 | ||
|
|
eef3e9b7ae | ||
|
|
3c081461b0 | ||
|
|
a916df64c9 | ||
|
|
1d0b96e10f | ||
|
|
28070f8682 | ||
|
|
3e428000bd | ||
|
|
165b11f948 | ||
|
|
c6495e7212 | ||
|
|
0fb0b9deab | ||
|
|
ecb1acf5af | ||
|
|
b239e822ef | ||
|
|
615aa594f0 | ||
|
|
2e346a7e80 | ||
|
|
f5b557b0bc | ||
|
|
2703075d39 | ||
|
|
f6fbd5a7a5 | ||
|
|
d1a05f41e5 | ||
|
|
6b67ad93b9 | ||
|
|
884d3c9477 | ||
|
|
26426123b8 | ||
|
|
6725a9f1ef | ||
|
|
8e0ae67803 | ||
|
|
979a43edc9 | ||
|
|
2aaac00015 | ||
|
|
c46cd1c514 | ||
|
|
74d2a6e46c | ||
|
|
1d57b4330b | ||
|
|
034516d0ef | ||
|
|
72d7758259 | ||
|
|
3e9a19b0ea | ||
|
|
6118ee6876 | ||
|
|
fea3c719af | ||
|
|
78eda6a2fc | ||
|
|
65db576654 | ||
|
|
69bca189fd | ||
|
|
5b78820b32 | ||
|
|
79954d3cee | ||
|
|
8a5c144e3b | ||
|
|
af28a2e01d | ||
|
|
55513b2f4b | ||
|
|
e4bf15feb3 | ||
|
|
0080caf55f | ||
|
|
aed8135d56 | ||
|
|
d0ba84068d | ||
|
|
d6d1a5cc23 | ||
|
|
6700de097a | ||
|
|
cf556de76e | ||
|
|
72cddf30fb | ||
|
|
5ec6a1cc0c | ||
|
|
fe0db8f357 | ||
|
|
1ab2b9c5d4 | ||
|
|
67cd5e321e | ||
|
|
81d9ea389d | ||
|
|
3ee9757a85 | ||
|
|
97e0150974 | ||
|
|
7f37c5912a | ||
|
|
2f5a1c99a5 | ||
|
|
5ce9b6ca99 | ||
|
|
bd9286f903 | ||
|
|
4ae7090a51 | ||
|
|
335074701f | ||
|
|
76f70d45dd | ||
|
|
6f147acd76 | ||
|
|
a84d2ce6bb | ||
|
|
ea9c1ab0b3 | ||
|
|
e1dc269171 | ||
|
|
16dc7de8e2 | ||
|
|
88e210d84d | ||
|
|
4f6bd5444b | ||
|
|
0f204f94eb | ||
|
|
f5ae76360e | ||
|
|
8d59024d8e | ||
|
|
32e0031242 | ||
|
|
3fdebdc217 | ||
|
|
7083807319 | ||
|
|
0f01dbc3e5 | ||
|
|
8289b028cf | ||
|
|
f72a6ee963 | ||
|
|
08359cdd05 | ||
|
|
de17439f55 | ||
|
|
6e555e29d1 | ||
|
|
9dae34461b | ||
|
|
06b1e581fe | ||
|
|
d4b515c7b7 | ||
|
|
20759eca23 | ||
|
|
4e420cbcd6 | ||
|
|
0e60c67910 | ||
|
|
808c1fa571 | ||
|
|
dbf8475883 | ||
|
|
2c2155a8e0 | ||
|
|
ffbd94d671 | ||
|
|
3abcdc9c58 | ||
|
|
242f499101 | ||
|
|
6b0106ff73 | ||
|
|
bf991ffb8a | ||
|
|
280afb35a9 | ||
|
|
e095749c90 | ||
|
|
ced0e7cc8f | ||
|
|
6df98c3d41 | ||
|
|
21e16fd2e8 | ||
|
|
bc2e25be99 | ||
|
|
0c5ebc3a84 | ||
|
|
43412b78e3 | ||
|
|
9126635cf2 | ||
|
|
d0986c048a | ||
|
|
fe0c52bedd | ||
|
|
8926c6783a | ||
|
|
95b6dd0cc3 | ||
|
|
121e39b738 | ||
|
|
48dee9c136 | ||
|
|
befde44215 | ||
|
|
a827eed59d | ||
|
|
1613c1bbdc | ||
|
|
909af7f59c | ||
|
|
495b39a7ab | ||
|
|
f9ff66941d | ||
|
|
29871bc6da | ||
|
|
1c983e8093 | ||
|
|
92a9a8c65f | ||
|
|
a3e1b95c8e | ||
|
|
bf2d98a1bf | ||
|
|
abe103eade | ||
|
|
034052cf4b | ||
|
|
4458f26379 | ||
|
|
007e1ded0d | ||
|
|
5651512f68 | ||
|
|
8c043f137c | ||
|
|
b3cce54ee9 | ||
|
|
e4e4e3b0bf | ||
|
|
1cb3b4bd96 | ||
|
|
97ee6570d2 | ||
|
|
8c8f711fe8 | ||
|
|
f140efd95c | ||
|
|
6b19758654 | ||
|
|
2899c82ef2 | ||
|
|
12c2929f1d | ||
|
|
9d28e43804 | ||
|
|
27217815d1 | ||
|
|
8e3cc471df | ||
|
|
58e35ea33b | ||
|
|
0b81601699 | ||
|
|
4d3ba6edd0 | ||
|
|
9de014c9cb | ||
|
|
75dbaada72 | ||
|
|
0d4dd0c67b | ||
|
|
fabdef37d2 | ||
|
|
e666631f63 | ||
|
|
280feca863 | ||
|
|
7690e43b39 | ||
|
|
0c66882990 | ||
|
|
7613ac7a9b | ||
|
|
7af10e5197 | ||
|
|
095c975ec6 | ||
|
|
9e1e5a8a47 | ||
|
|
2c026fa891 | ||
|
|
00d65aeb45 | ||
|
|
5702f603ac | ||
|
|
e3dc1b77cc | ||
|
|
c304f70ff4 | ||
|
|
53fd41e748 | ||
|
|
7577fbb8bf | ||
|
|
0fc912b357 | ||
|
|
c475e66176 | ||
|
|
cd1ba097cb | ||
|
|
fdca4d4cc3 | ||
|
|
4379b4d908 | ||
|
|
5b42f67a99 | ||
|
|
0b9ccc8e67 | ||
|
|
2d98ca2318 | ||
|
|
92add2bbfe | ||
|
|
f15bdb75ca | ||
|
|
a66830de17 | ||
|
|
714294e1b4 | ||
|
|
e3d2c1d761 | ||
|
|
59d95cc14b | ||
|
|
ddc5bbd1bb | ||
|
|
9bbee0d603 | ||
|
|
be1ef5b452 | ||
|
|
cbdc48b7e1 | ||
|
|
bb5679959e | ||
|
|
cdbb4470fc | ||
|
|
252d752bf5 | ||
|
|
99d0c05c1c | ||
|
|
5926e9d110 | ||
|
|
c8dd8c3806 | ||
|
|
70b07721e6 | ||
|
|
bd050dfe69 | ||
|
|
9eb477ce83 | ||
|
|
a742b6c610 | ||
|
|
fcf769666e | ||
|
|
72e628c5d5 | ||
|
|
bf28602ae6 | ||
|
|
b8a0d899f4 | ||
|
|
d335736cfc | ||
|
|
80f7d07378 | ||
|
|
0be6a8aef4 | ||
|
|
0fe857071d | ||
|
|
05981c8af8 | ||
|
|
0abea749fa | ||
|
|
78af6fdc84 | ||
|
|
1e97b5a8c0 | ||
|
|
0214243b63 | ||
|
|
d30871ac59 | ||
|
|
d247ea915d | ||
|
|
0e0aed1ba4 | ||
|
|
bc770ef3d5 | ||
|
|
56a4fb0b42 | ||
|
|
3139628dd8 | ||
|
|
7d9b19cd9e | ||
|
|
b3cb95d2f1 | ||
|
|
a6765cdc3a | ||
|
|
97d52fb539 | ||
|
|
ad936d7a2f | ||
|
|
090eb9a560 | ||
|
|
e4f58b5b98 | ||
|
|
c99a22c9f7 | ||
|
|
29cf20beaf | ||
|
|
e325d535ae | ||
|
|
11cfdb2a17 | ||
|
|
66720b9731 | ||
|
|
234f46cafa | ||
|
|
27c3ce0389 | ||
|
|
9d0e2fa64a | ||
|
|
e211633fed | ||
|
|
29967aa41a | ||
|
|
3ebb1cfc66 | ||
|
|
6c2221b8b6 | ||
|
|
c0b3aed12c | ||
|
|
53efc9f75e | ||
|
|
0d566f2e47 | ||
|
|
b294e3c734 | ||
|
|
6704f9eccf | ||
|
|
90cb6e1d49 | ||
|
|
c0aa4f2d69 | ||
|
|
01984c24c0 | ||
|
|
1da1e0b1bd | ||
|
|
39bcd3e4cd | ||
|
|
156cce6098 | ||
|
|
3130e95f4f | ||
|
|
e28f704f3e | ||
|
|
5c6c59941a | ||
|
|
bc9d47933e | ||
|
|
07a10e4146 | ||
|
|
e8a21870ad | ||
|
|
2e132d5c53 | ||
|
|
3359609349 | ||
|
|
2966759466 | ||
|
|
25395405e5 | ||
|
|
b589e2d001 | ||
|
|
27f3619cf5 | ||
|
|
616fc436d0 | ||
|
|
4f2f765207 | ||
|
|
9931fa1a6b | ||
|
|
a13f2750ea | ||
|
|
243d105f59 | ||
|
|
7783ba570e | ||
|
|
5a9fa2ac32 | ||
|
|
619e3de5a8 | ||
|
|
83064d68c7 | ||
|
|
f3c11e72cf | ||
|
|
c9e887a264 | ||
|
|
49fd482389 | ||
|
|
96de4e1796 | ||
|
|
44f413583c | ||
|
|
91104002ea | ||
|
|
f98ba1594c | ||
|
|
431dc5532b | ||
|
|
df473ae313 | ||
|
|
40ce74cffa | ||
|
|
7598c18ad6 | ||
|
|
d38b655f5f | ||
|
|
f4345ef312 | ||
|
|
13b548e95d | ||
|
|
3a988ab499 | ||
|
|
14a5f87d62 | ||
|
|
8dae459b12 | ||
|
|
8dc10424e8 | ||
|
|
54a6573948 | ||
|
|
1d5e0b6fe9 | ||
|
|
acb8a377dd | ||
|
|
d86f489c89 | ||
|
|
2c154b601d | ||
|
|
7dad079688 | ||
|
|
ace2eb6dab | ||
|
|
25f3c1cbc4 | ||
|
|
8f7e7c136d | ||
|
|
71a7e060e4 | ||
|
|
c450f1d02f | ||
|
|
4431762c10 | ||
|
|
97170892e6 | ||
|
|
5137d634e3 | ||
|
|
146a95baec | ||
|
|
2b71ea108a | ||
|
|
a7ac9126a2 | ||
|
|
ee544b646d | ||
|
|
344cce9fdd | ||
|
|
bfcfcdd4cc | ||
|
|
4410bf7e1f | ||
|
|
d1fda1dbc6 | ||
|
|
5c702be641 | ||
|
|
8a9ad78e9c | ||
|
|
778be8abce | ||
|
|
0760adcef6 | ||
|
|
29ca291860 | ||
|
|
93d0e4ae7c | ||
|
|
61bc07b598 | ||
|
|
00ee36f938 | ||
|
|
1aa5185c93 | ||
|
|
834ac00d37 | ||
|
|
957cb87b8d | ||
|
|
d0813f6607 | ||
|
|
cf4673d284 | ||
|
|
e3422f5fd9 | ||
|
|
d7a0878a39 | ||
|
|
076f0e00a7 | ||
|
|
eb04ecb5b4 | ||
|
|
3323d5532b | ||
|
|
d68d8204f2 | ||
|
|
4a50b95069 | ||
|
|
fd9e4f413c | ||
|
|
dca3fc996c | ||
|
|
c58c1bbf1d | ||
|
|
524919b946 | ||
|
|
e15953524a | ||
|
|
68f3c53faa | ||
|
|
82e3ddaacd | ||
|
|
dfbbee2a1c | ||
|
|
4843545171 | ||
|
|
4e58b8616a | ||
|
|
1a79dcd773 | ||
|
|
a4104d4315 | ||
|
|
4e1db7d7e2 | ||
|
|
8e6379a112 | ||
|
|
da217a1cb3 | ||
|
|
a683634a05 | ||
|
|
2f1b5d2736 | ||
|
|
c9bf784fce | ||
|
|
4ef10ae7c9 | ||
|
|
5174250ff1 | ||
|
|
7b6ac202c6 | ||
|
|
d960183403 | ||
|
|
ece3e0490d | ||
|
|
1562cb1f38 | ||
|
|
11a22abfd5 | ||
|
|
5254c9a633 | ||
|
|
f7a35072f5 | ||
|
|
b5f5780f35 | ||
|
|
f9064084f9 | ||
|
|
67afbd1d8d | ||
|
|
d6b9172e26 | ||
|
|
8e1e71c740 | ||
|
|
ebbf6e56be | ||
|
|
bd67b71f3d | ||
|
|
69ad10785b | ||
|
|
9aead898e2 | ||
|
|
a48ebfefba | ||
|
|
e2a6e6b846 | ||
|
|
7ca689e190 | ||
|
|
d8f36a8aa9 | ||
|
|
e279799bf8 | ||
|
|
a117906ff6 | ||
|
|
c76d4f12fd | ||
|
|
b16529e337 | ||
|
|
67e9212008 | ||
|
|
35b70bfc00 | ||
|
|
418312bca5 | ||
|
|
4748717e50 | ||
|
|
d90b1a3d82 | ||
|
|
76a237aad4 | ||
|
|
3694108f42 | ||
|
|
1a7755365c | ||
|
|
8cf75f826f | ||
|
|
a1da086a64 | ||
|
|
5d1d6d07a6 | ||
|
|
e6a38a8055 | ||
|
|
3b3f1806ce | ||
|
|
e46743af2a | ||
|
|
9048d09bf6 | ||
|
|
3cd03729b9 | ||
|
|
ff760dacbe | ||
|
|
94f662c71a | ||
|
|
c7d87ad5b0 | ||
|
|
ad9726b64c | ||
|
|
e32e275f40 | ||
|
|
195056492e | ||
|
|
aa9ab1ace5 | ||
|
|
bfe22b4ec0 | ||
|
|
f84bc9fbbc | ||
|
|
0dc03de7b2 | ||
|
|
af338de17f | ||
|
|
0cffd567c2 | ||
|
|
9d006c279b | ||
|
|
f410bff309 | ||
|
|
0e9ff71c0a | ||
|
|
a599a5c5b7 | ||
|
|
84dae0fb37 | ||
|
|
65e3ef32ed | ||
|
|
0396914068 | ||
|
|
0e55a30e5a | ||
|
|
c0fbf0c735 | ||
|
|
e59d472cec | ||
|
|
27e6840442 | ||
|
|
a6aa183e26 | ||
|
|
b5074c4cee | ||
|
|
fbb54c6d6d | ||
|
|
95066fbc49 | ||
|
|
e2958bd43f | ||
|
|
382135fde8 | ||
|
|
4d80235d89 | ||
|
|
8b9589744b | ||
|
|
250d95c3eb | ||
|
|
0997189969 | ||
|
|
410948fb4e | ||
|
|
9f75fae33f | ||
|
|
b1aebc6c7a | ||
|
|
85526b1983 | ||
|
|
2a1e0b7429 | ||
|
|
3a73bd3b24 | ||
|
|
d7ba0aac82 | ||
|
|
8259a79cd2 | ||
|
|
de71d490c1 | ||
|
|
60b015919d | ||
|
|
2ca11aa2b7 | ||
|
|
275daf3b0d | ||
|
|
671d112452 | ||
|
|
a23ea9a01f | ||
|
|
949b213f9d | ||
|
|
a940adc4ba | ||
|
|
5d994edd62 | ||
|
|
2a9ce75db0 | ||
|
|
f8fcfc4d7d | ||
|
|
663a9ab224 | ||
|
|
f604821bdf | ||
|
|
ac83c0334c | ||
|
|
457165b2fc | ||
|
|
b1ca1f2630 | ||
|
|
bce1bd8192 | ||
|
|
4d123e1450 | ||
|
|
58e315d7f6 | ||
|
|
1281d475e4 | ||
|
|
32ac999ff5 | ||
|
|
1059befa39 | ||
|
|
5db8bd06ba | ||
|
|
0f5130611d | ||
|
|
a1271da74a | ||
|
|
0d40235791 | ||
|
|
d1e594e225 | ||
|
|
bcc2d7140e | ||
|
|
ee5847ec82 | ||
|
|
108bc2fa1d | ||
|
|
efb7f84ec4 | ||
|
|
224031e0d6 | ||
|
|
e905838733 | ||
|
|
dd63f2b817 | ||
|
|
8f84c3b84b | ||
|
|
bc4f384948 | ||
|
|
00c58bb245 | ||
|
|
11f6c88ac7 | ||
|
|
77cf879f4f | ||
|
|
13f6f6ebe6 | ||
|
|
c413ac989e | ||
|
|
7a770b32ee | ||
|
|
9d8adae5c7 | ||
|
|
b0531597f0 | ||
|
|
0bdf43b085 | ||
|
|
27da8470b7 | ||
|
|
c0be48ceee | ||
|
|
6c561029e9 | ||
|
|
c417a454d6 | ||
|
|
d8e2a1cc7c | ||
|
|
51e24a4e35 | ||
|
|
a82f172ca4 | ||
|
|
8ef43452f8 | ||
|
|
18a4867b4d | ||
|
|
ff05a1d35e | ||
|
|
5a4c9393a4 | ||
|
|
4b03b42494 | ||
|
|
215dcfb465 | ||
|
|
b965138d57 | ||
|
|
485262ec0a | ||
|
|
dd8a51ea1b | ||
|
|
d85729803d | ||
|
|
3132d665e0 | ||
|
|
be03f3c6de | ||
|
|
3a876d5c75 | ||
|
|
147916062b | ||
|
|
bcfdf76b71 | ||
|
|
679542eaaa | ||
|
|
4d5ff22b60 | ||
|
|
d54c5b5f55 | ||
|
|
0de6f98add | ||
|
|
5bf6035bcf | ||
|
|
a076792e77 | ||
|
|
73132f981e | ||
|
|
80e4a46a20 | ||
|
|
95d9b845b6 | ||
|
|
609d9c7a60 | ||
|
|
3d338aff33 | ||
|
|
8a46900df9 | ||
|
|
8efbfd13d0 | ||
|
|
19264a7d64 | ||
|
|
742e3a2a20 | ||
|
|
117bf3046d | ||
|
|
b072e1bb3d | ||
|
|
d405fc0675 | ||
|
|
77527987ee | ||
|
|
2e6b773ba4 | ||
|
|
dd6a9fe16a | ||
|
|
d9ccc620c9 | ||
|
|
b245275c7d | ||
|
|
bf22c8b67a | ||
|
|
1b58d9b801 | ||
|
|
2d2a6857de | ||
|
|
373641e01d | ||
|
|
d11eceac62 | ||
|
|
3965806fa0 | ||
|
|
74eabafe31 | ||
|
|
2625d2da80 | ||
|
|
1b9eb2a6ad | ||
|
|
94c37b2e14 | ||
|
|
71d2d61c4e | ||
|
|
020dc8b819 | ||
|
|
c11ad30134 | ||
|
|
02d32a556d | ||
|
|
71fcc6f026 | ||
|
|
dd614f5931 | ||
|
|
499d429a0e | ||
|
|
7168860a0b | ||
|
|
a395c1760a | ||
|
|
4033773849 | ||
|
|
9fd6ba414d | ||
|
|
2aee978790 | ||
|
|
39efe1237e | ||
|
|
684c88e0b8 | ||
|
|
e7c7ca76ac | ||
|
|
a6e5969201 | ||
|
|
1e99ad436f | ||
|
|
8580eb6d2b | ||
|
|
c6b8526dc0 | ||
|
|
03c7d63588 | ||
|
|
32e714f4a6 | ||
|
|
b8ef4885e5 | ||
|
|
e669cfcea1 | ||
|
|
82f0cd5f3d | ||
|
|
b6661fde86 | ||
|
|
e46480032e | ||
|
|
aed1e7dfdc | ||
|
|
b32fd7e018 | ||
|
|
3ae8575c1d | ||
|
|
cc4b307bc9 | ||
|
|
6d004f83dd | ||
|
|
5691ca0327 | ||
|
|
8f68ca3f67 | ||
|
|
84741c19f0 | ||
|
|
96126d235f | ||
|
|
4de40e289f | ||
|
|
86f649affc | ||
|
|
4f5c987d8b | ||
|
|
9ccd26a541 | ||
|
|
e7b5953feb | ||
|
|
e441e5e78a | ||
|
|
6d57945bd1 | ||
|
|
0cb0f52b12 | ||
|
|
6be9a87c15 | ||
|
|
43daa2ef08 | ||
|
|
8f9a69fa60 | ||
|
|
1d94e12da8 | ||
|
|
0a04fe5917 | ||
|
|
b03389068e | ||
|
|
c9171ef30e | ||
|
|
3853d38d69 | ||
|
|
caa2a05bf4 | ||
|
|
fc39d8aca9 | ||
|
|
022887258f | ||
|
|
cf12578289 | ||
|
|
3105a7ef77 | ||
|
|
f5ee19370c | ||
|
|
923134d67d | ||
|
|
3c2c2d7a51 | ||
|
|
3ee4a14c56 | ||
|
|
dd284a6c1d | ||
|
|
c240a8991d | ||
|
|
0948c46dc7 | ||
|
|
d9755fe59d | ||
|
|
7f82e9262b | ||
|
|
44952d1ea0 | ||
|
|
7f15eed9a8 | ||
|
|
14930fbab2 | ||
|
|
2cbca93d77 | ||
|
|
c2f5cafaf3 | ||
|
|
81822efa0f | ||
|
|
104cf9479c | ||
|
|
a36632def6 | ||
|
|
6a1b1d7740 | ||
|
|
f544d53c36 | ||
|
|
3188204d05 | ||
|
|
a7294d48a6 | ||
|
|
42be0c20cb | ||
|
|
1c6ffab0e3 | ||
|
|
095d61b6f9 | ||
|
|
923a025f1c | ||
|
|
a4b6f4e37c | ||
|
|
78b2824c13 | ||
|
|
f0c73451a2 | ||
|
|
62f5eb5ae9 | ||
|
|
7d9416cc81 | ||
|
|
a82a2df6aa | ||
|
|
f1e4120364 | ||
|
|
3471d9d693 | ||
|
|
6d7bcb8a7c | ||
|
|
db6e813cba | ||
|
|
d74e3e6b42 | ||
|
|
e9648ad75e | ||
|
|
e53da47647 | ||
|
|
1128490d47 | ||
|
|
186ef1689e | ||
|
|
0487cb8701 | ||
|
|
27b930c31c | ||
|
|
a8ef1c7899 | ||
|
|
644f0bc94c | ||
|
|
f3b0f79a59 | ||
|
|
eaae79a5f0 | ||
|
|
bc10baed3e | ||
|
|
b740cb2afd | ||
|
|
c0ec7e02f8 | ||
|
|
5977c5dd9e | ||
|
|
1c2dcc7b9a | ||
|
|
6ad3c40c42 | ||
|
|
f49c8ce188 | ||
|
|
acf828b72e | ||
|
|
cb61d85572 | ||
|
|
626316e8d1 | ||
|
|
ab30bae46a | ||
|
|
bd2c0d1121 | ||
|
|
64764228a9 | ||
|
|
463593d6cf | ||
|
|
85aac7bf52 | ||
|
|
dc36b4648d | ||
|
|
25145a68af | ||
|
|
dab522caca | ||
|
|
88634bcd92 | ||
|
|
6ab6930986 | ||
|
|
1f3b467083 | ||
|
|
34647cc402 | ||
|
|
2a9e16bd2c | ||
|
|
ed1fdfa830 | ||
|
|
1124a584cd | ||
|
|
d2c014f718 | ||
|
|
e488ec8c38 | ||
|
|
18b2e54581 | ||
|
|
3c89313795 | ||
|
|
11278d7ba5 | ||
|
|
a6eeb3a50a | ||
|
|
1f1028bbc0 | ||
|
|
08f973f8c0 | ||
|
|
ad22113881 | ||
|
|
e5498e3292 | ||
|
|
16c9599739 | ||
|
|
a7040a911d | ||
|
|
8227796c87 | ||
|
|
bac92716f3 | ||
|
|
993dbed7e7 | ||
|
|
390dc1c945 | ||
|
|
09b12bd06e | ||
|
|
3a06f2eaa9 | ||
|
|
a8e1db8b3c | ||
|
|
2cc4448a15 | ||
|
|
d0d254ad59 | ||
|
|
8f62b63309 | ||
|
|
227c90b47f | ||
|
|
6fc3033100 | ||
|
|
cfab20519e | ||
|
|
cbc5bb70d0 | ||
|
|
d5d3a7af47 | ||
|
|
03fcc94b1f | ||
|
|
4a28aa316f | ||
|
|
88a615528f | ||
|
|
b3e1b7124a | ||
|
|
48d0fc6ddb | ||
|
|
8bf9987174 | ||
|
|
0f4c325aab | ||
|
|
93a1f027cb | ||
|
|
3e0971597b | ||
|
|
fce3934977 | ||
|
|
d8feda89a5 | ||
|
|
7aecdd0dc7 | ||
|
|
a478294211 | ||
|
|
9de1f1800d | ||
|
|
94c8dbac18 | ||
|
|
efc745f574 | ||
|
|
3e45dcdbb1 | ||
|
|
07257cc2d2 | ||
|
|
ae6e08d430 | ||
|
|
473a49ab6b | ||
|
|
0c58d67f41 | ||
|
|
d2c4a6e2e9 | ||
|
|
d1ccd5c8eb | ||
|
|
370a07f596 | ||
|
|
9ae056e663 | ||
|
|
ce596877a4 | ||
|
|
3d9540e895 | ||
|
|
27d8869665 | ||
|
|
3b3fa7b1f8 | ||
|
|
135b5e8adb | ||
|
|
52dbf23245 | ||
|
|
6b899d3f77 | ||
|
|
87ba67225a | ||
|
|
2ad3b3c27e | ||
|
|
46dc7709ab | ||
|
|
92a640e41a | ||
|
|
842df7646b | ||
|
|
01b38d2ed6 | ||
|
|
ddb5ffc11a | ||
|
|
2a62e28c66 | ||
|
|
973133ce0b | ||
|
|
be0a728f42 | ||
|
|
1bb7d9947c | ||
|
|
65d94bd914 | ||
|
|
d951d0def0 | ||
|
|
beadd9cd03 | ||
|
|
fac97324a3 | ||
|
|
f33919c312 | ||
|
|
74d8eda713 | ||
|
|
6790fdb9e8 | ||
|
|
d545123ca7 | ||
|
|
59678ef8c6 | ||
|
|
a6d20ab648 | ||
|
|
e5559a1899 | ||
|
|
f8757c6d5d | ||
|
|
7f4ef12c04 | ||
|
|
2a90557782 | ||
|
|
7b3595357a | ||
|
|
f6d9d0f08b | ||
|
|
f3cf154969 | ||
|
|
923a4048d3 | ||
|
|
0816d27456 | ||
|
|
d07199db7c | ||
|
|
903afff687 | ||
|
|
c0f5906dce | ||
|
|
fa3140cc13 | ||
|
|
22aaba3c65 | ||
|
|
5878b18b02 | ||
|
|
530ec111ef | ||
|
|
430049d7c4 | ||
|
|
e45fd92022 | ||
|
|
605165fc5a | ||
|
|
644944c6fd | ||
|
|
8313d61521 | ||
|
|
81d30ca9f4 | ||
|
|
6078ccc144 | ||
|
|
7b9d89e485 | ||
|
|
48bc8b2341 | ||
|
|
8f03ac4aa3 | ||
|
|
97f5f0b50d | ||
|
|
05594ccdd6 | ||
|
|
e2f301827a | ||
|
|
067cbe2afa | ||
|
|
a99f4c0666 | ||
|
|
d594f28ed9 | ||
|
|
deed948f54 | ||
|
|
25e88b889d | ||
|
|
d6695d3ad8 | ||
|
|
0bc9aed3fe | ||
|
|
98ec4bba53 | ||
|
|
e527324468 | ||
|
|
8ccada40d1 | ||
|
|
80d974f2b4 | ||
|
|
521c493fd1 | ||
|
|
b68ee1136e | ||
|
|
0f43b0a6de | ||
|
|
58d1a1d311 | ||
|
|
5b3ab2965f | ||
|
|
f97f55bcca | ||
|
|
3262ad7029 | ||
|
|
a5bb6a0c55 | ||
|
|
f0d6b41cee | ||
|
|
5c01d222c9 | ||
|
|
7e65541d3c | ||
|
|
83b8fbb6b5 | ||
|
|
5b9e5f8de9 | ||
|
|
ddceeafc52 | ||
|
|
56087d9686 | ||
|
|
5aef6f209b | ||
|
|
1b6d69037e | ||
|
|
3941f323c0 | ||
|
|
b19f9d4875 | ||
|
|
057bb1a296 | ||
|
|
192cb2df73 | ||
|
|
47c797daba | ||
|
|
33dcb35344 | ||
|
|
68ab84716b | ||
|
|
b60fb4fb99 | ||
|
|
8fb5a9acd0 | ||
|
|
4c289edac7 | ||
|
|
f6be3736fd | ||
|
|
f8f9c2d7bb |
@@ -77,24 +77,20 @@ Then, **for a method** of an eligible class:
|
||||
|
||||
:notebook: as spaces are used to mark code, the templates (`.doc/phpdoc-templates/combodo-wiki/*`) have very few indentation, thus they are awful to read (sorry).
|
||||
|
||||
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
cd .doc
|
||||
composer require phpdocumentor/phpdocumentor:~2 --dev
|
||||
```
|
||||
|
||||
## Generation
|
||||
`./bin/build-doc-object-manipulation` and `./bin/build-doc-extensions` contains examples of doc. generation, beware: they have to be called from the .doc directory:
|
||||
```shell
|
||||
cd /path/to/itop/.doc
|
||||
./bin/build-doc-object-manipulation
|
||||
```
|
||||
|
||||
the resulting documentation is written into `data/phpdocumentor/output`
|
||||
|
||||
1. Switch to this directory : `cd /path/to/itop/.doc`
|
||||
2. `composer install`
|
||||
3. `./bin/build-doc-object-manipulation`
|
||||
3. `./bin/build-doc-extensions`
|
||||
4. Get the generated files from `/path/to/itop/data/phpdocumentor/output`
|
||||
|
||||
## Dokuwiki requirements
|
||||
* the template uses the [wrap plugin](https://www.dokuwiki.org/plugin:wrap).
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh -x
|
||||
|
||||
rm -rf /tmp/phpdoc-twig-cache/ && rm -rf data/phpdocumentor/output/extensions/ && rm -rf data/phpdocumentor/temp/extensions/ && .doc/vendor/bin/phpdoc -c .doc/phpdoc-extensions.dist.xml -vvv
|
||||
rm -rf /tmp/phpdoc-twig-cache/ && rm -rf data/phpdocumentor/output/extensions/ && rm -rf data/phpdocumentor/temp/extensions/ && ./vendor/bin/phpdoc -c ./phpdoc-extensions.dist.xml -vvv
|
||||
|
||||
# now wee need to lowercase every generated file because dokuwiki can't handle uppercase
|
||||
cd data/phpdocumentor/output/extensions/ && for i in $( ls | grep [A-Z] ); do mv -i $i `echo $i | tr 'A-Z' 'a-z'`; done
|
||||
cd ../data/phpdocumentor/output/extensions/ && for i in $(ls | grep [A-Z]); do mv -i $i $(echo $i | tr 'A-Z' 'a-z'); done
|
||||
|
||||
@@ -90,7 +90,7 @@ ij_xml_attribute_wrap = normal
|
||||
ij_xml_block_comment_at_first_column = true
|
||||
ij_xml_keep_blank_lines = 2
|
||||
ij_xml_keep_indents_on_empty_lines = false
|
||||
ij_xml_keep_line_breaks = false
|
||||
ij_xml_keep_line_breaks = true
|
||||
ij_xml_keep_line_breaks_in_text = true
|
||||
ij_xml_keep_whitespaces = false
|
||||
ij_xml_keep_whitespaces_around_cdata = preserve
|
||||
@@ -110,6 +110,7 @@ ij_shell_keep_column_alignment_padding = false
|
||||
ij_shell_minify_program = false
|
||||
ij_shell_redirect_followed_by_space = false
|
||||
ij_shell_switch_cases_indented = false
|
||||
ij_shell_use_unix_line_separator = true
|
||||
|
||||
[{*.cjs,*.js}]
|
||||
indent_style = tab
|
||||
@@ -279,7 +280,7 @@ ij_javascript_while_brace_force = always
|
||||
ij_javascript_while_on_new_line = false
|
||||
ij_javascript_wrap_comments = false
|
||||
|
||||
[{*.ctp,*.hphp,*.inc,*.module,*.php,*.php4,*.php5,*.phtml}]
|
||||
[{*.ctp, *.hphp, *.inc, *.module, *.php, *.php4, *.php5, *.phtml}]
|
||||
indent_style = tab
|
||||
ij_continuation_indent_size = 4
|
||||
ij_smart_tabs = true
|
||||
@@ -288,8 +289,8 @@ ij_php_align_assignments = false
|
||||
ij_php_align_class_constants = false
|
||||
ij_php_align_group_field_declarations = false
|
||||
ij_php_align_inline_comments = false
|
||||
ij_php_align_key_value_pairs = false
|
||||
ij_php_align_multiline_array_initializer_expression = false
|
||||
ij_php_align_key_value_pairs = true
|
||||
ij_php_align_multiline_array_initializer_expression = true
|
||||
ij_php_align_multiline_binary_operation = false
|
||||
ij_php_align_multiline_chained_methods = false
|
||||
ij_php_align_multiline_extends_list = false
|
||||
@@ -514,7 +515,7 @@ ij_json_wrap_long_lines = false
|
||||
indent_style = tab
|
||||
ij_smart_tabs = true
|
||||
ij_visual_guides = none
|
||||
ij_html_add_new_line_before_tags = body, div, p, form, h1, h2, h3
|
||||
ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3
|
||||
ij_html_align_attributes = true
|
||||
ij_html_align_text = false
|
||||
ij_html_attribute_wrap = normal
|
||||
@@ -541,7 +542,21 @@ ij_html_space_inside_empty_tag = false
|
||||
ij_html_text_wrap = normal
|
||||
ij_html_uniform_ident = false
|
||||
|
||||
[{*.yaml, *.yml}]
|
||||
[{*.markdown,*.md}]
|
||||
ij_visual_guides = none
|
||||
ij_markdown_force_one_space_after_blockquote_symbol = true
|
||||
ij_markdown_force_one_space_after_header_symbol = true
|
||||
ij_markdown_force_one_space_after_list_bullet = true
|
||||
ij_markdown_force_one_space_between_words = true
|
||||
ij_markdown_keep_indents_on_empty_lines = false
|
||||
ij_markdown_max_lines_around_block_elements = 1
|
||||
ij_markdown_max_lines_around_header = 1
|
||||
ij_markdown_max_lines_between_paragraphs = 1
|
||||
ij_markdown_min_lines_around_block_elements = 1
|
||||
ij_markdown_min_lines_around_header = 1
|
||||
ij_markdown_min_lines_between_paragraphs = 1
|
||||
|
||||
[{*.yaml,*.yml}]
|
||||
indent_size = 2
|
||||
ij_visual_guides = none
|
||||
ij_yaml_align_values_properties = do_not_align
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -32,6 +32,8 @@ test/vendor/*
|
||||
!/data/.htaccess
|
||||
!/data/index.php
|
||||
!/data/web.config
|
||||
!/data/exclude.txt
|
||||
!/data/.compilation-symlinks
|
||||
|
||||
# iTop extensions
|
||||
/extensions/**
|
||||
|
||||
13
.make/git-hooks/README.md
Normal file
13
.make/git-hooks/README.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Git hooks for iTop
|
||||
|
||||
## ❓ Goal
|
||||
|
||||
Those [git hooks](https://git-scm.com/docs/githooks) aims to ease developing on [iTop](https://github.com/Combodo/iTop).
|
||||
|
||||
## ☑ Available hooks
|
||||
|
||||
* pre-commit : rejects commit if you have at least one SCSS file staged, and no CSS file
|
||||
|
||||
## ⚙ Install
|
||||
|
||||
Just run install.php !
|
||||
26
.make/git-hooks/install.php
Normal file
26
.make/git-hooks/install.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
$aHooks = [
|
||||
'pre-commit.php',
|
||||
];
|
||||
|
||||
$sAppRoot = dirname(__DIR__, 2);
|
||||
|
||||
|
||||
foreach ($aHooks as $sSourceHookFileName) {
|
||||
echo "Processing for `{$sSourceHookFileName}`...\n";
|
||||
$sSourceHookPath = __DIR__.DIRECTORY_SEPARATOR.$sSourceHookFileName;
|
||||
|
||||
$aPathParts = pathinfo($sSourceHookFileName);
|
||||
$sTargetHookPath = $sAppRoot.DIRECTORY_SEPARATOR.'.git'.DIRECTORY_SEPARATOR.'hooks'.DIRECTORY_SEPARATOR.$aPathParts['filename'];
|
||||
|
||||
if (file_exists($sTargetHookPath) || is_link($sTargetHookPath)) {
|
||||
echo "Existing $sTargetHookPath ! Removing...";
|
||||
unlink($sTargetHookPath);
|
||||
echo "OK !\n";
|
||||
}
|
||||
|
||||
echo "Creating symlink for hook in $sTargetHookPath...";
|
||||
symlink($sSourceHookPath, $sTargetHookPath);
|
||||
echo "OK !\n";
|
||||
}
|
||||
|
||||
49
.make/git-hooks/pre-commit.php
Normal file
49
.make/git-hooks/pre-commit.php
Normal file
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/php
|
||||
<?php
|
||||
/**
|
||||
* Reject any commit containing .scss files, but no .css file !
|
||||
*/
|
||||
|
||||
echo "Checking files staged...\n";
|
||||
$sFilesToCommit = shell_exec('git diff --cached --name-only --diff-filter=ACMRT');
|
||||
$aFilesToCommit = explode("\n", $sFilesToCommit);
|
||||
|
||||
$aScssFiles = GetFilesWithExtension('scss', $aFilesToCommit);
|
||||
if (count($aScssFiles) === 0) {
|
||||
echo "No scss file : OK to go !\n";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
$aCssFiles = GetFilesWithExtension('css', $aFilesToCommit);
|
||||
if (count($aCssFiles) === 0) {
|
||||
echo "There are SCSS files staged but no CSS file : REJECTING commit.\n";
|
||||
echo "You must add the compiled SCSS files by running the setup !\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
echo "We have SCSS but also CSS => OK to commit !\n";
|
||||
exit(0);
|
||||
|
||||
|
||||
|
||||
function GetFilesWithExtension($sExtension, $aFiles) {
|
||||
return array_filter(
|
||||
$aFiles,
|
||||
function($item) use ($sExtension) {
|
||||
return (endsWith($item, '.'.$sExtension));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function endsWith( $haystack, $needle ) {
|
||||
$length = strlen( $needle );
|
||||
if( !$length ) {
|
||||
return true;
|
||||
}
|
||||
return substr( $haystack, -$length ) === $needle;
|
||||
}
|
||||
|
||||
function exitWithMessage($sMessage, $iCode) {
|
||||
echo $sMessage;
|
||||
exit($iCode);
|
||||
}
|
||||
@@ -10,10 +10,6 @@
|
||||
* `curl -L -o /usr/bin/jq.exe https://github.com/stedolan/jq/releases/latest/download/jq-win64.exe`
|
||||
* this is a Windows port : https://stedolan.github.io/jq/
|
||||
*
|
||||
* Known bug on Windows :
|
||||
* Licenses added from Composer contains a path in the product node (N°3870)
|
||||
* `<product scope="lib">C:\Dev\wamp64\www\itop-dev\.make\license/../..//lib/symfony/console</product>`
|
||||
*
|
||||
* Licenses sources :
|
||||
* * `composer licenses --format json` (see https://getcomposer.org/doc/03-cli.md#licenses)
|
||||
* * keep every existing nodes with `/licenses/license[11]/product/@scope` not in ['lib', 'datamodels']
|
||||
@@ -70,39 +66,83 @@ function get_license_nodes($file_path)
|
||||
$xp = new DOMXPath($dom);
|
||||
|
||||
$licenseList = $xp->query('/licenses/license');
|
||||
$licenses = iterator_to_array($licenseList);
|
||||
$licenses = iterator_to_array($licenseList);
|
||||
|
||||
usort($licenses, 'sort_by_product');
|
||||
|
||||
return $licenses;
|
||||
}
|
||||
|
||||
/** @noinspection SuspiciousAssignmentsInspection */
|
||||
function fix_product_name(DOMNode &$oProductNode)
|
||||
{
|
||||
$sProductNameOrig = $oProductNode->nodeValue;
|
||||
|
||||
// sample : `C:\Dev\wamp64\www\itop-27\.make\license/../..//lib/symfony/polyfill-ctype`
|
||||
$sProductNameFixed = remove_dir_from_string($sProductNameOrig, 'lib/');
|
||||
|
||||
// sample : `C:\Dev\wamp64\www\itop-27\.make\license/../..//datamodels/2.x/authent-cas/vendor/apereo/phpcas`
|
||||
$sProductNameFixed = remove_dir_from_string($sProductNameFixed, 'vendor/');
|
||||
|
||||
$oProductNode->nodeValue = $sProductNameFixed;
|
||||
}
|
||||
|
||||
function remove_dir_from_string($sString, $sNeedle)
|
||||
{
|
||||
if (strpos($sString, $sNeedle) === false) {
|
||||
return $sString;
|
||||
}
|
||||
|
||||
$sStringTmp = strstr($sString, $sNeedle);
|
||||
$sStringFixed = str_replace($sNeedle, '', $sStringTmp);
|
||||
|
||||
// DEBUG trace O:)
|
||||
// echo "$sNeedle = $sString => $sStringFixed\n";
|
||||
|
||||
return $sStringFixed;
|
||||
}
|
||||
|
||||
$old_licenses = get_license_nodes($xmlFilePath);
|
||||
|
||||
//generate file with updated licenses
|
||||
$generated_license_file_path = __DIR__."/provfile.xml";
|
||||
exec("bash " . __DIR__ . "/gen-community-license.sh $iTopFolder > ". $generated_license_file_path);
|
||||
echo "- Generating licences...";
|
||||
exec("bash ".__DIR__."/gen-community-license.sh $iTopFolder > ".$generated_license_file_path);
|
||||
echo "OK!\n";
|
||||
|
||||
echo "- Get licenses nodes...";
|
||||
$new_licenses = get_license_nodes($generated_license_file_path);
|
||||
exec("rm -f ". $generated_license_file_path);
|
||||
unlink($generated_license_file_path);
|
||||
|
||||
foreach ($old_licenses as $b) {
|
||||
$aProductNode = get_product_node($b);
|
||||
|
||||
if (get_scope($aProductNode) !== "lib" && get_scope($aProductNode) !== "datamodels" )
|
||||
{
|
||||
if (get_scope($aProductNode) !== "lib" && get_scope($aProductNode) !== "datamodels") {
|
||||
$new_licenses[] = $b;
|
||||
}
|
||||
}
|
||||
|
||||
usort($new_licenses, 'sort_by_product');
|
||||
echo "OK!\n";
|
||||
|
||||
echo "- Overwritting Combodo license file...";
|
||||
$new_dom = new DOMDocument("1.0");
|
||||
$new_dom->formatOutput = true;
|
||||
$root = $new_dom->createElement("licenses");
|
||||
$new_dom->appendChild($root);
|
||||
|
||||
foreach ($new_licenses as $b) {
|
||||
$node = $new_dom->importNode($b,true);
|
||||
$root->appendChild($new_dom->importNode($b,true));
|
||||
$node = $new_dom->importNode($b, true);
|
||||
|
||||
// N°3870 fix when running script in Windows
|
||||
// fix should be in gen-community-license.sh but it is easier to do it here !
|
||||
if (strncasecmp(PHP_OS, 'WIN', 3) === 0) {
|
||||
$oProductNodeOrig = get_product_node($node);
|
||||
fix_product_name($oProductNodeOrig);
|
||||
}
|
||||
|
||||
$root->appendChild($node);
|
||||
}
|
||||
|
||||
$new_dom->save($xmlFilePath);
|
||||
$new_dom->save($xmlFilePath);
|
||||
echo "OK!\n";
|
||||
77
.make/release/changelog.php
Normal file
77
.make/release/changelog.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
/**
|
||||
* Usage :
|
||||
* `php changelog.php 2.7.4`
|
||||
*
|
||||
* As argument is passed the git ref (tag name or sha1) we want to use as reference
|
||||
*
|
||||
* Outputs :
|
||||
*
|
||||
* 1. List of bugs as CSV :
|
||||
* bug ref;link
|
||||
* Example :
|
||||
* <code>
|
||||
* Bug_ref;Bug_URL;sha1
|
||||
* 1234;https://support.combodo.com/pages/UI.php?operation=details&class=Bug&id=1234;949b213f9|b1ca1f263|a1271da74
|
||||
* </code>
|
||||
*
|
||||
* 2. List of commits sha1/message without bug ref
|
||||
* Example :
|
||||
* <code>
|
||||
* sha1;subject
|
||||
* a6aa183e2;:bookmark: Prepare 2.7.5
|
||||
* </code>
|
||||
*/
|
||||
|
||||
|
||||
if (count($argv) === 1) {
|
||||
echo '⚠ You must pass the base tag/sha1 as parameter';
|
||||
exit(1);
|
||||
}
|
||||
$sBaseReference = $argv[1];
|
||||
|
||||
|
||||
//--- Get log
|
||||
$sGitLogCommand = 'git log --decorate --pretty="%h;%s" --date-order --no-merges '.$sBaseReference.'..HEAD';
|
||||
$sGitLogRaw = shell_exec($sGitLogCommand);
|
||||
|
||||
|
||||
//--- Analyze log
|
||||
$aGitLogLines = preg_split('/\n/', trim($sGitLogRaw));;
|
||||
$aLogLinesWithBugRef = [];
|
||||
$aLogLineNoBug = [];
|
||||
foreach ($aGitLogLines as $sLogLine) {
|
||||
$sBugRef = preg_match('/[nN]°(\d{3,4})/', $sLogLine, $aLineBugRef);
|
||||
if (($sBugRef === false) || empty($aLineBugRef)) {
|
||||
$aLogLineNoBug[] = $sLogLine;
|
||||
continue;
|
||||
}
|
||||
|
||||
$iBugId = $aLineBugRef[1];
|
||||
$sSha = substr($sLogLine, 0, 9);
|
||||
|
||||
if (array_key_exists($iBugId, $aLogLinesWithBugRef)) {
|
||||
$aBugShaRefs = $aLogLinesWithBugRef[$iBugId];
|
||||
$aBugShaRefs[] = $sSha;
|
||||
$aLogLinesWithBugRef[$iBugId] = $aBugShaRefs;
|
||||
} else {
|
||||
$aLogLinesWithBugRef[$iBugId] = [$sSha];
|
||||
}
|
||||
}
|
||||
$aBugsList = array_keys($aLogLinesWithBugRef);
|
||||
sort($aBugsList, SORT_NUMERIC);
|
||||
|
||||
|
||||
//-- Output results
|
||||
echo "# Bugs included\n";
|
||||
echo "Bug_ref;Bug_URL;sha1\n";
|
||||
foreach ($aBugsList as $sBugRef) {
|
||||
$sShaRefs = implode('|', $aLogLinesWithBugRef[$sBugRef]);
|
||||
echo "{$sBugRef};https://support.combodo.com/pages/UI.php?operation=details&class=Bug&id={$sBugRef};$sShaRefs\n";
|
||||
}
|
||||
echo "\n";
|
||||
echo "# Logs line without bug referenced\n";
|
||||
echo "sha1;subject\n";
|
||||
foreach ($aLogLineNoBug as $sLogLine) {
|
||||
echo "$sLogLine\n";
|
||||
}
|
||||
@@ -125,8 +125,7 @@ Our tests are located in the `test/` directory, containing a PHPUnit config file
|
||||
* ⬆️ `:arrow_up:` when upgrading dependencies
|
||||
* ⬇️ `:arrow_down:` when downgrading dependencies
|
||||
* ♻️ `:recycle:` code refactoring
|
||||
* 💄 `:lipstick:` Updating the UI and style files.
|
||||
|
||||
* 💄 `:lipstick:` Updating the UI and style files.
|
||||
|
||||
## 👥 Pull request
|
||||
|
||||
@@ -137,3 +136,25 @@ When your code is working, please:
|
||||
* create a pull request.
|
||||
|
||||
Detailed procedure to work on fork and create PR is available [in GitHub help pages](https://help.github.com/articles/creating-a-pull-request-from-a-fork/).
|
||||
|
||||
You might check the ["Allow edits from maintainers" PR checkbox][allow_edits_checkbox] to ease review.
|
||||
|
||||
[allow_edits_checkbox]: https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork#enabling-repository-maintainer-permissions-on-existing-pull-requests
|
||||
|
||||
### 🙏 We are thankful
|
||||
|
||||
We are thankful for all your contributions to the iTop universe! As a thank you gift, we will send stickers to every iTop (& extensions) contributors!
|
||||
|
||||
Stickers' design might change from one year to another. For the first year we wanted to try a "craft beer label" look, see examples below:
|
||||
|
||||
* Bug hunter: Fix a bug
|
||||
* Translator: Add/update translations
|
||||
* White hat: Find and/or fix a vulnerability
|
||||
* Contributor: Contribute by finding a bug, making an extension or any other way
|
||||
* Partner: For Combodo's official partners
|
||||
* Graduated: Follow a Combodo's iTop training
|
||||
* Ambassador: Outstanding community contributors
|
||||
* Beta tester: Test and give feedback on beta releases
|
||||
* Extension developer: Develop and publish an extension
|
||||
|
||||

|
||||
|
||||
@@ -221,7 +221,7 @@ class URP_UserProfile extends UserRightsBaseClassGUI
|
||||
(
|
||||
"category" => "addon/userrights,grant_by_profile",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "userid",
|
||||
"name_attcode" => array("userlogin", "profile"),
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_urp_userprofile",
|
||||
@@ -246,11 +246,6 @@ class URP_UserProfile extends UserRightsBaseClassGUI
|
||||
MetaModel::Init_SetZListItems('advanced_search', array('userid', 'profileid')); // Criteria of the advanced search form
|
||||
}
|
||||
|
||||
public function GetName()
|
||||
{
|
||||
return Dict::Format('UI:UserManagement:LinkBetween_User_And_Profile', $this->Get('userlogin'), $this->Get('profile'));
|
||||
}
|
||||
|
||||
public function CheckToDelete(&$oDeletionPlan)
|
||||
{
|
||||
if (MetaModel::GetConfig()->Get('demo_mode')) {
|
||||
@@ -362,7 +357,7 @@ class URP_UserOrg extends UserRightsBaseClassGUI
|
||||
(
|
||||
"category" => "addon/userrights,grant_by_profile",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "userid",
|
||||
"name_attcode" => array("userlogin", "allowed_org_name"),
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_urp_userorg",
|
||||
@@ -387,12 +382,6 @@ class URP_UserOrg extends UserRightsBaseClassGUI
|
||||
MetaModel::Init_SetZListItems('advanced_search', array('userid', 'allowed_org_id')); // Criteria of the advanced search form
|
||||
}
|
||||
|
||||
public function GetName()
|
||||
{
|
||||
return Dict::Format('UI:UserManagement:LinkBetween_User_And_Org', $this->Get('userlogin'), $this->Get('allowed_org_name'));
|
||||
}
|
||||
|
||||
|
||||
protected function OnInsert()
|
||||
{
|
||||
$this->CheckIfOrgIsAllowed();
|
||||
|
||||
@@ -327,7 +327,7 @@ class URP_UserProfile extends UserRightsBaseClassGUI
|
||||
(
|
||||
"category" => "addon/userrights",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "userid",
|
||||
"name_attcode" => array("userlogin", "profile"),
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_urp_userprofile",
|
||||
@@ -351,11 +351,6 @@ class URP_UserProfile extends UserRightsBaseClassGUI
|
||||
MetaModel::Init_SetZListItems('standard_search', array('userid', 'profileid')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('advanced_search', array('userid', 'profileid')); // Criteria of the advanced search form
|
||||
}
|
||||
|
||||
public function GetName()
|
||||
{
|
||||
return Dict::Format('UI:UserManagement:LinkBetween_User_And_Profile', $this->Get('userlogin'), $this->Get('profile'));
|
||||
}
|
||||
}
|
||||
|
||||
class URP_UserOrg extends UserRightsBaseClassGUI
|
||||
@@ -366,7 +361,7 @@ class URP_UserOrg extends UserRightsBaseClassGUI
|
||||
(
|
||||
"category" => "addon/userrights",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "userid",
|
||||
"name_attcode" => array("userlogin", "allowed_org_name"),
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_urp_userorg",
|
||||
@@ -390,11 +385,6 @@ class URP_UserOrg extends UserRightsBaseClassGUI
|
||||
MetaModel::Init_SetZListItems('standard_search', array('userid', 'allowed_org_id')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('advanced_search', array('userid', 'allowed_org_id')); // Criteria of the advanced search form
|
||||
}
|
||||
|
||||
public function GetName()
|
||||
{
|
||||
return Dict::Format('UI:UserManagement:LinkBetween_User_And_Org', $this->Get('userlogin'), $this->Get('allowed_org_name'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -270,7 +270,7 @@ class URP_UserProfile extends UserRightsBaseClass
|
||||
(
|
||||
"category" => "addon/userrights",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "userid",
|
||||
"name_attcode" => array("userlogin", "profile"),
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_urp_userprofile",
|
||||
@@ -294,11 +294,6 @@ class URP_UserProfile extends UserRightsBaseClass
|
||||
MetaModel::Init_SetZListItems('standard_search', array('userid', 'profileid')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('advanced_search', array('userid', 'profileid')); // Criteria of the advanced search form
|
||||
}
|
||||
|
||||
public function GetName()
|
||||
{
|
||||
return Dict::Format('UI:UserManagement:LinkBetween_User_And_Profile', $this->Get('userlogin'), $this->Get('profile'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
60
application/DBSearchHelper.php
Normal file
60
application/DBSearchHelper.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class DBSearchHelper
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
class DBSearchHelper
|
||||
{
|
||||
/**
|
||||
* Add context filter to DBUnionSearch
|
||||
*
|
||||
* @param \DBSearch|null $oSearch
|
||||
*
|
||||
* @throws \Exception
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static function AddContextFilter(?DBSearch $oSearch): void
|
||||
{
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sClass = $oSearch->GetClass();
|
||||
foreach ($oAppContext->GetNames() as $key) {
|
||||
// Find the value of the object corresponding to each 'context' parameter
|
||||
$aCallSpec = [$sClass, 'MapContextParam'];
|
||||
$sAttCode = '';
|
||||
if (is_callable($aCallSpec)) {
|
||||
$sAttCode = call_user_func($aCallSpec, $key); // Returns null when there is no mapping for this parameter
|
||||
}
|
||||
|
||||
if (MetaModel::IsValidAttCode($sClass, $sAttCode)) {
|
||||
// Add Hierarchical condition if hierarchical key
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
if (isset($oAttDef) && ($oAttDef->IsExternalKey())) {
|
||||
$iDefaultValue = intval($oAppContext->GetCurrentValue($key));
|
||||
if ($iDefaultValue != 0) {
|
||||
try {
|
||||
/** @var AttributeExternalKey $oAttDef */
|
||||
$sTargetClass = $oAttDef->GetTargetClass();
|
||||
$sHierarchicalKeyCode = MetaModel::IsHierarchicalClass($sTargetClass);
|
||||
if ($sHierarchicalKeyCode !== false) {
|
||||
$oFilter = new DBObjectSearch($sTargetClass);
|
||||
$oFilter->AddCondition('id', $iDefaultValue);
|
||||
$oHKFilter = new DBObjectSearch($sTargetClass);
|
||||
$oHKFilter->AddCondition_PointingTo($oFilter, $sHierarchicalKeyCode, TREE_OPERATOR_BELOW);
|
||||
$oSearch->AddCondition_PointingTo($oHKFilter, $sAttCode);
|
||||
}
|
||||
}
|
||||
catch (Exception $e) {
|
||||
// If filtering fails just ignore it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,16 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/AjaxPage.php
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/AjaxPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
*/
|
||||
|
||||
// cannot notify depreciation for now as this is still MASSIVELY used in iTop core !
|
||||
//DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/AjaxPage.php, now loadable using autoloader');
|
||||
|
||||
/**
|
||||
* Class ajax_page
|
||||
*
|
||||
* @deprecated will be removed in 3.1.0 - moved to AjaxPage
|
||||
*/
|
||||
class ajax_page extends AjaxPage
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Application\Helper\Session;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Input\InputUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock;
|
||||
use Combodo\iTop\Application\UI\Base\UIBlock;
|
||||
@@ -227,6 +228,20 @@ class ApplicationContext
|
||||
}
|
||||
return $sContext;
|
||||
}
|
||||
/**
|
||||
* Returns the context an array of input blocks
|
||||
*
|
||||
* @return array The context as a sequence of <input type="hidden" /> tags
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function GetForUIForm()
|
||||
{
|
||||
$aContextInputBlocks = [];
|
||||
foreach ($this->aValues as $sName => $sValue) {
|
||||
$aContextInputBlocks[] = InputUIBlockFactory::MakeForHidden("c[$sName]", htmlentities($sValue, ENT_QUOTES, 'UTF-8'));
|
||||
}
|
||||
return $aContextInputBlocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the context as sequence of input tags to be inserted inside a <form> tag
|
||||
@@ -321,7 +336,7 @@ class ApplicationContext
|
||||
$sPrevious = self::GetUrlMakerClass();
|
||||
|
||||
self::$m_sUrlMakerClass = $sClass;
|
||||
$_SESSION['UrlMakerClass'] = $sClass;
|
||||
Session::Set('UrlMakerClass', $sClass);
|
||||
|
||||
return $sPrevious;
|
||||
}
|
||||
@@ -334,9 +349,9 @@ class ApplicationContext
|
||||
{
|
||||
if (is_null(self::$m_sUrlMakerClass))
|
||||
{
|
||||
if (isset($_SESSION['UrlMakerClass']))
|
||||
if (Session::IsSet('UrlMakerClass'))
|
||||
{
|
||||
self::$m_sUrlMakerClass = $_SESSION['UrlMakerClass'];
|
||||
self::$m_sUrlMakerClass = Session::Get('UrlMakerClass');
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -389,9 +404,9 @@ class ApplicationContext
|
||||
*/
|
||||
protected static function LoadPluginProperties()
|
||||
{
|
||||
if (isset($_SESSION['PluginProperties']))
|
||||
if (Session::IsSet('PluginProperties'))
|
||||
{
|
||||
self::$m_aPluginProperties = $_SESSION['PluginProperties'];
|
||||
self::$m_aPluginProperties = Session::Get('PluginProperties');
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -411,7 +426,7 @@ class ApplicationContext
|
||||
if (is_null(self::$m_aPluginProperties)) self::LoadPluginProperties();
|
||||
|
||||
self::$m_aPluginProperties[$sPluginClass][$sProperty] = $value;
|
||||
$_SESSION['PluginProperties'][$sPluginClass][$sProperty] = $value;
|
||||
Session::Set(['PluginProperties', $sPluginClass, $sProperty], $value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -73,15 +73,14 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public abstract function ListSupportedLoginModes();
|
||||
abstract public function ListSupportedLoginModes();
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function LoginAction($sLoginState, &$iErrorCode)
|
||||
{
|
||||
switch ($sLoginState)
|
||||
{
|
||||
switch ($sLoginState) {
|
||||
case LoginWebPage::LOGIN_STATE_START:
|
||||
return $this->OnStart($iErrorCode);
|
||||
|
||||
@@ -777,6 +776,10 @@ abstract class ApplicationPopupMenuItem
|
||||
/** @ignore */
|
||||
protected $sLabel;
|
||||
/** @ignore */
|
||||
protected $sTooltip;
|
||||
/** @ignore */
|
||||
protected $sIconClass;
|
||||
/** @ignore */
|
||||
protected $aCssClasses;
|
||||
|
||||
/**
|
||||
@@ -791,6 +794,8 @@ abstract class ApplicationPopupMenuItem
|
||||
{
|
||||
$this->sUID = $sUID;
|
||||
$this->sLabel = $sLabel;
|
||||
$this->sTooltip = '';
|
||||
$this->sIconClass = '';
|
||||
$this->aCssClasses = array();
|
||||
}
|
||||
|
||||
@@ -845,6 +850,47 @@ abstract class ApplicationPopupMenuItem
|
||||
$this->aCssClasses[] = $sCssClass;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $sTooltip
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function SetTooltip($sTooltip)
|
||||
{
|
||||
$this->sTooltip = $sTooltip;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function GetTooltip()
|
||||
{
|
||||
return $this->sTooltip;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sIconClass
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function SetIconClass($sIconClass)
|
||||
{
|
||||
$this->sIconClass = $sIconClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function GetIconClass()
|
||||
{
|
||||
return $this->sIconClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the components to create a popup menu item in HTML
|
||||
*
|
||||
@@ -863,6 +909,8 @@ abstract class ApplicationPopupMenuItem
|
||||
/**
|
||||
* Class for adding an item into a popup menu that browses to the given URL
|
||||
*
|
||||
* Note: This works only in the backoffice, {@see \URLButtonItem} for the end-user portal
|
||||
*
|
||||
* @api
|
||||
* @package Extensibility
|
||||
* @since 2.0
|
||||
@@ -892,7 +940,13 @@ class URLPopupMenuItem extends ApplicationPopupMenuItem
|
||||
/** @ignore */
|
||||
public function GetMenuItem()
|
||||
{
|
||||
return array('label' => $this->GetLabel(), 'url' => $this->GetUrl(), 'target' => $this-> GetTarget(), 'css_classes' => $this->aCssClasses);
|
||||
return array('label' => $this->GetLabel(),
|
||||
'url' => $this->GetUrl(),
|
||||
'target' => $this-> GetTarget(),
|
||||
'css_classes' => $this->aCssClasses,
|
||||
'icon_class' => $this->sIconClass,
|
||||
'tooltip' => $this->sTooltip
|
||||
);
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
@@ -911,6 +965,8 @@ class URLPopupMenuItem extends ApplicationPopupMenuItem
|
||||
/**
|
||||
* Class for adding an item into a popup menu that triggers some Javascript code
|
||||
*
|
||||
* Note: This works only in the backoffice, {@see \JSButtonItem} for the end-user portal
|
||||
*
|
||||
* @api
|
||||
* @package Extensibility
|
||||
* @since 2.0
|
||||
@@ -952,6 +1008,8 @@ class JSPopupMenuItem extends ApplicationPopupMenuItem
|
||||
'onclick' => $this->GetJsCode().'; return false;',
|
||||
'url' => $this->GetUrl(),
|
||||
'css_classes' => $this->GetCssClasses(),
|
||||
'icon_class' => $this->sIconClass,
|
||||
'tooltip' => $this->sTooltip
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1133,6 +1191,8 @@ abstract class AbstractPageUIExtension implements iPageUIExtension
|
||||
*/
|
||||
public function GetNorthPaneHtml(iTopWebPage $oPage)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use iPageUIBlockExtension instead');
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -1141,6 +1201,8 @@ abstract class AbstractPageUIExtension implements iPageUIExtension
|
||||
*/
|
||||
public function GetSouthPaneHtml(iTopWebPage $oPage)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use iPageUIBlockExtension instead');
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -1149,6 +1211,8 @@ abstract class AbstractPageUIExtension implements iPageUIExtension
|
||||
*/
|
||||
public function GetBannerHtml(iTopWebPage $oPage)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use iPageUIBlockExtension instead');
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/CaptureWebPage.php
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/CaptureWebPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
*/
|
||||
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/CaptureWebPage.php, now loadable using autoloader');
|
||||
@@ -1,7 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/CLIPage.php
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/CLIPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
*/
|
||||
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/CLIPage.php, now loadable using autoloader');
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/CSVPage.php
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/CSVPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
*/
|
||||
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/CSVPage.php, now loadable using autoloader');
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableSettings;
|
||||
use Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenu;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Toolbar\ToolbarUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\Dashboard\DashboardLayout as DashboardLayoutUIBlock;
|
||||
|
||||
@@ -542,7 +543,7 @@ EOF
|
||||
|
||||
if ($bFromDasboardPage) {
|
||||
$sTitleForHTML = utils::HtmlEntities(Dict::S($this->sTitle));
|
||||
$sHtml = "<div class=\"ibo-top-bar--toolbar-dashboard-title\">{$sTitleForHTML}</div>";
|
||||
$sHtml = "<div class=\"ibo-top-bar--toolbar-dashboard-title\" title=\"{$sTitleForHTML}\">{$sTitleForHTML}</div>";
|
||||
if ($oPage instanceof iTopWebPage) {
|
||||
$oTopBar = $oPage->GetTopBarLayout();
|
||||
$oToolbar = ToolbarUIBlockFactory::MakeStandard();
|
||||
@@ -551,7 +552,7 @@ EOF
|
||||
$oToolbar->AddHtml($sHtml);
|
||||
} else {
|
||||
$oPage->add_script(<<<JS
|
||||
$(".ibo-top-bar--toolbar-dashboard-title").html("$sTitleForHTML");
|
||||
$(".ibo-top-bar--toolbar-dashboard-title").html("$sTitleForHTML").attr("title", "$sTitleForHTML");
|
||||
JS
|
||||
);
|
||||
}
|
||||
@@ -1147,7 +1148,8 @@ JS
|
||||
utils::GetPopupMenuItems($oPage, iPopupMenuExtension::MENU_DASHBOARD_ACTIONS, $this, $aActions);
|
||||
|
||||
$oActionsMenu = $oPage->GetPopoverMenu($sPopoverMenuId, $aActions)
|
||||
->SetTogglerJSSelector("#$sMenuTogglerId");
|
||||
->SetTogglerJSSelector("#$sMenuTogglerId")
|
||||
->SetContainer(PopoverMenu::ENUM_CONTAINER_BODY);
|
||||
|
||||
$oToolbar->AddSubBlock($oActionButton)
|
||||
->AddSubBlock($oActionsMenu);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
use Combodo\iTop\Application\Helper\WebResourcesHelper;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Dashlet\DashletContainer;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Dashlet\DashletFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
|
||||
@@ -202,6 +203,24 @@ abstract class Dashlet
|
||||
$this->OnUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array Rel. path to the app. root of the JS files required by the dashlet
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function GetJSFilesRelPaths(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array Rel. path to the app. root of the CSS files required by the dashlet
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function GetCSSFilesRelPaths(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \WebPage $oPage
|
||||
* @param bool $bEditMode
|
||||
@@ -224,6 +243,9 @@ abstract class Dashlet
|
||||
$oDashletContainer->AddCSSClasses($this->aCSSClasses);
|
||||
}
|
||||
|
||||
$oDashletContainer->AddMultipleJsFilesRelPaths($this->GetJSFilesRelPaths());
|
||||
$oDashletContainer->AddMultipleCssFilesRelPaths($this->GetCSSFilesRelPaths());
|
||||
|
||||
try {
|
||||
if (get_class($this->oModelReflection) == 'ModelReflectionRuntime') {
|
||||
$oBlock = $this->Render($oPage, $bEditMode, $aExtraParams);
|
||||
@@ -604,8 +626,7 @@ class DashletUnknown extends Dashlet
|
||||
|
||||
$oDashletContainer = new DashletContainer(null, ['dashlet-content']);
|
||||
|
||||
$oDashletContainer->AddHtml('<div class="dashlet-ukn-image"><img src="'.$sIconUrl.'" /></div>');
|
||||
$oDashletContainer->AddHtml('<div class="dashlet-ukn-text">'.$sExplainText.'</div>');
|
||||
$oDashletContainer->AddHtml('<div class="dashlet-ukn-image"><img src="'.$sIconUrl.'" /></div><div class="dashlet-ukn-text">'.$sExplainText.'</div>');
|
||||
|
||||
return $oDashletContainer;
|
||||
}
|
||||
@@ -624,8 +645,7 @@ class DashletUnknown extends Dashlet
|
||||
|
||||
$oDashletContainer = new DashletContainer(null, ['dashlet-content']);
|
||||
|
||||
$oDashletContainer->AddHtml('<div class="dashlet-ukn-image"><img src="'.$sIconUrl.'" /></div>');
|
||||
$oDashletContainer->AddHtml('<div class="dashlet-ukn-text">'.$sExplainText.'</div>');
|
||||
$oDashletContainer->AddHtml('<div class="dashlet-ukn-image"><img src="'.$sIconUrl.'" /></div><div class="dashlet-ukn-text">'.$sExplainText.'</div>');
|
||||
|
||||
return $oDashletContainer;
|
||||
}
|
||||
@@ -906,17 +926,22 @@ class DashletObjectList extends Dashlet
|
||||
$sShowMenu = $this->aProperties['menu'] ? '1' : '0';
|
||||
$oFilter = $this->GetDBSearch($aExtraParams);
|
||||
$sClass = $oFilter->GetClass();
|
||||
$oPanel = PanelUIBlockFactory::MakeForClass($sClass, Dict::S($sTitle))
|
||||
->AddCSSClass('ibo-datatable-panel');
|
||||
//$oPanel = PanelUIBlockFactory::MakeForClass($sClass, Dict::S($sTitle))
|
||||
// ->AddCSSClass('ibo-datatable-panel');
|
||||
|
||||
$oBlock = new DisplayBlock($oFilter, 'list');
|
||||
$aParams = array(
|
||||
'menu' => $sShowMenu,
|
||||
'table_id' => self::APPUSERPREFERENCES_PREFIX.$this->sId,
|
||||
'surround_with_panel' => false,
|
||||
'surround_with_panel' => true,
|
||||
'max_height' => '500px',
|
||||
"panel_title" => Dict::S($sTitle),
|
||||
"panel_class" => $sClass,
|
||||
);
|
||||
$sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occurring in the same DOM)
|
||||
$oBlock->DisplayIntoContentBlock($oPanel, $oPage, $sBlockId, array_merge($aExtraParams, $aParams));
|
||||
//$oBlock->DisplayIntoContentBlock($oPanel, $oPage, $sBlockId, array_merge($aExtraParams, $aParams));
|
||||
|
||||
$oPanel = $oBlock->GetDisplay($oPage, $sBlockId, array_merge($aExtraParams, $aParams));
|
||||
|
||||
return $oPanel;
|
||||
}
|
||||
@@ -926,24 +951,36 @@ class DashletObjectList extends Dashlet
|
||||
*/
|
||||
public function RenderNoData($oPage, $bEditMode = false, $aExtraParams = array())
|
||||
{
|
||||
$oDashletContainer = new DashletContainer($this->sId, ['dashlet-content']);
|
||||
$sTitle = $this->aProperties['title'];
|
||||
$sQuery = $this->aProperties['query'];
|
||||
$bShowMenu = $this->aProperties['menu'];
|
||||
|
||||
$oPage->add('<div class="dashlet-content">');
|
||||
$sHtmlTitle = utils::HtmlEntities($this->oModelReflection->DictString($sTitle)); // done in the itop block
|
||||
$sHtmlTitle = utils::HtmlEntities($this->oModelReflection->DictString($sTitle));
|
||||
if ($sHtmlTitle != '') {
|
||||
$oPage->add('<h1>'.$sHtmlTitle.'</h1>');
|
||||
$sHtmlTitle = '<h1>'.$sHtmlTitle.'</h1>';
|
||||
}
|
||||
$oQuery = $this->oModelReflection->GetQuery($sQuery);
|
||||
$sClass = $oQuery->GetClass();
|
||||
$oPage->add('<div id="block_fake_'.$this->sId.'" class="display_block">');
|
||||
$oPage->p(Dict::S('UI:NoObjectToDisplay'));
|
||||
$sId = $this->sId;
|
||||
$sMessage = Dict::S('UI:NoObjectToDisplay');
|
||||
$sMenu = '';
|
||||
if ($bShowMenu) {
|
||||
$oPage->p('<a>'.Dict::Format('UI:ClickToCreateNew', $this->oModelReflection->GetName($sClass)).'</a>');
|
||||
$sMenu = '<p><a>'.Dict::Format('UI:ClickToCreateNew', $this->oModelReflection->GetName($sClass)).'</a></p>';
|
||||
}
|
||||
$oPage->add('</div>');
|
||||
$oPage->add('</div>');
|
||||
|
||||
$sHtml = <<<HTML
|
||||
<div class="dashlet-content">
|
||||
<h1>$sHtmlTitle</h1>
|
||||
<div id="block_fake_$sId" class="display_block">
|
||||
<p>$sMessage</p>
|
||||
$sMenu
|
||||
</div>
|
||||
</div>
|
||||
HTML;
|
||||
|
||||
$oDashletContainer->AddHtml($sHtml);
|
||||
|
||||
return $oDashletContainer;
|
||||
}
|
||||
|
||||
public function GetDBSearch($aExtraParams = array())
|
||||
@@ -971,6 +1008,7 @@ class DashletObjectList extends Dashlet
|
||||
|
||||
$oField = new DesignerLongTextField('query', Dict::S('UI:DashletObjectList:Prop-Query'), $this->aProperties['query']);
|
||||
$oField->SetMandatory();
|
||||
$oField->AddCSSClass("ibo-queryoql");
|
||||
$oForm->AddField($oField);
|
||||
|
||||
$oField = new DesignerBooleanField('menu', Dict::S('UI:DashletObjectList:Prop-Menu'), $this->aProperties['menu']);
|
||||
@@ -1007,6 +1045,7 @@ class DashletObjectList extends Dashlet
|
||||
|
||||
$oField = new DesignerHiddenField('query', Dict::S('UI:DashletObjectList:Prop-Query'), $sOQL);
|
||||
$oField->SetMandatory();
|
||||
$oField->AddCSSClass("ibo-queryoql");
|
||||
$oForm->AddField($oField);
|
||||
|
||||
$oField = new DesignerBooleanField('menu', Dict::S('UI:DashletObjectList:Prop-Menu'), $this->aProperties['menu']);
|
||||
@@ -1243,15 +1282,21 @@ abstract class DashletGroupBy extends Dashlet
|
||||
break;
|
||||
}
|
||||
|
||||
$oPanel = PanelUIBlockFactory::MakeForClass($sClass, Dict::S($sTitle));
|
||||
//$oPanel = \Combodo\iTop\Application\UI\Base\Layout\UIContentBlockUIBlockFactory::MakeStandard();
|
||||
//PanelUIBlockFactory::MakeForClass($sClass, Dict::S($sTitle));
|
||||
|
||||
|
||||
$sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occurring in the same DOM)
|
||||
$oBlock = new DisplayBlock($oFilter, $sType);
|
||||
$oBlock->DisplayIntoContentBlock($oPanel, $oPage, $sBlockId, array_merge($aExtraParams, $aParams));
|
||||
//$oBlock->DisplayIntoContentBlock($oPanel, $oPage, $sBlockId, array_merge($aExtraParams, $aParams));
|
||||
$aExtraParams["surround_with_panel"] = true;
|
||||
$aExtraParams["panel_title"] = Dict::S($sTitle);
|
||||
$aExtraParams["panel_class"] = $sClass;
|
||||
$oPanel = $oBlock->GetDisplay($oPage, $sBlockId, array_merge($aExtraParams, $aParams));
|
||||
if ($bEditMode) {
|
||||
$oPanel->AddHtml('<div class="dashlet-blocker"></div>');
|
||||
$oPanel->AddHtml('<div class="ibo-dashlet-blocker dashlet-blocker"></div>');
|
||||
}
|
||||
|
||||
return $oPanel;
|
||||
}
|
||||
|
||||
@@ -1350,10 +1395,10 @@ abstract class DashletGroupBy extends Dashlet
|
||||
|
||||
$oField = new DesignerLongTextField('query', Dict::S('UI:DashletGroupBy:Prop-Query'), $this->aProperties['query']);
|
||||
$oField->SetMandatory();
|
||||
$oField->AddCSSClass("ibo-queryoql");
|
||||
$oForm->AddField($oField);
|
||||
|
||||
try
|
||||
{
|
||||
try {
|
||||
// Group by field: build the list of possible values (attribute codes + ...)
|
||||
$aGroupBy = $this->GetGroupByOptions($this->aProperties['query']);
|
||||
|
||||
@@ -1607,16 +1652,14 @@ abstract class DashletGroupBy extends Dashlet
|
||||
|
||||
$oField = new DesignerHiddenField('query', Dict::S('UI:DashletGroupBy:Prop-Query'), $sOQL);
|
||||
$oField->SetMandatory();
|
||||
$oField->AddCSSClass("ibo-queryoql");
|
||||
$oForm->AddField($oField);
|
||||
|
||||
if (!is_null($sOQL))
|
||||
{
|
||||
if (!is_null($sOQL)) {
|
||||
$oField = new DesignerComboField('group_by', Dict::S('UI:DashletGroupBy:Prop-GroupBy'), null);
|
||||
$aGroupBy = $this->GetGroupByOptions($sOQL);
|
||||
$oField->SetAllowedValues($aGroupBy);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Creating a form for reading parameters!
|
||||
$oField = new DesignerTextField('group_by', Dict::S('UI:DashletGroupBy:Prop-GroupBy'), null);
|
||||
}
|
||||
@@ -1653,6 +1696,28 @@ class DashletGroupByPie extends DashletGroupBy
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetJSFilesRelPaths(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::GetJSFilesRelPaths(),
|
||||
WebResourcesHelper::GetJSFilesRelPathsForC3JS()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetCSSFilesRelPaths(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::GetCSSFilesRelPaths(),
|
||||
WebResourcesHelper::GetCSSFilesRelPathsForC3JS()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
@@ -2085,8 +2150,20 @@ class DashletHeaderDynamic extends Dashlet
|
||||
$sQuery = $this->aProperties['query'];
|
||||
$sGroupBy = $this->aProperties['group_by'];
|
||||
|
||||
$oQuery = $this->oModelReflection->GetQuery($sQuery);
|
||||
$sClass = $oQuery->GetClass();
|
||||
$aValueLabels = [];
|
||||
$aValues = [];
|
||||
try {
|
||||
$oQuery = $this->oModelReflection->GetQuery($sQuery);
|
||||
$sClass = $oQuery->GetClass();
|
||||
$aValues = $this->GetValues();
|
||||
foreach ($aValues as $sValue) {
|
||||
$aValueLabels[] = $this->oModelReflection->GetValueLabel($sClass, $sGroupBy, $sValue);
|
||||
}
|
||||
}
|
||||
catch (UnknownClassOqlException $e) {
|
||||
$aValueLabels[] = $e->GetUserFriendlyDescription();
|
||||
$aValues[] = 1;
|
||||
}
|
||||
|
||||
$oIconSelect = $this->oModelReflection->GetIconSelectionField('icon');
|
||||
$sIconPath = utils::HtmlEntities($oIconSelect->MakeFileUrl($sIcon));
|
||||
@@ -2099,20 +2176,18 @@ class DashletHeaderDynamic extends Dashlet
|
||||
$sBlockId = 'block_fake_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM)
|
||||
|
||||
$iTotal = 0;
|
||||
$aValues = $this->GetValues();
|
||||
|
||||
$sHtml .= '<div class="display_block" id="'.$sBlockId.'">';
|
||||
$sHtml .= '<div class="summary-details">';
|
||||
$sHtml .= '<table><tbody>';
|
||||
$sHtml .= '<tr>';
|
||||
foreach ($aValues as $sValue) {
|
||||
$sValueLabel = $this->oModelReflection->GetValueLabel($sClass, $sGroupBy, $sValue);
|
||||
foreach ($aValueLabels as $sValueLabel) {
|
||||
$sHtml .= ' <th>'.$sValueLabel.'</th>';
|
||||
}
|
||||
$sHtml .= '</tr>';
|
||||
$sHtml .= '<tr>';
|
||||
foreach ($aValues as $sValue) {
|
||||
$iCount = (int)rand(2, 100);
|
||||
$iCount = rand(2, 100);
|
||||
$iTotal += $iCount;
|
||||
$sHtml .= ' <td>'.$iCount.'</td>';
|
||||
}
|
||||
@@ -2127,7 +2202,6 @@ class DashletHeaderDynamic extends Dashlet
|
||||
$sHtml .= '<a class="summary">'.utils::HtmlEntities($sSubtitle).'</a>';
|
||||
$sHtml .= '</div>';
|
||||
|
||||
$sHtml .= '</div>';
|
||||
|
||||
$oDashletContainer->AddHtml($sHtml);
|
||||
|
||||
@@ -2151,6 +2225,7 @@ class DashletHeaderDynamic extends Dashlet
|
||||
|
||||
$oField = new DesignerLongTextField('query', Dict::S('UI:DashletHeaderDynamic:Prop-Query'), $this->aProperties['query']);
|
||||
$oField->SetMandatory();
|
||||
$oField->AddCSSClass("ibo-queryoql");
|
||||
$oForm->AddField($oField);
|
||||
|
||||
try
|
||||
@@ -2309,18 +2384,19 @@ class DashletBadge extends Dashlet
|
||||
$oDashletContainer = new DashletContainer($this->sId, ['dashlet-content']);
|
||||
|
||||
$sClass = $this->aProperties['class'];
|
||||
$sIconUrl = $this->oModelReflection->GetClassIcon($sClass, false);
|
||||
$sIconUrl = utils::HtmlEntities($this->oModelReflection->GetClassIcon($sClass, false));
|
||||
$sClassLabel = $this->oModelReflection->GetName($sClass);
|
||||
$sId = $this->sId;
|
||||
$sClassCreate = Dict::Format('UI:ClickToCreateNew', $sClassLabel);
|
||||
|
||||
$sHtml = '';
|
||||
$sHtml .= '<div id="block_fake_'.$this->sId.'" class="display_block">';
|
||||
$sHtml .= '<p>';
|
||||
$sHtml .= ' <a class="actions"><img src="'.utils::HtmlEntities($sIconUrl).'" style="vertical-align:middle;float;left;margin-right:10px;border:0;">'.$sClassLabel.': 947</a>';
|
||||
$sHtml .= '</p>';
|
||||
$sHtml .= '<p>';
|
||||
$sHtml .= ' <a>'.Dict::Format('UI:ClickToCreateNew', $sClassLabel).'</a>';
|
||||
$sHtml .= '</p>';
|
||||
$sHtml .= '</div>';
|
||||
$sHtml = <<<HTML
|
||||
<div id="block_fake_$sId" class="display_block">
|
||||
<div class="ibo-dashlet-badge--body" data-role="ibo-dashlet-badge--body" title="$sClassLabel">
|
||||
<div class="ibo-dashlet-badge--icon-container"><img class="ibo-dashlet-badge--icon" src="$sIconUrl"></div>
|
||||
<div class="ibo-dashlet-badge--actions"><a class="ibo-dashlet-badge--action-list" href="#" data-role="ibo-dashlet-badge--action-list"><span class="ibo-dashlet-badge--action-list-count">4</span><span class="ibo-dashlet-badge--action-list-label">$sClassLabel</span></a><a class="ibo-dashlet-badge--action-create" href="#"><span class="ibo-dashlet-badge--action-create-icon fas fa-plus"></span><span class="ibo-dashlet-badge--action-create-label"> $sClassCreate </span></a></div>
|
||||
</div>
|
||||
</div>
|
||||
HTML;
|
||||
|
||||
$oDashletContainer->AddHtml($sHtml);
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ class DataTable
|
||||
*/
|
||||
public function __construct($iListId, $oSet, $aClassAliases, $sTableId = null)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use Combodo\iTop\Application\UI\Base\Component\DataTable\Datatable');
|
||||
$this->iListId = utils::GetSafeId($iListId); // Make a "safe" ID for jQuery
|
||||
$this->sDatatableContainerId = 'datatable_'.utils::GetSafeId($iListId);
|
||||
$this->oSet = $oSet;
|
||||
@@ -115,8 +116,7 @@ class DataTable
|
||||
// See if this column is a must to load
|
||||
$sClass = $this->aClassAliases[$sAlias];
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
if ($oAttDef->alwaysLoadInTables())
|
||||
{
|
||||
if ($oAttDef->AlwaysLoadInTables()) {
|
||||
$aColumnsToLoad[$sAlias][] = $sAttCode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,10 @@ use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Dashlet\DashletFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Pill\PillFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenu;
|
||||
use Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenuItem\PopoverMenuItemFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Toolbar\Separator\ToolbarSeparatorUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Toolbar\ToolbarUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\iUIBlock;
|
||||
@@ -168,6 +170,10 @@ class DisplayBlock
|
||||
/** bool add toolkit menu */
|
||||
'selectionMode',
|
||||
/**positive or negative*/
|
||||
'max_height',
|
||||
/** string Max. height of the list, if not specified will occupy all the available height no matter the pagination */
|
||||
'localize_values',
|
||||
/** param for export.php */
|
||||
], DataTableUIBlockFactory::GetAllowedParams()),
|
||||
'list_search' => array_merge([
|
||||
'update_history',
|
||||
@@ -186,6 +192,8 @@ class DisplayBlock
|
||||
/** string */
|
||||
'open',
|
||||
/** bool open by default the search */
|
||||
'submit_on_load',
|
||||
/** bool submit the search on loading page */
|
||||
'class', /** class name */
|
||||
'search_header_force_dropdown', /** Html for <select> to choose the class to search */
|
||||
'this',
|
||||
@@ -195,6 +203,8 @@ class DisplayBlock
|
||||
/** string search root class */
|
||||
'open',
|
||||
/** bool open the search panel by default */
|
||||
'submit_on_load',
|
||||
/** bool submit the search on loading page */
|
||||
'result_list_outer_selector',
|
||||
/** string js selector of the search result display */
|
||||
'search_header_force_dropdown',
|
||||
@@ -254,6 +264,14 @@ class DisplayBlock
|
||||
'withJSRefreshCallBack',
|
||||
/** true if dashboard page */
|
||||
'from_dashboard_page',
|
||||
/** bool true if list may be render in panel block */
|
||||
'surround_with_panel',
|
||||
/** string title of panel block */
|
||||
'panel_title',
|
||||
/** string class for panel block style */
|
||||
'panel_class',
|
||||
/** string class for panel block style */
|
||||
'panel_icon',
|
||||
];
|
||||
|
||||
if (isset($aAllowedParams[$sStyle])) {
|
||||
@@ -994,9 +1012,10 @@ JS
|
||||
$aCountsQueryResults[$aCountGroupBySingleResult[0]] = $aCountGroupBySingleResult[1];
|
||||
}
|
||||
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sStateAttrCode);
|
||||
$aValues = $oAttDef->GetAllowedValues();
|
||||
foreach ($aStates as $sStateValue) {
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sStateAttrCode);
|
||||
$aStateLabels[$sStateValue] = $oAttDef->GetAsPlainText($sStateValue);
|
||||
$aStateLabels[$sStateValue] = $aValues[$sStateValue];
|
||||
$aCounts[$sStateValue] = (array_key_exists($sStateValue, $aCountsQueryResults))
|
||||
? $aCountsQueryResults[$sStateValue]
|
||||
: 0;
|
||||
@@ -1023,10 +1042,11 @@ JS
|
||||
$sHyperlink = $aCount['link'];
|
||||
$sCountLabel = $aCount['label'];
|
||||
$oPill = PillFactory::MakeForState($sClass, $sStateValue)
|
||||
->SetUrl($sHyperlink)
|
||||
->SetTooltip($sStateLabel)
|
||||
->AddHtml("<span class=\"ibo-dashlet-header-dynamic--count\">$sCountLabel</span>")
|
||||
->AddHtml("<span class=\"ibo-dashlet-header-dynamic--label ibo-text-truncated-with-ellipsis\">$sStateLabel</span>");
|
||||
->AddHtml("<span class=\"ibo-dashlet-header-dynamic--count\">$sCountLabel</span><span class=\"ibo-dashlet-header-dynamic--label ibo-text-truncated-with-ellipsis\">$sStateLabel</span>");
|
||||
if ($sHyperlink != '-') {
|
||||
$oPill->SetUrl($sHyperlink);
|
||||
}
|
||||
$oBlock->AddSubBlock($oPill);
|
||||
}
|
||||
$aExtraParams['query_params'] = $this->m_oFilter->GetInternalParams();
|
||||
@@ -1138,7 +1158,7 @@ JS
|
||||
$aValues[$iRow] = $sValue;
|
||||
$sHtmlValue = $oGroupByExp->MakeValueLabel($this->m_oFilter, $sValue, $sValue);
|
||||
$aLabels[$iRow] = $sHtmlValue;
|
||||
$aGroupBy[$iRow] = (int)$aRow[$sFctVar];
|
||||
$aGroupBy[$iRow] = (float)$aRow[$sFctVar];
|
||||
$iTotalCount += $aRow['_itop_count_'];
|
||||
}
|
||||
|
||||
@@ -1170,11 +1190,23 @@ JS
|
||||
),
|
||||
);
|
||||
$sFormat = isset($aExtraParams['format']) ? $aExtraParams['format'] : 'UI:Pagination:HeaderNoSelection';
|
||||
$sTitle = Dict::Format($sFormat, $iTotalCount);
|
||||
|
||||
$aExtraParams['query_params'] = $this->m_oFilter->GetInternalParams();
|
||||
$aOption['dom'] = 'pl';
|
||||
$oBlock = DataTableUIBlockFactory::MakeForStaticData($sTitle, $aAttribs, $aData, null, $aExtraParams, $this->m_oFilter->ToOQL(), $aOption);
|
||||
|
||||
if (isset($aExtraParams["surround_with_panel"]) && $aExtraParams["surround_with_panel"]) {
|
||||
$sTitle = Dict::Format($sFormat, $iTotalCount);
|
||||
$oBlock = PanelUIBlockFactory::MakeForClass($aExtraParams["panel_class"], $aExtraParams["panel_title"]);
|
||||
$oBlock->AddSubTitleBlock(new Html($sTitle));
|
||||
if(isset($aExtraParams["panel_icon"]) && strlen($aExtraParams["panel_icon"]) > 0){
|
||||
$oBlock->SetIcon($aExtraParams["panel_icon"]);
|
||||
}
|
||||
$oDataTable = DataTableUIBlockFactory::MakeForStaticData("", $aAttribs, $aData, null, $aExtraParams, $this->m_oFilter->ToOQL(), $aOption);
|
||||
$oBlock->AddSubBlock($oDataTable);
|
||||
} else {
|
||||
$sTitle = Dict::Format($sFormat, $iTotalCount);
|
||||
$oBlock = DataTableUIBlockFactory::MakeForStaticData($sTitle, $aAttribs, $aData, null, $aExtraParams, $this->m_oFilter->ToOQL(), $aOption);
|
||||
}
|
||||
|
||||
} else {
|
||||
// Simply count the number of elements in the set
|
||||
@@ -1183,7 +1215,15 @@ JS
|
||||
if (isset($aExtraParams['format'])) {
|
||||
$sFormat = $aExtraParams['format'];
|
||||
}
|
||||
$oBlock = new Html('<p>'.Dict::Format($sFormat, $iCount).'</p>');
|
||||
if (isset($aExtraParams["surround_with_panel"]) && $aExtraParams["surround_with_panel"]) {
|
||||
$oBlock = PanelUIBlockFactory::MakeForClass($aExtraParams["panel_class"], $aExtraParams["panel_title"]);
|
||||
if(isset($aExtraParams["panel_icon"]) && strlen($aExtraParams["panel_icon"]) > 0){
|
||||
$oBlock->SetIcon($aExtraParams["panel_icon"]);
|
||||
}
|
||||
$oBlock->AddSubBlock(new Html('<p>'.Dict::Format($sFormat, $iCount).'</p>'));
|
||||
} else {
|
||||
$oBlock = new Html('<p>'.Dict::Format($sFormat, $iCount).'</p>');
|
||||
}
|
||||
}
|
||||
|
||||
return $oBlock;
|
||||
@@ -1270,6 +1310,22 @@ JS
|
||||
$oBlock->bNotAuthorized = true;
|
||||
}
|
||||
} else {
|
||||
if (isset($aExtraParams['update_history']) && true == $aExtraParams['update_history']) {
|
||||
$sSearchFilter = $this->m_oSet->GetFilter()->serialize();
|
||||
// Limit the size of the URL (N°1585 - request uri too long)
|
||||
if (strlen($sSearchFilter) < SERVER_MAX_URL_LENGTH) {
|
||||
$oBlock->sEventAttachedData = json_encode(array(
|
||||
'filter' => $sSearchFilter,
|
||||
'breadcrumb_id' => "ui-search-".$this->m_oSet->GetClass(),
|
||||
'breadcrumb_label' => MetaModel::GetName($this->m_oSet->GetClass()),
|
||||
'breadcrumb_max_count' => utils::GetConfig()->Get('breadcrumb.max_count'),
|
||||
'breadcrumb_instance_id' => MetaModel::GetConfig()->GetItopInstanceid(),
|
||||
'breadcrumb_icon' => 'fas fa-search',
|
||||
'breadcrumb_icon_type' => iTopWebPage::ENUM_BREADCRUMB_ENTRY_ICON_TYPE_CSS_CLASSES,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// The list is made of only 1 class of objects, actions on the list are possible
|
||||
if (($this->m_oSet->CountWithLimit(1) > 0) && (UserRights::IsActionAllowed($this->m_oSet->GetClass(), UR_ACTION_READ, $this->m_oSet) == UR_ALLOWED_YES)) {
|
||||
$oBlock->AddSubBlock(cmdbAbstractObject::GetDisplaySetBlock($oPage, $this->m_oSet, $aExtraParams));
|
||||
@@ -1296,25 +1352,19 @@ JS
|
||||
$oBlock->bCreateNew = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($aExtraParams['update_history']) && true == $aExtraParams['update_history']) {
|
||||
$sSearchFilter = $this->m_oSet->GetFilter()->serialize();
|
||||
// Limit the size of the URL (N°1585 - request uri too long)
|
||||
if (strlen($sSearchFilter) < SERVER_MAX_URL_LENGTH) {
|
||||
$oBlock->sEventAttachedData = json_encode(array(
|
||||
'filter' => $sSearchFilter,
|
||||
'breadcrumb_id' => "ui-search-".$this->m_oSet->GetClass(),
|
||||
'breadcrumb_label' => MetaModel::GetName($this->m_oSet->GetClass()),
|
||||
'breadcrumb_max_count' => utils::GetConfig()->Get('breadcrumb.max_count'),
|
||||
'breadcrumb_instance_id' => MetaModel::GetConfig()->GetItopInstanceid(),
|
||||
'breadcrumb_icon' => 'fas fa-search',
|
||||
'breadcrumb_icon_type' => iTopWebPage::ENUM_BREADCRUMB_ENTRY_ICON_TYPE_CSS_CLASSES,
|
||||
));
|
||||
if (isset($aExtraParams["surround_with_panel"]) && $aExtraParams["surround_with_panel"]) {
|
||||
$oPanel = PanelUIBlockFactory::MakeForClass($aExtraParams["panel_class"], $aExtraParams["panel_title"]);
|
||||
if(isset($aExtraParams["panel_icon"]) && strlen($aExtraParams["panel_icon"]) > 0){
|
||||
$oPanel->SetIcon($aExtraParams["panel_icon"]);
|
||||
}
|
||||
$oPanel->AddSubBlock($oBlock);
|
||||
|
||||
return $oPanel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $oBlock;
|
||||
}
|
||||
@@ -1503,6 +1553,16 @@ JS
|
||||
|
||||
$oBlock->sUrl = $sUrl;
|
||||
|
||||
if (isset($aExtraParams["surround_with_panel"]) && $aExtraParams["surround_with_panel"]) {
|
||||
$oPanel = PanelUIBlockFactory::MakeForClass($aExtraParams["panel_class"], $aExtraParams["panel_title"]);
|
||||
if(isset($aExtraParams["panel_icon"]) && strlen($aExtraParams["panel_icon"]) > 0){
|
||||
$oPanel->SetIcon($aExtraParams["panel_icon"]);
|
||||
}
|
||||
$oPanel->AddSubBlock($oBlock);
|
||||
|
||||
return $oPanel;
|
||||
}
|
||||
|
||||
return $oBlock;
|
||||
}
|
||||
|
||||
@@ -1538,7 +1598,7 @@ JS
|
||||
$aValues[] = array(
|
||||
'label' => html_entity_decode(strip_tags($sHtmlValue), ENT_QUOTES, 'UTF-8'),
|
||||
'label_html' => $sHtmlValue,
|
||||
'value' => (int)$aRow[$sFctVar],
|
||||
'value' => (float)$aRow[$sFctVar],
|
||||
);
|
||||
|
||||
// Build the search for this subset
|
||||
@@ -1573,7 +1633,7 @@ JS
|
||||
$aColumns = array();
|
||||
$aNames = array();
|
||||
foreach ($aValues as $idx => $aValue) {
|
||||
$aColumns[] = array('series_'.$idx, (int)$aValue['value']);
|
||||
$aColumns[] = array('series_'.$idx, (float)$aValue['value']);
|
||||
$aNames['series_'.$idx] = $aValue['label'];
|
||||
}
|
||||
$oBlock = new BlockChartAjaxPie();
|
||||
@@ -1584,6 +1644,16 @@ JS
|
||||
$oBlock->sURLForRefresh = str_replace("'", "\'", $sUrl);
|
||||
break;
|
||||
}
|
||||
if (isset($aExtraParams["surround_with_panel"]) && $aExtraParams["surround_with_panel"]) {
|
||||
$oPanel = PanelUIBlockFactory::MakeForClass($aExtraParams["panel_class"], $aExtraParams["panel_title"]);
|
||||
if(isset($aExtraParams["panel_icon"]) && strlen($aExtraParams["panel_icon"]) > 0){
|
||||
$oPanel->SetIcon($aExtraParams["panel_icon"]);
|
||||
}
|
||||
$oPanel->AddSubBlock($oBlock);
|
||||
|
||||
return $oPanel;
|
||||
}
|
||||
|
||||
return $oBlock;
|
||||
}
|
||||
|
||||
@@ -1646,6 +1716,7 @@ class HistoryBlock extends DisplayBlock
|
||||
|
||||
public function __construct(DBSearch $oFilter, $sStyle = 'list', $bAsynchronous = false, $aParams = array(), $oSet = null)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod();
|
||||
parent::__construct($oFilter, $sStyle, $bAsynchronous, $aParams, $oSet);
|
||||
$this->iLimitStart = 0;
|
||||
$this->iLimitCount = 0;
|
||||
@@ -1771,6 +1842,13 @@ EOF
|
||||
*/
|
||||
class MenuBlock extends DisplayBlock
|
||||
{
|
||||
/**
|
||||
* @var string Prefix to use for the ID of the actions toolbar
|
||||
* @used-by static::GetRenderContent
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public const ACTIONS_TOOLBAR_ID_PREFIX = 'ibo-actions-toolbar-';
|
||||
|
||||
/**
|
||||
* Renders the "Actions" popup menu for the given set of objects
|
||||
*
|
||||
@@ -1812,6 +1890,8 @@ class MenuBlock extends DisplayBlock
|
||||
$aRegularActions = [];
|
||||
/** @var array $aTransitionActions Only transitions */
|
||||
$aTransitionActions = [];
|
||||
/** @var array $aToolkitActions Any "legacy" toolkit menu item, which are now displayed in the same menu as the $aRegularActions, after them */
|
||||
$aToolkitActions = [];
|
||||
if ((!isset($aExtraParams['selection_mode']) || $aExtraParams['selection_mode'] == "") && $this->m_sStyle != 'listInObject') {
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sContext = $oAppContext->GetForLink();
|
||||
@@ -1981,13 +2061,9 @@ class MenuBlock extends DisplayBlock
|
||||
|
||||
$this->AddMenuSeparator($aRegularActions);
|
||||
|
||||
/** @var \iApplicationUIExtension $oExtensionInstance */
|
||||
foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance) {
|
||||
$oSet->Rewind();
|
||||
foreach ($oExtensionInstance->EnumAllowedActions($oSet) as $sLabel => $sUrl) {
|
||||
$aRegularActions[$sLabel] = array('label' => $sLabel, 'url' => $sUrl) + $aActionParams;
|
||||
}
|
||||
}
|
||||
$this->GetEnumAllowedActions($oSet, function ($sLabel, $data) use (&$aRegularActions, $aActionParams) {
|
||||
$aRegularActions[$sLabel] = array('label' => $sLabel, 'url' => $data) + $aActionParams;
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -2089,24 +2165,20 @@ class MenuBlock extends DisplayBlock
|
||||
|
||||
$this->AddMenuSeparator($aRegularActions);
|
||||
|
||||
/** @var \iApplicationUIExtension $oExtensionInstance */
|
||||
foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance) {
|
||||
$oSet->Rewind();
|
||||
foreach ($oExtensionInstance->EnumAllowedActions($oSet) as $sLabel => $data) {
|
||||
if (is_array($data)) {
|
||||
// New plugins can provide javascript handlers via the 'onclick' property
|
||||
//TODO: enable extension of different menus by checking the 'target' property ??
|
||||
$aRegularActions[$sLabel] = [
|
||||
'label' => $sLabel,
|
||||
'url' => isset($data['url']) ? $data['url'] : '#',
|
||||
'onclick' => isset($data['onclick']) ? $data['onclick'] : '',
|
||||
];
|
||||
} else {
|
||||
// Backward compatibility with old plugins
|
||||
$aRegularActions[$sLabel] = ['label' => $sLabel, 'url' => $data] + $aActionParams;
|
||||
}
|
||||
$this->GetEnumAllowedActions($oSet, function ($sLabel, $data) use (&$aRegularActions, $aActionParams) {
|
||||
if (is_array($data)) {
|
||||
// New plugins can provide javascript handlers via the 'onclick' property
|
||||
//TODO: enable extension of different menus by checking the 'target' property ??
|
||||
$aRegularActions[$sLabel] = [
|
||||
'label' => $sLabel,
|
||||
'url' => isset($data['url']) ? $data['url'] : '#',
|
||||
'onclick' => isset($data['onclick']) ? $data['onclick'] : '',
|
||||
];
|
||||
} else {
|
||||
// Backward compatibility with old plugins
|
||||
$aRegularActions[$sLabel] = ['label' => $sLabel, 'url' => $data] + $aActionParams;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (empty($sRefreshAction) && $this->m_sStyle == 'list') {
|
||||
//for the detail page this var is defined way beyond this line
|
||||
@@ -2116,18 +2188,17 @@ class MenuBlock extends DisplayBlock
|
||||
//it's easier just display configure this list and MENU_OBJLIST_TOOLKIT
|
||||
}
|
||||
$param = null;
|
||||
$iMenuId = null;
|
||||
if (is_null($sId)) {
|
||||
$sId = uniqid();
|
||||
}
|
||||
|
||||
// New extensions based on iPopupMenuItem interface
|
||||
$oPopupMenuItemsBlock = new UIContentBlock();
|
||||
switch ($this->m_sStyle) {
|
||||
case 'list':
|
||||
case 'listInObject':
|
||||
$oSet->Rewind();
|
||||
$param = $oSet;
|
||||
$iMenuId = iPopupMenuExtension::MENU_OBJLIST_ACTIONS;
|
||||
$bToolkitMenu = true;
|
||||
if (isset($aExtraParams['toolkit_menu'])) {
|
||||
$bToolkitMenu = (bool)$aExtraParams['toolkit_menu'];
|
||||
@@ -2136,23 +2207,27 @@ class MenuBlock extends DisplayBlock
|
||||
$sLabel = Dict::S('UI:ConfigureThisList');
|
||||
$aRegularActions['iTop::ConfigureList'] = ['label' => $sLabel, 'url' => '#', 'onclick' => "$('#datatable_dlg_datatable_{$sId}').dialog('open'); return false;"];
|
||||
}
|
||||
utils::GetPopupMenuItemsBlock($oPopupMenuItemsBlock, iPopupMenuExtension::MENU_OBJLIST_ACTIONS, $param, $aRegularActions, $sId);
|
||||
utils::GetPopupMenuItemsBlock($oPopupMenuItemsBlock, iPopupMenuExtension::MENU_OBJLIST_TOOLKIT, $param, $aToolkitActions, $sId);
|
||||
break;
|
||||
|
||||
case 'details':
|
||||
$oSet->Rewind();
|
||||
$param = $oSet->Fetch();
|
||||
$iMenuId = iPopupMenuExtension::MENU_OBJDETAILS_ACTIONS;
|
||||
utils::GetPopupMenuItemsBlock($oPopupMenuItemsBlock, iPopupMenuExtension::MENU_OBJDETAILS_ACTIONS, $param, $aRegularActions, $sId);
|
||||
break;
|
||||
|
||||
}
|
||||
$oRenderBlock->AddSubBlock(utils::GetPopupMenuItemsBlock($iMenuId, $param, $aRegularActions, $sId));
|
||||
if ($oPopupMenuItemsBlock->HasSubBlocks()) {
|
||||
$oRenderBlock->AddSubBlock($oPopupMenuItemsBlock);
|
||||
}
|
||||
|
||||
// Extract favorite actions from their menus
|
||||
$aFavoriteRegularActions = [];
|
||||
$aFavoriteTransitionActions = [];
|
||||
$aCallSpec = [$sClass, 'GetShortcutActions'];
|
||||
if (is_callable($aCallSpec)) {
|
||||
$aShortcutActions = call_user_func($aCallSpec, $sClass);
|
||||
if (is_callable([$sClass, 'GetShortcutActions'])) {
|
||||
/** @var cmdbAbstractObject $sClass */
|
||||
$aShortcutActions = $sClass::GetShortcutActions($sClass);
|
||||
foreach ($aShortcutActions as $key) {
|
||||
// Regular actions
|
||||
if (isset($aRegularActions[$key])) {
|
||||
@@ -2168,7 +2243,7 @@ class MenuBlock extends DisplayBlock
|
||||
}
|
||||
}
|
||||
|
||||
$oActionsToolbar = ToolbarUIBlockFactory::MakeForAction("ibo-actions-toolbar-{$sId}");
|
||||
$oActionsToolbar = ToolbarUIBlockFactory::MakeForAction(static::ACTIONS_TOOLBAR_ID_PREFIX.$sId);
|
||||
$oRenderBlock->AddSubBlock($oActionsToolbar);
|
||||
$sRegularActionsMenuTogglerId = "ibo-regular-actions-menu-toggler-{$sId}";
|
||||
$sRegularActionsPopoverMenuId = "ibo-regular-actions-popover-{$sId}";
|
||||
@@ -2261,7 +2336,7 @@ class MenuBlock extends DisplayBlock
|
||||
// - Refresh
|
||||
if ($sRefreshAction != '') {
|
||||
$oActionButton = ButtonUIBlockFactory::MakeAlternativeNeutral('', 'UI:Button:Refresh');
|
||||
$oActionButton->SetIconClass('fas fa-sync')
|
||||
$oActionButton->SetIconClass('fas fa-sync-alt')
|
||||
->SetOnClickJsCode($sRefreshAction)
|
||||
->SetTooltip(Dict::S('UI:Button:Refresh'))
|
||||
->AddCSSClasses(['ibo-action-button', 'ibo-regular-action-button']);
|
||||
@@ -2276,7 +2351,7 @@ class MenuBlock extends DisplayBlock
|
||||
}
|
||||
|
||||
// - Others
|
||||
if (!empty($aRegularActions)) {
|
||||
if (!empty($aRegularActions) || !empty($aToolkitActions)) {
|
||||
if (count($aFavoriteRegularActions) > 0) {
|
||||
$sName = 'UI:Menu:OtherActions';
|
||||
} else {
|
||||
@@ -2291,15 +2366,75 @@ class MenuBlock extends DisplayBlock
|
||||
|
||||
$oActionsToolbar->AddSubBlock($oActionButton)
|
||||
->AddSubBlock($oRegularActionsMenu);
|
||||
|
||||
// Toolkit actions
|
||||
if (!empty($aToolkitActions)) {
|
||||
foreach ($aToolkitActions as $sActionId => $aActionData) {
|
||||
$oRegularActionsMenu->AddItem('toolkit-actions', PopoverMenuItemFactory::MakeFromApplicationPopupMenuItemData($sActionId, $aActionData));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $oRenderBlock;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If an extension doesn't return an array as expected :
|
||||
* - calls IssueLog:Warning
|
||||
* - if is dev env, then throw CoreUnexpectedValue exception
|
||||
*
|
||||
* @param \DBObjectSet $oSet
|
||||
* @param callable $callback EnumAllowedActions returns an array, we will call this anonymous function on each of its value
|
||||
* with two parameters : label (array index), data (array value)
|
||||
*
|
||||
* @throws \CoreUnexpectedValue
|
||||
*
|
||||
* @uses \MetaModel::EnumPlugins()
|
||||
* @uses \iApplicationUIExtension::EnumAllowedActions()
|
||||
* @uses \utils::IsDevelopmentEnvironment()
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
private function GetEnumAllowedActions(DBObjectSet $oSet, callable $callback): void
|
||||
{
|
||||
$aInvalidExtensions = [];
|
||||
|
||||
/** @var \iApplicationUIExtension $oExtensionInstance */
|
||||
foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance) {
|
||||
$oSet->Rewind();
|
||||
$aExtEnumAllowedActions = $oExtensionInstance->EnumAllowedActions($oSet);
|
||||
|
||||
if (!is_array($aExtEnumAllowedActions)) {
|
||||
$aInvalidExtensions[] = get_class($oExtensionInstance);
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($aExtEnumAllowedActions as $sLabel => $data) {
|
||||
$callback($sLabel, $data);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($aInvalidExtensions)) {
|
||||
$sMessage = 'Some extensions returned non array value for EnumAllowedActions() method impl';
|
||||
|
||||
IssueLog::Warning(
|
||||
$sMessage,
|
||||
null,
|
||||
['extensions' => $aInvalidExtensions]
|
||||
);
|
||||
|
||||
if (utils::IsDevelopmentEnvironment()) {
|
||||
throw new CoreUnexpectedValue($sMessage, $aInvalidExtensions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a menu separator to the current list of actions
|
||||
*
|
||||
* @param array $aActions The current actions list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function AddMenuSeparator(&$aActions)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/ErrorPage.php
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/ErrorPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
*/
|
||||
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/ErrorPage.php, now loadable using autoloader');
|
||||
@@ -28,14 +28,14 @@ class CoreCannotSaveObjectException extends CoreException
|
||||
*
|
||||
* @param array $aContextData containing at least those keys : issues, id, class
|
||||
*/
|
||||
public function __construct($aContextData)
|
||||
public function __construct($aContextData, $oPrevious = null)
|
||||
{
|
||||
$this->aIssues = $aContextData['issues'];
|
||||
$this->iObjectId = $aContextData['id'];
|
||||
$this->sObjectClass = $aContextData['class'];
|
||||
|
||||
$sIssues = implode(', ', $this->aIssues);
|
||||
parent::__construct($sIssues, $aContextData);
|
||||
parent::__construct($sIssues, $aContextData, '', $oPrevious);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,5 +30,10 @@ class MySQLException extends CoreException
|
||||
$aContext['mysql_error'] = CMDBSource::GetError();
|
||||
}
|
||||
parent::__construct($sIssue, $aContext);
|
||||
//if is connection error, don't log the default message with password in
|
||||
if (mysqli_connect_errno()) {
|
||||
error_log($this->message);
|
||||
error_reporting(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -102,12 +102,20 @@ class DesignerForm
|
||||
$sReturn .= '<fieldset>';
|
||||
$sReturn .= '<legend>'.$sLabel.'</legend>';
|
||||
}
|
||||
/** @var \DesignerFormField $oField */
|
||||
foreach($aFields as $oField) {
|
||||
$aRow = $oField->Render($oP, $sFormId);
|
||||
if ($oField->IsVisible()) {
|
||||
$sValidation = '<span class="prop_apply ibo-prop--apply">'.$this->GetValidationArea($oField->GetFieldId()).'</span>';
|
||||
$sValidation = '<span class="prop_apply ibo-prop--apply ibo-button ibo-is-alternative">'.$this->GetValidationArea($oField->GetFieldId()).'</span>';
|
||||
$sField = $aRow['value'].$sValidation;
|
||||
$aDetails[] = array('label' => $aRow['label'], 'value' => $sField);
|
||||
$aDetails[] = array(
|
||||
'label' => $aRow['label'],
|
||||
'value' => $sField,
|
||||
'attcode' => $oField->GetCode(),
|
||||
'attlabel' => $aRow['label'],
|
||||
'inputid' => $this->GetFieldId($oField->GetCode()),
|
||||
'inputtype' => $oField->GetInputType(),
|
||||
);
|
||||
} else {
|
||||
$sHiddenFields .= $aRow['value'];
|
||||
}
|
||||
@@ -203,51 +211,41 @@ class DesignerForm
|
||||
$sActionUrl = addslashes($this->sSubmitTo);
|
||||
$sJSSubmitParams = json_encode($this->aSubmitParams);
|
||||
$sFormId = $this->GetFormId();
|
||||
if ($this->oParentForm == null)
|
||||
{
|
||||
if ($this->oParentForm == null) {
|
||||
$sReturn = '<form id="'.$sFormId.'" onsubmit="return false;">';
|
||||
$sReturn .= '<table class="prop_table">';
|
||||
$sReturn .= '<thead><tr><th class="prop_header">'.Dict::S('UI:Form:Property').'</th><th class="prop_header">'.Dict::S('UI:Form:Value').'</th><th colspan="2" class="prop_header"> </th></tr></thead><tbody>';
|
||||
$sReturn .= '<thead><tr><th class="ibo-prop-header">'.Dict::S('UI:Form:Property').'</th><th class="ibo-prop-header">'.Dict::S('UI:Form:Value').'</th><th colspan="2" class="ibo-prop-header"> </th></tr></thead><tbody>';
|
||||
}
|
||||
|
||||
$sHiddenFields = '';
|
||||
foreach($this->aFieldSets as $sLabel => $aFields)
|
||||
{
|
||||
foreach ($this->aFieldSets as $sLabel => $aFields) {
|
||||
$aDetails = array();
|
||||
if ($sLabel != '')
|
||||
{
|
||||
if ($sLabel != '') {
|
||||
$sReturn .= $this->StartRow().'<th colspan="4">'.$sLabel.'</th>'.$this->EndRow();
|
||||
}
|
||||
|
||||
|
||||
foreach($aFields as $oField)
|
||||
{
|
||||
foreach ($aFields as $oField) {
|
||||
$aRow = $oField->Render($oP, $sFormId, 'property');
|
||||
if ($oField->IsVisible())
|
||||
{
|
||||
if ($oField->IsVisible()) {
|
||||
$sFieldId = $this->GetFieldId($oField->GetCode());
|
||||
$sValidation = $this->GetValidationArea($sFieldId, '<span data-tooltip-content="Apply"><i class="fas fa-check"></i></span>');
|
||||
$sValidationFields = '</td><td class="prop_icon prop_apply ibo-prop--apply">'.$sValidation.'</td><td class="prop_icon prop_cancel ibo-prop--cancel"><span data-tooltip-content="Revert"><i class="fas fa-times"></i></span></td>'.$this->EndRow();
|
||||
|
||||
$sPath = $this->GetHierarchyPath().'/'.$oField->GetCode();
|
||||
|
||||
if (is_null($aRow['label']))
|
||||
{
|
||||
$sValidation = $this->GetValidationArea($sFieldId, '<div class="ibo-button ibo-is-alternative ibo-is-success" data-tooltip-content="'.Dict::Format('UI:DashboardEdit:Apply').'"><i class="fas fa-check"></i></div>');
|
||||
$sValidationFields = '</td><td class="prop_icon prop_apply ibo-prop--apply" >'.$sValidation.'</td><td class="prop_icon prop_cancel ibo-prop--cancel"><span><div class="ibo-button ibo-is-alternative ibo-is-neutral" data-tooltip-content="'.Dict::Format('UI:DashboardEdit:Revert').'"><i class="fas fa-undo"></i></div></span></td>'
|
||||
.$this->EndRow();
|
||||
|
||||
if (is_null($aRow['label'])) {
|
||||
$sReturn .= $this->StartRow($sFieldId).'<td class="prop_value ibo-field--value" colspan="2">'.$aRow['value'];
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$sReturn .= $this->StartRow($sFieldId).'<td class="prop_label ibo-field--label">'.$aRow['label'].'</td><td class="prop_value ibo-field--value">'.$aRow['value'];
|
||||
}
|
||||
if (!($oField instanceof DesignerFormSelectorField) && !($oField instanceof DesignerMultipleSubFormField))
|
||||
{
|
||||
if (!($oField instanceof DesignerFormSelectorField) && !($oField instanceof DesignerMultipleSubFormField)) {
|
||||
$sReturn .= $sValidationFields;
|
||||
}
|
||||
$sNotifyParentSelectorJS = is_null($sNotifyParentSelector) ? 'null' : "'".addslashes($sNotifyParentSelector)."'";
|
||||
$sAutoApply = $oField->IsAutoApply() ? 'true' : 'false';
|
||||
$sHandlerEquals = $oField->GetHandlerEquals();
|
||||
$sHandlerGetValue = $oField->GetHandlerGetValue();
|
||||
|
||||
|
||||
$sWidgetClass = $oField->GetWidgetClass();
|
||||
$sJSExtraParams = '';
|
||||
if (count($oField->GetWidgetExtraParams()) > 0)
|
||||
@@ -717,6 +715,19 @@ class DesignerFormField
|
||||
$this->aWidgetExtraParams = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Important, for now we use constants from the \cmdbAbstractObject class, introducing a coupling that should not exist.
|
||||
* This has been traced under N°4241 and will be discussed during the next modernization batch.
|
||||
*
|
||||
* @return string|null Return the input type of the field
|
||||
* @see \cmdbAbstractObject::ENUM_INPUT_TYPE_XXX
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function GetInputType(): ?string
|
||||
{
|
||||
return cmdbAbstractObject::ENUM_INPUT_TYPE_SINGLE_INPUT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
@@ -1057,6 +1068,14 @@ class DesignerLongTextField extends DesignerTextField
|
||||
$this->aCSSClasses[] = 'ibo-input-text';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetInputType(): string
|
||||
{
|
||||
return cmdbAbstractObject::ENUM_INPUT_TYPE_TEXTAREA;
|
||||
}
|
||||
|
||||
public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
|
||||
{
|
||||
$sId = $this->oForm->GetFieldId($this->sCode);
|
||||
@@ -1185,9 +1204,27 @@ class DesignerComboField extends DesignerFormField
|
||||
$this->bAutoApply = true;
|
||||
$this->bSorted = true; // Sorted by default
|
||||
}
|
||||
|
||||
public function SetAllowedValues($aAllowedValues)
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetInputType(): ?string
|
||||
{
|
||||
if ($this->bMultipleSelection) {
|
||||
return cmdbAbstractObject::ENUM_INPUT_TYPE_DROPDOWN_MULTIPLE_CHOICES;
|
||||
}
|
||||
else {
|
||||
return cmdbAbstractObject::ENUM_INPUT_TYPE_DROPDOWN_RAW;
|
||||
}
|
||||
}
|
||||
|
||||
public function SetAllowedValues(?array $aAllowedValues)
|
||||
{
|
||||
// Make sure to have an actual array for values
|
||||
if (is_null($aAllowedValues)) {
|
||||
$aAllowedValues = [];
|
||||
}
|
||||
|
||||
$this->aAllowedValues = $aAllowedValues;
|
||||
}
|
||||
|
||||
@@ -1264,11 +1301,11 @@ class DesignerComboField extends DesignerFormField
|
||||
{
|
||||
if ($this->bMultipleSelection)
|
||||
{
|
||||
$sHtml = "<select $sCSSClasses multiple size=\"8\"id=\"$sId\" name=\"$sName\">";
|
||||
$sHtml = "<span><select $sCSSClasses multiple size=\"8\"id=\"$sId\" name=\"$sName\">";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sHtml = "<select $sCSSClasses id=\"$sId\" name=\"$sName\">";
|
||||
$sHtml = "<span class=\"ibo-input-select-wrapper\"><select $sCSSClasses id=\"$sId\" name=\"$sName\">";
|
||||
if ($this->sNullLabel != '')
|
||||
{
|
||||
$sHtml .= "<option value=\"\">".$this->sNullLabel."</option>";
|
||||
@@ -1288,7 +1325,7 @@ class DesignerComboField extends DesignerFormField
|
||||
$sHtmlValue = str_replace(' ', ' ', htmlentities($sDisplayValue, ENT_QUOTES, 'UTF-8'));
|
||||
$sHtml .= "<option value=\"".htmlentities($sKey, ENT_QUOTES, 'UTF-8')."\" $sSelected>$sHtmlValue</option>";
|
||||
}
|
||||
$sHtml .= "</select>";
|
||||
$sHtml .= "</select></span>";
|
||||
if ($this->bOtherChoices)
|
||||
{
|
||||
$sHtml .= '<br/><input type="checkbox" id="other_chk_'.$sId.'"><label for="other_chk_'.$sId.'"> Other:</label> <input type="text" id="other_'.$sId.'" name="other_'.$sName.'" size="30"/>';
|
||||
@@ -1321,6 +1358,14 @@ class DesignerBooleanField extends DesignerFormField
|
||||
$this->bAutoApply = true;
|
||||
$this->aCSSClasses[] = 'ibo-input-checkbox';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetInputType(): ?string
|
||||
{
|
||||
return cmdbAbstractObject::ENUM_INPUT_TYPE_CHECKBOX;
|
||||
}
|
||||
|
||||
public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
|
||||
{
|
||||
@@ -1380,6 +1425,14 @@ class DesignerHiddenField extends DesignerFormField
|
||||
{
|
||||
parent::__construct($sCode, $sLabel, $defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetInputType(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function IsVisible()
|
||||
{
|
||||
@@ -1407,6 +1460,14 @@ class DesignerIconSelectionField extends DesignerFormField
|
||||
$this->bAutoApply = true;
|
||||
$this->sUploadUrl = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetInputType(): ?string
|
||||
{
|
||||
return cmdbAbstractObject::ENUM_INPUT_TYPE_DROPDOWN_DECORATED;
|
||||
}
|
||||
|
||||
public function SetAllowedValues($aAllowedValues)
|
||||
{
|
||||
@@ -1423,28 +1484,32 @@ class DesignerIconSelectionField extends DesignerFormField
|
||||
$sId = $this->oForm->GetFieldId($this->sCode);
|
||||
$sName = $this->oForm->GetFieldName($this->sCode);
|
||||
$idx = 0;
|
||||
foreach($this->aAllowedValues as $index => $aValue)
|
||||
{
|
||||
if ($aValue['value'] == $this->defaultValue)
|
||||
{
|
||||
$idxFallback = 0;
|
||||
foreach ($this->aAllowedValues as $index => $aValue) {
|
||||
if ($aValue['value'] == $this->defaultValue) {
|
||||
$idx = $index;
|
||||
break;
|
||||
}
|
||||
//fallback if url of default value contains ../
|
||||
//for contact, icon is http://localhost/env-production/itop-structure/../../images/icons/icons8-customer.svg => not found http://localhost/images/icons/icons8-customer.svg
|
||||
if (basename($aValue['value']) == basename($this->defaultValue)) {
|
||||
$idxFallback = $index;
|
||||
}
|
||||
}
|
||||
if ($idx == 0) {
|
||||
$idx = $idxFallback;
|
||||
}
|
||||
$sJSItems = json_encode($this->aAllowedValues);
|
||||
$sPostUploadTo = ($this->sUploadUrl == null) ? 'null' : "'{$this->sUploadUrl}'";
|
||||
if (!$this->IsReadOnly())
|
||||
{
|
||||
if (!$this->IsReadOnly()) {
|
||||
$sDefaultValue = ($this->defaultValue !== '') ? $this->defaultValue : $this->aAllowedValues[$idx]['value'];
|
||||
$sValue = "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"{$sDefaultValue}\"/>";
|
||||
$sValue = "<span class=\"ibo-input-select-wrapper\"><input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"{$sDefaultValue}\"/></span>";
|
||||
$oP->add_ready_script(
|
||||
<<<EOF
|
||||
<<<EOF
|
||||
$('#$sId').icon_select({current_idx: $idx, items: $sJSItems, post_upload_to: $sPostUploadTo});
|
||||
EOF
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$sValue = '<span style="display:inline-block;line-height:48px;height:48px;"><span><img style="vertical-align:middle" src="'.$this->aAllowedValues[$idx]['icon'].'" /> '.htmlentities($this->aAllowedValues[$idx]['label'], ENT_QUOTES, 'UTF-8').'</span></span>';
|
||||
}
|
||||
$sReadOnly = $this->IsReadOnly() ? 'disabled' : '';
|
||||
@@ -1459,18 +1524,21 @@ class RunTimeIconSelectionField extends DesignerIconSelectionField
|
||||
public function __construct($sCode, $sLabel = '', $defaultValue = '')
|
||||
{
|
||||
parent::__construct($sCode, $sLabel, $defaultValue);
|
||||
$aFolderList = [
|
||||
APPROOT.'env-'.utils::GetCurrentEnvironment() => utils::GetAbsoluteUrlModulesRoot(),
|
||||
APPROOT.'images/icons' => utils::GetAbsoluteUrlAppRoot().'images/icons',
|
||||
];
|
||||
if (count(self::$aAllIcons) == 0) {
|
||||
foreach ($aFolderList as $sFolderPath => $sUrlPrefix) {
|
||||
$aIcons = self::FindIconsOnDisk($sFolderPath);
|
||||
ksort($aIcons);
|
||||
|
||||
if (count(self::$aAllIcons) == 0)
|
||||
{
|
||||
self::$aAllIcons = self::FindIconsOnDisk(APPROOT.'env-'.utils::GetCurrentEnvironment());
|
||||
ksort(self::$aAllIcons);
|
||||
foreach ($aIcons as $sFilePath) {
|
||||
self::$aAllIcons[] = array('value' => $sFilePath, 'label' => basename($sFilePath), 'icon' => $sUrlPrefix.$sFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
$aValues = array();
|
||||
foreach(self::$aAllIcons as $sFilePath)
|
||||
{
|
||||
$aValues[] = array('value' => $sFilePath, 'label' => basename($sFilePath), 'icon' => utils::GetAbsoluteUrlModulesRoot().$sFilePath);
|
||||
}
|
||||
$this->SetAllowedValues($aValues);
|
||||
$this->SetAllowedValues(self::$aAllIcons);
|
||||
}
|
||||
|
||||
static protected function FindIconsOnDisk($sBaseDir, $sDir = '')
|
||||
@@ -1501,26 +1569,29 @@ class RunTimeIconSelectionField extends DesignerIconSelectionField
|
||||
SetupUtils::builddir(dirname($sCacheFile));
|
||||
file_put_contents($sCacheFile, $sAvailableIcons, LOCK_EX);
|
||||
}
|
||||
|
||||
return $aFiles;
|
||||
}
|
||||
|
||||
static protected function _FindIconsOnDisk($sBaseDir, $sDir = '')
|
||||
static protected function _FindIconsOnDisk($sBaseDir, $sDir = '', &$aFilesSpecs = [])
|
||||
{
|
||||
$aResult = array();
|
||||
$aResult = [];
|
||||
// Populate automatically the list of icon files
|
||||
if ($hDir = @opendir($sBaseDir.'/'.$sDir))
|
||||
{
|
||||
while (($sFile = readdir($hDir)) !== false)
|
||||
{
|
||||
if ($hDir = @opendir($sBaseDir.'/'.$sDir)) {
|
||||
while (($sFile = readdir($hDir)) !== false) {
|
||||
$aMatches = array();
|
||||
if (($sFile != '.') && ($sFile != '..') && ($sFile != 'lifecycle') && is_dir($sBaseDir.'/'.$sDir.'/'.$sFile))
|
||||
{
|
||||
if (($sFile != '.') && ($sFile != '..') && ($sFile != 'lifecycle') && is_dir($sBaseDir.'/'.$sDir.'/'.$sFile)) {
|
||||
$sDirSubPath = ($sDir == '') ? $sFile : $sDir.'/'.$sFile;
|
||||
$aResult = array_merge($aResult, self::_FindIconsOnDisk($sBaseDir, $sDirSubPath));
|
||||
$aResult = array_merge($aResult, self::_FindIconsOnDisk($sBaseDir, $sDirSubPath, $aFilesSpecs));
|
||||
}
|
||||
if (preg_match("/\.(png|jpg|jpeg|gif)$/i", $sFile, $aMatches)) // png, jp(e)g and gif are considered valid
|
||||
$sSize = filesize($sBaseDir.'/'.$sDir.'/'.$sFile);
|
||||
if (isset($aFilesSpecs[$sFile]) && $aFilesSpecs[$sFile] == $sSize) {
|
||||
continue;
|
||||
}
|
||||
if (preg_match("/\.(png|jpg|jpeg|gif|svg)$/i", $sFile, $aMatches)) // png, jp(e)g, gif and svg are considered valid
|
||||
{
|
||||
$aResult[$sFile.'_'.$sDir] = $sDir.'/'.$sFile;
|
||||
$aFilesSpecs[$sFile] = $sSize;
|
||||
}
|
||||
}
|
||||
closedir($hDir);
|
||||
@@ -1566,6 +1637,14 @@ class DesignerSortableField extends DesignerFormField
|
||||
parent::__construct($sCode, $sLabel, $defaultValue);
|
||||
$this->aAllowedValues = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetInputType(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function SetAllowedValues($aAllowedValues)
|
||||
{
|
||||
@@ -1605,6 +1684,14 @@ class DesignerFormSelectorField extends DesignerFormField
|
||||
$this->aCSSClasses[] = 'ibo-input-select';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetInputType(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function IsSorted()
|
||||
{
|
||||
return $this->bSorted;
|
||||
@@ -1645,27 +1732,23 @@ class DesignerFormSelectorField extends DesignerFormField
|
||||
{
|
||||
$sId = $this->oForm->GetFieldId($this->sCode);
|
||||
$sName = $this->oForm->GetFieldName($this->sCode);
|
||||
$sReadOnly = $this->IsReadOnly() ? 'disabled="disabled"' : '';
|
||||
$sReadOnly = $this->IsReadOnly() ? 'disabled="disabled"' : '';
|
||||
|
||||
$this->aCSSClasses[] = 'formSelector';
|
||||
|
||||
|
||||
$sCSSClasses = '';
|
||||
if (count($this->aCSSClasses) > 0)
|
||||
{
|
||||
if (count($this->aCSSClasses) > 0) {
|
||||
$sCSSClasses = 'class="'.implode(' ', $this->aCSSClasses).'"';
|
||||
}
|
||||
|
||||
if ($this->IsSorted())
|
||||
{
|
||||
if ($this->IsSorted()) {
|
||||
uasort($this->aSubForms, array(get_class($this), 'SortOnFormLabel'));
|
||||
}
|
||||
|
||||
if ($this->IsReadOnly())
|
||||
{
|
||||
|
||||
if ($this->IsReadOnly()) {
|
||||
$sDisplayValue = '';
|
||||
$sHiddenValue = '';
|
||||
foreach($this->aSubForms as $iKey => $aFormData)
|
||||
{
|
||||
foreach ($this->aSubForms as $iKey => $aFormData) {
|
||||
if ($iKey == $this->defaultValue) // Default value is actually the index
|
||||
{
|
||||
$sDisplayValue = htmlentities($aFormData['label'], ENT_QUOTES, 'UTF-8');
|
||||
@@ -1674,35 +1757,29 @@ class DesignerFormSelectorField extends DesignerFormField
|
||||
}
|
||||
}
|
||||
$sHtml = "<span $sCSSClasses>".$sDisplayValue.$sHiddenValue."</span>";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sHtml = "<select $sCSSClasses id=\"$sId\" name=\"$sName\" $sReadOnly>";
|
||||
foreach($this->aSubForms as $iKey => $aFormData)
|
||||
{
|
||||
} else {
|
||||
$sHtml = "<span class=\"ibo-input-select-wrapper\"><select $sCSSClasses id=\"$sId\" name=\"$sName\" $sReadOnly>";
|
||||
foreach ($this->aSubForms as $iKey => $aFormData) {
|
||||
$sDisplayValue = htmlentities($aFormData['label'], ENT_QUOTES, 'UTF-8');
|
||||
$sValue = htmlentities($aFormData['value'], ENT_QUOTES, 'UTF-8');
|
||||
$sSelected = ($iKey == $this->defaultValue) ? 'selected' : '';
|
||||
$sHtml .= "<option data-value=\"$sValue\" value=\"$iKey\" $sSelected>".$sDisplayValue."</option>";
|
||||
}
|
||||
$sHtml .= "</select>";
|
||||
$sHtml .= "</select></span>";
|
||||
}
|
||||
|
||||
if ($sRenderMode == 'property')
|
||||
{
|
||||
$sHtml .= '</td><td class="prop_icon prop_apply"><span title="Apply" class="ui-icon ui-icon-circle-check"/></td><td class="prop_icon prop_cancel"><span title="Revert" class="ui-icon ui-icon-circle-close"/></td></tr>';
|
||||
|
||||
if ($sRenderMode == 'property') {
|
||||
$sHtml .= '</td><td class="prop_icon prop_apply ibo-prop--apply"><span><button class="ibo-button ibo-is-alternative ibo-is-success" data-tooltip-content="'.Dict::Format('UI:DashboardEdit:Apply').'"><i class="fas fa-check"></i></button></span></td><td class="prop_icon prop_cancel ibo-prop--cancel"><span><button class="ibo-button ibo-is-alternative ibo-is-neutral" data-tooltip-content="'.Dict::Format('UI:DashboardEdit:Revert').'"><i class="fas fa-times"></i></button></span></td></tr>';
|
||||
}
|
||||
foreach($this->aSubForms as $sKey => $aFormData)
|
||||
{
|
||||
foreach ($this->aSubForms as $sKey => $aFormData) {
|
||||
$sId = $this->oForm->GetFieldId($this->sCode);
|
||||
$sStyle = (($sKey == $this->defaultValue) && $this->oForm->IsDisplayed()) ? '' : 'style="display:none"';
|
||||
$oSubForm = $aFormData['form'];
|
||||
$oSubForm->SetParentForm($this->oForm);
|
||||
$oSubForm->CopySubmitParams($this->oForm);
|
||||
$oSubForm->SetPrefix($this->oForm->GetPrefix().$sKey.'_');
|
||||
|
||||
if ($sRenderMode == 'property')
|
||||
{
|
||||
|
||||
if ($sRenderMode == 'property') {
|
||||
// Note: Managing the visibility of nested subforms had several implications
|
||||
// 1) Attributes are displayed in a table and we have to group them in as many tbodys as necessary to hide/show the various options depending on the current selection
|
||||
// 2) It is not possible to nest tbody tags. Therefore, it is not possible to manage the visibility the same way as it is done for the dialog mode (using nested divs).
|
||||
@@ -1800,6 +1877,14 @@ class DesignerSubFormField extends DesignerFormField
|
||||
parent::__construct('', $sLabel, '');
|
||||
$this->oSubForm = $oSubForm;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetInputType(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
|
||||
{
|
||||
@@ -1844,6 +1929,14 @@ class DesignerStaticTextField extends DesignerFormField
|
||||
parent::__construct($sCode, $sLabel, $defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetInputType(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
|
||||
{
|
||||
return array('label' => $this->sLabel, 'value' => $this->defaultValue);
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/iTopWebPage.php
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/iTopWebPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
*/
|
||||
|
||||
|
||||
// cannot notify depreciation for now as this is still MASSIVELY used in iTop core !
|
||||
//DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/iTopWebPage.php, now loadable using autoloader');
|
||||
@@ -1,7 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/iTopWizardWebPage.php
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/iTopWizardWebPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
*/
|
||||
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/iTopWizardWebPage.php, now loadable using autoloader');
|
||||
@@ -1,4 +1,7 @@
|
||||
<?php
|
||||
|
||||
use Combodo\iTop\Application\Helper\Session;
|
||||
|
||||
/**
|
||||
* Class LoginBasic
|
||||
*
|
||||
@@ -20,19 +23,19 @@ class LoginBasic extends AbstractLoginFSMExtension
|
||||
|
||||
protected function OnModeDetection(&$iErrorCode)
|
||||
{
|
||||
if (!isset($_SESSION['login_mode']))
|
||||
if (!Session::IsSet('login_mode'))
|
||||
{
|
||||
if (isset($_SERVER['HTTP_AUTHORIZATION']) && !empty($_SERVER['HTTP_AUTHORIZATION']))
|
||||
{
|
||||
$_SESSION['login_mode'] = 'basic';
|
||||
Session::Set('login_mode', 'basic');
|
||||
}
|
||||
elseif (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION']) && !empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION']))
|
||||
{
|
||||
$_SESSION['login_mode'] = 'basic';
|
||||
Session::Set('login_mode', 'basic');
|
||||
}
|
||||
elseif (isset($_SERVER['PHP_AUTH_USER']))
|
||||
{
|
||||
$_SESSION['login_mode'] = 'basic';
|
||||
Session::Set('login_mode', 'basic');
|
||||
}
|
||||
}
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
@@ -40,10 +43,10 @@ class LoginBasic extends AbstractLoginFSMExtension
|
||||
|
||||
protected function OnReadCredentials(&$iErrorCode)
|
||||
{
|
||||
if (!isset($_SESSION['login_mode']) || $_SESSION['login_mode'] == 'basic')
|
||||
if (!Session::IsSet('login_mode') || Session::Get('login_mode') == 'basic')
|
||||
{
|
||||
list($sAuthUser, $sAuthPwd) = $this->GetAuthUserAndPassword();
|
||||
$_SESSION['login_temp_auth_user'] = $sAuthUser;
|
||||
list($sAuthUser) = $this->GetAuthUserAndPassword();
|
||||
Session::Set('login_temp_auth_user', $sAuthUser);
|
||||
}
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
}
|
||||
@@ -51,10 +54,10 @@ class LoginBasic extends AbstractLoginFSMExtension
|
||||
|
||||
protected function OnCheckCredentials(&$iErrorCode)
|
||||
{
|
||||
if ($_SESSION['login_mode'] == 'basic')
|
||||
if (Session::Get('login_mode') == 'basic')
|
||||
{
|
||||
list($sAuthUser, $sAuthPwd) = $this->GetAuthUserAndPassword();
|
||||
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, $_SESSION['login_mode'], 'internal'))
|
||||
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, Session::Get('login_mode'), 'internal'))
|
||||
{
|
||||
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
|
||||
return LoginWebPage::LOGIN_FSM_ERROR;
|
||||
@@ -65,17 +68,17 @@ class LoginBasic extends AbstractLoginFSMExtension
|
||||
|
||||
protected function OnCredentialsOK(&$iErrorCode)
|
||||
{
|
||||
if ($_SESSION['login_mode'] == 'basic')
|
||||
if (Session::Get('login_mode') == 'basic')
|
||||
{
|
||||
list($sAuthUser) = $this->GetAuthUserAndPassword();
|
||||
LoginWebPage::OnLoginSuccess($sAuthUser, 'internal', $_SESSION['login_mode']);
|
||||
LoginWebPage::OnLoginSuccess($sAuthUser, 'internal', Session::Get('login_mode'));
|
||||
}
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
}
|
||||
|
||||
protected function OnError(&$iErrorCode)
|
||||
{
|
||||
if ($_SESSION['login_mode'] == 'basic')
|
||||
if (Session::Get('login_mode') == 'basic')
|
||||
{
|
||||
LoginWebPage::HTTP401Error();
|
||||
}
|
||||
@@ -84,9 +87,9 @@ class LoginBasic extends AbstractLoginFSMExtension
|
||||
|
||||
protected function OnConnected(&$iErrorCode)
|
||||
{
|
||||
if ($_SESSION['login_mode'] == 'basic')
|
||||
if (Session::Get('login_mode') == 'basic')
|
||||
{
|
||||
$_SESSION['can_logoff'] = true;
|
||||
Session::Set('can_logoff', true);
|
||||
return LoginWebPage::CheckLoggedUser($iErrorCode);
|
||||
}
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Application\Helper\Session;
|
||||
|
||||
/**
|
||||
* Class LoginDefaultBefore
|
||||
*/
|
||||
@@ -23,7 +25,7 @@ class LoginDefaultBefore extends AbstractLoginFSMExtension
|
||||
{
|
||||
$iErrorCode = LoginWebPage::EXIT_CODE_OK;
|
||||
|
||||
unset($_SESSION['login_temp_auth_user']);
|
||||
Session::Unset('login_temp_auth_user');
|
||||
|
||||
// Check if proposed login mode is present and allowed
|
||||
$aAllowedLoginTypes = MetaModel::GetConfig()->GetAllowedLoginTypes();
|
||||
@@ -32,11 +34,11 @@ class LoginDefaultBefore extends AbstractLoginFSMExtension
|
||||
if ($index !== false)
|
||||
{
|
||||
// Force login mode
|
||||
$_SESSION['login_mode'] = $sProposedLoginMode;
|
||||
Session::Set('login_mode', $sProposedLoginMode);
|
||||
}
|
||||
else
|
||||
{
|
||||
unset($_SESSION['login_mode']);
|
||||
Session::Unset('login_mode');
|
||||
}
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
}
|
||||
@@ -91,7 +93,7 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
|
||||
|
||||
protected function OnCredentialsOk(&$iErrorCode)
|
||||
{
|
||||
if (!isset($_SESSION['login_mode']))
|
||||
if (!Session::IsSet('login_mode'))
|
||||
{
|
||||
// If no plugin validated the user, exit
|
||||
self::ResetLoginSession();
|
||||
@@ -110,7 +112,7 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
|
||||
|
||||
protected function OnConnected(&$iErrorCode)
|
||||
{
|
||||
unset($_SESSION['login_temp_auth_user']);
|
||||
Session::Unset('login_temp_auth_user');
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
}
|
||||
|
||||
@@ -118,11 +120,11 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
|
||||
private static function ResetLoginSession()
|
||||
{
|
||||
LoginWebPage::ResetSession();
|
||||
foreach (array_keys($_SESSION) as $sKey)
|
||||
foreach (Session::ListVariables() as $sKey)
|
||||
{
|
||||
if (utils::StartsWith($sKey, 'login_'))
|
||||
{
|
||||
unset($_SESSION[$sKey]);
|
||||
Session::Unset($sKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
use Combodo\iTop\Application\Helper\Session;
|
||||
|
||||
/**
|
||||
* Class LoginExternal
|
||||
*
|
||||
@@ -22,12 +24,12 @@ class LoginExternal extends AbstractLoginFSMExtension
|
||||
|
||||
protected function OnModeDetection(&$iErrorCode)
|
||||
{
|
||||
if (!isset($_SESSION['login_mode']))
|
||||
if (!Session::IsSet('login_mode'))
|
||||
{
|
||||
$sAuthUser = $this->GetAuthUser();
|
||||
if ($sAuthUser && (strlen($sAuthUser) > 0))
|
||||
{
|
||||
$_SESSION['login_mode'] = 'external';
|
||||
Session::Set('login_mode', 'external');
|
||||
}
|
||||
}
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
@@ -35,10 +37,10 @@ class LoginExternal extends AbstractLoginFSMExtension
|
||||
|
||||
protected function OnCheckCredentials(&$iErrorCode)
|
||||
{
|
||||
if ($_SESSION['login_mode'] == 'external')
|
||||
if (Session::Get('login_mode') == 'external')
|
||||
{
|
||||
$sAuthUser = $this->GetAuthUser();
|
||||
if (!UserRights::CheckCredentials($sAuthUser, '', $_SESSION['login_mode'], 'external'))
|
||||
if (!UserRights::CheckCredentials($sAuthUser, '', Session::Get('login_mode'), 'external'))
|
||||
{
|
||||
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
|
||||
return LoginWebPage::LOGIN_FSM_ERROR;
|
||||
@@ -49,19 +51,19 @@ class LoginExternal extends AbstractLoginFSMExtension
|
||||
|
||||
protected function OnCredentialsOK(&$iErrorCode)
|
||||
{
|
||||
if ($_SESSION['login_mode'] == 'external')
|
||||
if (Session::Get('login_mode') == 'external')
|
||||
{
|
||||
$sAuthUser = $this->GetAuthUser();
|
||||
LoginWebPage::OnLoginSuccess($sAuthUser, 'external', $_SESSION['login_mode']);
|
||||
LoginWebPage::OnLoginSuccess($sAuthUser, 'external', Session::Get('login_mode'));
|
||||
}
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
}
|
||||
|
||||
protected function OnConnected(&$iErrorCode)
|
||||
{
|
||||
if ($_SESSION['login_mode'] == 'external')
|
||||
if (Session::Get('login_mode') == 'external')
|
||||
{
|
||||
$_SESSION['can_logoff'] = false;
|
||||
Session::Set('can_logoff', false);
|
||||
return LoginWebPage::CheckLoggedUser($iErrorCode);
|
||||
}
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
@@ -69,7 +71,7 @@ class LoginExternal extends AbstractLoginFSMExtension
|
||||
|
||||
protected function OnError(&$iErrorCode)
|
||||
{
|
||||
if ($_SESSION['login_mode'] == 'external')
|
||||
if (Session::Get('login_mode') == 'external')
|
||||
{
|
||||
LoginWebPage::HTTP401Error();
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Application\Helper\Session;
|
||||
|
||||
/**
|
||||
* Class LoginForm
|
||||
*
|
||||
@@ -29,8 +31,7 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
|
||||
*/
|
||||
protected function OnReadCredentials(&$iErrorCode)
|
||||
{
|
||||
if (!isset($_SESSION['login_mode']) || ($_SESSION['login_mode'] == 'form'))
|
||||
{
|
||||
if (!Session::IsSet('login_mode') || Session::Get('login_mode') == 'form') {
|
||||
$sAuthUser = utils::ReadPostedParam('auth_user', '', 'raw_data');
|
||||
$sAuthPwd = utils::ReadPostedParam('auth_pwd', null, 'raw_data');
|
||||
if ($this->bForceFormOnError || empty($sAuthUser) || empty($sAuthPwd))
|
||||
@@ -50,9 +51,8 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
|
||||
$this->bForceFormOnError = false;
|
||||
exit;
|
||||
}
|
||||
|
||||
$_SESSION['login_temp_auth_user'] = $sAuthUser;
|
||||
$_SESSION['login_mode'] = 'form';
|
||||
Session::Set('login_temp_auth_user', $sAuthUser);
|
||||
Session::Set('login_mode', 'form');
|
||||
}
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
}
|
||||
@@ -62,11 +62,11 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
|
||||
*/
|
||||
protected function OnCheckCredentials(&$iErrorCode)
|
||||
{
|
||||
if ($_SESSION['login_mode'] == 'form')
|
||||
if (Session::Get('login_mode') == 'form')
|
||||
{
|
||||
$sAuthUser = utils::ReadPostedParam('auth_user', '', 'raw_data');
|
||||
$sAuthPwd = utils::ReadPostedParam('auth_pwd', null, 'raw_data');
|
||||
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, $_SESSION['login_mode'], 'internal'))
|
||||
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, Session::Get('login_mode'), 'internal'))
|
||||
{
|
||||
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
|
||||
return LoginWebPage::LOGIN_FSM_ERROR;
|
||||
@@ -80,19 +80,19 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
|
||||
*/
|
||||
protected function OnCredentialsOK(&$iErrorCode)
|
||||
{
|
||||
if ($_SESSION['login_mode'] == 'form')
|
||||
if (Session::Get('login_mode') == 'form')
|
||||
{
|
||||
if (isset($_SESSION['auth_user']))
|
||||
if (Session::IsSet('auth_user'))
|
||||
{
|
||||
// If FSM reenter this state (example 2FA) then the auth_user is not resubmitted
|
||||
$sAuthUser = $_SESSION['auth_user'];
|
||||
$sAuthUser = Session::Get('auth_user');
|
||||
}
|
||||
else
|
||||
{
|
||||
$sAuthUser = utils::ReadPostedParam('auth_user', '', 'raw_data');
|
||||
}
|
||||
// Store 'auth_user' in session for further use
|
||||
LoginWebPage::OnLoginSuccess($sAuthUser, 'internal', $_SESSION['login_mode']);
|
||||
LoginWebPage::OnLoginSuccess($sAuthUser, 'internal', Session::Get('login_mode'));
|
||||
}
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
}
|
||||
@@ -102,7 +102,7 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
|
||||
*/
|
||||
protected function OnError(&$iErrorCode)
|
||||
{
|
||||
if ($_SESSION['login_mode'] == 'form')
|
||||
if (Session::Get('login_mode') == 'form')
|
||||
{
|
||||
$this->bForceFormOnError = true;
|
||||
}
|
||||
@@ -114,9 +114,9 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
|
||||
*/
|
||||
protected function OnConnected(&$iErrorCode)
|
||||
{
|
||||
if ($_SESSION['login_mode'] == 'form')
|
||||
if (Session::Get('login_mode') == 'form')
|
||||
{
|
||||
$_SESSION['can_logoff'] = true;
|
||||
Session::Set('can_logoff', true);
|
||||
return LoginWebPage::CheckLoggedUser($iErrorCode);
|
||||
}
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
|
||||
use Combodo\iTop\Application\Branding;
|
||||
use Combodo\iTop\TwigExtension;
|
||||
|
||||
/**
|
||||
@@ -238,16 +239,9 @@ class LoginTwigRenderer
|
||||
|
||||
public function GetDefaultVars()
|
||||
{
|
||||
$sLogo = 'itop-logo-external.png';
|
||||
$sBrandingLogo = 'login-logo.png';
|
||||
|
||||
$sVersionShort = Dict::Format('UI:iTopVersion:Short', ITOP_APPLICATION, ITOP_VERSION);
|
||||
$sIconUrl = Utils::GetConfig()->Get('app_icon_url');
|
||||
$sDisplayIcon = utils::GetAbsoluteUrlAppRoot().'images/'.$sLogo.'?t='.utils::GetCacheBusterTimestamp();
|
||||
if (file_exists(MODULESROOT.'branding/'.$sBrandingLogo))
|
||||
{
|
||||
$sDisplayIcon = utils::GetAbsoluteUrlModulesRoot().'branding/'.$sBrandingLogo.'?t='.utils::GetCacheBusterTimestamp();
|
||||
}
|
||||
$sDisplayIcon = Branding::GetLoginLogoAbsoluteUrl();
|
||||
|
||||
$aVars = array(
|
||||
'sAppRootUrl' => utils::GetAbsoluteUrlAppRoot(),
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
use Combodo\iTop\Application\Helper\Session;
|
||||
|
||||
/**
|
||||
* Class LoginURL
|
||||
*
|
||||
@@ -26,13 +28,13 @@ class LoginURL extends AbstractLoginFSMExtension
|
||||
|
||||
protected function OnModeDetection(&$iErrorCode)
|
||||
{
|
||||
if (!isset($_SESSION['login_mode']) && !$this->bErrorOccurred)
|
||||
if (!Session::IsSet('login_mode') && !$this->bErrorOccurred)
|
||||
{
|
||||
$sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data');
|
||||
$sAuthPwd = utils::ReadParam('auth_pwd', null, false, 'raw_data');
|
||||
if (!empty($sAuthUser) && !empty($sAuthPwd))
|
||||
{
|
||||
$_SESSION['login_mode'] = 'url';
|
||||
Session::Set('login_mode', 'url');
|
||||
}
|
||||
}
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
@@ -40,20 +42,20 @@ class LoginURL extends AbstractLoginFSMExtension
|
||||
|
||||
protected function OnReadCredentials(&$iErrorCode)
|
||||
{
|
||||
if ($_SESSION['login_mode'] == 'url')
|
||||
if (Session::Get('login_mode') == 'url')
|
||||
{
|
||||
$_SESSION['login_temp_auth_user'] = utils::ReadParam('auth_user', '', false, 'raw_data');
|
||||
Session::Set('login_temp_auth_user', utils::ReadParam('auth_user', '', false, 'raw_data'));
|
||||
}
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
}
|
||||
|
||||
protected function OnCheckCredentials(&$iErrorCode)
|
||||
{
|
||||
if ($_SESSION['login_mode'] == 'url')
|
||||
if (Session::Get('login_mode') == 'url')
|
||||
{
|
||||
$sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data');
|
||||
$sAuthPwd = utils::ReadParam('auth_pwd', null, false, 'raw_data');
|
||||
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, $_SESSION['login_mode'], 'internal'))
|
||||
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, Session::Get('login_mode'), 'internal'))
|
||||
{
|
||||
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
|
||||
return LoginWebPage::LOGIN_FSM_ERROR;
|
||||
@@ -64,17 +66,17 @@ class LoginURL extends AbstractLoginFSMExtension
|
||||
|
||||
protected function OnCredentialsOK(&$iErrorCode)
|
||||
{
|
||||
if ($_SESSION['login_mode'] == 'url')
|
||||
if (Session::Get('login_mode') == 'url')
|
||||
{
|
||||
$sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data');
|
||||
LoginWebPage::OnLoginSuccess($sAuthUser, 'internal', $_SESSION['login_mode']);
|
||||
LoginWebPage::OnLoginSuccess($sAuthUser, 'internal', Session::Get('login_mode'));
|
||||
}
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
}
|
||||
|
||||
protected function OnError(&$iErrorCode)
|
||||
{
|
||||
if ($_SESSION['login_mode'] == 'url')
|
||||
if (Session::Get('login_mode') == 'url')
|
||||
{
|
||||
$this->bErrorOccurred = true;
|
||||
}
|
||||
@@ -83,9 +85,9 @@ class LoginURL extends AbstractLoginFSMExtension
|
||||
|
||||
protected function OnConnected(&$iErrorCode)
|
||||
{
|
||||
if ($_SESSION['login_mode'] == 'url')
|
||||
if (Session::Get('login_mode') == 'url')
|
||||
{
|
||||
$_SESSION['can_logoff'] = true;
|
||||
Session::Set('can_logoff', true);
|
||||
return LoginWebPage::CheckLoggedUser($iErrorCode);
|
||||
}
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Application\Branding;
|
||||
use Combodo\iTop\Application\Helper\Session;
|
||||
|
||||
/**
|
||||
* Web page used for displaying the login form
|
||||
*/
|
||||
@@ -139,16 +142,9 @@ class LoginWebPage extends NiceWebPage
|
||||
|
||||
public function DisplayLoginHeader($bMainAppLogo = false)
|
||||
{
|
||||
$sLogo = 'itop-logo-external.png';
|
||||
$sBrandingLogo = 'login-logo.png';
|
||||
|
||||
$sVersionShort = Dict::Format('UI:iTopVersion:Short', ITOP_APPLICATION, ITOP_VERSION);
|
||||
$sIconUrl = Utils::GetConfig()->Get('app_icon_url');
|
||||
$sDisplayIcon = utils::GetAbsoluteUrlAppRoot().'images/'.$sLogo.'?t='.utils::GetCacheBusterTimestamp();
|
||||
if (file_exists(MODULESROOT.'branding/'.$sBrandingLogo))
|
||||
{
|
||||
$sDisplayIcon = utils::GetAbsoluteUrlModulesRoot().'branding/'.$sBrandingLogo.'?t='.utils::GetCacheBusterTimestamp();
|
||||
}
|
||||
$sDisplayIcon = Branding::GetLoginLogoAbsoluteUrl();
|
||||
$this->add("<div id=\"login-logo\"><a href=\"".htmlentities($sIconUrl, ENT_QUOTES,
|
||||
self::PAGES_CHARSET)."\"><img title=\"$sVersionShort\" src=\"$sDisplayIcon\"></a></div>\n");
|
||||
}
|
||||
@@ -392,11 +388,11 @@ class LoginWebPage extends NiceWebPage
|
||||
public static function ResetSession()
|
||||
{
|
||||
// Unset all of the session variables.
|
||||
unset($_SESSION['auth_user']);
|
||||
unset($_SESSION['login_state']);
|
||||
unset($_SESSION['can_logoff']);
|
||||
unset($_SESSION['archive_mode']);
|
||||
unset($_SESSION['impersonate_user']);
|
||||
Session::Unset('auth_user');
|
||||
Session::Unset('login_state');
|
||||
Session::Unset('can_logoff');
|
||||
Session::Unset('archive_mode');
|
||||
Session::Unset('impersonate_user');
|
||||
UserRights::_ResetSessionCache();
|
||||
// If it's desired to kill the session, also delete the session cookie.
|
||||
// Note: This will destroy the session, and not just the session data!
|
||||
@@ -442,11 +438,11 @@ class LoginWebPage extends NiceWebPage
|
||||
}
|
||||
$bLoginDebug = MetaModel::GetConfig()->Get('login_debug');
|
||||
|
||||
if (!isset($_SESSION['login_state']) || ($_SESSION['login_state'] == self::LOGIN_STATE_ERROR))
|
||||
if (Session::Get('login_state') == self::LOGIN_STATE_ERROR)
|
||||
{
|
||||
$_SESSION['login_state'] = self::LOGIN_STATE_START;
|
||||
Session::Set('login_state', self::LOGIN_STATE_START);
|
||||
}
|
||||
$sLoginState = $_SESSION['login_state'];
|
||||
$sLoginState = Session::Get('login_state');
|
||||
|
||||
$sSessionLog = '';
|
||||
if ($bLoginDebug)
|
||||
@@ -500,7 +496,7 @@ class LoginWebPage extends NiceWebPage
|
||||
|
||||
// Every plugin has nothing else to do in this state, go forward
|
||||
$sLoginState = self::AdvanceLoginFSMState($sLoginState);
|
||||
$_SESSION['login_state'] = $sLoginState;
|
||||
Session::Set('login_state', $sLoginState);
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
@@ -526,7 +522,7 @@ class LoginWebPage extends NiceWebPage
|
||||
|
||||
if ($bFilterWithMode)
|
||||
{
|
||||
$sCurrentLoginMode = isset($_SESSION['login_mode']) ? $_SESSION['login_mode'] : '';
|
||||
$sCurrentLoginMode = Session::Get('login_mode', '');
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -665,8 +661,8 @@ class LoginWebPage extends NiceWebPage
|
||||
$oLog->DBInsertNoReload();
|
||||
}
|
||||
|
||||
$_SESSION['auth_user'] = $sAuthUser;
|
||||
$_SESSION['login_mode'] = $sLoginMode;
|
||||
Session::Set('auth_user', $sAuthUser);
|
||||
Session::Set('login_mode', $sLoginMode);
|
||||
UserRights::_InitSessionCache();
|
||||
}
|
||||
|
||||
@@ -681,10 +677,10 @@ class LoginWebPage extends NiceWebPage
|
||||
*/
|
||||
public static function CheckLoggedUser(&$iErrorCode)
|
||||
{
|
||||
if (isset($_SESSION['auth_user']))
|
||||
if (Session::IsSet('auth_user'))
|
||||
{
|
||||
// Already authenticated
|
||||
$bRet = UserRights::Login($_SESSION['auth_user']); // Login & set the user's language
|
||||
$bRet = UserRights::Login(Session::Get('auth_user')); // Login & set the user's language
|
||||
if ($bRet)
|
||||
{
|
||||
$iErrorCode = self::EXIT_CODE_OK;
|
||||
@@ -712,11 +708,11 @@ class LoginWebPage extends NiceWebPage
|
||||
|
||||
public static function SetLoginModeAndReload($sNewLoginMode)
|
||||
{
|
||||
if (isset($_SESSION['login_mode']) && ($_SESSION['login_mode'] == $sNewLoginMode))
|
||||
if (Session::Get('login_mode') == $sNewLoginMode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
$_SESSION['login_mode'] = $sNewLoginMode;
|
||||
Session::Set('login_mode', $sNewLoginMode);
|
||||
self::HTTPReload();
|
||||
}
|
||||
|
||||
@@ -829,9 +825,9 @@ class LoginWebPage extends NiceWebPage
|
||||
{
|
||||
CMDBObject::SetTrackOrigin('custom-extension');
|
||||
$sInfo = 'External User provisioning';
|
||||
if (isset($_SESSION['login_mode']))
|
||||
if (Session::IsSet('login_mode'))
|
||||
{
|
||||
$sInfo .= " ({$_SESSION['login_mode']})";
|
||||
$sInfo .= " (".Session::Get('login_mode').")";
|
||||
}
|
||||
CMDBObject::SetTrackInfo($sInfo);
|
||||
|
||||
@@ -883,9 +879,9 @@ class LoginWebPage extends NiceWebPage
|
||||
{
|
||||
CMDBObject::SetTrackOrigin('custom-extension');
|
||||
$sInfo = 'External User provisioning';
|
||||
if (isset($_SESSION['login_mode']))
|
||||
if (Session::IsSet('login_mode'))
|
||||
{
|
||||
$sInfo .= " ({$_SESSION['login_mode']})";
|
||||
$sInfo .= " (".Session::Get('login_mode').")";
|
||||
}
|
||||
CMDBObject::SetTrackInfo($sInfo);
|
||||
|
||||
@@ -924,9 +920,9 @@ class LoginWebPage extends NiceWebPage
|
||||
|
||||
// Now synchronize the profiles
|
||||
$sOrigin = 'External User provisioning';
|
||||
if (isset($_SESSION['login_mode']))
|
||||
if (Session::IsSet('login_mode'))
|
||||
{
|
||||
$sOrigin .= " ({$_SESSION['login_mode']})";
|
||||
$sOrigin .= " (".Session::Get('login_mode').")";
|
||||
}
|
||||
$aExistingProfiles = self::SynchronizeProfiles($oUser, $aProfiles, $sOrigin);
|
||||
if ($oUser->IsModified())
|
||||
@@ -1011,7 +1007,6 @@ class LoginWebPage extends NiceWebPage
|
||||
$sMessage = self::HandleOperations($operation); // May exit directly
|
||||
|
||||
$iRet = self::Login($iOnExit);
|
||||
|
||||
if ($iRet == self::EXIT_CODE_OK)
|
||||
{
|
||||
if ($bMustBeAdmin && !UserRights::IsAdministrator())
|
||||
@@ -1091,19 +1086,23 @@ class LoginWebPage extends NiceWebPage
|
||||
}
|
||||
else if ($operation == 'change_pwd')
|
||||
{
|
||||
if (isset($_SESSION['auth_user']))
|
||||
if (Session::IsSet('auth_user'))
|
||||
{
|
||||
$sAuthUser = $_SESSION['auth_user'];
|
||||
$sAuthUser = Session::Get('auth_user');
|
||||
$sIssue = Session::Get('pwd_issue');
|
||||
Session::Unset('pwd_issue');
|
||||
$bFailedLogin = ($sIssue != null); // Force the "failed login" flag to display the "issue" message
|
||||
|
||||
UserRights::Login($sAuthUser); // Set the user's language
|
||||
$oPage = self::NewLoginWebPage();
|
||||
$oPage->DisplayChangePwdForm();
|
||||
$oPage->DisplayChangePwdForm($bFailedLogin, $sIssue);
|
||||
$oPage->output();
|
||||
exit;
|
||||
}
|
||||
}
|
||||
else if ($operation == 'check_pwd_policy')
|
||||
{
|
||||
$sAuthUser = $_SESSION['auth_user'];
|
||||
$sAuthUser = Session::Get('auth_user');
|
||||
UserRights::Login($sAuthUser); // Set the user's language
|
||||
|
||||
$aPwdMap = array();
|
||||
@@ -1121,9 +1120,9 @@ class LoginWebPage extends NiceWebPage
|
||||
}
|
||||
if ($operation == 'do_change_pwd')
|
||||
{
|
||||
if (isset($_SESSION['auth_user']))
|
||||
if (Session::IsSet('auth_user'))
|
||||
{
|
||||
$sAuthUser = $_SESSION['auth_user'];
|
||||
$sAuthUser = Session::Get('auth_user');
|
||||
UserRights::Login($sAuthUser); // Set the user's language
|
||||
$sOldPwd = utils::ReadPostedParam('old_pwd', '', 'raw_data');
|
||||
$sNewPwd = utils::ReadPostedParam('new_pwd', '', 'raw_data');
|
||||
|
||||
@@ -1,22 +1,10 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2013-2021 Combodo SARL
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
* iTop is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* iTop is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Application\Helper\WebResourcesHelper;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Title\TitleUIBlockFactory;
|
||||
|
||||
require_once(APPROOT.'/application/utils.inc.php');
|
||||
@@ -71,6 +59,10 @@ class ApplicationMenu
|
||||
* @var array
|
||||
*/
|
||||
static $aMenusIndex = array();
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
static $aMenusById = [];
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
@@ -179,6 +171,7 @@ class ApplicationMenu
|
||||
$aBacktrace = debug_backtrace();
|
||||
$sFile = isset($aBacktrace[2]["file"]) ? $aBacktrace[2]["file"] : $aBacktrace[1]["file"];
|
||||
self::$aMenusIndex[$index] = array('node' => $oMenuNode, 'children' => array(), 'parent' => $sParentId, 'rank' => $fRank, 'source_file' => $sFile);
|
||||
self::$aMenusById[$oMenuNode->GetMenuId()] = $index;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -342,15 +335,17 @@ class ApplicationMenu
|
||||
*/
|
||||
public static function DisplayMenu($oPage, $aExtraParams)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use static::GetMenuGroups() instead');
|
||||
self::LoadAdditionalMenus();
|
||||
// Sort the root menu based on the rank
|
||||
usort(self::$aRootMenus, array('ApplicationMenu', 'CompareOnRank'));
|
||||
$iAccordion = 0;
|
||||
$iActiveAccordion = $iAccordion;
|
||||
$iActiveMenu = self::GetMenuIndexById(self::GetActiveNodeId());
|
||||
foreach(self::$aRootMenus as $aMenu)
|
||||
{
|
||||
if (!self::CanDisplayMenu($aMenu)) { continue; }
|
||||
foreach (self::$aRootMenus as $aMenu) {
|
||||
if (!self::CanDisplayMenu($aMenu)) {
|
||||
continue;
|
||||
}
|
||||
$oMenuNode = self::GetMenuNode($aMenu['index']);
|
||||
$oPage->AddToMenu('<h3 id="'.utils::GetSafeId('AccordionMenu_'.$oMenuNode->GetMenuID()).'" class="navigation-menu-group" data-menu-id="'.$oMenuNode->GetMenuId().'">'.$oMenuNode->GetTitle().'</h3>');
|
||||
$oPage->AddToMenu('<div>');
|
||||
@@ -418,13 +413,12 @@ EOF
|
||||
*/
|
||||
protected static function DisplaySubMenu($oPage, $aMenus, $aExtraParams, $iActiveMenu = -1)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use static::GetSubMenuNodes() instead');
|
||||
// Sort the menu based on the rank
|
||||
$bActive = false;
|
||||
usort($aMenus, array('ApplicationMenu', 'CompareOnRank'));
|
||||
foreach($aMenus as $aMenu)
|
||||
{
|
||||
if (!self::CanDisplayMenu($aMenu))
|
||||
{
|
||||
foreach ($aMenus as $aMenu) {
|
||||
if (!self::CanDisplayMenu($aMenu)) {
|
||||
continue;
|
||||
}
|
||||
$index = $aMenu['index'];
|
||||
@@ -518,17 +512,11 @@ EOF
|
||||
*/
|
||||
public static function GetMenuIndexById($sTitle)
|
||||
{
|
||||
$index = -1;
|
||||
/** @var MenuNode[] $aMenu */
|
||||
foreach(self::$aMenusIndex as $aMenu)
|
||||
{
|
||||
if ($aMenu['node']->GetMenuId() == $sTitle)
|
||||
{
|
||||
$index = $aMenu['node']->GetIndex();
|
||||
break;
|
||||
}
|
||||
if (isset(self::$aMenusById[$sTitle])) {
|
||||
return self::$aMenusById[$sTitle];
|
||||
}
|
||||
return $index;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -727,42 +715,9 @@ abstract class MenuNode
|
||||
{
|
||||
// Count the entries up to 99
|
||||
$oSearch = DBSearch::FromOQL($sOQL);
|
||||
$oSearch->SetShowObsoleteData(utils::ShowObsoleteData());
|
||||
DBSearchHelper::AddContextFilter($oSearch);
|
||||
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sClass = $oSearch->GetClass();
|
||||
foreach ($oAppContext->GetNames() as $key) {
|
||||
// Find the value of the object corresponding to each 'context' parameter
|
||||
$aCallSpec = [$sClass, 'MapContextParam'];
|
||||
$sAttCode = '';
|
||||
if (is_callable($aCallSpec)) {
|
||||
$sAttCode = call_user_func($aCallSpec, $key); // Returns null when there is no mapping for this parameter
|
||||
}
|
||||
|
||||
if (MetaModel::IsValidAttCode($sClass, $sAttCode)) {
|
||||
// Add Hierarchical condition if hierarchical key
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
if (isset($oAttDef) && ($oAttDef->IsExternalKey())) {
|
||||
$iDefaultValue = intval($oAppContext->GetCurrentValue($key));
|
||||
if ($iDefaultValue != 0) {
|
||||
try {
|
||||
/** @var AttributeExternalKey $oAttDef */
|
||||
$sTargetClass = $oAttDef->GetTargetClass();
|
||||
$sHierarchicalKeyCode = MetaModel::IsHierarchicalClass($sTargetClass);
|
||||
if ($sHierarchicalKeyCode !== false) {
|
||||
$oFilter = new DBObjectSearch($sTargetClass);
|
||||
$oFilter->AddCondition('id', $iDefaultValue);
|
||||
$oHKFilter = new DBObjectSearch($sTargetClass);
|
||||
$oHKFilter->AddCondition_PointingTo($oFilter, $sHierarchicalKeyCode, TREE_OPERATOR_BELOW);
|
||||
$oSearch->AddCondition_PointingTo($oHKFilter, $sAttCode);
|
||||
}
|
||||
}
|
||||
catch (Exception $e) {
|
||||
// If filtering fails just ignore it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$oSet = new DBObjectSet($oSearch);
|
||||
$iCount = $oSet->CountWithLimit(99);
|
||||
@@ -1171,15 +1126,13 @@ class OQLMenuNode extends MenuNode
|
||||
{
|
||||
$sUsageId = utils::GetSafeId($sUsageId);
|
||||
$oSearch = DBObjectSearch::FromOQL($sOql);
|
||||
//$sIcon = MetaModel::GetClassIcon($oSearch->GetClass(), false);
|
||||
|
||||
if ($bSearchPane) {
|
||||
$aParams = array_merge(array('open' => $bSearchOpen, 'table_id' => $sUsageId), $aExtraParams);
|
||||
$aParams = array_merge(['open' => $bSearchOpen, 'table_id' => $sUsageId, 'submit_on_load' => false], $aExtraParams);
|
||||
$oBlock = new DisplayBlock($oSearch, 'search', false /* Asynchronous */, $aParams);
|
||||
$oBlock->Display($oPage, 0);
|
||||
}
|
||||
|
||||
//$oPage->add("<p class=\"page-header\">$sIcon ".utils::HtmlEntities(Dict::S($sTitle))."</p>");
|
||||
$oPage->add("<div class='sf_results_area' data-target='search_results'>");
|
||||
$oTitle = TitleUIBlockFactory::MakeForPage($sTitle);
|
||||
$oPage->AddUiBlock($oTitle);
|
||||
@@ -1255,7 +1208,8 @@ class SearchMenuNode extends MenuNode
|
||||
$oPage->SetBreadCrumbEntry("menu-".$this->sMenuId, $this->GetTitle(), '', '', 'fas fa-search', iTopWebPage::ENUM_BREADCRUMB_ENTRY_ICON_TYPE_CSS_CLASSES);
|
||||
|
||||
$oSearch = new DBObjectSearch($this->sClass);
|
||||
$aParams = array_merge(array('table_id' => 'Menu_'.utils::GetSafeId($this->GetMenuId())), $aExtraParams);
|
||||
$sUsageId = 'Menu_'.utils::GetSafeId($this->GetMenuId());
|
||||
$aParams = array_merge(array('table_id' =>$sUsageId), $aExtraParams);
|
||||
$oBlock = new DisplayBlock($oSearch, 'search', false /* Asynchronous */, $aParams);
|
||||
$oBlock->Display($oPage, 0);
|
||||
}
|
||||
@@ -1461,6 +1415,8 @@ class DashboardMenuNode extends MenuNode
|
||||
$oDashboard = $this->GetDashboard();
|
||||
if ($oDashboard != null)
|
||||
{
|
||||
WebResourcesHelper::EnableC3JSToWebPage($oPage);
|
||||
|
||||
$sDivId = utils::Sanitize($this->sMenuId, '', 'element_identifier');
|
||||
$oPage->add('<div id="'.$sDivId.'" class="ibo-dashboard" data-role="ibo-dashboard">');
|
||||
$aExtraParams['dashboard_div_id'] = $sDivId;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/NiceWebPage.php
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/NiceWebPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
*/
|
||||
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/NiceWebPage.php, now loadable using autoloader');
|
||||
@@ -1,7 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/PDFPage.php
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/PDFPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
*/
|
||||
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/PDFPage.php, now loadable using autoloader');
|
||||
@@ -17,6 +17,12 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Application\UI\Base\Component\Alert\AlertUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Field\Field;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Field\FieldUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Input\TextArea;
|
||||
|
||||
abstract class Query extends cmdbAbstractObject
|
||||
{
|
||||
/**
|
||||
@@ -120,36 +126,37 @@ class QueryOQL extends Query
|
||||
function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array())
|
||||
{
|
||||
$aFieldsMap = parent::DisplayBareProperties($oPage, $bEditMode, $sPrefix, $aExtraParams);
|
||||
|
||||
if (!$bEditMode)
|
||||
{
|
||||
$oPage->add_script("$('[name=\"attr_oql\"]').addClass('ibo-queryoql'); $('[data-attribute-code=\"oql\"]').addClass('ibo-queryoql');");
|
||||
|
||||
if (!$bEditMode) {
|
||||
$sFields = trim($this->Get('fields'));
|
||||
$bExportV1Recommended = ($sFields == '');
|
||||
if ($bExportV1Recommended)
|
||||
{
|
||||
if ($bExportV1Recommended) {
|
||||
$oFieldAttDef = MetaModel::GetAttributeDef('QueryOQL', 'fields');
|
||||
$oPage->add('<div class="message message_error" style="padding-left: 30px;"><div style="padding: 10px;">'.Dict::Format('UI:Query:UrlV1', $oFieldAttDef->GetLabel()).'</div></div>');
|
||||
$oAlert = AlertUIBlockFactory::MakeForFailure()
|
||||
->SetIsClosable(false)
|
||||
->SetIsCollapsible(false);
|
||||
$oAlert->AddCSSClass('mb-5');
|
||||
$oAlert->AddSubBlock(new Html(Dict::Format('UI:Query:UrlV1', '')));
|
||||
$oPage->AddSubBlock($oAlert);
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot().'webservices/export.php?format=spreadsheet&login_mode=basic&query='.$this->GetKey();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot().'webservices/export-v2.php?format=spreadsheet&login_mode=basic&date_format='.urlencode((string)AttributeDateTime::GetFormat()).'&query='.$this->GetKey();
|
||||
}
|
||||
$sOql = $this->Get('oql');
|
||||
$sMessage = null;
|
||||
try
|
||||
{
|
||||
try {
|
||||
$oSearch = DBObjectSearch::FromOQL($sOql);
|
||||
$aParameters = $oSearch->GetQueryParams();
|
||||
foreach($aParameters as $sParam => $val)
|
||||
{
|
||||
foreach ($aParameters as $sParam => $val) {
|
||||
$sUrl .= '&arg_'.$sParam.'=["'.$sParam.'"]';
|
||||
}
|
||||
|
||||
$oPage->p(Dict::S('UI:Query:UrlForExcel').':<br/><textarea cols="80" rows="3" READONLY>'.$sUrl.'</textarea>');
|
||||
$oTextArea = new TextArea("", $sUrl, null, 80, 3);
|
||||
$oFieldUrl = FieldUIBlockFactory::MakeFromObject(Dict::S('UI:Query:UrlForExcel'), $oTextArea, Field::ENUM_FIELD_LAYOUT_LARGE);
|
||||
$oPage->AddSubBlock($oFieldUrl);
|
||||
|
||||
if (count($aParameters) == 0)
|
||||
{
|
||||
if (count($aParameters) == 0) {
|
||||
$oBlock = new DisplayBlock($oSearch, 'list');
|
||||
$aExtraParams = array(
|
||||
//'menu' => $sShowMenu,
|
||||
@@ -159,10 +166,13 @@ class QueryOQL extends Query
|
||||
$oBlock->Display($oPage, $sBlockId, $aExtraParams);
|
||||
}
|
||||
}
|
||||
catch (OQLException $e)
|
||||
{
|
||||
$sMessage = '<div class="message message_error" style="padding-left: 30px;"><div style="padding: 10px;">'.Dict::Format('UI:RunQuery:Error', $e->getHtmlDesc()).'</div></div>';
|
||||
$oPage->p($sMessage);
|
||||
catch
|
||||
(OQLException $e) {
|
||||
$oAlert = AlertUIBlockFactory::MakeForFailure(Dict::Format('UI:RunQuery:Error'), $e->getHtmlDesc())
|
||||
->SetIsClosable(false)
|
||||
->SetIsCollapsible(false);
|
||||
$oAlert->AddCSSClass('mb-5');
|
||||
$oPage->AddSubBlock($oAlert);
|
||||
}
|
||||
}
|
||||
return $aFieldsMap;
|
||||
|
||||
@@ -15,9 +15,12 @@
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
use Combodo\iTop\Application\Helper\Session;
|
||||
|
||||
require_once(APPROOT.'/core/cmdbobject.class.inc.php');
|
||||
require_once(APPROOT.'/application/utils.inc.php');
|
||||
require_once(APPROOT.'/core/contexttag.class.inc.php');
|
||||
require_once(APPROOT.'/core/kpi.class.inc.php');
|
||||
|
||||
|
||||
/**
|
||||
@@ -27,6 +30,9 @@ require_once(APPROOT.'/core/contexttag.class.inc.php');
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
ExecutionKPI::EnableDuration(1);
|
||||
ExecutionKPI::EnableMemory(1);
|
||||
|
||||
// This storage is freed on error (case of allowed memory exhausted)
|
||||
$sReservedMemory = str_repeat('*', 1024 * 1024);
|
||||
register_shutdown_function(function()
|
||||
@@ -45,7 +51,7 @@ register_shutdown_function(function()
|
||||
$sMessage = substr($sMessage, 0, $iStackTracePos);
|
||||
}
|
||||
}
|
||||
IssueLog::error($sMessage);
|
||||
IssueLog::error($sMessage, null, $err);
|
||||
if (strpos($err['message'], 'Allowed memory size of') !== false)
|
||||
{
|
||||
$sLimit = ini_get('memory_limit');
|
||||
@@ -62,14 +68,16 @@ register_shutdown_function(function()
|
||||
}
|
||||
}
|
||||
});
|
||||
$oKPI = new ExecutionKPI();
|
||||
Session::Start();
|
||||
Session::WriteClose();
|
||||
$oKPI->ComputeAndReport("Session Start");
|
||||
|
||||
session_name('itop-'.md5(APPROOT));
|
||||
session_start();
|
||||
$sSwitchEnv = utils::ReadParam('switch_env', null);
|
||||
$bAllowCache = true;
|
||||
if (($sSwitchEnv != null) && (file_exists(APPCONF.$sSwitchEnv.'/'.ITOP_CONFIG_FILE)) && isset($_SESSION['itop_env']) && ($_SESSION['itop_env'] !== $sSwitchEnv))
|
||||
if (($sSwitchEnv != null) && file_exists(APPCONF.$sSwitchEnv.'/'.ITOP_CONFIG_FILE) &&( Session::Get('itop_env') !== $sSwitchEnv))
|
||||
{
|
||||
$_SESSION['itop_env'] = $sSwitchEnv;
|
||||
Session::Set('itop_env', $sSwitchEnv);
|
||||
$sEnv = $sSwitchEnv;
|
||||
$bAllowCache = false;
|
||||
// Reset the opcache since otherwise the PHP "model" files may still be cached !!
|
||||
@@ -85,14 +93,14 @@ if (($sSwitchEnv != null) && (file_exists(APPCONF.$sSwitchEnv.'/'.ITOP_CONFIG_FI
|
||||
}
|
||||
// TODO: reset the credentials as well ??
|
||||
}
|
||||
else if (isset($_SESSION['itop_env']))
|
||||
else if (Session::IsSet('itop_env'))
|
||||
{
|
||||
$sEnv = $_SESSION['itop_env'];
|
||||
$sEnv = Session::Get('itop_env');
|
||||
}
|
||||
else
|
||||
{
|
||||
$sEnv = ITOP_DEFAULT_ENV;
|
||||
$_SESSION['itop_env'] = ITOP_DEFAULT_ENV;
|
||||
Session::Set('itop_env', ITOP_DEFAULT_ENV);
|
||||
}
|
||||
$sConfigFile = APPCONF.$sEnv.'/'.ITOP_CONFIG_FILE;
|
||||
MetaModel::Startup($sConfigFile, false /* $bModelOnly */, $bAllowCache, false /* $bTraceSourceFiles */, $sEnv);
|
||||
|
||||
@@ -31,14 +31,15 @@ class DisplayTemplate
|
||||
|
||||
public function __construct($sTemplate)
|
||||
{
|
||||
$this->m_aTags = array (
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod();
|
||||
$this->m_aTags = array(
|
||||
'itopblock',
|
||||
'itopcheck',
|
||||
'itoptabs',
|
||||
'itoptab',
|
||||
'itoptoggle',
|
||||
'itopstring',
|
||||
'sqlblock'
|
||||
'sqlblock',
|
||||
);
|
||||
$this->m_sTemplate = $sTemplate;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ class ThemeHandler
|
||||
{
|
||||
const IMAGE_EXTENSIONS = ['png', 'gif', 'jpg', 'jpeg'];
|
||||
|
||||
/** @var \CompileCSSService */
|
||||
private static $oCompileCSSService;
|
||||
|
||||
public static function GetAppRootWithSlashes()
|
||||
@@ -46,11 +47,10 @@ class ThemeHandler
|
||||
'name' => 'fullmoon',
|
||||
'parameters' => [
|
||||
'variables' => [],
|
||||
'imports' => [
|
||||
],
|
||||
'imports' => [],
|
||||
'stylesheets' => [
|
||||
'main' => '../css/backoffice/main.scss',
|
||||
],
|
||||
]
|
||||
],
|
||||
];
|
||||
}
|
||||
@@ -58,22 +58,27 @@ class ThemeHandler
|
||||
/**
|
||||
* Return the ID of the theme currently defined in the config. file
|
||||
*
|
||||
* @deprecated 3.0.0, will be removed in 3.1, see N°3898
|
||||
* @return string
|
||||
*/
|
||||
public static function GetCurrentThemeId()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (is_null(MetaModel::GetConfig()))
|
||||
{
|
||||
throw new CoreException('no config');
|
||||
}
|
||||
$sThemeId = MetaModel::GetConfig()->Get('backoffice_default_theme');
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod();
|
||||
static::GetCurrentUserThemeId();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string ID of the theme currently defined in the config. file, which applies to all users by default. If non defined, fallback on the default one.
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static function GetApplicationThemeId(): string
|
||||
{
|
||||
try {
|
||||
$sThemeId = utils::GetConfig()->Get('backoffice_default_theme');
|
||||
}
|
||||
catch(CoreException $oCompileException)
|
||||
{
|
||||
catch (CoreException $oCompileException) {
|
||||
// Fallback on our default theme in case the config. is not available yet
|
||||
$aDefaultTheme = ThemeHandler::GetDefaultThemeInformation();
|
||||
$aDefaultTheme = ThemeHandler::GetDefaultThemeInformation();
|
||||
$sThemeId = $aDefaultTheme['name'];
|
||||
}
|
||||
|
||||
@@ -81,49 +86,173 @@ class ThemeHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the absolute path of the compiled theme folder.
|
||||
*
|
||||
* @return string ID of the theme to use for the current user as per they preferences. If non defined, fallback on the app. theme ID.
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static function GetCurrentUserThemeId(): string
|
||||
{
|
||||
$sThemeId = null;
|
||||
|
||||
try {
|
||||
if (true === utils::GetConfig()->Get('user_preferences.allow_backoffice_theme_override')) {
|
||||
$sThemeId = appUserPreferences::GetPref('backoffice_theme', null);
|
||||
}
|
||||
}
|
||||
catch (Exception $oException) {
|
||||
// Do nothing, already handled by $sThemeId null by default
|
||||
}
|
||||
|
||||
// Fallback on the app. theme
|
||||
if (is_null($sThemeId)) {
|
||||
$sThemeId = static::GetApplicationThemeId();
|
||||
}
|
||||
|
||||
return $sThemeId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sThemeId
|
||||
*
|
||||
* @return string
|
||||
* @return string Label of the theme which is either a dict entry ('theme:<THEME_ID>') or the ID if no localized dict. entry found.
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static function GetCompiledThemeFolderAbsolutePath($sThemeId)
|
||||
public static function GetThemeLabel(string $sThemeId): string
|
||||
{
|
||||
return APPROOT.'env-'.utils::GetCurrentEnvironment().'/branding/themes/'.$sThemeId.'/';
|
||||
$sDictEntryCode = 'theme:'.$sThemeId;
|
||||
$sDictEntryValue = Dict::S('theme:'.$sThemeId);
|
||||
|
||||
return ($sDictEntryCode === $sDictEntryValue) ? $sThemeId : $sDictEntryValue;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return array Associative array of <THEME_ID> => <THEME_LABEL>, ordered by labels
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static function GetAvailableThemes(): array
|
||||
{
|
||||
$aThemes = [];
|
||||
|
||||
foreach (glob(static::GetCompiledThemesFolderAbsolutePath().'/*') as $sPath) {
|
||||
if (is_dir($sPath)) {
|
||||
$sThemeId = basename($sPath);
|
||||
$sThemeLabel = static::GetThemeLabel($sThemeId);
|
||||
|
||||
$aThemes[$sThemeId] = $sThemeLabel;
|
||||
}
|
||||
}
|
||||
asort($aThemes);
|
||||
|
||||
return $aThemes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sThemeId
|
||||
*
|
||||
* @return bool True if $sThemeId is a valid theme that can be used.
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static function IsValidTheme(string $sThemeId): bool
|
||||
{
|
||||
return array_key_exists($sThemeId, static::GetAvailableThemes());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string Absolute path to the folder containing all the compiled themes
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static function GetCompiledThemesFolderAbsolutePath(): string
|
||||
{
|
||||
return APPROOT.'env-'.utils::GetCurrentEnvironment().'/branding/themes/';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sThemeId
|
||||
*
|
||||
* @return string Absolute path to the folder containing the $sThemeId theme
|
||||
*/
|
||||
public static function GetCompiledThemeFolderAbsolutePath(string $sThemeId): string
|
||||
{
|
||||
return static::GetCompiledThemesFolderAbsolutePath().$sThemeId.'/';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sThemeId
|
||||
*
|
||||
* @return string Absolute path of the compiled file for the $sThemeId theme (Note: It doesn't mean that the theme is actually compiled)
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static function GetCompiledThemeFileAbsolutePath(string $sThemeId): string
|
||||
{
|
||||
return static::GetCompiledThemeFolderAbsolutePath($sThemeId).'main.css';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sThemeId
|
||||
*
|
||||
* @return string Absolute URL of the compiled file for the $sThemeId theme (Note: It doesn't mean that the theme is actually compiled)
|
||||
* @throws \Exception
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static function GetCompiledThemeFileAbsoluteUrl(string $sThemeId): string
|
||||
{
|
||||
return utils::GetAbsoluteUrlModulesRoot().'branding/themes/'.$sThemeId.'/main.css';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the absolute URL for the current theme CSS file
|
||||
*
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function GetCurrentThemeUrl()
|
||||
public static function GetCurrentThemeUrl(): string
|
||||
{
|
||||
try
|
||||
{
|
||||
try {
|
||||
// Try to compile theme defined in the configuration
|
||||
$sThemeId = static::GetCurrentThemeId();
|
||||
static::CompileTheme($sThemeId);
|
||||
// Note: In maintenance mode we should stick to the app theme (also we don't have access to many PHP classes, including the user preferences)
|
||||
$sThemeId = SetupUtils::IsInMaintenanceMode() ? static::GetApplicationThemeId() : static::GetCurrentUserThemeId();
|
||||
if (static::ShouldThemeSignatureCheckBeForced($sThemeId)) {
|
||||
static::CompileTheme($sThemeId);
|
||||
}
|
||||
}
|
||||
catch(CoreException $oCompileException)
|
||||
{
|
||||
catch (CoreException $oCompileException) {
|
||||
// Fallback on our default theme (should always be compilable) in case the previous theme doesn't exists
|
||||
$aDefaultTheme = ThemeHandler::GetDefaultThemeInformation();
|
||||
$aDefaultTheme = ThemeHandler::GetDefaultThemeInformation();
|
||||
$sThemeId = $aDefaultTheme['name'];
|
||||
$sDefaultThemeDirPath = static::GetCompiledThemeFolderAbsolutePath($sThemeId);
|
||||
|
||||
|
||||
// Create our theme dir if it doesn't exist (XML theme node removed, renamed etc..)
|
||||
if(!is_dir($sDefaultThemeDirPath))
|
||||
{
|
||||
if (!is_dir($sDefaultThemeDirPath)) {
|
||||
SetupUtils::builddir($sDefaultThemeDirPath);
|
||||
}
|
||||
|
||||
static::CompileTheme($sThemeId, false, "", $aDefaultTheme['parameters']);
|
||||
|
||||
if (static::ShouldThemeSignatureCheckBeForced($sThemeId)) {
|
||||
static::CompileTheme($sThemeId, false, "", $aDefaultTheme['parameters']);
|
||||
}
|
||||
}
|
||||
|
||||
// Return absolute url to theme compiled css
|
||||
return utils::GetAbsoluteUrlModulesRoot().'branding/themes/'.$sThemeId.'/main.css';
|
||||
return static::GetCompiledThemeFileAbsoluteUrl($sThemeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sThemeId
|
||||
*
|
||||
* @return bool True if the $sThemeId signature check -and possibly the compilation- should be forced (dev. environment, missing compiled file, ...)
|
||||
*/
|
||||
protected static function ShouldThemeSignatureCheckBeForced(string $sThemeId): bool
|
||||
{
|
||||
if (utils::IsDevelopmentEnvironment()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (false === file_exists(static::GetCompiledThemeFileAbsolutePath($sThemeId))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (true === utils::GetConfig()->Get('theme.force_signature_check_at_runtime')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -135,7 +264,7 @@ class ThemeHandler
|
||||
* @param boolean $bSetup
|
||||
* @param string $sSetupCompilationTimestamp : setup compilation timestamp in micro secunds
|
||||
* @param array|null $aThemeParameters Parameters (variables, imports, stylesheets) for the theme, if not passed, will be retrieved from compiled DM
|
||||
* @param array|null $aImportsPaths Paths where imports can be found. Must end with '/'
|
||||
* @param array|null $aImportsPaths Folder paths where imports can be found. Must end with '/'
|
||||
* @param string|null $sWorkingPath Path of the folder used during compilation. Must end with a '/'
|
||||
*
|
||||
* @throws \CoreException
|
||||
@@ -180,15 +309,16 @@ class ThemeHandler
|
||||
}
|
||||
}
|
||||
|
||||
$aThemeParametersWithVersion = self::CloneThemeParameterAndIncludeVersion($aThemeParameters, $sSetupCompilationTimestampInSecunds);
|
||||
$aThemeParametersWithVersion = self::CloneThemeParameterAndIncludeVersion($aThemeParameters, $sSetupCompilationTimestampInSecunds, $aImportsPaths);
|
||||
|
||||
clearstatcache();
|
||||
|
||||
// Loading files to import and stylesheet to compile, also getting most recent modification time on overall files
|
||||
$sTmpThemeScssContent = '';
|
||||
$oFindStylesheetObject = new FindStylesheetObject();
|
||||
if (isset($aThemeParameters['imports_utility'])) {
|
||||
foreach ($aThemeParameters['imports_utility'] as $sImport) {
|
||||
|
||||
if (isset($aThemeParameters['utility_imports'])) {
|
||||
foreach ($aThemeParameters['utility_imports'] as $sImport) {
|
||||
static::FindStylesheetFile($sImport, $aImportsPaths, $oFindStylesheetObject);
|
||||
}
|
||||
}
|
||||
@@ -203,6 +333,12 @@ class ThemeHandler
|
||||
$sTmpThemeScssContent .= '@import "'.$sStylesheet.'";'."\n";
|
||||
}
|
||||
|
||||
if (isset($aThemeParameters['variable_imports'])) {
|
||||
foreach ($aThemeParameters['variable_imports'] as $sImport) {
|
||||
static::FindStylesheetFile($sImport, $aImportsPaths, $oFindStylesheetObject);
|
||||
}
|
||||
}
|
||||
|
||||
$iStyleLastModified = $oFindStylesheetObject->GetLastModified();
|
||||
|
||||
$aIncludedImages=static::GetIncludedImages($aThemeParametersWithVersion, $oFindStylesheetObject->GetAllStylesheetPaths(), $sThemeId);
|
||||
@@ -262,11 +398,12 @@ CSS;
|
||||
static::$oCompileCSSService = new CompileCSSService();
|
||||
}
|
||||
//store it again to change $version with latest compiled time
|
||||
SetupLog::Info("Compiling theme $sThemeId...");
|
||||
$sTmpThemeCssContent = static::$oCompileCSSService->CompileCSSFromSASS($sTmpThemeScssContent, $aImportsPaths,
|
||||
$aThemeParametersWithVersion);
|
||||
SetupLog::Info("$sThemeId theme compilation done.");
|
||||
file_put_contents($sThemeFolderPath.'/theme-parameters.json', json_encode($aThemeParameters));
|
||||
file_put_contents($sThemeCssPath, $sSignatureComment.$sTmpThemeCssContent);
|
||||
SetupLog::Info("Theme $sThemeId file compiled.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -292,27 +429,29 @@ CSS;
|
||||
$aSignature = [
|
||||
'variables' => md5(json_encode($aThemeParameters['variables'])),
|
||||
'stylesheets' => [],
|
||||
'imports' => [],
|
||||
'images' => []
|
||||
'variable_imports' => [],
|
||||
'images' => [],
|
||||
'utility_imports' => []
|
||||
];
|
||||
|
||||
$oFindStylesheetObject = new FindStylesheetObject();
|
||||
|
||||
if (isset($aThemeParameters['imports_variable'])) {
|
||||
foreach ($aThemeParameters['imports_variable'] as $key => $sImport) {
|
||||
if (isset($aThemeParameters['variable_imports'])) {
|
||||
foreach ($aThemeParameters['variable_imports'] as $key => $sImport) {
|
||||
static::FindStylesheetFile($sImport, $aImportsPaths, $oFindStylesheetObject);
|
||||
$sFile = $oFindStylesheetObject->GetLastStylesheetFile();
|
||||
if (!empty($sFile)) {
|
||||
$aSignature['stylesheets'][$key] = md5_file($sFile);
|
||||
$aSignature['variable_imports'][$key] = md5_file($sFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isset($aThemeParameters['imports_utility'])) {
|
||||
foreach ($aThemeParameters['imports_utility'] as $key => $sImport) {
|
||||
|
||||
if (isset($aThemeParameters['utility_imports'])) {
|
||||
foreach ($aThemeParameters['utility_imports'] as $key => $sImport) {
|
||||
static::FindStylesheetFile($sImport, $aImportsPaths, $oFindStylesheetObject);
|
||||
$sFile = $oFindStylesheetObject->GetLastStylesheetFile();
|
||||
if (!empty($sFile)) {
|
||||
$aSignature['stylesheets'][$key] = md5_file($sFile);
|
||||
$aSignature['utility_imports'][$key] = md5_file($sFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -330,7 +469,7 @@ CSS;
|
||||
$aFiles = $oFindStylesheetObject->GetImportPaths();
|
||||
if (count($aFiles) !== 0) {
|
||||
foreach ($aFiles as $sFileURI => $sFilePath) {
|
||||
$aSignature['imports_utility'][$sFileURI] = md5_file($sFilePath);
|
||||
$aSignature['utility_imports'][$sFileURI] = md5_file($sFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -767,7 +906,8 @@ CSS;
|
||||
|
||||
foreach($aImportsPaths as $sPath)
|
||||
{
|
||||
$sFilePath = $sPath.'/'.$sFileURI;
|
||||
$sAlterableFileURI = $sFileURI;
|
||||
$sFilePath = $sPath.'/'.$sAlterableFileURI;
|
||||
$sImportedFile = realpath($sFilePath);
|
||||
if ($sImportedFile === false){
|
||||
// Handle shortcut syntax : @import "typo" ;
|
||||
@@ -775,7 +915,7 @@ CSS;
|
||||
$sFilePath2 = "$sFilePath.scss";
|
||||
$sImportedFile = realpath($sFilePath2);
|
||||
if ($sImportedFile){
|
||||
self::FindStylesheetFile("$sFileURI.scss", [ $sPath ], $oFindStylesheetObject, $bImports);
|
||||
self::FindStylesheetFile("$sAlterableFileURI.scss", [ $sPath ], $oFindStylesheetObject, $bImports);
|
||||
$sImportedFile = false;
|
||||
}
|
||||
}
|
||||
@@ -785,7 +925,7 @@ CSS;
|
||||
// file matched: _typo.scss
|
||||
$sShortCut = substr($sFilePath, strrpos($sFilePath, '/') + 1);
|
||||
$sFilePath = static::ReplaceLastOccurrence($sShortCut, "_$sShortCut.scss", $sFilePath);
|
||||
$sFileURI = static::ReplaceLastOccurrence($sShortCut, "_$sShortCut.scss", $sFileURI);
|
||||
$sAlterableFileURI = static::ReplaceLastOccurrence($sShortCut, "_$sShortCut.scss", $sAlterableFileURI);
|
||||
$sImportedFile = realpath($sFilePath);
|
||||
}
|
||||
|
||||
@@ -793,14 +933,14 @@ CSS;
|
||||
&& (!$oFindStylesheetObject->AlreadyFetched($sImportedFile)))
|
||||
{
|
||||
if ($bImports){
|
||||
$oFindStylesheetObject->AddImport($sFileURI, $sImportedFile);
|
||||
$oFindStylesheetObject->AddImport($sAlterableFileURI, $sImportedFile);
|
||||
}else{
|
||||
$oFindStylesheetObject->AddStylesheet($sFileURI, $sImportedFile);
|
||||
$oFindStylesheetObject->AddStylesheet($sAlterableFileURI, $sImportedFile);
|
||||
}
|
||||
$oFindStylesheetObject->UpdateLastModified($sImportedFile);
|
||||
|
||||
//Regexp matching on all included scss files : @import 'XXX.scss';
|
||||
$sDirUri = dirname($sFileURI);
|
||||
$sDirUri = dirname($sAlterableFileURI);
|
||||
preg_match_all('/@import \s*[\"\']([^\"\']*)\s*[\"\']\s*;/', file_get_contents($sImportedFile), $aMatches);
|
||||
if ( (is_array($aMatches)) && (count($aMatches)!==0) ){
|
||||
foreach ($aMatches[1] as $sImportedFile){
|
||||
@@ -847,10 +987,11 @@ CSS;
|
||||
* Clone variable array and include $version with bSetupCompilationTimestamp value
|
||||
* @param $aThemeParameters
|
||||
* @param $bSetupCompilationTimestamp
|
||||
* @param $aImportsPaths
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function CloneThemeParameterAndIncludeVersion($aThemeParameters, $bSetupCompilationTimestamp)
|
||||
public static function CloneThemeParameterAndIncludeVersion($aThemeParameters, $bSetupCompilationTimestamp, $aImportsPaths)
|
||||
{
|
||||
$aThemeParametersVariable = [];
|
||||
if (array_key_exists('variables', $aThemeParameters))
|
||||
@@ -859,12 +1000,13 @@ CSS;
|
||||
{
|
||||
$aThemeParametersVariable = array_merge([], $aThemeParameters['variables']);
|
||||
}
|
||||
}
|
||||
if (array_key_exists('imports_variable', $aThemeParameters))
|
||||
}
|
||||
|
||||
if (array_key_exists('variable_imports', $aThemeParameters))
|
||||
{
|
||||
if (is_array($aThemeParameters['imports_variable']))
|
||||
if (is_array($aThemeParameters['variable_imports']))
|
||||
{
|
||||
$aThemeParametersVariable = array_merge($aThemeParametersVariable, static::GetVariablesFromFile($aThemeParameters['imports_variable']));
|
||||
$aThemeParametersVariable = array_merge($aThemeParametersVariable, static::GetVariablesFromFile($aThemeParameters['variable_imports'], $aImportsPaths));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -874,20 +1016,30 @@ CSS;
|
||||
|
||||
/**
|
||||
* @param $aVariableFiles
|
||||
* @param $aImportsPaths
|
||||
*
|
||||
* @return array
|
||||
* @since 3.0.0 N°3593
|
||||
*/
|
||||
public static function GetVariablesFromFile($aVariableFiles){
|
||||
public static function GetVariablesFromFile($aVariableFiles, $aImportsPaths){
|
||||
$aVariablesResults = [];
|
||||
foreach ($aVariableFiles as $sVariableFile)
|
||||
{
|
||||
$sFileContent = file_get_contents(APPROOT.'env-'.utils::GetCurrentEnvironment().'/'.$sVariableFile);
|
||||
$aVariableMatches = [];
|
||||
|
||||
preg_match_all( '/\$(.*?):(.*?);/', $sFileContent,$aVariableMatches);
|
||||
$aVariableMatches = array_combine( $aVariableMatches[1], array_map( function($sVariableValue) { return ltrim($sVariableValue); }, $aVariableMatches[2] ) );
|
||||
$aVariablesResults = array_merge($aVariablesResults, $aVariableMatches);
|
||||
foreach($aImportsPaths as $sPath) {
|
||||
$sFilePath = $sPath.'/'.$sVariableFile;
|
||||
$sImportedFile = realpath($sFilePath);
|
||||
if ($sImportedFile !== false) {
|
||||
$sFileContent = file_get_contents($sImportedFile);
|
||||
$aVariableMatches = [];
|
||||
|
||||
preg_match_all('/\s*\$(.*?)\s*:\s*[\"\']{0,1}(.*?)[\"\']{0,1}\s*[;!]/', $sFileContent, $aVariableMatches);
|
||||
$aVariableMatches = array_combine($aVariableMatches[1], array_map(function ($sVariableValue) {
|
||||
return $sVariableValue;
|
||||
}, $aVariableMatches[2]));
|
||||
$aVariablesResults = array_merge($aVariablesResults, $aVariableMatches);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
array_map( function($sVariableValue) { return ltrim($sVariableValue); }, $aVariablesResults );
|
||||
return $aVariablesResults;
|
||||
|
||||
35
application/themehandlerservice.class.inc.php
Normal file
35
application/themehandlerservice.class.inc.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2013-2020 Combodo SARL
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
* iTop is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* iTop is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class ThemeHandlerService : used to ease testing MFCompiler::CompileThemes class via mocks
|
||||
*
|
||||
* @author Olivier DAIN <olivier.dain@combodo.com>
|
||||
* @since 3.0.0 N°2982
|
||||
*/
|
||||
class ThemeHandlerService
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public function CompileTheme($sThemeId, $bSetup = false, $sSetupCompilationTimestamp="", $aThemeParameters = null, $aImportsPaths = null, $sWorkingPath = null){
|
||||
return ThemeHandler::CompileTheme($sThemeId, $bSetup, $sSetupCompilationTimestamp="", $aThemeParameters, $aImportsPaths, $sWorkingPath);
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
use Combodo\iTop\Application\Helper\Session;
|
||||
|
||||
/**
|
||||
* This class records the pending "transactions" corresponding to forms that have not been
|
||||
@@ -100,7 +101,8 @@ class privUITransaction
|
||||
|
||||
/**
|
||||
* The original (and by default) mechanism for storing transaction information
|
||||
* as an array in the $_SESSION variable
|
||||
* as an array in the _SESSION variable
|
||||
* @see \Combodo\iTop\Application\Helper\Session
|
||||
*
|
||||
*/
|
||||
class privUITransactionSession
|
||||
@@ -112,15 +114,15 @@ class privUITransactionSession
|
||||
*/
|
||||
public static function GetNewTransactionId()
|
||||
{
|
||||
if (!isset($_SESSION['transactions']))
|
||||
if (!Session::IsSet('transactions'))
|
||||
{
|
||||
$_SESSION['transactions'] = array();
|
||||
Session::Set('transactions', []);
|
||||
}
|
||||
// Strictly speaking, the two lines below should be grouped together
|
||||
// by a critical section
|
||||
// sem_acquire($rSemIdentified);
|
||||
$id = static::GetUserPrefix() . str_replace(array('.', ' '), '', microtime()); //1 + count($_SESSION['transactions']);
|
||||
$_SESSION['transactions'][$id] = true;
|
||||
$id = static::GetUserPrefix() . str_replace(array('.', ' '), '', microtime());
|
||||
Session::Set(['transactions', $id], true);
|
||||
// sem_release($rSemIdentified);
|
||||
|
||||
return (string)$id;
|
||||
@@ -137,17 +139,17 @@ class privUITransactionSession
|
||||
public static function IsTransactionValid($id, $bRemoveTransaction = true)
|
||||
{
|
||||
$bResult = false;
|
||||
if (isset($_SESSION['transactions']))
|
||||
if (Session::IsSet('transactions'))
|
||||
{
|
||||
// Strictly speaking, the eight lines below should be grouped together
|
||||
// inside the same critical section as above
|
||||
// sem_acquire($rSemIdentified);
|
||||
if (isset($_SESSION['transactions'][$id]))
|
||||
if (Session::IsSet(['transactions', $id]))
|
||||
{
|
||||
$bResult = true;
|
||||
if ($bRemoveTransaction)
|
||||
{
|
||||
unset($_SESSION['transactions'][$id]);
|
||||
Session::Unset(['transactions', $id]);
|
||||
}
|
||||
}
|
||||
// sem_release($rSemIdentified);
|
||||
@@ -162,14 +164,14 @@ class privUITransactionSession
|
||||
*/
|
||||
public static function RemoveTransaction($id)
|
||||
{
|
||||
if (isset($_SESSION['transactions']))
|
||||
if (Session::IsSet('transactions'))
|
||||
{
|
||||
// Strictly speaking, the three lines below should be grouped together
|
||||
// inside the same critical section as above
|
||||
// sem_acquire($rSemIdentified);
|
||||
if (isset($_SESSION['transactions'][$id]))
|
||||
if (Session::IsSet(['transactions', $id]))
|
||||
{
|
||||
unset($_SESSION['transactions'][$id]);
|
||||
Session::Unset(['transactions', $id]);
|
||||
}
|
||||
// sem_release($rSemIdentified);
|
||||
}
|
||||
|
||||
@@ -80,30 +80,14 @@ class TwigExtension
|
||||
|
||||
// Filter to add itopversion to an url
|
||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('add_itop_version', function ($sUrl) {
|
||||
if (strpos($sUrl, '?') === false)
|
||||
{
|
||||
$sUrl = $sUrl."?itopversion=".ITOP_VERSION;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sUrl = $sUrl."&itopversion=".ITOP_VERSION;
|
||||
}
|
||||
|
||||
$sUrl = utils::AddParameterToUrl($sUrl, 'itopversion', ITOP_VERSION);
|
||||
return $sUrl;
|
||||
}));
|
||||
|
||||
// Filter to add a module's version to an url
|
||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('add_module_version', function ($sUrl, $sModuleName) {
|
||||
$sModuleVersion = utils::GetCompiledModuleVersion($sModuleName);
|
||||
|
||||
if (strpos($sUrl, '?') === false)
|
||||
{
|
||||
$sUrl = $sUrl."?moduleversion=".$sModuleVersion;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sUrl = $sUrl."&moduleversion=".$sModuleVersion;
|
||||
}
|
||||
$sUrl = utils::AddParameterToUrl($sUrl, 'moduleversion', $sModuleVersion);
|
||||
|
||||
return $sUrl;
|
||||
}));
|
||||
@@ -137,4 +121,5 @@ class TwigExtension
|
||||
return utils::GetAbsoluteUrlModulePage($sModuleName, $sPage);
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Application\UI\Base\Component\Form\FormUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlockUIBlockFactory;
|
||||
use Combodo\iTop\Core\MetaModel\FriendlyNameType;
|
||||
|
||||
require_once(APPROOT.'/application/displayblock.class.inc.php');
|
||||
|
||||
/**
|
||||
@@ -82,6 +86,12 @@ class UIExtKeyWidget
|
||||
$aArgs = [], $bSearchMode = false, &$sInputType = ''
|
||||
)
|
||||
{
|
||||
// we will only use key & name, so let's reduce fields loaded !
|
||||
$aAttToLoad = [
|
||||
$sClass => [], // nothing, id and friendlyname are automatically added by the API
|
||||
];
|
||||
$oAllowedValues->OptimizeColumnLoad($aAttToLoad);
|
||||
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
$sTargetClass = $oAttDef->GetTargetClass();
|
||||
$iMaxComboLength = $oAttDef->GetMaximumComboLength();
|
||||
@@ -160,7 +170,8 @@ class UIExtKeyWidget
|
||||
$sMessage = Dict::S('UI:Message:EmptyList:UseSearchForm');
|
||||
$sAttrFieldPrefix = ($this->bSearchMode) ? '' : 'attr_';
|
||||
|
||||
$sHTMLValue = "<div class=\"field_input_zone field_input_extkey ibo-input-wrapper ibo-input-select-wrapper--with-buttons\" data-attcode=\"".$this->sAttCode."\" data-validation=\"untouched\">";
|
||||
|
||||
|
||||
$sFilter = addslashes($oAllowedValues->GetFilter()->ToOQL());
|
||||
if ($this->bSearchMode) {
|
||||
$sWizHelper = 'null';
|
||||
@@ -183,48 +194,45 @@ class UIExtKeyWidget
|
||||
$bDoSearch = !utils::IsHighCardinality($this->sTargetClass);
|
||||
$sJSDoSearch = $bDoSearch ? 'true' : 'false';
|
||||
|
||||
$bIsAutocomplete = $oAllowedValues->CountExceeds($iMaxComboLength);
|
||||
$sWrapperCssClass = $bIsAutocomplete ? 'ibo-input-select-autocomplete-wrapper' : 'ibo-input-select-wrapper';
|
||||
$sHTMLValue = "<div class=\"field_input_zone field_input_extkey ibo-input-wrapper ibo-input-select-wrapper--with-buttons $sWrapperCssClass\" data-attcode=\"".$this->sAttCode."\" data-validation=\"untouched\">";
|
||||
|
||||
// We just need to compare the number of entries with MaxComboLength, so no need to get the real count.
|
||||
if (!$oAllowedValues->CountExceeds($iMaxComboLength)) {
|
||||
if (!$bIsAutocomplete) {
|
||||
// Discrete list of values, use a SELECT or RADIO buttons depending on the config
|
||||
$sHelpText = ''; //$this->oAttDef->GetHelpOnEdition();
|
||||
//$sHTMLValue .= "<div class=\"field_select_wrapper\">\n";
|
||||
$aOptions = [];
|
||||
$sDisplayValue = "";
|
||||
|
||||
$aOption = [];
|
||||
$aOption['value'] = "";
|
||||
$aOption['label'] = Dict::S('UI:SelectOne');
|
||||
array_push($aOptions,$aOption);
|
||||
array_push($aOptions, $aOption);
|
||||
|
||||
$oAllowedValues->Rewind();
|
||||
$bAddingValue=false;
|
||||
$sClassAllowed = $oAllowedValues->GetClass();
|
||||
$bAddingValue = false;
|
||||
|
||||
$aComplementAttributeSpec = MetaModel::GetComplementAttributeSpec($oAllowedValues->GetClass());
|
||||
$aComplementAttributeSpec = MetaModel::GetNameSpec($oAllowedValues->GetClass(), FriendlyNameType::COMPLEMENTARY);
|
||||
$sFormatAdditionalField = $aComplementAttributeSpec[0];
|
||||
$aAdditionalField = $aComplementAttributeSpec[1];
|
||||
|
||||
if (count($aAdditionalField)>0)
|
||||
{
|
||||
$bAddingValue=true;
|
||||
if (count($aAdditionalField) > 0) {
|
||||
$bAddingValue = true;
|
||||
}
|
||||
while($oObj = $oAllowedValues->Fetch())
|
||||
{
|
||||
$aOption=[];
|
||||
$sObjectImageAttCode = MetaModel::GetImageAttributeCode($sClassAllowed);
|
||||
$bInitValue = false;
|
||||
while ($oObj = $oAllowedValues->Fetch()) {
|
||||
$aOption = [];
|
||||
$aOption['value'] = $oObj->GetKey();
|
||||
$aOption['label'] = $oObj->GetName();//.'<span class=\"object-ref-icon fas fa-eye-slash object-obsolete fa-1x fa-fw\"></span>';
|
||||
|
||||
if (($oAllowedValues->Count() == 1) && ($bMandatory == 'true') )
|
||||
{
|
||||
if (($oAllowedValues->Count() == 1) && ($bMandatory == 'true')) {
|
||||
// When there is only once choice, select it by default
|
||||
$sDisplayValue=$oObj->GetName();
|
||||
if($value != $oObj->GetKey())
|
||||
{
|
||||
$value=$oObj->GetKey();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ((is_array($value) && in_array($oObj->GetKey(), $value)) || ($value == $oObj->GetKey())) {
|
||||
$sDisplayValue = $oObj->GetName();
|
||||
if ($value != $oObj->GetKey()) {
|
||||
$value = $oObj->GetKey();
|
||||
$bInitValue = true;
|
||||
}
|
||||
}
|
||||
if ($oObj->IsObsolete()) {
|
||||
@@ -237,21 +245,34 @@ class UIExtKeyWidget
|
||||
}
|
||||
$aOption['additional_field'] = vsprintf($sFormatAdditionalField, $aArguments);
|
||||
}
|
||||
if (!empty($sObjectImageAttCode)) {
|
||||
// Try to retrieve image for contact
|
||||
/** @var \ormDocument $oImage */
|
||||
$oImage = $oObj->Get($sObjectImageAttCode);
|
||||
if (!$oImage->IsEmpty()) {
|
||||
$aOption['picture_url'] = $oImage->GetDisplayURL($sClassAllowed, $oObj->GetKey(), $sObjectImageAttCode);
|
||||
$aOption['initials'] = '';
|
||||
} else {
|
||||
$aOption['initials'] = utils::ToAcronym($oObj->Get('friendlyname'));
|
||||
}
|
||||
}
|
||||
array_push($aOptions, $aOption);
|
||||
}
|
||||
$sInputType = CmdbAbstractObject::ENUM_INPUT_TYPE_DROPDOWN_DECORATED;
|
||||
$sHTMLValue .= "<select title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" id=\"$this->iId\" tabindex=\"0\"></select>";
|
||||
$sJsonOptions = json_encode($aOptions);
|
||||
$sJsonOptions = str_replace('\\', '\\\\', json_encode($aOptions));
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', true, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode, $sJSDoSearch);
|
||||
oACWidget_{$this->iId}.emptyHtml = "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>$sMessage</p></div>";
|
||||
oACWidget_{$this->iId}.AddSelectize('$sJsonOptions','$value');
|
||||
$('#$this->iId').on('update', function() { oACWidget_{$this->iId}.Update(); } );
|
||||
$('#$this->iId').on('change', function() { $(this).trigger('extkeychange') } );
|
||||
|
||||
$('#$this->iId').on('change', function() { $(this).trigger('extkeychange'); } );
|
||||
EOF
|
||||
);
|
||||
if ($bInitValue) {
|
||||
$oPage->add_ready_script("$('#$this->iId').one('validate', function() { $(this).trigger('change'); } );");
|
||||
}
|
||||
$sHTMLValue .= "<div class=\"ibo-input-select--action-buttons\">";
|
||||
}
|
||||
else
|
||||
@@ -295,12 +316,12 @@ EOF
|
||||
EOF
|
||||
);
|
||||
$sHTMLValue .= "<div class=\"ibo-input-select--action-buttons\">";
|
||||
$sHTMLValue .= " <div class=\"ibo-input-select--action-button ibo-input-select--action-button--clear ibo-is-hidden\" id=\"mini_clear_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.Clear();\" data-tooltip-content='".Dict::S('UI:Button:Clear')."''><i class=\"fas fa-times\"></i></div>";
|
||||
$sHTMLValue .= " <div class=\"ibo-input-select--action-button ibo-input-select--action-button--clear ibo-is-hidden\" id=\"mini_clear_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.Clear();\" data-tooltip-content='".Dict::S('UI:Button:Clear')."'><i class=\"fas fa-times\"></i></div>";
|
||||
}
|
||||
if ($bCreate && $bExtensions) {
|
||||
$sCallbackName = (MetaModel::IsAbstract($this->sTargetClass)) ? 'SelectObjectClass' : 'CreateObject';
|
||||
|
||||
$sHTMLValue .= "<div class=\"ibo-input-select--action-button ibo-input-select--action-button--create\" id=\"mini_add_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.{$sCallbackName}();\" data-tooltip-content='".Dict::S('UI:Button:Create')."''><i class=\"fas fa-plus\"></i></div>";
|
||||
$sHTMLValue .= "<div class=\"ibo-input-select--action-button ibo-input-select--action-button--create\" id=\"mini_add_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.{$sCallbackName}();\" data-tooltip-content='".Dict::S('UI:Button:Create')."'><i class=\"fas fa-plus\"></i></div>";
|
||||
$oPage->add_ready_script(
|
||||
<<<JS
|
||||
if ($('#ajax_{$this->iId}').length == 0)
|
||||
@@ -311,7 +332,7 @@ JS
|
||||
);
|
||||
}
|
||||
if ($bExtensions && MetaModel::IsHierarchicalClass($this->sTargetClass) !== false) {
|
||||
$sHTMLValue .= "<div class=\"ibo-input-select--action-button ibo-input-select--action-button--hierarchy\" id=\"mini_tree_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.HKDisplay();\" data-tooltip-content='".Dict::S('UI:Button:SearchInHierarchy')."''><i class=\"fas fa-sitemap\"></i></div>";
|
||||
$sHTMLValue .= "<div class=\"ibo-input-select--action-button ibo-input-select--action-button--hierarchy\" id=\"mini_tree_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.HKDisplay();\" data-tooltip-content='".Dict::S('UI:Button:SearchInHierarchy')."'><i class=\"fas fa-sitemap\"></i></div>";
|
||||
$oPage->add_ready_script(
|
||||
<<<JS
|
||||
if ($('#ac_tree_{$this->iId}').length == 0)
|
||||
@@ -322,7 +343,7 @@ JS
|
||||
);
|
||||
}
|
||||
if ($oAllowedValues->CountExceeds($iMaxComboLength)) {
|
||||
$sHTMLValue .= " <div class=\"ibo-input-select--action-button ibo-input-select--action-button--search\" id=\"mini_search_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.Search();\" data-tooltip-content='".Dict::S('UI:Button:Search')."''><i class=\"fas fa-search\"></i></div>";
|
||||
$sHTMLValue .= " <div class=\"ibo-input-select--action-button ibo-input-select--action-button--search\" id=\"mini_search_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.Search();\" data-tooltip-content='".Dict::S('UI:Button:Search')."'><i class=\"fas fa-search\"></i></div>";
|
||||
}
|
||||
$sHTMLValue .= "</div>";
|
||||
$sHTMLValue .= "</div>";
|
||||
@@ -588,7 +609,7 @@ EOF
|
||||
|
||||
// the input for the auto-complete
|
||||
$sHTMLValue .= "<input class=\"field_autocomplete ibo-input-select\" type=\"text\" id=\"label_$this->iId\" value=\"$sDisplayValue\"/>";
|
||||
$sHTMLValue .= "<span class=\"field_input_btn\"><div class=\"mini_button\" id=\"mini_search_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.Search();\"><i class=\"fas fa-search\"></i></div></span>";
|
||||
$sHTMLValue .= "<div class=\"ibo-input-select--action-buttons\"><span class=\"field_input_btn\"><div class=\"mini_button ibo-input-select--action-button\" id=\"mini_search_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.Search();\"><i class=\"fas fa-search\"></i></div></span></div>";
|
||||
|
||||
// another hidden input to store & pass the object's Id
|
||||
$sHTMLValue .= "<input type=\"hidden\" id=\"$this->iId\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" value=\"".htmlentities($value, ENT_QUOTES, 'UTF-8')."\" />\n";
|
||||
@@ -673,11 +694,9 @@ JS
|
||||
$sEmptyList = Dict::S('UI:Message:EmptyList:UseSearchForm');
|
||||
$oPage->add(<<<HTML
|
||||
<form id="fr_{$this->iId}" OnSubmit="return oACWidget_{$this->iId}.DoOk();">
|
||||
<div id="dr_{$this->iId}" style="vertical-align:top;background: #fff;height:100%;overflow:auto;padding:0;border:0;">
|
||||
<div style="background: #fff; border:0; text-align:center; vertical-align:middle;"><p>{$sEmptyList}</p></div>
|
||||
<div id="dr_{$this->iId}">
|
||||
<div><p>{$sEmptyList}</p></div>
|
||||
</div>
|
||||
<input type="button" id="btn_cancel_{$this->iId}" value="{$sCancel}" onClick="$('#ac_dlg_{$this->iId}').dialog('close');">
|
||||
<input type="button" id="btn_ok_{$this->iId}_results" value="{$sOK}" onClick="oACWidget_{$this->iId}.DoOk();">
|
||||
<input type="hidden" id="count_{$this->iId}_results" value="0">
|
||||
</form>
|
||||
</div></div>
|
||||
@@ -686,7 +705,27 @@ HTML
|
||||
|
||||
$sDialogTitle = addslashes($sTitle);
|
||||
$oPage->add_ready_script(<<<JS
|
||||
$('#ac_dlg_{$this->iId}').dialog({ width: $(window).width()*0.8, height: $(window).height()*0.8, autoOpen: false, modal: true, title: '$sDialogTitle', resizeStop: oACWidget_{$this->iId}.UpdateSizes, close: oACWidget_{$this->iId}.OnClose });
|
||||
$('#ac_dlg_{$this->iId}').dialog({
|
||||
width: $(window).width()*0.8,
|
||||
height: $(window).height()*0.8,
|
||||
autoOpen: false,
|
||||
modal: true,
|
||||
title: '$sDialogTitle',
|
||||
resizeStop: oACWidget_{$this->iId}.UpdateSizes,
|
||||
close: oACWidget_{$this->iId}.OnClose,
|
||||
buttons: [
|
||||
{ text: "$sCancel",
|
||||
class: "ibo-is-alternative ibo-is-neutral",
|
||||
click: function() {
|
||||
$(this).dialog('close');
|
||||
} },
|
||||
{ text: "$sOK",
|
||||
class: "ibo-is-regular ibo-is-primary",
|
||||
click: function() {
|
||||
oACWidget_{$this->iId}.DoOk();
|
||||
} },
|
||||
],
|
||||
});
|
||||
$('#fs_{$this->iId}').on('submit.uiAutocomplete', oACWidget_{$this->iId}.DoSearchObjects);
|
||||
$('#dc_{$this->iId}').resize(oACWidget_{$this->iId}.UpdateSizes);
|
||||
JS
|
||||
@@ -748,7 +787,7 @@ JS
|
||||
// Current extkey value, so we can display event if it is not available anymore (eg. archived).
|
||||
$iCurrentExtKeyId = (is_null($oObj) || $this->sAttCode === '') ? 0 : $oObj->Get($this->sAttCode);
|
||||
$oValuesSet = new ValueSetObjects($sFilter, 'friendlyname'); // Bypass GetName() to avoid the encoding by htmlentities
|
||||
$iMax = 150;
|
||||
$iMax = MetaModel::GetConfig()->Get('max_autocomplete_results');
|
||||
$oValuesSet->SetLimit($iMax);
|
||||
$oValuesSet->SetSort(false);
|
||||
$oValuesSet->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
|
||||
@@ -756,9 +795,8 @@ JS
|
||||
$aValuesContains = $oValuesSet->GetValuesForAutocomplete(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'start_with');
|
||||
asort($aValuesContains);
|
||||
$aValues = $aValuesContains;
|
||||
if (sizeof($aValues) < $iMax)
|
||||
{
|
||||
$aValuesContains = $oValuesSet->GetValuesForAutocomplete(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'contains');
|
||||
if (sizeof($aValues) < $iMax) {
|
||||
$aValuesContains = $oValuesSet->GetValuesForAutocomplete(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'contains');
|
||||
asort($aValuesContains);
|
||||
$iSize = sizeof($aValuesContains);
|
||||
foreach ($aValuesContains as $sKey => $sFriendlyName)
|
||||
@@ -783,21 +821,24 @@ JS
|
||||
{
|
||||
case static::ENUM_OUTPUT_FORMAT_JSON:
|
||||
|
||||
$aJsonMap = array();
|
||||
foreach ($aValues as $sKey => $aValue)
|
||||
{
|
||||
if ($aValue['additional_field'] != '')
|
||||
{
|
||||
$aJsonMap[] = array('value' => $sKey, 'label' => $aValue['label'], 'obsolescence_flag' => $aValue['obsolescence_flag'], 'additional_field' => $aValue['additional_field']);
|
||||
}
|
||||
else
|
||||
{
|
||||
$aJsonMap[] = array('value' => $sKey, 'label' => $aValue['label'], 'obsolescence_flag' => $aValue['obsolescence_flag']);
|
||||
}
|
||||
}
|
||||
$aJsonMap = array();
|
||||
foreach ($aValues as $sKey => $aValue) {
|
||||
$aElt = ['value' => $sKey, 'label' => $aValue['label'], 'obsolescence_flag' => $aValue['obsolescence_flag']];
|
||||
if ($aValue['additional_field'] != '') {
|
||||
$aElt['additional_field'] = $aValue['additional_field'];
|
||||
}
|
||||
|
||||
$oP->SetContentType('application/json');
|
||||
$oP->add(json_encode($aJsonMap));
|
||||
if (array_key_exists('initials', $aValue)) {
|
||||
$aElt['initials'] = $aValue['initials'];
|
||||
if (array_key_exists('picture_url', $aValue)) {
|
||||
$aElt['picture_url'] = $aValue['picture_url'];
|
||||
}
|
||||
}
|
||||
$aJsonMap[] = $aElt;
|
||||
}
|
||||
|
||||
$oP->SetContentType('application/json');
|
||||
$oP->add(json_encode($aJsonMap));
|
||||
break;
|
||||
|
||||
case static::ENUM_OUTPUT_FORMAT_CSV:
|
||||
@@ -864,26 +905,17 @@ JS
|
||||
}
|
||||
}
|
||||
|
||||
$sDialogTitle = '';
|
||||
$oPage->add('<div id="ac_create_'.$this->iId.'"><div class="wizContainer" style="vertical-align:top;"><div id="dcr_'.$this->iId.'">');
|
||||
$oPage->add('<form>');
|
||||
|
||||
$sClassLabel = MetaModel::GetName($this->sTargetClass);
|
||||
$oPage->add('<p>'.Dict::Format('UI:SelectTheTypeOf_Class_ToCreate', $sClassLabel));
|
||||
$oPage->add('<nobr><select name="class">');
|
||||
asort($aPossibleClasses);
|
||||
foreach($aPossibleClasses as $sClassName => $sClassLabel)
|
||||
{
|
||||
$oPage->add("<option value=\"$sClassName\">$sClassLabel</option>");
|
||||
}
|
||||
$oPage->add('</select>');
|
||||
$oPage->add(' <button type="submit" class="action" style="margin-top:15px;"><span>' . Dict::S('UI:Button:Ok') . '</span></button></nobr></p>');
|
||||
$sDialogTitle = Dict::Format('UI:CreationTitle_Class', $sClassLabel);;
|
||||
$oBlock = UIContentBlockUIBlockFactory::MakeStandard('ac_create_'.$this->iId,['ibo-is-visible']);
|
||||
$oPage->AddSubBlock($oBlock);
|
||||
$oClassForm = FormUIBlockFactory::MakeStandard();
|
||||
$oBlock->AddSubBlock($oClassForm);
|
||||
$oClassForm->AddSubBlock(cmdbAbstractObject::DisplayBlockSelectClassToCreate( $sClassLabel, $this->sTargetClass, $aPossibleClasses));
|
||||
|
||||
$oPage->add('</form>');
|
||||
$oPage->add('</div></div></div>');
|
||||
$oPage->add_ready_script("\$('#ac_create_$this->iId').dialog({ width: 'auto', height: 'auto', maxHeight: $(window).height() - 50, autoOpen: false, modal: true, title: '$sDialogTitle'});\n");
|
||||
$oPage->add_ready_script("$('#dcr_{$this->iId} form').removeAttr('onsubmit');");
|
||||
$oPage->add_ready_script("$('#dcr_{$this->iId} form').on('submit.uilinksWizard', oACWidget_{$this->iId}.DoSelectObjectClass);");
|
||||
$oPage->add_ready_script("$('#ac_create_$this->iId').dialog({ width: 'auto', height: 'auto', maxHeight: $(window).height() - 50, autoOpen: false, modal: true, title: '$sDialogTitle'});\n");
|
||||
$oPage->add_ready_script("$('#ac_create_{$this->iId} form').removeAttr('onsubmit');");
|
||||
$oPage->add_ready_script("$('#ac_create_{$this->iId} form').on('submit.uilinksWizard', oACWidget_{$this->iId}.DoSelectObjectClass);");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -954,7 +986,7 @@ JS
|
||||
public function DisplayHierarchy(WebPage $oPage, $sFilter, $currValue, $oObj)
|
||||
{
|
||||
$sDialogTitle = addslashes(Dict::Format('UI:HierarchyOf_Class', MetaModel::GetName($this->sTargetClass)));
|
||||
$oPage->add('<div id="dlg_tree_'.$this->iId.'"><div class="wizContainer" style="vertical-align:top;"><div style="overflow:auto;background:#fff;margin-bottom:5px;" id="tree_'.$this->iId.'">');
|
||||
$oPage->add('<div id="dlg_tree_'.$this->iId.'"><div class="wizContainer" style="vertical-align:top;"><div style="margin-bottom:5px;" id="tree_'.$this->iId.'">');
|
||||
$oPage->add('<table style="width:100%"><tr><td>');
|
||||
if (is_null($sFilter))
|
||||
{
|
||||
@@ -978,8 +1010,8 @@ JS
|
||||
$oPage->add('<div class="treecontrol" id="treecontrolid"><a href="?#">'.Dict::S("UI:Treeview:CollapseAll").'</a> | <a href="?#">'.Dict::S("UI:Treeview:ExpandAll").'</a></div>');
|
||||
}
|
||||
|
||||
$oPage->add("<input type=\"button\" id=\"btn_cancel_{$this->iId}\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"$('#dlg_tree_{$this->iId}').dialog('close');\"> ");
|
||||
$oPage->add("<input type=\"button\" id=\"btn_ok_{$this->iId}\" value=\"".Dict::S('UI:Button:Ok')."\" onClick=\"oACWidget_{$this->iId}.DoHKOk();\">");
|
||||
$oPage->add("<input type=\"button\" class=\"ibo-button ibo-is-regular ibo-is-neutral\" id=\"btn_cancel_{$this->iId}\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"$('#dlg_tree_{$this->iId}').dialog('close');\"> ");
|
||||
$oPage->add("<input type=\"button\" class=\"ibo-button ibo-is-regular ibo-is-primary\" id=\"btn_ok_{$this->iId}\" value=\"".Dict::S('UI:Button:Ok')."\" onClick=\"oACWidget_{$this->iId}.DoHKOk();\">");
|
||||
|
||||
$oPage->add('</div></div>');
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
use Combodo\iTop\Application\Helper\WebResourcesHelper;
|
||||
|
||||
/**
|
||||
* Class UIHTMLEditorWidget
|
||||
@@ -83,6 +84,7 @@ class UIHTMLEditorWidget
|
||||
}
|
||||
$sConfigJS = json_encode($aConfig);
|
||||
|
||||
WebResourcesHelper::EnableCKEditorToWebPage($oPage);
|
||||
$oPage->add_ready_script("$('#$iId').ckeditor(function() { /* callback code */ }, $sConfigJS);"); // Transform $iId into a CKEdit
|
||||
|
||||
// Please read...
|
||||
|
||||
@@ -337,11 +337,10 @@ class UILinksWidgetDirect
|
||||
|
||||
$oPage->add(<<<HTML
|
||||
<form id="ObjectsAddForm_{$this->sInputid}">
|
||||
<div id="SearchResultsToAdd_{$this->sInputid}" style="vertical-align:top;background: #fff;height:100%;overflow:auto;padding:0;border:0;">
|
||||
<div id="SearchResultsToAdd_{$this->sInputid}">
|
||||
<div style="background: #fff; border:0; text-align:center; vertical-align:middle;"><p>{$sEmptyList}</p></div>
|
||||
</div>
|
||||
<input type="hidden" id="count_{$this->sInputid}" value="0"/>
|
||||
<button type="button" class="cancel">{$sCancel}</button> <button type="button" class="ok" disabled="disabled">{$sAdd}</button>
|
||||
</form>
|
||||
HTML
|
||||
);
|
||||
|
||||
@@ -49,6 +49,8 @@ class UIPasswordWidget
|
||||
*/
|
||||
public function Display(WebPage $oPage, $aArgs = array())
|
||||
{
|
||||
$oPage->add_dict_entry('UI:Component:Input:Password:DoesNotMatch');
|
||||
|
||||
$sCode = $this->sAttCode.$this->sNameSuffix;
|
||||
$iWidgetIndex = self::$iWidgetIndex;
|
||||
|
||||
@@ -57,11 +59,12 @@ class UIPasswordWidget
|
||||
$sConfirmPasswordValue = $aPasswordValues ? $aPasswordValues['confirm'] : '*****';
|
||||
$sChangedValue = (($sPasswordValue != '*****') || ($sConfirmPasswordValue != '*****')) ? 1 : 0;
|
||||
$sHtmlValue = '';
|
||||
$sHtmlValue .= '<div class="field_input_zone field_input_onewaypassword">';
|
||||
$sHtmlValue .= '<input type="password" maxlength="255" name="attr_'.$sCode.'[value]" id="'.$this->iId.'" value="'.htmlentities($sPasswordValue, ENT_QUOTES, 'UTF-8').'"/>';
|
||||
$sHtmlValue .= '<input type="password" maxlength="255" id="'.$this->iId.'_confirm" value="'.htmlentities($sConfirmPasswordValue, ENT_QUOTES, 'UTF-8').'" name="attr_'.$sCode.'[confirm]"/>';
|
||||
$sHtmlValue .= '<span>'.Dict::S('UI:PasswordConfirm').'</span>';
|
||||
$sHtmlValue .= '<input id="'.$this->iId.'_reset" type="button" value="'.Dict::S('UI:Button:ResetPassword').'" onClick="ResetPwd(\''.$this->iId.'\');">';
|
||||
$sHtmlValue .= '<div class="field_input_zone field_input_onewaypassword ibo-input-wrapper">';
|
||||
$sHtmlValue .= '<input class="ibo-input" type="password" maxlength="255" name="attr_'.$sCode.'[value]" id="'.$this->iId.'" value="'.htmlentities($sPasswordValue, ENT_QUOTES, 'UTF-8').'"/>';
|
||||
$sHtmlValue .= '<div class="ibo-input-wrapper ibo-input-wrapper--with-buttons"><input class="ibo-input" type="password" maxlength="255" id="'.$this->iId.'_confirm" value="'.htmlentities($sConfirmPasswordValue, ENT_QUOTES, 'UTF-8').'" name="attr_'.$sCode.'[confirm]"/>';
|
||||
$sHtmlValue .= '<div class="ibo-input-select--action-buttons"><div class="ibo-input-select--action-button ibo-input-select--action-button--create" data-tooltip-content="'.Dict::S('UI:PasswordConfirm').'"><i class="fas fa-question-circle"></i></div></div></div>';
|
||||
$sHtmlValue .= '<button id="'.$this->iId.'_reset" class="ibo-button ibo-is-regular ibo-is-neutral" onClick="ResetPwd(\''.$this->iId.'\');">';
|
||||
$sHtmlValue .= '<span class="ibo-button--icon fas fa-undo"></span><span class="ibo-button--label">'.Dict::S('UI:Button:ResetPassword').'</span></button>';
|
||||
$sHtmlValue .= '<input type="hidden" id="'.$this->iId.'_changed" name="attr_'.$sCode.'[changed]" value="'.$sChangedValue.'"/>';
|
||||
$sHtmlValue .= '</div>';
|
||||
|
||||
|
||||
@@ -64,16 +64,42 @@ class UISearchFormForeignKeys
|
||||
<div style="background: #fff; border:0; text-align:center; vertical-align:middle;"><p>{$sEmptyList}</p></div>
|
||||
</div>
|
||||
<input type="hidden" id="count_{$this->m_iInputId}" value="0"/>
|
||||
<input type="button" value="{$sCancel}" onClick="$('#dlg_{$this->m_iInputId}').dialog('close');">
|
||||
<input id="btn_ok_add_{$this->m_iInputId}" disabled="disabled" type="button" onclick="return oForeignKeysWidget{$this->m_iInputId}.DoAddObjects(this.id);" value="{$sAdd}">
|
||||
</form>
|
||||
HTML
|
||||
);
|
||||
|
||||
$oPage->add_ready_script("$('#dlg_{$this->m_iInputId}').dialog({ width: $(window).width()*0.8, height: $(window).height()*0.8, autoOpen: false, modal: true, resizeStop: oForeignKeysWidget{$this->m_iInputId}.UpdateSizes });");
|
||||
$oPage->add_ready_script("$('#dlg_{$this->m_iInputId}').dialog('option', {title:'$sTitle'});");
|
||||
$oPage->add_ready_script("$('#SearchFormToAdd_{$this->m_iInputId} form').on('submit.uilinksWizard', oForeignKeysWidget{$this->m_iInputId}.SearchObjectsToAdd);");
|
||||
$oPage->add_ready_script("$('#SearchFormToAdd_{$this->m_iInputId}').resize(oForeignKeysWidget{$this->m_iInputId}.UpdateSizes);");
|
||||
$oPage->add_ready_script(
|
||||
<<<JS
|
||||
$('#dlg_{$this->m_iInputId}').dialog({
|
||||
width: $(window).width()*0.8,
|
||||
height: $(window).height()*0.8,
|
||||
autoOpen: false,
|
||||
modal: true,
|
||||
resizeStop: oForeignKeysWidget{$this->m_iInputId}.UpdateSizes,
|
||||
buttons: [
|
||||
{
|
||||
text: Dict.S('UI:Button:Cancel'),
|
||||
class: "cancel ibo-is-alternative ibo-is-neutral",
|
||||
click: function() {
|
||||
$('#dlg_{$this->m_iInputId}').dialog('close');
|
||||
}
|
||||
},
|
||||
{
|
||||
text: Dict.S('UI:Button:Add'),
|
||||
id: 'btn_ok_{$this->m_iInputId}',
|
||||
class: "ok ibo-is-regular ibo-is-primary",
|
||||
click: function() {
|
||||
oForeignKeysWidget{$this->m_iInputId}.DoAddObjects(this.id);
|
||||
}
|
||||
},
|
||||
],
|
||||
|
||||
});
|
||||
$('#dlg_{$this->m_iInputId}').dialog('option', {title:'$sTitle'});
|
||||
$('#SearchFormToAdd_{$this->m_iInputId} form').on('submit.uilinksWizard', oForeignKeysWidget{$this->m_iInputId}.SearchObjectsToAdd);
|
||||
$('#SearchFormToAdd_{$this->m_iInputId}').resize(oForeignKeysWidget{$this->m_iInputId}.UpdateSizes);
|
||||
JS
|
||||
);
|
||||
}
|
||||
|
||||
public function GetFullListForeignKeysFromSelection($oPage, $oFullSetFilter)
|
||||
|
||||
@@ -270,23 +270,20 @@ $sJSHandlerCode
|
||||
$aFields = array(); // reset
|
||||
foreach(MetaModel::ListAttributeDefs($this->m_sClass) as $sAttCode=>$oAttDef)
|
||||
{
|
||||
$iOptions = (isset($aStates[$this->m_sTargetState]['attribute_list'][$sAttCode])) ? $aStates[$this->m_sTargetState]['attribute_list'][$sAttCode] : 0;
|
||||
if ( ($sStateAttCode != $sAttCode) &&
|
||||
(!$oAttDef->IsExternalField()) &&
|
||||
(($iOptions & (OPT_ATT_HIDDEN | OPT_ATT_READONLY)) == 0) &&
|
||||
(!isset($aFieldsDone[$sAttCode])) )
|
||||
|
||||
{
|
||||
// 'State', external fields, read-only and hidden fields
|
||||
$iOptions = (isset($aStates[$this->m_sTargetState]['attribute_list'][$sAttCode])) ? $aStates[$this->m_sTargetState]['attribute_list'][$sAttCode] : 0;
|
||||
if (($sStateAttCode != $sAttCode) &&
|
||||
(!$oAttDef->IsExternalField()) &&
|
||||
($oAttDef->IsWritable()) &&
|
||||
(($iOptions & (OPT_ATT_HIDDEN | OPT_ATT_READONLY)) == 0) &&
|
||||
(!isset($aFieldsDone[$sAttCode]))) {
|
||||
// 'State', external fields, read-only (both because of the flags or the attribute type) and hidden fields
|
||||
// and fields that are already listed in the wizard
|
||||
// are removed from the 'optional' part of the wizard
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
|
||||
$aPrerequisites = $oAttDef->GetPrerequisiteAttributes();
|
||||
$aFields[$sAttCode] = array();
|
||||
foreach($aPrerequisites as $sCode)
|
||||
{
|
||||
if (!isset($aFieldsDone[$sCode]))
|
||||
{
|
||||
foreach ($aPrerequisites as $sCode) {
|
||||
if (!isset($aFieldsDone[$sCode])) {
|
||||
// retain only the dependencies that were not covered
|
||||
// in the 'mandatory' part of the wizard
|
||||
$aFields[$sAttCode][$sCode] = '';
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Application\Helper\Session;
|
||||
use Combodo\iTop\Application\UI\Base\iUIBlock;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock;
|
||||
use ScssPhp\ScssPhp\Compiler;
|
||||
|
||||
@@ -90,12 +92,31 @@ class utils
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public const ENUM_SANITIZATION_FILTER_VARIABLE_NAME = 'variable_name';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public const ENUM_SANITIZATION_FILTER_RAW_DATA = 'raw_data';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @since 3.0.0
|
||||
* @used-by static::GetMentionedObjectsFromText
|
||||
*/
|
||||
public const ENUM_TEXT_FORMAT_PLAIN = 'text';
|
||||
/**
|
||||
* @var string
|
||||
* @since 3.0.0
|
||||
* @used-by static::GetMentionedObjectsFromText
|
||||
*/
|
||||
public const ENUM_TEXT_FORMAT_HTML = 'html';
|
||||
/**
|
||||
* @var string
|
||||
* @since 3.0.0
|
||||
* @used-by static::GetMentionedObjectsFromText
|
||||
*/
|
||||
public const ENUM_TEXT_FORMAT_MARKDOWN = 'markdown';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @since 3.0.0
|
||||
@@ -219,9 +240,9 @@ class utils
|
||||
|
||||
public static function InitArchiveMode()
|
||||
{
|
||||
if (isset($_SESSION['archive_mode']))
|
||||
if (Session::IsSet('archive_mode'))
|
||||
{
|
||||
$iDefault = $_SESSION['archive_mode'];
|
||||
$iDefault = Session::Get('archive_mode');
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -229,9 +250,9 @@ class utils
|
||||
}
|
||||
// Read and record the value for switching the archive mode
|
||||
$iCurrent = self::ReadParam('with-archive', $iDefault);
|
||||
if (isset($_SESSION))
|
||||
if (Session::IsInitialized())
|
||||
{
|
||||
$_SESSION['archive_mode'] = $iCurrent;
|
||||
Session::Set('archive_mode', $iCurrent);
|
||||
}
|
||||
// Read and use the value for the current page (web services)
|
||||
$iCurrent = self::ReadParam('with_archive', $iCurrent, true);
|
||||
@@ -520,12 +541,10 @@ class utils
|
||||
{
|
||||
$aSelectedObj = utils::ReadParam('selectObject', array());
|
||||
$sSelectionMode = utils::ReadParam('selectionMode', '');
|
||||
if ($sSelectionMode != '')
|
||||
{
|
||||
if ($sSelectionMode != '') {
|
||||
// Paginated selection
|
||||
$aExceptions = utils::ReadParam('storedSelection', array());
|
||||
if ($sSelectionMode == 'positive')
|
||||
{
|
||||
if ($sSelectionMode == 'positive') {
|
||||
// Only the explicitely listed items are selected
|
||||
$aSelectedObj = $aExceptions;
|
||||
}
|
||||
@@ -638,48 +657,93 @@ class utils
|
||||
|
||||
public static function ReadFromFile($sFileName)
|
||||
{
|
||||
if (!file_exists($sFileName)) return false;
|
||||
if (!file_exists($sFileName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return file_get_contents($sFileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to convert a value expressed in a 'user friendly format'
|
||||
* as in php.ini, e.g. 256k, 2M, 1G etc. Into a number of bytes
|
||||
* @param mixed $value The value as read from php.ini
|
||||
* @return number
|
||||
* @param mixed $value The value as read from php.ini (eg 256k, 2M, 1G etc.)
|
||||
*
|
||||
* @return int conversion to number of bytes
|
||||
*
|
||||
* @since 2.7.5 3.0.0 convert to int numeric values
|
||||
*
|
||||
* @link https://www.php.net/manual/en/faq.using.php#faq.using.shorthandbytes Shorthand bytes value reference in PHP.net FAQ
|
||||
*/
|
||||
public static function ConvertToBytes( $value )
|
||||
public static function ConvertToBytes($value)
|
||||
{
|
||||
$iReturn = $value;
|
||||
if ( !is_numeric( $value ) )
|
||||
{
|
||||
$iLength = strlen( $value );
|
||||
$iReturn = substr( $value, 0, $iLength - 1 );
|
||||
$sUnit = strtoupper( substr( $value, $iLength - 1 ) );
|
||||
switch ( $sUnit )
|
||||
{
|
||||
case 'G':
|
||||
$iReturn *= 1024;
|
||||
case 'M':
|
||||
$iReturn *= 1024;
|
||||
case 'K':
|
||||
$iReturn *= 1024;
|
||||
}
|
||||
}
|
||||
return $iReturn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the memory limit is at least what is required
|
||||
*
|
||||
* @param int $memoryLimit set limit in bytes
|
||||
* @param int $requiredLimit required limit in bytes
|
||||
* @return bool
|
||||
*/
|
||||
public static function IsMemoryLimitOk($memoryLimit, $requiredLimit)
|
||||
{
|
||||
return ($memoryLimit >= $requiredLimit) || ($memoryLimit == -1);
|
||||
}
|
||||
if (!is_numeric($value)) {
|
||||
$iLength = strlen($value);
|
||||
$iReturn = substr($value, 0, $iLength - 1);
|
||||
$sUnit = strtoupper(substr($value, $iLength - 1));
|
||||
switch ($sUnit) {
|
||||
case 'G':
|
||||
$iReturn *= 1024;
|
||||
case 'M':
|
||||
$iReturn *= 1024;
|
||||
case 'K':
|
||||
$iReturn *= 1024;
|
||||
}
|
||||
} else {
|
||||
$iReturn = (int)$value;
|
||||
}
|
||||
|
||||
return $iReturn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the memory limit is at least what is required
|
||||
*
|
||||
* @param int $iMemoryLimit set limit in bytes, use {@link utils::ConvertToBytes()} to convert current php.ini value
|
||||
* @param int $iRequiredLimit required limit in bytes
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function IsMemoryLimitOk($iMemoryLimit, $iRequiredLimit)
|
||||
{
|
||||
if ($iMemoryLimit === -1) {
|
||||
// -1 means : no limit (see https://www.php.net/manual/fr/ini.core.php#ini.memory-limit)
|
||||
return true;
|
||||
}
|
||||
|
||||
return ($iMemoryLimit >= $iRequiredLimit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set memory_limit to required value
|
||||
*
|
||||
* @param string $sRequiredLimit required limit, for example '512M'
|
||||
*
|
||||
* @return bool|null null if nothing was done, true if modifying memory_limit was successful, false otherwise
|
||||
*
|
||||
* @uses utils::ConvertToBytes()
|
||||
* @uses \ini_get('memory_limit')
|
||||
* @uses \ini_set()
|
||||
* @uses utils::ConvertToBytes()
|
||||
*
|
||||
* @since 2.7.5 N°3806
|
||||
*/
|
||||
public static function SetMinMemoryLimit($sRequiredLimit)
|
||||
{
|
||||
$iRequiredLimit = static::ConvertToBytes($sRequiredLimit);
|
||||
$sMemoryLimit = trim(ini_get('memory_limit'));
|
||||
if (empty($sMemoryLimit)) {
|
||||
// On some PHP installations, memory_limit does not exist as a PHP setting!
|
||||
// (encountered on a 5.2.0 under Windows)
|
||||
// In that case, ini_set will not work
|
||||
return false;
|
||||
}
|
||||
$iMemoryLimit = static::ConvertToBytes($sMemoryLimit);
|
||||
|
||||
if (static::IsMemoryLimitOk($iMemoryLimit, $iRequiredLimit)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return ini_set('memory_limit', $iRequiredLimit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a value into a more friendly format (KB, MB, GB, TB) instead a juste a Bytes amount.
|
||||
@@ -797,6 +861,8 @@ class utils
|
||||
}
|
||||
|
||||
/**
|
||||
* @param boolean $bForceGetFromDisk if true then will always read from disk without using instances in memory
|
||||
*
|
||||
* @return \Config Get object in the following order :
|
||||
* <ol>
|
||||
* <li>from {@link MetaModel::GetConfig} if loaded
|
||||
@@ -808,26 +874,27 @@ class utils
|
||||
* @throws \ConfigException
|
||||
* @throws \CoreException
|
||||
*
|
||||
* @since 2.7.0 N°2478 always call {@link MetaModel::GetConfig} first, cache is only set when loading from disk
|
||||
* @since 2.7.0 N°2478 this method will now always call {@link MetaModel::GetConfig} first, and cache in this class is only set when loading from disk
|
||||
* @since 3.0.0 N°4158 new $bReadFromDisk parameter
|
||||
*/
|
||||
public static function GetConfig()
|
||||
public static function GetConfig($bForceGetFromDisk = false)
|
||||
{
|
||||
$oMetaModelConfig = MetaModel::GetConfig();
|
||||
if ($oMetaModelConfig !== null)
|
||||
{
|
||||
return $oMetaModelConfig;
|
||||
}
|
||||
if (!$bForceGetFromDisk) {
|
||||
$oMetaModelConfig = MetaModel::GetConfig();
|
||||
if ($oMetaModelConfig !== null) {
|
||||
return $oMetaModelConfig;
|
||||
}
|
||||
|
||||
if (self::$oConfig !== null)
|
||||
{
|
||||
return self::$oConfig;
|
||||
if (self::$oConfig !== null) {
|
||||
return self::$oConfig;
|
||||
}
|
||||
}
|
||||
|
||||
$sCurrentEnvConfigPath = self::GetConfigFilePath();
|
||||
if (file_exists($sCurrentEnvConfigPath))
|
||||
{
|
||||
if (file_exists($sCurrentEnvConfigPath)) {
|
||||
$oCurrentEnvDiskConfig = new Config($sCurrentEnvConfigPath);
|
||||
self::SetConfig($oCurrentEnvDiskConfig);
|
||||
|
||||
return self::$oConfig;
|
||||
}
|
||||
|
||||
@@ -1147,7 +1214,7 @@ class utils
|
||||
*/
|
||||
static function CanLogOff()
|
||||
{
|
||||
return (isset($_SESSION['can_logoff']) ? $_SESSION['can_logoff'] : false);
|
||||
return Session::Get('can_logoff', false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1156,7 +1223,7 @@ class utils
|
||||
*/
|
||||
public static function GetSessionLog()
|
||||
{
|
||||
return print_r($_SESSION, true);
|
||||
return Session::GetLog();
|
||||
}
|
||||
|
||||
static function DebugBacktrace($iLimit = 5)
|
||||
@@ -1249,14 +1316,7 @@ class utils
|
||||
*/
|
||||
public static function GetCurrentEnvironment()
|
||||
{
|
||||
if (isset($_SESSION['itop_env']))
|
||||
{
|
||||
return $_SESSION['itop_env'];
|
||||
}
|
||||
else
|
||||
{
|
||||
return ITOP_DEFAULT_ENV;
|
||||
}
|
||||
return Session::Get('itop_env', ITOP_DEFAULT_ENV);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1291,12 +1351,15 @@ class utils
|
||||
*/
|
||||
public static function GetPopupMenuItems($oPage, $iMenuId, $param, &$aActions, $sTableId = null, $sDataTableId = null)
|
||||
{
|
||||
$oPage->AddUiBlock(static::GetPopupMenuItemsBlock($iMenuId, $param, $aActions, $sDataTableId));
|
||||
$oBlock = new UIContentBlock();
|
||||
static::GetPopupMenuItemsBlock($oBlock, $iMenuId, $param, $aActions, $sDataTableId);
|
||||
$oPage->AddUiBlock($oBlock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge standard menu items with plugin provided menus items
|
||||
*
|
||||
* @param \Combodo\iTop\Application\UI\Base\iUIBlock $oContainerBlock The UIBlock containing the menu
|
||||
* @param int $iMenuId
|
||||
* @param \DBObjectSet $param
|
||||
* @param array $aActions
|
||||
@@ -1306,9 +1369,8 @@ class utils
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public static function GetPopupMenuItemsBlock($iMenuId, $param, &$aActions, $sDataTableId = null)
|
||||
public static function GetPopupMenuItemsBlock(iUIBlock &$oContainerBlock, $iMenuId, $param, &$aActions, $sDataTableId = null)
|
||||
{
|
||||
$oBlock = new UIContentBlock();
|
||||
// 1st - add standard built-in menu items
|
||||
//
|
||||
switch($iMenuId)
|
||||
@@ -1322,9 +1384,9 @@ class utils
|
||||
$sOQL = addslashes($param->GetFilter()->ToOQL(true));
|
||||
$sFilter = urlencode($param->GetFilter()->serialize());
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/$sUIPage?operation=search&filter=".$sFilter."&{$sContext}";
|
||||
$oBlock->AddJsFileRelPath('js/tabularfieldsselector.js');
|
||||
$oBlock->AddJsFileRelPath('js/jquery.dragtable.js');
|
||||
$oBlock->AddCssFileRelPath('css/dragtable.css');
|
||||
$oContainerBlock->AddJsFileRelPath('js/tabularfieldsselector.js');
|
||||
$oContainerBlock->AddJsFileRelPath('js/jquery.dragtable.js');
|
||||
$oContainerBlock->AddCssFileRelPath('css/dragtable.css');
|
||||
|
||||
$aResult = array();
|
||||
if (strlen($sUrl) < SERVER_MAX_URL_LENGTH)
|
||||
@@ -1357,12 +1419,12 @@ class utils
|
||||
$oObj = $param;
|
||||
$sOQL = "SELECT ".get_class($oObj)." WHERE id=".$oObj->GetKey();
|
||||
$sUrl = ApplicationContext::MakeObjectUrl(get_class($oObj), $oObj->GetKey());
|
||||
$oBlock->AddJsFileRelPath('js/tabularfieldsselector.js');
|
||||
$oBlock->AddJsFileRelPath('js/jquery.dragtable.js');
|
||||
$oBlock->AddCssFileRelPath('css/dragtable.css');
|
||||
$oBlock->AddJsFileRelPath('js/tabularfieldsselector.js');
|
||||
$oBlock->AddJsFileRelPath('js/jquery.dragtable.js');
|
||||
$oBlock->AddCssFileRelPath('css/dragtable.css');
|
||||
$oContainerBlock->AddJsFileRelPath('js/tabularfieldsselector.js');
|
||||
$oContainerBlock->AddJsFileRelPath('js/jquery.dragtable.js');
|
||||
$oContainerBlock->AddCssFileRelPath('css/dragtable.css');
|
||||
$oContainerBlock->AddJsFileRelPath('js/tabularfieldsselector.js');
|
||||
$oContainerBlock->AddJsFileRelPath('js/jquery.dragtable.js');
|
||||
$oContainerBlock->AddCssFileRelPath('css/dragtable.css');
|
||||
|
||||
$aResult = array(
|
||||
new SeparatorPopupMenuItem(),
|
||||
@@ -1430,13 +1492,11 @@ class utils
|
||||
|
||||
foreach($oMenuItem->GetLinkedScripts() as $sLinkedScript)
|
||||
{
|
||||
$oBlock->AddJsFileRelPath($sLinkedScript);
|
||||
$oContainerBlock->AddJsFileRelPath($sLinkedScript);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $oBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1859,7 +1919,7 @@ class utils
|
||||
public static function CompileCSSFromSASS($sSassContent, $aImportPaths = array(), $aVariables = array())
|
||||
{
|
||||
$oSass = new Compiler();
|
||||
$oSass->setFormatter('ScssPhp\\ScssPhp\\Formatter\\Expanded');
|
||||
$oSass->setFormatter('ScssPhp\\ScssPhp\\Formatter\\Compressed');
|
||||
// Setting our variables
|
||||
$oSass->setVariables($aVariables);
|
||||
// Setting our imports paths
|
||||
@@ -2567,7 +2627,7 @@ class utils
|
||||
$aDefaultConf = array(
|
||||
'language'=> $sLanguage,
|
||||
'contentsLanguage' => $sLanguage,
|
||||
'extraPlugins' => 'disabler,codesnippet,mentions',
|
||||
'extraPlugins' => 'disabler,codesnippet,mentions,objectshortcut',
|
||||
);
|
||||
|
||||
// Mentions
|
||||
@@ -2575,24 +2635,36 @@ class utils
|
||||
if(!empty($aMentionsAllowedClasses)) {
|
||||
$aDefaultConf['mentions'] = [];
|
||||
|
||||
foreach($aMentionsAllowedClasses as $sMentionChar => $sMentionClass) {
|
||||
foreach($aMentionsAllowedClasses as $sMentionMarker => $sMentionScope) {
|
||||
// Retrieve mention class
|
||||
// - First test if the conf is a simple Datamodel class
|
||||
if (MetaModel::IsValidClass($sMentionScope)) {
|
||||
$sMentionClass = $sMentionScope;
|
||||
}
|
||||
// - Otherwise it must be a valid OQL
|
||||
else {
|
||||
$oTmpSearch = DBSearch::FromOQL($sMentionScope);
|
||||
$sMentionClass = $oTmpSearch->GetClass();
|
||||
unset($oTmpSearch);
|
||||
}
|
||||
|
||||
// Note: Endpoints are defaults only and should be overloaded by other GUIs such as the end-users portal
|
||||
$sMentionEndpoint = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=cke_mentions&target_class='.$sMentionClass.'&needle={encodedQuery}';
|
||||
$sMentionEndpoint = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=cke_mentions&marker='.$sMentionMarker.'&needle={encodedQuery}';
|
||||
$sMentionItemUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=details&class='.$sMentionClass.'&id={id}';
|
||||
|
||||
$sMentionItemPictureTemplate = (empty(MetaModel::GetImageAttributeCode($sMentionClass))) ? '' : <<<HTML
|
||||
<span class="ibo-vendors-ckeditor--autocomplete-item-image" style="background-image: url('{picture_url}');">{initials}</span>
|
||||
<span class="ibo-vendors-ckeditor--autocomplete-item-image" style="{picture_style}">{initials}</span>
|
||||
HTML;
|
||||
$sMentionItemTemplate = <<<HTML
|
||||
<li class="ibo-vendors-ckeditor--autocomplete-item" data-id="{id}">{$sMentionItemPictureTemplate}<span class="ibo-vendors-ckeditor--autocomplete-item-title">{friendlyname}</span></li>
|
||||
HTML;
|
||||
$sMentionOutputTemplate = <<<HTML
|
||||
<a href="$sMentionItemUrl" data-role="object-mention" data-object-class="{class}" data-object-id="{id}">{$sMentionChar}{friendlyname}</a>
|
||||
<a href="$sMentionItemUrl" data-role="object-mention" data-object-class="{class}" data-object-id="{id}">{$sMentionMarker}{friendlyname}</a>
|
||||
HTML;
|
||||
|
||||
$aDefaultConf['mentions'][] = [
|
||||
'feed' => $sMentionEndpoint,
|
||||
'marker' => $sMentionChar,
|
||||
'marker' => $sMentionMarker,
|
||||
'minChars' => MetaModel::GetConfig()->Get('min_autocomplete_chars'),
|
||||
'itemTemplate' => $sMentionItemTemplate,
|
||||
'outputTemplate' => $sMentionOutputTemplate,
|
||||
@@ -2684,15 +2756,13 @@ HTML;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return keyboard shortcuts config as an array
|
||||
*
|
||||
* @return array
|
||||
* @return array All keyboard shortcuts config as an array
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \MySQLException
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static function GetKeyboardShortcutPref(): array
|
||||
public static function GetAllKeyboardShortcutsPrefs(): array
|
||||
{
|
||||
$aResultPref = [];
|
||||
$aShortcutPrefs = appUserPreferences::GetPref('keyboard_shortcuts', []);
|
||||
@@ -2703,29 +2773,113 @@ HTML;
|
||||
$sTriggeredElement = $cShortcutPlugin::GetShortcutTriggeredElementSelector();
|
||||
foreach ($cShortcutPlugin::GetShortcutKeys() as $aShortcutKey) {
|
||||
$sKey = isset($aShortcutPrefs[$aShortcutKey['id']]) ? $aShortcutPrefs[$aShortcutKey['id']] : $aShortcutKey['key'];
|
||||
$aResultPref[$aShortcutKey['id']] = ['key' => $sKey, 'label' => $aShortcutKey['label'], 'event' => $aShortcutKey['event'], 'triggered_element_selector' => $sTriggeredElement];
|
||||
|
||||
// Format key for display
|
||||
$aKeyParts = explode('+', $sKey);
|
||||
$aFormattedKeyParts = [];
|
||||
foreach ($aKeyParts as $sKeyPart) {
|
||||
$aFormattedKeyParts[] = ucfirst(trim($sKeyPart));
|
||||
}
|
||||
$sFormattedKey = implode(' + ', $aFormattedKeyParts);
|
||||
|
||||
$aResultPref[$aShortcutKey['id']] = [
|
||||
'key' => $sKey,
|
||||
'key_for_display' => $sFormattedKey,
|
||||
'label' => $aShortcutKey['label'],
|
||||
'event' => $aShortcutKey['event'],
|
||||
'triggered_element_selector' => $sTriggeredElement,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $aResultPref;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sShortcutId
|
||||
*
|
||||
* @return array The properties of the $sShortcutId shorcut
|
||||
* @throws \Exception
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \MySQLException
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static function GetKeyboardShortcutPref(string $sShortcutId): array
|
||||
{
|
||||
$aPrefs = static::GetAllKeyboardShortcutsPrefs();
|
||||
if (false === array_key_exists($sShortcutId, $aPrefs)) {
|
||||
throw new Exception('No shortcut identified as "'.$sShortcutId.'" is currently handled by the application.');
|
||||
}
|
||||
|
||||
return $aPrefs[$sShortcutId];
|
||||
}
|
||||
|
||||
//----------------------------------------------
|
||||
// Environment helpers
|
||||
//----------------------------------------------
|
||||
|
||||
/**
|
||||
* Check if iTop is in a development environment (VCS vs build number)
|
||||
* Check if iTop is in a development environment
|
||||
*
|
||||
* @return bool
|
||||
* @return bool true if development environment
|
||||
*
|
||||
* @since 2.6.0 method creation
|
||||
* @since 3.0.0 add the `developer_mode.enabled` config parameter
|
||||
*
|
||||
* @uses GetDeveloperModeParam
|
||||
* @uses ITOP_REVISION constant (check 'svn' value)
|
||||
*/
|
||||
public static function IsDevelopmentEnvironment()
|
||||
{
|
||||
$bIsDevEnvInConfig = static::GetDeveloperModeParam();
|
||||
if ($bIsDevEnvInConfig === true) {
|
||||
return true;
|
||||
}
|
||||
if ($bIsDevEnvInConfig === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!defined('ITOP_REVISION')) {
|
||||
//defensive behaviour: by default we are not in dev environment
|
||||
//can happen even in production (unattended install for example) or with exotic use of iTop
|
||||
return false;
|
||||
}
|
||||
|
||||
return ITOP_REVISION === 'svn';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool : indicate whether we run under a windows environnement or not
|
||||
* In the setup there are times when the MetaModel config attribute is loaded but partially (only setup parameters are set, others have the default value)
|
||||
* So we need to load from disk then !
|
||||
*
|
||||
* But in other scenario we want to read from memory : for example when changing the option in a PHPUnit setUp method
|
||||
*
|
||||
* This method will first try to get the `developer_mode.enabled` config parameter the standard way (call to GetConfig without modification).
|
||||
* If we are getting null (not defined parameter), then we will load config from disk only (GetConfig(true))
|
||||
*
|
||||
* @return bool|null
|
||||
* @throws \ConfigException
|
||||
* @throws \CoreException
|
||||
*
|
||||
* @uses developer_mode.enabled config parameter
|
||||
*/
|
||||
private static function GetDeveloperModeParam(): ?bool
|
||||
{
|
||||
$oConfig = static::GetConfig(false);
|
||||
$bIsDevEnvInConfig = $oConfig->Get('developer_mode.enabled');
|
||||
|
||||
if (!is_null($bIsDevEnvInConfig)) {
|
||||
return $bIsDevEnvInConfig;
|
||||
}
|
||||
|
||||
$oConfigFromDisk = static::GetConfig(true);
|
||||
|
||||
return $oConfigFromDisk->Get('developer_mode.enabled');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool true if we are running under a Windows environment
|
||||
* @since 2.7.4 : N°3412
|
||||
*/
|
||||
public static function IsWindowsEnvironment()
|
||||
@@ -2830,4 +2984,87 @@ HTML;
|
||||
|
||||
return $sAcronym;
|
||||
}
|
||||
|
||||
//----------------------------------------------
|
||||
// Text manipulation
|
||||
//----------------------------------------------
|
||||
|
||||
/**
|
||||
* Note: Only works for backoffice URLs for now
|
||||
*
|
||||
* @param string $sText Text containing the mentioned objects to be found
|
||||
* @param string $sFormat {@uses static::ENUM_TEXT_FORMAT_HTML, ...}
|
||||
*
|
||||
* @return array Array of object classes / IDs for the ones found in $sText
|
||||
*
|
||||
* [
|
||||
* 'ClassA' => ['ID1', 'ID2', ...],
|
||||
* 'ClassB' => ['ID3'],
|
||||
* ]
|
||||
*
|
||||
* @throws \Exception
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static function GetMentionedObjectsFromText(string $sText, string $sFormat = self::ENUM_TEXT_FORMAT_HTML): array
|
||||
{
|
||||
// First transform text so it can be parsed
|
||||
switch ($sFormat) {
|
||||
case static::ENUM_TEXT_FORMAT_HTML:
|
||||
$sText = static::HtmlToText($sText);
|
||||
break;
|
||||
|
||||
default:
|
||||
// Don't transform it
|
||||
break;
|
||||
}
|
||||
|
||||
// Then parse text to find objects
|
||||
$aMentionedObjects = array();
|
||||
$aMentionMatches = array();
|
||||
|
||||
// Note: As the sanitizer (or CKEditor autocomplete plugin? 🤔) removes data-* attributes from the hyperlink,
|
||||
// - we can't use the following (simpler) regexp that only checks data attributes on hyperlinks, which would have worked for hyperlinks pointing to any GUIs: '/<a\s*([^>]*)data-object-class="([^"]*)"\s*data-object-id="([^"]*)">/i'
|
||||
// - instead we use a regexp to match the following pattern '[Some object label](<APP_ROOT_URL>...&class=<OBJECT_CLASS>&id=<OBJECT_ID>...)' which only works for the backoffice
|
||||
// If we change the sanitizer, we might want to switch to the other regexp as it's universal and easier to read
|
||||
$sAppRootUrlForRegExp = addcslashes(utils::GetAbsoluteUrlAppRoot(), '/&');
|
||||
preg_match_all("/\[([^\]]*)\]\({$sAppRootUrlForRegExp}[^\)]*\&class=([^\)\&]*)\&id=([\d]*)[^\)]*\)/i", $sText, $aMentionMatches);
|
||||
|
||||
foreach ($aMentionMatches[0] as $iMatchIdx => $sCompleteMatch) {
|
||||
$sMatchedClass = $aMentionMatches[2][$iMatchIdx];
|
||||
$sMatchedId = $aMentionMatches[3][$iMatchIdx];
|
||||
|
||||
// Prepare array for matched class if not already present
|
||||
if (!array_key_exists($sMatchedClass, $aMentionedObjects)) {
|
||||
$aMentionedObjects[$sMatchedClass] = array();
|
||||
}
|
||||
// Add matched ID if not already there
|
||||
if (!in_array($sMatchedId, $aMentionedObjects[$sMatchedClass])) {
|
||||
$aMentionedObjects[$sMatchedClass][] = $sMatchedId;
|
||||
}
|
||||
}
|
||||
|
||||
return $aMentionedObjects;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sUrl
|
||||
* @param string $sParamName
|
||||
* @param string $sParamValue
|
||||
*
|
||||
* @return string
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static function AddParameterToUrl(string $sUrl, string $sParamName, string $sParamValue): string
|
||||
{
|
||||
if (strpos($sUrl, '?') === false)
|
||||
{
|
||||
$sUrl = $sUrl.'?'.urlencode($sParamName).'='.urlencode($sParamValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sUrl = $sUrl.'&'.urlencode($sParamName).'='.urlencode($sParamValue);
|
||||
}
|
||||
|
||||
return $sUrl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/WebPage.php
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/WebPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
*/
|
||||
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/WebPage.php, now loadable using autoloader');
|
||||
@@ -1,7 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/XMLPage.php
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/XMLPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
*/
|
||||
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/XMLPage.php, now loadable using autoloader');
|
||||
@@ -44,14 +44,9 @@ define('ITOP_DEFAULT_ENV', 'production');
|
||||
define('MAINTENANCE_MODE_FILE', APPROOT.'data/.maintenance');
|
||||
define('READONLY_MODE_FILE', APPROOT.'data/.readonly');
|
||||
|
||||
if (function_exists('microtime')) {
|
||||
$fItopStarted = microtime(true);
|
||||
} else {
|
||||
$fItopStarted = 1000 * time();
|
||||
}
|
||||
$fItopStarted = microtime(true);
|
||||
|
||||
if (! isset($GLOBALS['bBypassAutoload']) || $GLOBALS['bBypassAutoload'] == false)
|
||||
{
|
||||
if (!isset($GLOBALS['bBypassAutoload']) || $GLOBALS['bBypassAutoload'] == false) {
|
||||
require_once APPROOT.'/lib/autoload.php';
|
||||
}
|
||||
|
||||
@@ -60,8 +55,7 @@ if (! isset($GLOBALS['bBypassAutoload']) || $GLOBALS['bBypassAutoload'] == false
|
||||
//
|
||||
|
||||
// Use 'maintenance' parameter to bypass maintenance mode
|
||||
if (!isset($bBypassMaintenance))
|
||||
{
|
||||
if (!isset($bBypassMaintenance)) {
|
||||
$bBypassMaintenance = isset($_REQUEST['maintenance']) ? boolval($_REQUEST['maintenance']) : false;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"type": "project",
|
||||
"license": "AGPLv3",
|
||||
"require": {
|
||||
"php": ">=7.1.3",
|
||||
"php": ">=7.1.3 <8.0.0",
|
||||
"ext-ctype": "*",
|
||||
"ext-dom": "*",
|
||||
"ext-gd": "*",
|
||||
@@ -11,9 +11,9 @@
|
||||
"ext-mysqli": "*",
|
||||
"ext-soap": "*",
|
||||
"combodo/tcpdf": "6.3.5",
|
||||
"nikic/php-parser": "^3.1",
|
||||
"pear/archive_tar": "1.4.10",
|
||||
"pelago/emogrifier": "2.1.0",
|
||||
"nikic/php-parser": "^4.12.0",
|
||||
"pear/archive_tar": "1.4.13",
|
||||
"pelago/emogrifier": "3.1.0",
|
||||
"scssphp/scssphp": "1.0.6",
|
||||
"swiftmailer/swiftmailer": "5.4.12",
|
||||
"symfony/console": "3.4.*",
|
||||
@@ -37,24 +37,21 @@
|
||||
},
|
||||
"config": {
|
||||
"platform": {
|
||||
"php": "7.2.0"
|
||||
"php": "7.1.3"
|
||||
},
|
||||
"vendor-dir": "lib",
|
||||
"preferred-install": {
|
||||
"*": "dist"
|
||||
},
|
||||
"sort-packages": true,
|
||||
"classmap-authoritative": true
|
||||
"classmap-authoritative": true,
|
||||
"platform-check": true
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
"core",
|
||||
"application",
|
||||
"sources/application",
|
||||
"sources/Composer",
|
||||
"sources/Controller",
|
||||
"sources/Form",
|
||||
"sources/Renderer"
|
||||
"sources"
|
||||
],
|
||||
"exclude-from-classmap": [
|
||||
"core/dbobjectsearch.class.php",
|
||||
|
||||
128
composer.lock
generated
128
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "27af144ea2acf2c138f587052a4ceddc",
|
||||
"content-hash": "fb56686981ee4945791fe5b93735b022",
|
||||
"packages": [
|
||||
{
|
||||
"name": "combodo/tcpdf",
|
||||
@@ -68,24 +68,25 @@
|
||||
},
|
||||
{
|
||||
"name": "nikic/php-parser",
|
||||
"version": "v3.1.5",
|
||||
"version": "v4.12.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nikic/PHP-Parser.git",
|
||||
"reference": "bb87e28e7d7b8d9a7fda231d37457c9210faf6ce"
|
||||
"reference": "6608f01670c3cc5079e18c1dab1104e002579143"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/bb87e28e7d7b8d9a7fda231d37457c9210faf6ce",
|
||||
"reference": "bb87e28e7d7b8d9a7fda231d37457c9210faf6ce",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6608f01670c3cc5079e18c1dab1104e002579143",
|
||||
"reference": "6608f01670c3cc5079e18c1dab1104e002579143",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-tokenizer": "*",
|
||||
"php": ">=5.5"
|
||||
"php": ">=7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~4.0|~5.0"
|
||||
"ircmaxell/php-yacc": "^0.0.7",
|
||||
"phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0"
|
||||
},
|
||||
"bin": [
|
||||
"bin/php-parse"
|
||||
@@ -93,7 +94,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.0-dev"
|
||||
"dev-master": "4.9-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -115,7 +116,11 @@
|
||||
"parser",
|
||||
"php"
|
||||
],
|
||||
"time": "2018-02-28T20:30:58+00:00"
|
||||
"support": {
|
||||
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v4.12.0"
|
||||
},
|
||||
"time": "2021-07-21T10:44:31+00:00"
|
||||
},
|
||||
{
|
||||
"name": "paragonie/random_compat",
|
||||
@@ -168,16 +173,16 @@
|
||||
},
|
||||
{
|
||||
"name": "pear/archive_tar",
|
||||
"version": "1.4.10",
|
||||
"version": "1.4.13",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pear/Archive_Tar.git",
|
||||
"reference": "bbb4f10f71a1da2715ec6d9a683f4f23c507a49b"
|
||||
"reference": "2b87b41178cc6d4ad3cba678a46a1cae49786011"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pear/Archive_Tar/zipball/bbb4f10f71a1da2715ec6d9a683f4f23c507a49b",
|
||||
"reference": "bbb4f10f71a1da2715ec6d9a683f4f23c507a49b",
|
||||
"url": "https://api.github.com/repos/pear/Archive_Tar/zipball/19bb8e95490d3e3ad92fcac95500ca80bdcc7495",
|
||||
"reference": "19bb8e95490d3e3ad92fcac95500ca80bdcc7495",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -230,7 +235,21 @@
|
||||
"archive",
|
||||
"tar"
|
||||
],
|
||||
"time": "2020-09-15T14:13:23+00:00"
|
||||
"support": {
|
||||
"issues": "http://pear.php.net/bugs/search.php?cmd=display&package_name[]=Archive_Tar",
|
||||
"source": "https://github.com/pear/Archive_Tar"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/mrook",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://www.patreon.com/michielrook",
|
||||
"type": "patreon"
|
||||
}
|
||||
],
|
||||
"time": "2021-01-18T19:32:54+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pear/console_getopt",
|
||||
@@ -277,6 +296,10 @@
|
||||
}
|
||||
],
|
||||
"description": "More info available on: http://pear.php.net/package/Console_Getopt",
|
||||
"support": {
|
||||
"issues": "http://pear.php.net/bugs/search.php?cmd=display&package_name[]=Console_Getopt",
|
||||
"source": "https://github.com/pear/Console_Getopt"
|
||||
},
|
||||
"time": "2019-11-20T18:27:48+00:00"
|
||||
},
|
||||
{
|
||||
@@ -321,27 +344,31 @@
|
||||
}
|
||||
],
|
||||
"description": "Minimal set of PEAR core files to be used as composer dependency",
|
||||
"support": {
|
||||
"issues": "http://pear.php.net/bugs/search.php?cmd=display&package_name[]=PEAR",
|
||||
"source": "https://github.com/pear/pear-core-minimal"
|
||||
},
|
||||
"time": "2019-11-19T19:00:24+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pear/pear_exception",
|
||||
"version": "v1.0.1",
|
||||
"version": "v1.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pear/PEAR_Exception.git",
|
||||
"reference": "dbb42a5a0e45f3adcf99babfb2a1ba77b8ac36a7"
|
||||
"reference": "b14fbe2ddb0b9f94f5b24cf08783d599f776fff0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pear/PEAR_Exception/zipball/dbb42a5a0e45f3adcf99babfb2a1ba77b8ac36a7",
|
||||
"reference": "dbb42a5a0e45f3adcf99babfb2a1ba77b8ac36a7",
|
||||
"url": "https://api.github.com/repos/pear/PEAR_Exception/zipball/b14fbe2ddb0b9f94f5b24cf08783d599f776fff0",
|
||||
"reference": "b14fbe2ddb0b9f94f5b24cf08783d599f776fff0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=4.4.0"
|
||||
"php": ">=5.2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "*"
|
||||
"phpunit/phpunit": "<9"
|
||||
},
|
||||
"type": "class",
|
||||
"extra": {
|
||||
@@ -376,38 +403,42 @@
|
||||
"keywords": [
|
||||
"exception"
|
||||
],
|
||||
"time": "2019-12-10T10:24:42+00:00"
|
||||
"support": {
|
||||
"issues": "http://pear.php.net/bugs/search.php?cmd=display&package_name[]=PEAR_Exception",
|
||||
"source": "https://github.com/pear/PEAR_Exception"
|
||||
},
|
||||
"time": "2021-03-21T15:43:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pelago/emogrifier",
|
||||
"version": "v2.1.0",
|
||||
"version": "v3.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/MyIntervals/emogrifier.git",
|
||||
"reference": "40c3d4f475d44ffc7265a760d1dd0e81f579f96f"
|
||||
"reference": "f6a5c7d44612d86c3901c93f1592f5440e6b2cd8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/MyIntervals/emogrifier/zipball/40c3d4f475d44ffc7265a760d1dd0e81f579f96f",
|
||||
"reference": "40c3d4f475d44ffc7265a760d1dd0e81f579f96f",
|
||||
"url": "https://api.github.com/repos/MyIntervals/emogrifier/zipball/f6a5c7d44612d86c3901c93f1592f5440e6b2cd8",
|
||||
"reference": "f6a5c7d44612d86c3901c93f1592f5440e6b2cd8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-dom": "*",
|
||||
"ext-libxml": "*",
|
||||
"php": "^5.5.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0",
|
||||
"symfony/css-selector": "^3.4.0 || ^4.0.0"
|
||||
"php": "^5.6 || ~7.0 || ~7.1 || ~7.2 || ~7.3 || ~7.4",
|
||||
"symfony/css-selector": "^2.8 || ^3.0 || ^4.0 || ^5.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^2.2.0",
|
||||
"phpmd/phpmd": "^2.6.0",
|
||||
"phpunit/phpunit": "^4.8.0",
|
||||
"squizlabs/php_codesniffer": "^3.3.2"
|
||||
"friendsofphp/php-cs-fixer": "^2.15.3",
|
||||
"phpmd/phpmd": "^2.7.0",
|
||||
"phpunit/phpunit": "^5.7.27",
|
||||
"squizlabs/php_codesniffer": "^3.5.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.1.x-dev"
|
||||
"dev-master": "4.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -420,16 +451,6 @@
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "John Reeve",
|
||||
"email": "jreeve@pelagodesign.com"
|
||||
},
|
||||
{
|
||||
"name": "Cameron Brooks"
|
||||
},
|
||||
{
|
||||
"name": "Jaime Prado"
|
||||
},
|
||||
{
|
||||
"name": "Oliver Klee",
|
||||
"email": "github@oliverklee.de"
|
||||
@@ -438,9 +459,19 @@
|
||||
"name": "Zoli Szabó",
|
||||
"email": "zoli.szabo+github@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "John Reeve",
|
||||
"email": "jreeve@pelagodesign.com"
|
||||
},
|
||||
{
|
||||
"name": "Jake Hotson",
|
||||
"email": "jake@qzdesign.co.uk"
|
||||
},
|
||||
{
|
||||
"name": "Cameron Brooks"
|
||||
},
|
||||
{
|
||||
"name": "Jaime Prado"
|
||||
}
|
||||
],
|
||||
"description": "Converts CSS styles into inline style attributes in your HTML code",
|
||||
@@ -450,7 +481,11 @@
|
||||
"email",
|
||||
"pre-processing"
|
||||
],
|
||||
"time": "2018-12-08T13:55:46+00:00"
|
||||
"support": {
|
||||
"issues": "https://github.com/MyIntervals/emogrifier/issues",
|
||||
"source": "https://github.com/MyIntervals/emogrifier"
|
||||
},
|
||||
"time": "2019-12-26T19:37:31+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/cache",
|
||||
@@ -2578,7 +2613,7 @@
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
"php": ">=5.6.0",
|
||||
"php": ">=7.1.3 <8.0.0",
|
||||
"ext-ctype": "*",
|
||||
"ext-dom": "*",
|
||||
"ext-gd": "*",
|
||||
@@ -2589,6 +2624,7 @@
|
||||
},
|
||||
"platform-dev": [],
|
||||
"platform-overrides": {
|
||||
"php": "5.6.0"
|
||||
}
|
||||
"php": "7.1.3"
|
||||
},
|
||||
"plugin-api-version": "2.1.0"
|
||||
}
|
||||
|
||||
67
core/DbConnectionWrapper.php
Normal file
67
core/DbConnectionWrapper.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core;
|
||||
|
||||
use mysqli;
|
||||
|
||||
/**
|
||||
* mysqli object is really hard to mock as it contains lots of attributes & methods ! Thought we need to mock it to test transactions !
|
||||
*
|
||||
* To solve this, a new attribute exists and is only used in specific use cases, so there are just few things to mock.
|
||||
*
|
||||
* This object adds more readability than previous model with 2 attributes in {@see CMDBSource}.
|
||||
*
|
||||
* @used-by \CMDBSource
|
||||
*
|
||||
* @since 3.0.0 N°4325 Object creation
|
||||
* This wrapper handles the 2 {@mysqli myqsli} attributes that were previously in {@see CMDBSource}
|
||||
* To allow testing we added a second mysqli object (N°3513 in 2.7.5) and code became a bit confusing :/
|
||||
* With this wrapper everything is in the same place, and we can express the intention more clearly !
|
||||
*/
|
||||
class DbConnectionWrapper
|
||||
{
|
||||
/** @var mysqli */
|
||||
protected static $oDbCnxStandard;
|
||||
|
||||
/**
|
||||
* Can contain a genuine mysqli object, or a mock that would emulate {@see mysqli::query()}
|
||||
*
|
||||
* @var mysqli
|
||||
* @used-by \Combodo\iTop\Test\UnitTest\Core\TransactionsTest
|
||||
*/
|
||||
protected static $oDbCnxMockableForQuery;
|
||||
|
||||
/**
|
||||
* @param bool $bIsForQuery set to true if using {@see mysqli::query()}
|
||||
*
|
||||
* @return \mysqli|null
|
||||
*/
|
||||
public static function GetDbConnection(bool $bIsForQuery = false): ?mysqli
|
||||
{
|
||||
if ($bIsForQuery) {
|
||||
return static::$oDbCnxMockableForQuery;
|
||||
}
|
||||
|
||||
return static::$oDbCnxStandard;
|
||||
}
|
||||
|
||||
public static function SetDbConnection(mysqli $oMysqli): void
|
||||
{
|
||||
static::$oDbCnxStandard = $oMysqli;
|
||||
static::SetDbConnectionMockForQuery($oMysqli);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this to register a mock that will handle {@see mysqli::query()}
|
||||
*
|
||||
* @param \mysqli $oMysqli
|
||||
*/
|
||||
public static function SetDbConnectionMockForQuery(mysqli $oMysqli): void
|
||||
{
|
||||
static::$oDbCnxMockableForQuery = $oMysqli;
|
||||
}
|
||||
}
|
||||
@@ -66,9 +66,8 @@ class MyHelpers
|
||||
// getmicrotime()
|
||||
// format sss.mmmuuupppnnn
|
||||
public static function getmicrotime()
|
||||
{
|
||||
list($usec, $sec) = explode(" ",microtime());
|
||||
return ((float)$usec + (float)$sec);
|
||||
{
|
||||
return microtime(true);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -388,7 +388,12 @@ class AsyncSendEmail extends AsyncTask
|
||||
return "Bug - the email should be sent in synchronous mode";
|
||||
|
||||
case EMAIL_SEND_ERROR:
|
||||
return "Failed: ".implode(', ', $aIssues);
|
||||
if (is_array($aIssues)) {
|
||||
$sMessage = "Sending eMail failed: ".implode(', ', $aIssues);
|
||||
} else {
|
||||
$sMessage = "Sending eMail failed.";
|
||||
}
|
||||
throw new Exception($sMessage);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -1,25 +1,13 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2013-2021 Combodo SARL
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
* iTop is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* iTop is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Application\UI\Base\Component\FieldBadge\FieldBadgeUIBlockFactory;
|
||||
use Combodo\iTop\Form\Field\LabelField;
|
||||
use Combodo\iTop\Form\Field\TextAreaField;
|
||||
use Combodo\iTop\Form\Form;
|
||||
use Combodo\iTop\Form\Validator\NotEmptyExtKeyValidator;
|
||||
use Combodo\iTop\Form\Validator\Validator;
|
||||
use Combodo\iTop\Renderer\BlockRenderer;
|
||||
@@ -317,6 +305,8 @@ abstract class AttributeDefinition
|
||||
*/
|
||||
public function ListDBJoins()
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod();
|
||||
|
||||
return "";
|
||||
// e.g: return array("Site", "infrid", "name");
|
||||
}
|
||||
@@ -842,9 +832,9 @@ abstract class AttributeDefinition
|
||||
*/
|
||||
public function MakeValue()
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod();
|
||||
$sComputeFunc = $this->Get("compute_func");
|
||||
if (empty($sComputeFunc))
|
||||
{
|
||||
if (empty($sComputeFunc)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -4133,7 +4123,8 @@ class AttributeText extends AttributeString
|
||||
{
|
||||
// Propose a std link to the object
|
||||
$sClassLabel = MetaModel::GetName($sClass);
|
||||
$sReplacement = "<span class=\"wiki_broken_link\">$sClassLabel:$sName" . (!empty($sLabel) ? " ($sLabel)" : "") . "</span>";
|
||||
$sToolTipForHtml = utils::EscapeHtml(Dict::Format('Core:UnknownObjectLabel', $sClass, $sName));
|
||||
$sReplacement = "<span class=\"wiki_broken_link ibo-is-broken-hyperlink\" data-tooltip-content=\"$sToolTipForHtml\">$sClassLabel:$sName" . (!empty($sLabel) ? " ($sLabel)" : "") . "</span>";
|
||||
$sText = str_replace($aMatches[0], $sReplacement, $sText);
|
||||
// Later: propose a link to create a new object
|
||||
// Anyhow... there is no easy way to suggest default values based on the given FRIENDLY name
|
||||
@@ -4169,13 +4160,13 @@ class AttributeText extends AttributeString
|
||||
$sValue = parent::GetAsHTML($sValue, $oHostObject, $bLocalize);
|
||||
$sValue = self::RenderWikiHtml($sValue);
|
||||
|
||||
return "<div $sStyle>".str_replace("\n", "<br>\n", $sValue).'</div>';
|
||||
return "<div $sStyle>$sValue</div>";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sValue = self::RenderWikiHtml($sValue, true /* wiki only */);
|
||||
|
||||
return "<div class=\"HTML\" $sStyle>".InlineImage::FixUrls($sValue).'</div>';
|
||||
return "<div class=\"HTML ibo-is-html-content\" $sStyle>".InlineImage::FixUrls($sValue).'</div>';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6818,23 +6809,6 @@ class AttributeExternalKey extends AttributeDBFieldVoid
|
||||
return (int)$proposedValue;
|
||||
}
|
||||
|
||||
public function GetPrerequisiteAttributes($sClass = null)
|
||||
{
|
||||
$aAttributes = parent::GetPrerequisiteAttributes($sClass);
|
||||
$oExpression = DBSearch::FromOQL($this->GetValuesDef()->GetFilterExpression())->GetCriteria();
|
||||
foreach ($oExpression->GetParameters('this') as $sAttCode)
|
||||
{
|
||||
// Skip the id as it cannot change anyway
|
||||
if ($sAttCode =='id') continue;
|
||||
|
||||
if (!in_array($sAttCode, $aAttributes))
|
||||
{
|
||||
$aAttributes[] = $sAttCode;
|
||||
}
|
||||
}
|
||||
return $aAttributes;
|
||||
}
|
||||
|
||||
public function GetMaximumComboLength()
|
||||
{
|
||||
return $this->GetOptional('max_combo_length', MetaModel::GetConfig()->Get('max_combo_length'));
|
||||
@@ -6845,6 +6819,15 @@ class AttributeExternalKey extends AttributeDBFieldVoid
|
||||
return $this->GetOptional('min_autocomplete_chars', MetaModel::GetConfig()->Get('min_autocomplete_chars'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function GetMaxAutoCompleteResults(): int
|
||||
{
|
||||
return MetaModel::GetConfig()->Get('max_autocomplete_results');
|
||||
}
|
||||
|
||||
public function AllowTargetCreation()
|
||||
{
|
||||
return $this->GetOptional('allow_target_creation', MetaModel::GetConfig()->Get('allow_target_creation'));
|
||||
@@ -6889,8 +6872,7 @@ class AttributeExternalKey extends AttributeDBFieldVoid
|
||||
|
||||
public function MakeFormField(DBObject $oObject, $oFormField = null)
|
||||
{
|
||||
if ($oFormField === null)
|
||||
{
|
||||
if ($oFormField === null) {
|
||||
// Later : We should check $this->Get('display_style') and create a Radio / Select / ... regarding its value
|
||||
$sFormFieldClass = static::GetFormFieldClass();
|
||||
$oFormField = new $sFormFieldClass($this->GetCode());
|
||||
@@ -6899,11 +6881,11 @@ class AttributeExternalKey extends AttributeDBFieldVoid
|
||||
// Setting params
|
||||
$oFormField->SetMaximumComboLength($this->GetMaximumComboLength());
|
||||
$oFormField->SetMinAutoCompleteChars($this->GetMinAutoCompleteChars());
|
||||
$oFormField->SetMaxAutoCompleteResults($this->GetMaxAutoCompleteResults());
|
||||
$oFormField->SetHierarchical(MetaModel::IsHierarchicalClass($this->GetTargetClass()));
|
||||
// Setting choices regarding the field dependencies
|
||||
$aFieldDependencies = $this->GetPrerequisiteAttributes();
|
||||
if (!empty($aFieldDependencies))
|
||||
{
|
||||
if (!empty($aFieldDependencies)) {
|
||||
$oTmpAttDef = $this;
|
||||
$oTmpField = $oFormField;
|
||||
$oFormField->SetOnFinalizeCallback(function () use ($oTmpField, $oTmpAttDef, $oObject) {
|
||||
@@ -10301,7 +10283,6 @@ abstract class AttributeSet extends AttributeDBFieldVoid
|
||||
} else {
|
||||
$sTooltipContent = <<<HTML
|
||||
<h4>$sLabel</h4>
|
||||
<br>
|
||||
<div>$sDescription</div>
|
||||
HTML;
|
||||
$sTooltipHtmlEnabled = 'true';
|
||||
@@ -11592,7 +11573,6 @@ class AttributeTagSet extends AttributeSet
|
||||
} else {
|
||||
$sTooltipContent = <<<HTML
|
||||
<h4>$sTagLabel</h4>
|
||||
<br>
|
||||
<div>$sTagDescription</div>
|
||||
HTML;
|
||||
$sTooltipHtmlEnabled = 'true';
|
||||
@@ -11796,13 +11776,12 @@ class AttributeFriendlyName extends AttributeDefinition
|
||||
// Code duplicated with AttributeObsolescenceFlag
|
||||
$aAttributes = $this->GetOptional("depends_on", array());
|
||||
$oExpression = $this->GetOQLExpression();
|
||||
foreach ($oExpression->ListRequiredFields() as $sClass => $sAttCode)
|
||||
{
|
||||
if (!in_array($sAttCode, $aAttributes))
|
||||
{
|
||||
foreach ($oExpression->ListRequiredFields() as $sAttCode) {
|
||||
if (!in_array($sAttCode, $aAttributes)) {
|
||||
$aAttributes[] = $sAttCode;
|
||||
}
|
||||
}
|
||||
|
||||
return $aAttributes;
|
||||
}
|
||||
|
||||
@@ -12564,10 +12543,12 @@ class AttributeCustomFields extends AttributeDefinition
|
||||
*/
|
||||
public function ReadValueFromPostedForm($oHostObject, $sFormPrefix)
|
||||
{
|
||||
$aRawData = json_decode(utils::ReadPostedParam("attr_{$sFormPrefix}{$this->GetCode()}", '{}', 'raw_data'),
|
||||
true);
|
||||
|
||||
return new ormCustomFieldsValue($oHostObject, $this->GetCode(), $aRawData);
|
||||
$aRawData = json_decode(utils::ReadPostedParam("attr_{$sFormPrefix}{$this->GetCode()}", '{}', 'raw_data'), true);
|
||||
if ($aRawData != null) {
|
||||
return new ormCustomFieldsValue($oHostObject, $this->GetCode(), $aRawData);
|
||||
} else{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function MakeRealValue($proposedValue, $oHostObject)
|
||||
@@ -12626,16 +12607,15 @@ class AttributeCustomFields extends AttributeDefinition
|
||||
*/
|
||||
public function GetForm(DBObject $oHostObject, $sFormPrefix = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
try {
|
||||
$oValue = $oHostObject->Get($this->GetCode());
|
||||
$oHandler = $this->GetHandler($oValue->GetValues());
|
||||
$sFormId = is_null($sFormPrefix) ? 'cf_'.$this->GetCode() : $sFormPrefix.'_cf_'.$this->GetCode();
|
||||
$oHandler->BuildForm($oHostObject, $sFormId);
|
||||
$oForm = $oHandler->GetForm();
|
||||
} catch (Exception $e)
|
||||
{
|
||||
$oForm = new \Combodo\iTop\Form\Form('');
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$oForm = new Form('');
|
||||
$oField = new LabelField('');
|
||||
$oField->SetLabel('Custom field error: '.$e->getMessage());
|
||||
$oForm->AddField($oField);
|
||||
|
||||
@@ -452,20 +452,23 @@ class BulkChange
|
||||
foreach ($this->m_aAttList as $sAttCode => $iCol)
|
||||
{
|
||||
// skip the private key, if any
|
||||
if ($sAttCode == 'id') continue;
|
||||
if (($sAttCode == 'id') || ($sAttCode == 'friendlyname')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
|
||||
|
||||
// skip reconciliation keys
|
||||
if (!$oAttDef->IsWritable() && in_array($sAttCode, $this->m_aReconcilKeys)){ continue; }
|
||||
if (!$oAttDef->IsWritable() && in_array($sAttCode, $this->m_aReconcilKeys)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$aReasons = array();
|
||||
$iFlags = ($oTargetObj->IsNew())
|
||||
? $oTargetObj->GetInitialStateAttributeFlags($sAttCode, $aReasons)
|
||||
: $oTargetObj->GetAttributeFlags($sAttCode, $aReasons);
|
||||
if ( (($iFlags & OPT_ATT_READONLY) == OPT_ATT_READONLY) && ( $oTargetObj->Get($sAttCode) != $aRowData[$iCol]) )
|
||||
{
|
||||
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-Readonly', $sAttCode, $oTargetObj->Get($sAttCode), $aRowData[$iCol]);
|
||||
if ( (($iFlags & OPT_ATT_READONLY) == OPT_ATT_READONLY) && ( $oTargetObj->Get($sAttCode) != $aRowData[$iCol]) ) {
|
||||
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-Readonly', $sAttCode, $oTargetObj->Get($sAttCode), $aRowData[$iCol]);
|
||||
}
|
||||
else if ($oAttDef->IsLinkSet() && $oAttDef->IsIndirect())
|
||||
{
|
||||
@@ -1180,16 +1183,16 @@ class BulkChange
|
||||
$oPage->add('<p>'.$sCollapsedLabel.' <a class="truncated" onclick="OnTruncatedHistoryToggle(true);">'.$sLinkLabel.'</p>');
|
||||
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
<<<EOF
|
||||
$('#$sAjaxDivId table.listResults').addClass('truncated');
|
||||
$('#$sAjaxDivId table.listResults tr:last td').addClass('truncated');
|
||||
EOF
|
||||
);
|
||||
|
||||
|
||||
|
||||
$sAppContext = $oAppContext->GetForLink();
|
||||
$oPage->add_script(
|
||||
<<<EOF
|
||||
<<<EOF
|
||||
function OnTruncatedHistoryToggle(bShowAll)
|
||||
{
|
||||
$('#csv_history_reload').html('<img src="../images/indicator.gif"/>');
|
||||
|
||||
@@ -376,10 +376,11 @@ abstract class BulkExport
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 3.0 replaced by GetFormPart
|
||||
* @deprecated 3.0.0 use GetFormPart instead
|
||||
*/
|
||||
public function DisplayFormPart(WebPage $oP, $sPartId)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use GetFormPart instead');
|
||||
$oP->AddSubBlock($this->GetFormPart($oP, $sPartId));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,22 +1,4 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2021 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Persistent class (internal) cmdbChange
|
||||
*
|
||||
@@ -24,6 +6,7 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Core\CMDBChange\CMDBChangeOrigin;
|
||||
|
||||
/**
|
||||
* A change as requested/validated at once by user, may groups many atomic changes
|
||||
@@ -53,7 +36,7 @@ class CMDBChange extends DBObject
|
||||
MetaModel::Init_AddAttribute(new AttributeDateTime("date", array("allowed_values"=>null, "sql"=>"date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("userinfo", array("allowed_values"=>null, "sql"=>"userinfo", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", array("allowed_values"=>null, "sql"=>"user_id", "targetclass"=>"User", "is_null_allowed"=>true, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeEnum("origin", array("allowed_values"=>new ValueSetEnum('interactive,csv-interactive,csv-import.php,webservice-soap,webservice-rest,synchro-data-source,email-processing,custom-extension'), "sql"=>"origin", "default_value"=>"interactive", "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeEnum("origin", array("allowed_values"=>new ValueSetEnum(implode(',', [CMDBChangeOrigin::INTERACTIVE, CMDBChangeOrigin::CSV_INTERACTIVE, CMDBChangeOrigin::CSV_IMPORT, CMDBChangeOrigin::WEBSERVICE_SOAP, CMDBChangeOrigin::WEBSERVICE_REST, CMDBChangeOrigin::SYNCHRO_DATA_SOURCE, CMDBChangeOrigin::EMAIL_PROCESSING, CMDBChangeOrigin::CUSTOM_EXTENSION])), "sql"=>"origin", "default_value"=>CMDBChangeOrigin::INTERACTIVE, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -436,28 +436,31 @@ class CMDBChangeOpSetAttributeBlob extends CMDBChangeOpSetAttribute
|
||||
$oMonoObjectSet = new DBObjectSet($oTargetSearch);
|
||||
if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES)
|
||||
{
|
||||
if (MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode')))
|
||||
{
|
||||
if (MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode'))) {
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
|
||||
$sAttName = $oAttDef->GetLabel();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// The attribute was renamed or removed from the object ?
|
||||
$sAttName = $this->Get('attcode');
|
||||
}
|
||||
/** @var \ormDocument $oPrevDoc */
|
||||
$oPrevDoc = $this->Get('prevdata');
|
||||
if ($oPrevDoc->IsEmpty())
|
||||
{
|
||||
if ($oPrevDoc->IsEmpty()) {
|
||||
$sPrevious = '';
|
||||
$sResult = Dict::Format('Change:AttName_Changed_PreviousValue_OldValue', $sAttName, $sPrevious);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sDocView = $oPrevDoc->GetAsHtml();
|
||||
$sDocView .= "<br/>".Dict::Format('UI:OpenDocumentInNewWindow_', $oPrevDoc->GetDisplayLink(get_class($this), $this->GetKey(), 'prevdata')).", \n";
|
||||
$sDocView .= Dict::Format('UI:DownloadDocument_', $oPrevDoc->GetDownloadLink(get_class($this), $this->GetKey(), 'prevdata'))."\n";
|
||||
//$sDocView = $oPrevDoc->GetDisplayInline(get_class($this), $this->GetKey(), 'prevdata');
|
||||
} else {
|
||||
$sFieldAsHtml = $oPrevDoc->GetAsHTML();
|
||||
|
||||
$sDisplayLabel = Dict::S('UI:OpenDocumentInNewWindow_');
|
||||
$sDisplayUrl = $oPrevDoc->GetDisplayURL(get_class($this), $this->GetKey(), 'prevdata');
|
||||
|
||||
$sDownloadLabel = Dict::Format('UI:DownloadDocument_');
|
||||
$sDownloadUrl = $oPrevDoc->GetDownloadURL(get_class($this), $this->GetKey(), 'prevdata');
|
||||
|
||||
$sDocView = <<<HTML
|
||||
{$sFieldAsHtml}
|
||||
<a href="{$sDisplayUrl}" target="_blank">{$sDisplayLabel}</a> / <a href="{$sDownloadUrl}">{$sDownloadLabel}</a>
|
||||
HTML;
|
||||
$sResult = Dict::Format('Change:AttName_Changed_PreviousValue_OldValue', $sAttName, $sDocView);
|
||||
}
|
||||
}
|
||||
@@ -770,18 +773,15 @@ class CMDBChangeOpSetAttributeHTML extends CMDBChangeOpSetAttributeLongText
|
||||
$oMonoObjectSet = new DBObjectSet($oTargetSearch);
|
||||
if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES)
|
||||
{
|
||||
if (MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode')))
|
||||
{
|
||||
if (MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode'))) {
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
|
||||
$sAttName = $oAttDef->GetLabel();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// The attribute was renamed or removed from the object ?
|
||||
$sAttName = $this->Get('attcode');
|
||||
}
|
||||
$sTextView = '<div class="history_entry history_entry_truncated"><div class="history_html_content">'.$this->Get('prevdata').'</div></div>';
|
||||
|
||||
$sTextView = $this->Get('prevdata');
|
||||
|
||||
//$sDocView = $oPrevDoc->GetDisplayInline(get_class($this), $this->GetKey(), 'prevdata');
|
||||
$sResult = Dict::Format('Change:AttName_Changed_PreviousValue_OldValue', $sAttName, $sTextView);
|
||||
}
|
||||
|
||||
@@ -566,8 +566,8 @@ abstract class CMDBObject extends DBObject
|
||||
*/
|
||||
protected function CheckUserRights($bSkipStrongSecurity, $iActionCode)
|
||||
{
|
||||
if (is_null($bSkipStrongSecurity))
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod();
|
||||
if (is_null($bSkipStrongSecurity)) {
|
||||
// This is temporary
|
||||
// We have implemented this safety net right before releasing iTop 1.0
|
||||
// and we decided that it was too risky to activate it
|
||||
|
||||
@@ -24,13 +24,15 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Core\DbConnectionWrapper;
|
||||
|
||||
require_once('MyHelpers.class.inc.php');
|
||||
require_once(APPROOT.'core/kpi.class.inc.php');
|
||||
|
||||
|
||||
/**
|
||||
* CMDBSource
|
||||
* database access wrapper
|
||||
* database access wrapper
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
@@ -66,9 +68,6 @@ class CMDBSource
|
||||
*/
|
||||
protected static $m_sDBTlsCA;
|
||||
|
||||
/** @var mysqli $m_oMysqli */
|
||||
protected static $m_oMysqli;
|
||||
|
||||
/**
|
||||
* @var int number of level for nested transactions : 0 if no transaction was ever opened, +1 for each 'START TRANSACTION' sent
|
||||
* @since 2.7.0 N°679
|
||||
@@ -133,7 +132,8 @@ class CMDBSource
|
||||
self::$m_bDBTlsEnabled = empty($bTlsEnabled) ? false : $bTlsEnabled;
|
||||
self::$m_sDBTlsCA = empty($sTlsCA) ? null : $sTlsCA;
|
||||
|
||||
self::$m_oMysqli = self::GetMysqliInstance($sServer, $sUser, $sPwd, $sSource, $bTlsEnabled, $sTlsCA, true);
|
||||
$oMysqli = self::GetMysqliInstance($sServer, $sUser, $sPwd, $sSource, $bTlsEnabled, $sTlsCA, true);
|
||||
DbConnectionWrapper::SetDbConnection($oMysqli);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -151,8 +151,6 @@ class CMDBSource
|
||||
public static function GetMysqliInstance(
|
||||
$sDbHost, $sUser, $sPwd, $sSource = '', $bTlsEnabled = false, $sTlsCa = null, $bCheckTlsAfterConnection = false
|
||||
) {
|
||||
$oMysqli = null;
|
||||
|
||||
$sServer = null;
|
||||
$iPort = null;
|
||||
self::InitServerAndPort($sDbHost, $sServer, $iPort);
|
||||
@@ -182,7 +180,7 @@ class CMDBSource
|
||||
}
|
||||
catch(mysqli_sql_exception $e)
|
||||
{
|
||||
throw new MySQLException('Could not connect to the DB server', array('host' => $sServer, 'user' => $sUser), $e);
|
||||
throw new MySQLException('Could not connect to the DB server', array('host' => $sServer, 'user' => $sUser),$e);
|
||||
}
|
||||
|
||||
if ($bTlsEnabled
|
||||
@@ -251,7 +249,7 @@ class CMDBSource
|
||||
* parameters were used.<br>
|
||||
* This method can be called to ensure that the DB connection really uses TLS.
|
||||
*
|
||||
* <p>We're using this object connection : {@link self::$m_oMysqli}
|
||||
* <p>We're using this object connection : {@see self::$m_oMysqli}
|
||||
*
|
||||
* @param \mysqli $oMysqli
|
||||
*
|
||||
@@ -344,7 +342,8 @@ class CMDBSource
|
||||
{
|
||||
// In case we don't have rights to enumerate the databases
|
||||
// Let's try to connect directly
|
||||
return @((bool)self::$m_oMysqli->query("USE `$sSource`"));
|
||||
/** @noinspection NullPointerExceptionInspection this shouldn't be called with un-init DB */
|
||||
return @((bool)DbConnectionWrapper::GetDbConnection(true)->query("USE `$sSource`"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -360,7 +359,7 @@ class CMDBSource
|
||||
*/
|
||||
public static function GetServerInfo()
|
||||
{
|
||||
return mysqli_get_server_info ( self::$m_oMysqli );
|
||||
return mysqli_get_server_info(DbConnectionWrapper::GetDbConnection());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -391,9 +390,9 @@ class CMDBSource
|
||||
*/
|
||||
public static function SelectDB($sSource)
|
||||
{
|
||||
if (!((bool)self::$m_oMysqli->query("USE `$sSource`")))
|
||||
{
|
||||
throw new MySQLException('Could not select DB', array('db_name'=>$sSource));
|
||||
/** @noinspection NullPointerExceptionInspection this shouldn't be called with un-init DB */
|
||||
if (!((bool)DbConnectionWrapper::GetDbConnection(true)->query("USE `$sSource`"))) {
|
||||
throw new MySQLException('Could not select DB', array('db_name' => $sSource));
|
||||
}
|
||||
self::$m_sDBName = $sSource;
|
||||
}
|
||||
@@ -444,33 +443,29 @@ class CMDBSource
|
||||
|
||||
/**
|
||||
* @return \mysqli
|
||||
*
|
||||
* @since 2.5.0 N°1260
|
||||
*/
|
||||
public static function GetMysqli()
|
||||
{
|
||||
return self::$m_oMysqli;
|
||||
return DbConnectionWrapper::GetDbConnection(false);
|
||||
}
|
||||
|
||||
public static function GetErrNo()
|
||||
{
|
||||
if (self::$m_oMysqli->errno != 0)
|
||||
{
|
||||
return self::$m_oMysqli->errno;
|
||||
}
|
||||
else
|
||||
{
|
||||
return self::$m_oMysqli->connect_errno;
|
||||
if (DbConnectionWrapper::GetDbConnection()->errno != 0) {
|
||||
return DbConnectionWrapper::GetDbConnection()->errno;
|
||||
} else {
|
||||
return DbConnectionWrapper::GetDbConnection()->connect_errno;
|
||||
}
|
||||
}
|
||||
|
||||
public static function GetError()
|
||||
{
|
||||
if (self::$m_oMysqli->error != '')
|
||||
{
|
||||
return self::$m_oMysqli->error;
|
||||
}
|
||||
else
|
||||
{
|
||||
return self::$m_oMysqli->connect_error;
|
||||
if (DbConnectionWrapper::GetDbConnection()->error != '') {
|
||||
return DbConnectionWrapper::GetDbConnection()->error;
|
||||
} else {
|
||||
return DbConnectionWrapper::GetDbConnection()->connect_error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -510,7 +505,8 @@ class CMDBSource
|
||||
// Quote if not a number or a numeric string
|
||||
if ($bAlways || is_string($value))
|
||||
{
|
||||
$value = $cQuoteStyle . self::$m_oMysqli->real_escape_string($value) . $cQuoteStyle;
|
||||
/** @noinspection NullPointerExceptionInspection this shouldn't be called with un-init DB */
|
||||
$value = $cQuoteStyle.DbConnectionWrapper::GetDbConnection()->real_escape_string($value).$cQuoteStyle;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
@@ -571,7 +567,7 @@ class CMDBSource
|
||||
/**
|
||||
* Send the query directly to the DB. **Be extra cautious with this !**
|
||||
*
|
||||
* Use {@link Query} if you're not sure.
|
||||
* Use {@see Query} if you're not sure.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
@@ -585,29 +581,33 @@ class CMDBSource
|
||||
*/
|
||||
private static function DBQuery($sSql)
|
||||
{
|
||||
$sShortSQL = substr(preg_replace("/\s+/", " ", substr($sSql, 0, 180)), 0, 150);
|
||||
if (substr_compare($sShortSQL, "SELECT", 0, strlen("SELECT")) !== 0) {
|
||||
IssueLog::Trace("$sShortSQL", LogChannels::CMDB_SOURCE);
|
||||
}
|
||||
|
||||
$oKPI = new ExecutionKPI();
|
||||
try
|
||||
{
|
||||
$oResult = self::$m_oMysqli->query($sSql);
|
||||
/** @noinspection NullPointerExceptionInspection this shouldn't be called with un-init DB */
|
||||
$oResult = DbConnectionWrapper::GetDbConnection(true)->query($sSql);
|
||||
}
|
||||
catch (mysqli_sql_exception $e)
|
||||
{
|
||||
self::LogDeadLock($e);
|
||||
self::LogDeadLock($e, true);
|
||||
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
|
||||
}
|
||||
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
|
||||
if ($oResult === false)
|
||||
{
|
||||
if ($oResult === false) {
|
||||
$aContext = array('query' => $sSql);
|
||||
|
||||
$iMySqlErrorNo = self::$m_oMysqli->errno;
|
||||
$iMySqlErrorNo = DbConnectionWrapper::GetDbConnection(true)->errno;
|
||||
$aMySqlHasGoneAwayErrorCodes = MySQLHasGoneAwayException::getErrorCodes();
|
||||
if (in_array($iMySqlErrorNo, $aMySqlHasGoneAwayErrorCodes))
|
||||
{
|
||||
if (in_array($iMySqlErrorNo, $aMySqlHasGoneAwayErrorCodes)) {
|
||||
throw new MySQLHasGoneAwayException(self::GetError(), $aContext);
|
||||
}
|
||||
$e = new MySQLException('Failed to issue SQL query', $aContext);
|
||||
self::LogDeadLock($e);
|
||||
self::LogDeadLock($e, true);
|
||||
throw $e;
|
||||
}
|
||||
|
||||
@@ -616,23 +616,24 @@ class CMDBSource
|
||||
|
||||
/**
|
||||
* @param \Exception $e
|
||||
* @param bool $bForQuery to get the proper DB connection
|
||||
*
|
||||
* @since 2.7.1
|
||||
* @since 3.0.0 N°4325 add new optional parameter to use the correct DB connection
|
||||
*/
|
||||
private static function LogDeadLock(Exception $e)
|
||||
private static function LogDeadLock(Exception $e, $bForQuery = false)
|
||||
{
|
||||
// checks MySQL error code
|
||||
$iMySqlErrorNo = self::$m_oMysqli->errno;
|
||||
if (!in_array($iMySqlErrorNo, array(self::MYSQL_ERRNO_WAIT_TIMEOUT, self::MYSQL_ERRNO_DEADLOCK)))
|
||||
{
|
||||
$iMySqlErrorNo = DbConnectionWrapper::GetDbConnection($bForQuery)->errno;
|
||||
if (!in_array($iMySqlErrorNo, array(self::MYSQL_ERRNO_WAIT_TIMEOUT, self::MYSQL_ERRNO_DEADLOCK))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get error info
|
||||
$sUser = UserRights::GetUser();
|
||||
$oError = self::$m_oMysqli->query('SHOW ENGINE INNODB STATUS');
|
||||
if ($oError !== false)
|
||||
{
|
||||
/** @noinspection NullPointerExceptionInspection this shouldn't be called with un-init DB */
|
||||
$oError = DbConnectionWrapper::GetDbConnection(true)->query('SHOW ENGINE INNODB STATUS');
|
||||
if ($oError !== false) {
|
||||
$aData = $oError->fetch_all(MYSQLI_ASSOC);
|
||||
$sInnodbStatus = $aData[0];
|
||||
}
|
||||
@@ -652,7 +653,7 @@ class CMDBSource
|
||||
);
|
||||
DeadLockLog::Info($sMessage, $iMySqlErrorNo, $aLogContext);
|
||||
|
||||
IssueLog::Error($sMessage, 'DeadLock', $e->getMessage());
|
||||
IssueLog::Error($sMessage, LogChannels::DEADLOCK, $e->getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -668,10 +669,14 @@ class CMDBSource
|
||||
*/
|
||||
private static function StartTransaction()
|
||||
{
|
||||
$aStackTrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT , 3);
|
||||
$sCaller = 'From '.$aStackTrace[1]['file'].'('.$aStackTrace[1]['line'].'): '.$aStackTrace[2]['class'].'->'.$aStackTrace[2]['function'].'()';
|
||||
$bHasExistingTransactions = self::IsInsideTransaction();
|
||||
if (!$bHasExistingTransactions)
|
||||
{
|
||||
if (!$bHasExistingTransactions) {
|
||||
IssueLog::Trace("START TRANSACTION $sCaller", LogChannels::CMDB_SOURCE);
|
||||
self::DBQuery('START TRANSACTION');
|
||||
} else {
|
||||
IssueLog::Trace("Ignore nested (".self::$m_iTransactionLevel.") START TRANSACTION $sCaller", LogChannels::CMDB_SOURCE);
|
||||
}
|
||||
|
||||
self::AddTransactionLevel();
|
||||
@@ -689,18 +694,22 @@ class CMDBSource
|
||||
*/
|
||||
private static function Commit()
|
||||
{
|
||||
if (!self::IsInsideTransaction())
|
||||
{
|
||||
$aStackTrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT , 3);
|
||||
$sCaller = 'From '.$aStackTrace[1]['file'].'('.$aStackTrace[1]['line'].'): '.$aStackTrace[2]['class'].'->'.$aStackTrace[2]['function'].'()';
|
||||
if (!self::IsInsideTransaction()) {
|
||||
// should not happen !
|
||||
IssueLog::Error("No Transaction COMMIT $sCaller", LogChannels::CMDB_SOURCE);
|
||||
throw new MySQLNoTransactionException('Trying to commit transaction whereas none have been started !', null);
|
||||
}
|
||||
|
||||
self::RemoveLastTransactionLevel();
|
||||
|
||||
if (self::IsInsideTransaction())
|
||||
{
|
||||
if (self::IsInsideTransaction()) {
|
||||
IssueLog::Trace("Ignore nested (".self::$m_iTransactionLevel.") COMMIT $sCaller", LogChannels::CMDB_SOURCE);
|
||||
|
||||
return;
|
||||
}
|
||||
IssueLog::Trace("COMMIT $sCaller", LogChannels::CMDB_SOURCE);
|
||||
self::DBQuery('COMMIT');
|
||||
}
|
||||
|
||||
@@ -719,17 +728,21 @@ class CMDBSource
|
||||
*/
|
||||
private static function Rollback()
|
||||
{
|
||||
if (!self::IsInsideTransaction())
|
||||
{
|
||||
$aStackTrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT , 3);
|
||||
$sCaller = 'From '.$aStackTrace[1]['file'].'('.$aStackTrace[1]['line'].'): '.$aStackTrace[2]['class'].'->'.$aStackTrace[2]['function'].'()';
|
||||
if (!self::IsInsideTransaction()) {
|
||||
// should not happen !
|
||||
IssueLog::Error("No Transaction ROLLBACK $sCaller", LogChannels::CMDB_SOURCE);
|
||||
throw new MySQLNoTransactionException('Trying to commit transaction whereas none have been started !', null);
|
||||
}
|
||||
self::RemoveLastTransactionLevel();
|
||||
if (self::IsInsideTransaction())
|
||||
{
|
||||
if (self::IsInsideTransaction()) {
|
||||
IssueLog::Trace("Ignore nested (".self::$m_iTransactionLevel.") ROLLBACK $sCaller", LogChannels::CMDB_SOURCE);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
IssueLog::Trace("ROLLBACK $sCaller", LogChannels::CMDB_SOURCE);
|
||||
self::DBQuery('ROLLBACK');
|
||||
}
|
||||
|
||||
@@ -779,9 +792,21 @@ class CMDBSource
|
||||
self::$m_iTransactionLevel = 0;
|
||||
}
|
||||
|
||||
public static function IsDeadlockException(Exception $e)
|
||||
{
|
||||
while ($e instanceof Exception) {
|
||||
if (($e instanceof MySQLException) && ($e->getCode() == 1213)) {
|
||||
return true;
|
||||
}
|
||||
$e = $e->getPrevious();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public static function GetInsertId()
|
||||
{
|
||||
$iRes = self::$m_oMysqli->insert_id;
|
||||
$iRes = DbConnectionWrapper::GetDbConnection()->insert_id;
|
||||
if (is_null($iRes))
|
||||
{
|
||||
return 0;
|
||||
@@ -823,7 +848,8 @@ class CMDBSource
|
||||
$oKPI = new ExecutionKPI();
|
||||
try
|
||||
{
|
||||
$oResult = self::$m_oMysqli->query($sSql);
|
||||
/** @noinspection NullPointerExceptionInspection this shouldn't be called with un-init DB */
|
||||
$oResult = DbConnectionWrapper::GetDbConnection(true)->query($sSql);
|
||||
}
|
||||
catch(mysqli_sql_exception $e)
|
||||
{
|
||||
@@ -853,17 +879,19 @@ class CMDBSource
|
||||
|
||||
/**
|
||||
* @param string $sSql
|
||||
* @param int $iMode
|
||||
*
|
||||
* @return array
|
||||
* @throws \MySQLException if query cannot be processed
|
||||
*/
|
||||
public static function QueryToArray($sSql)
|
||||
public static function QueryToArray($sSql, $iMode = MYSQLI_BOTH)
|
||||
{
|
||||
$aData = array();
|
||||
$oKPI = new ExecutionKPI();
|
||||
try
|
||||
{
|
||||
$oResult = self::$m_oMysqli->query($sSql);
|
||||
/** @noinspection NullPointerExceptionInspection this shouldn't be called with un-init DB */
|
||||
$oResult = DbConnectionWrapper::GetDbConnection(true)->query($sSql);
|
||||
}
|
||||
catch(mysqli_sql_exception $e)
|
||||
{
|
||||
@@ -876,7 +904,7 @@ class CMDBSource
|
||||
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql));
|
||||
}
|
||||
|
||||
while ($aRow = $oResult->fetch_array(MYSQLI_BOTH))
|
||||
while ($aRow = $oResult->fetch_array($iMode))
|
||||
{
|
||||
$aData[] = $aRow;
|
||||
}
|
||||
@@ -913,7 +941,8 @@ class CMDBSource
|
||||
$aData = array();
|
||||
try
|
||||
{
|
||||
$oResult = self::$m_oMysqli->query($sSql);
|
||||
/** @noinspection NullPointerExceptionInspection this shouldn't be called with un-init DB */
|
||||
$oResult = DbConnectionWrapper::GetDbConnection(true)->query($sSql);
|
||||
}
|
||||
catch(mysqli_sql_exception $e)
|
||||
{
|
||||
@@ -945,7 +974,8 @@ class CMDBSource
|
||||
{
|
||||
try
|
||||
{
|
||||
$oResult = self::$m_oMysqli->query($sSql);
|
||||
/** @noinspection NullPointerExceptionInspection this shouldn't be called with un-init DB */
|
||||
$oResult = DbConnectionWrapper::GetDbConnection(true)->query($sSql);
|
||||
}
|
||||
catch(mysqli_sql_exception $e)
|
||||
{
|
||||
@@ -970,7 +1000,7 @@ class CMDBSource
|
||||
|
||||
public static function AffectedRows()
|
||||
{
|
||||
return self::$m_oMysqli->affected_rows;
|
||||
return DbConnectionWrapper::GetDbConnection()->affected_rows;
|
||||
}
|
||||
|
||||
public static function FetchArray($oResult)
|
||||
@@ -1088,7 +1118,7 @@ class CMDBSource
|
||||
*
|
||||
* We still need to do a case sensitive comparison for enum values !
|
||||
*
|
||||
* A better solution would be to generate SQL field definitions ({@link GetFieldSpec} method) based on the DB used... But for
|
||||
* A better solution would be to generate SQL field definitions ({@see GetFieldSpec} method) based on the DB used... But for
|
||||
* now (N°2490 / SF #1756 / PR #91) we did implement this simpler solution
|
||||
*
|
||||
* @see GetFieldDataTypeAndOptions extracts all info from the SQL field definition
|
||||
@@ -1433,7 +1463,8 @@ class CMDBSource
|
||||
$sSql = "SELECT * FROM `$sTable`";
|
||||
try
|
||||
{
|
||||
$oResult = self::$m_oMysqli->query($sSql);
|
||||
/** @noinspection NullPointerExceptionInspection this shouldn't be called with un-init DB */
|
||||
$oResult = DbConnectionWrapper::GetDbConnection(true)->query($sSql);
|
||||
}
|
||||
catch(mysqli_sql_exception $e)
|
||||
{
|
||||
|
||||
@@ -113,6 +113,14 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'log_level_min.write_in_db' => [
|
||||
'type' => 'array',
|
||||
'description' => 'Additional configuration that enable "in DB" logs for Exception on compatible code.',
|
||||
'default' => [ 'Exception' => 'Error', ],
|
||||
'value' => [ 'Exception' => 'Error', ],
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'app_env_label' => [
|
||||
'type' => 'string',
|
||||
'description' => 'Label displayed to describe the current application environment, defaults to the environment name (e.g. "production")',
|
||||
@@ -301,6 +309,14 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'max_autocomplete_results' => [
|
||||
'type' => 'integer',
|
||||
'description' => 'The maximum number of result elements to display, no matter the total number of results',
|
||||
'default' => 150,
|
||||
'value' => 150,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'allow_menu_on_linkset' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'Display Action menus in view mode on any LinkedSet with edit_mode != none',
|
||||
@@ -1133,6 +1149,38 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
],
|
||||
'compatibility.include_moved_js_files' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'Include back JS files which are now only included when necessary to ease usage of not migrated extensions',
|
||||
'default' => false,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'compatibility.include_deprecated_js_files' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'Include the deprecated JS files to ease usage of not migrated extensions',
|
||||
'default' => false,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'compatibility.include_moved_css_files' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'Include back CSS files which are now only included when necessary to ease usage of not migrated extensions',
|
||||
'default' => false,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'compatibility.include_deprecated_css_files' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'Include the deprecated CSS files to ease usage of not migrated extensions',
|
||||
'default' => false,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'navigation_menu.show_menus_count' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'Display count badges for OQL menu entries',
|
||||
@@ -1221,6 +1269,38 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'activity_panel.prefilter_only_current_log' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'Whether the "Logs" filter should only be set to the log from the current tab or to all of them.',
|
||||
'default' => true,
|
||||
'value' => true,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'activity_panel.prefilter_state_changes_on_logs' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'Whether the "State changes" filter should be set by default on all log tabs.',
|
||||
'default' => false,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'activity_panel.prefilter_edits_on_logs' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'Whether the "Edits" filter should be set by default on all log tabs.',
|
||||
'default' => false,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'activity_panel.hide_avatars' => [
|
||||
'type' => 'array',
|
||||
'description' => 'GUIs IDs ("backoffice", "itop-portal" for the standard end-users portal, ...) in which the user avatars should be hidden and replaced if possible by their initials (eg. array("backoffice", "itop-portal", "another-portal-id"))',
|
||||
'default' => [],
|
||||
'value' => [],
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'activity_panel.show_author_name_below_entries' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'Whether or not to show the author friendlyname next to the date on the last entry.',
|
||||
@@ -1247,9 +1327,9 @@ class Config
|
||||
],
|
||||
'mentions.allowed_classes' => [
|
||||
'type' => 'array',
|
||||
'description' => 'Classes which can be mentioned through the autocomplete in the caselogs. Key of the array must be a single character that will trigger the autocomplete (eg. "@" => "Person")',
|
||||
'description' => 'Classes which can be mentioned through the autocomplete in the caselogs. Key of the array must be a single character that will trigger the autocomplete, value can be either a DM class or a valid OQL (eg. "@" => "Person", "?" => "SELECT FAQ WHERE status = \'published\'")',
|
||||
'default' => [
|
||||
'@' => 'Person',
|
||||
'@' => 'SELECT Person WHERE status = \'active\'',
|
||||
],
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
@@ -1271,6 +1351,14 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'user_preferences.allow_backoffice_theme_override' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'Whether the user can choose which theme to use in the backoffice. If set to false, all users will have the theme defined in "backoffice_default_theme"',
|
||||
'default' => true,
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'secure_rest_services' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'When set to true, only the users with the profile "REST Services User" are allowed to use the REST web services.',
|
||||
@@ -1367,6 +1455,14 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'security.disable_inline_documents_sandbox' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'If true then the sandbox for documents displayed in a browser tab will be disabled; enabling scripts and other interactive content. Note that setting this to true will open the application to potential XSS attacks!',
|
||||
'default' => false,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'behind_reverse_proxy' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'If true, then proxies custom header (X-Forwarded-*) are taken into account. Use only if the webserver is not publicly accessible (reachable only by the reverse proxy)',
|
||||
@@ -1375,6 +1471,30 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
],
|
||||
'developer_mode.enabled' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'If true then unlocks dev env functionalities, see \utils::IsDevelopmentEnvironment',
|
||||
'default' => null,
|
||||
'value' => null,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'theme.enable_precompilation' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'If false, theme compilation will not use any precompiled file setup optimization.)',
|
||||
'default' => true,
|
||||
'value' => true,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'theme.force_signature_check_at_runtime' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'If true, checking that the current theme signature matches the compiled file -to recompile it if necessary- will be done for each page. This can slow the application, only use it if you are experiencing issues while customizing a theme.)',
|
||||
'default' => false,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
];
|
||||
|
||||
public function IsProperty($sPropCode)
|
||||
|
||||
@@ -3,4 +3,6 @@
|
||||
* This file is only here for compatibility issues. Will be removed in iTop 3.1.0 (N°3664)
|
||||
*
|
||||
* @deprecated 3.0.0 N°3663 Exception classes were moved to `/application/exceptions`, use autoloader instead of require !
|
||||
*/
|
||||
*/
|
||||
require_once '../approot.inc.php';
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('Classes were moved to /application/exceptions');
|
||||
|
||||
@@ -185,13 +185,13 @@ class CSVBulkExport extends TabularBulkExport
|
||||
$oFieldSetLocalization->AddSubBlock(new Html('</br>'));
|
||||
|
||||
$oSelect = SelectUIBlockFactory::MakeForSelectWithLabel("charset", Dict::S('UI:CSVImport:Encoding'));
|
||||
$oSelect->SetBeforeInput(true);
|
||||
$oSelect->SetIsLabelBefore(true);
|
||||
$oFieldSetLocalization->AddSubBlock($oSelect);
|
||||
|
||||
$aPossibleEncodings = utils::GetPossibleEncodings(MetaModel::GetConfig()->GetCSVImportCharsets());
|
||||
$sDefaultEncoding = MetaModel::GetConfig()->Get('csv_file_default_charset');
|
||||
foreach ($aPossibleEncodings as $sIconvCode => $sDisplayName) {
|
||||
$oSelect->GetInput()->AddSubBlock(SelectOptionUIBlockFactory::MakeForSelectOption($sIconvCode, $sDisplayName, ($sIconvCode == $sDefaultEncoding)));
|
||||
$oSelect->AddSubBlock(SelectOptionUIBlockFactory::MakeForSelectOption($sIconvCode, $sDisplayName, ($sIconvCode == $sDefaultEncoding)));
|
||||
}
|
||||
//markup
|
||||
$oFieldSetMarkup = FieldSetUIBlockFactory::MakeStandard(Dict::S('Core:BulkExport:TextFormat'));
|
||||
@@ -211,15 +211,15 @@ class CSVBulkExport extends TabularBulkExport
|
||||
|
||||
$sDefaultFormat = htmlentities((string)AttributeDateTime::GetFormat(), ENT_QUOTES, 'UTF-8');
|
||||
$sExample = htmlentities(date((string)AttributeDateTime::GetFormat()), ENT_QUOTES, 'UTF-8');
|
||||
$oRadioDefault = InputUIBlockFactory::MakeForInputWithLabel(Dict::Format('Core:BulkExport:DateTimeFormatDefault_Example', $sDefaultFormat, $sExample), "csv_custom_date_time_format", "default", "csv_date_time_format_default", "radio");
|
||||
$oRadioDefault = InputUIBlockFactory::MakeForInputWithLabel(Dict::Format('Core:BulkExport:DateTimeFormatDefault_Example', $sDefaultFormat, $sExample), "csv_date_format_radio", "default", "csv_date_time_format_default", "radio");
|
||||
$oRadioDefault->GetInput()->SetIsChecked(($sDateTimeFormat == (string)AttributeDateTime::GetFormat()));
|
||||
$oRadioDefault->SetBeforeInput(false);
|
||||
$oRadioDefault->GetInput()->AddCSSClass('ibo-input-checkbox');
|
||||
$oFieldSetDate->AddSubBlock($oRadioDefault);
|
||||
$oFieldSetDate->AddSubBlock(new Html('</br>'));
|
||||
|
||||
$sFormatInput = '<input type="text" size="15" name="date_format" id="excel_custom_date_time_format" title="" value="'.htmlentities($sDateTimeFormat, ENT_QUOTES, 'UTF-8').'"/>';
|
||||
$oRadioCustom = InputUIBlockFactory::MakeForInputWithLabel(Dict::Format('Core:BulkExport:DateTimeFormatCustom_Format', $sFormatInput), "csv_custom_date_time_format", "custom", "csv_date_time_format_custom", "radio");
|
||||
$sFormatInput = '<input type="text" size="15" name="date_format" id="csv_custom_date_time_format" title="" value="'.htmlentities($sDateTimeFormat, ENT_QUOTES, 'UTF-8').'"/>';
|
||||
$oRadioCustom = InputUIBlockFactory::MakeForInputWithLabel(Dict::Format('Core:BulkExport:DateTimeFormatCustom_Format', $sFormatInput), "csv_date_format_radio", "custom", "csv_date_time_format_custom", "radio");
|
||||
$oRadioCustom->GetInput()->SetIsChecked($sDateTimeFormat !== (string)AttributeDateTime::GetFormat());
|
||||
$oRadioCustom->SetBeforeInput(false);
|
||||
$oRadioCustom->GetInput()->AddCSSClass('ibo-input-checkbox');
|
||||
|
||||
@@ -1,22 +1,11 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2013-2021 Combodo SARL
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
* iTop is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* iTop is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Core\MetaModel\FriendlyNameType;
|
||||
|
||||
/**
|
||||
* All objects to be displayed in the application (either as a list or as details)
|
||||
* must implement this interface.
|
||||
@@ -803,7 +792,7 @@ abstract class DBObject implements iDisplay
|
||||
//
|
||||
/** @var \AttributeExternalField $oAttDef */
|
||||
$sExtKeyAttCode = $oAttDef->GetKeyAttCode();
|
||||
|
||||
|
||||
if (($iRemote = $this->Get($sExtKeyAttCode)) && ($iRemote > 0)) // Objects in memory have negative IDs
|
||||
{
|
||||
$oExtKeyAttDef = MetaModel::GetAttributeDef(get_class($this), $sExtKeyAttCode);
|
||||
@@ -959,10 +948,9 @@ abstract class DBObject implements iDisplay
|
||||
*/
|
||||
protected function ComputeHighlightCode()
|
||||
{
|
||||
// First if the state defines a HiglightCode, apply it
|
||||
$sState = $this->GetState();
|
||||
if ($sState != '')
|
||||
if (MetaModel::HasLifecycle(get_class($this)))
|
||||
{
|
||||
$sState = $this->GetState();
|
||||
$sCode = MetaModel::GetHighlightCode(get_class($this), $sState);
|
||||
$this->SetHighlightCode($sCode);
|
||||
}
|
||||
@@ -1290,13 +1278,20 @@ abstract class DBObject implements iDisplay
|
||||
{
|
||||
// If the object if not issued from a query but constructed programmatically
|
||||
// the label may be empty. In this case run a query to get the object's friendly name
|
||||
$oTmpObj = MetaModel::GetObject($sObjClass, $sObjKey, false);
|
||||
if (is_object($oTmpObj))
|
||||
{
|
||||
$sObjOql = 'SELECT '.$sObjClass.' WHERE id='.$sObjKey;
|
||||
$oObjFilter = DBSearch::FromOQL($sObjOql);
|
||||
$oSet = new DBObjectSet($oObjFilter);
|
||||
|
||||
// we will only use id and friendlyname, so let's remove other fields !
|
||||
$aAttToLoad = [
|
||||
$sObjClass => [],
|
||||
];
|
||||
$oSet->OptimizeColumnLoad($aAttToLoad);
|
||||
|
||||
$oTmpObj = $oSet->Fetch();
|
||||
if (is_object($oTmpObj)) {
|
||||
$sHtmlLabel = $oTmpObj->GetName();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// May happen in case the target object is not in the list of allowed values for this attribute
|
||||
$sHtmlLabel = "<em>$sObjClass::$sObjKey</em>";
|
||||
}
|
||||
@@ -1530,31 +1525,40 @@ abstract class DBObject implements iDisplay
|
||||
/**
|
||||
* Helper to get the friendly name in a safe manner for displaying inside a web page
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @internal
|
||||
* @return string
|
||||
* @throws \CoreException
|
||||
* @since 3.0.0 N°4106 This method is now internal. It will be set final in 3.1.0 (N°4107)
|
||||
* @since 3.0.0 N°580 New $sType parameter
|
||||
*
|
||||
*/
|
||||
public function GetName()
|
||||
public function GetName($sType = FriendlyNameType::SHORT)
|
||||
{
|
||||
return htmlentities($this->GetRawName(), ENT_QUOTES, 'UTF-8');
|
||||
return htmlentities($this->GetRawName($sType), ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to get the friendly name
|
||||
*
|
||||
* This is not safe for displaying inside a web page since the " < > characters are not escaped.
|
||||
* In example, the name may contain some XSS script instructions.
|
||||
* Helper to get the friendly name
|
||||
*
|
||||
* This is not safe for displaying inside a web page since the " < > characters are not escaped.
|
||||
* In example, the name may contain some XSS script instructions.
|
||||
* Use this function only for internal computations or for an output to a non-HTML destination
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @internal
|
||||
* @return string
|
||||
* @throws \CoreException
|
||||
* @since 3.0.0 N°4106 This method is now internal. It will be set final in 3.1.0 (N°4107)
|
||||
* @since 3.0.0 N°580 New $sType parameter
|
||||
*
|
||||
*/
|
||||
public function GetRawName()
|
||||
public function GetRawName($sType = FriendlyNameType::SHORT)
|
||||
{
|
||||
return $this->Get('friendlyname');
|
||||
if ($sType == FriendlyNameType::SHORT) {
|
||||
return $this->Get('friendlyname');
|
||||
} else {
|
||||
$oExpression = MetaModel::GetNameExpression(get_class($this), $sType);
|
||||
$this->EvaluateExpression($oExpression);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1870,9 +1874,7 @@ abstract class DBObject implements iDisplay
|
||||
{
|
||||
/** @var \AttributeExternalKey $oAtt */
|
||||
$sTargetClass = $oAtt->GetTargetClass();
|
||||
$oTargetObj = MetaModel::GetObject($sTargetClass, $toCheck, false /*must be found*/, true /*allow all data*/);
|
||||
if (is_null($oTargetObj))
|
||||
{
|
||||
if (false === MetaModel::IsObjectInDB($sTargetClass, $toCheck)) {
|
||||
return "Target object not found ($sTargetClass::$toCheck)";
|
||||
}
|
||||
}
|
||||
@@ -2140,28 +2142,23 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
|
||||
if ($oAttDef->DuplicatesAllowed()) {
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
// To control duplicates go through all the entries and check if the remote has been seen
|
||||
/** @var \ormLinkSet $value */
|
||||
$aModifiedLnk = $value->ListModifiedLinks();
|
||||
$sExtKeyToRemote = $oAttDef->GetExtKeyToRemote();
|
||||
$aExistingRemotesId = $value->GetColumnAsArray($sExtKeyToRemote, true);
|
||||
$aExistingRemotesFriendlyName = $value->GetColumnAsArray($sExtKeyToRemote.'_friendlyname', true);
|
||||
$aCurrentRemoteIds = [];
|
||||
$aDuplicatesFields = [];
|
||||
foreach ($aModifiedLnk as $oModifiedLnk) {
|
||||
$iModifiedLnkId = $oModifiedLnk->GetKey();
|
||||
$iModifiedLnkRemoteId = $oModifiedLnk->Get($sExtKeyToRemote);
|
||||
$aExistingRemotesIdToCheck = array_filter($aExistingRemotesId, function ($iLnkKey) use ($iModifiedLnkId) {
|
||||
return ($iLnkKey != $iModifiedLnkId);
|
||||
}, ARRAY_FILTER_USE_KEY);
|
||||
|
||||
if (!in_array($iModifiedLnkRemoteId, $aExistingRemotesIdToCheck, true)) {
|
||||
continue;
|
||||
$value->rewind();
|
||||
while ($oCurrentLnk = $value->current()) {
|
||||
$iExtKeyToRemote = $oCurrentLnk->Get($sExtKeyToRemote);
|
||||
if (isset($aCurrentRemoteIds[$iExtKeyToRemote])) {
|
||||
$aDuplicatesFields[] = $oCurrentLnk->Get($sExtKeyToRemote.'_friendlyname');
|
||||
} else {
|
||||
$aCurrentRemoteIds[$iExtKeyToRemote] = true;
|
||||
}
|
||||
|
||||
$iLnkId = $oModifiedLnk->GetKey();
|
||||
$aDuplicatesFields[] = $aExistingRemotesFriendlyName[$iLnkId];
|
||||
$value->next();
|
||||
}
|
||||
|
||||
if (!empty($aDuplicatesFields)) {
|
||||
@@ -2434,11 +2431,10 @@ abstract class DBObject implements iDisplay
|
||||
* Reset during {@see DBObject::DBUpdate()}
|
||||
* @throws Exception
|
||||
* @uses m_aCurrValues
|
||||
*/
|
||||
*/
|
||||
public function ListChanges()
|
||||
{
|
||||
if ($this->m_bIsInDB)
|
||||
{
|
||||
if ($this->m_bIsInDB) {
|
||||
return $this->ListChangedValues($this->m_aCurrValues);
|
||||
}
|
||||
|
||||
@@ -2784,50 +2780,75 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
}
|
||||
|
||||
$iTransactionRetry = 1;
|
||||
$bIsTransactionEnabled = MetaModel::GetConfig()->Get('db_core_transactions_enabled');
|
||||
try
|
||||
if ($bIsTransactionEnabled)
|
||||
{
|
||||
if ($bIsTransactionEnabled)
|
||||
{
|
||||
CMDBSource::Query('START TRANSACTION');
|
||||
}
|
||||
|
||||
// First query built upon on the root class, because the ID must be created first
|
||||
$this->m_iKey = $this->DBInsertSingleTable($sRootClass);
|
||||
|
||||
// Then do the leaf class, if different from the root class
|
||||
if ($sClass != $sRootClass)
|
||||
{
|
||||
$this->DBInsertSingleTable($sClass);
|
||||
}
|
||||
|
||||
// Then do the other classes
|
||||
foreach (MetaModel::EnumParentClasses($sClass) as $sParentClass)
|
||||
{
|
||||
if ($sParentClass == $sRootClass)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
$this->DBInsertSingleTable($sParentClass);
|
||||
}
|
||||
|
||||
$this->OnObjectKeyReady();
|
||||
|
||||
$this->DBWriteLinks();
|
||||
$this->WriteExternalAttributes();
|
||||
|
||||
if ($bIsTransactionEnabled)
|
||||
{
|
||||
CMDBSource::Query('COMMIT');
|
||||
}
|
||||
// TODO Deep clone this object before the transaction (to use it in case of rollback)
|
||||
// $iTransactionRetryCount = MetaModel::GetConfig()->Get('db_core_transactions_retry_count');
|
||||
$iTransactionRetryCount = 1;
|
||||
$iTransactionRetryDelay = MetaModel::GetConfig()->Get('db_core_transactions_retry_delay_ms');
|
||||
$iTransactionRetry = $iTransactionRetryCount;
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
if ($bIsTransactionEnabled)
|
||||
{
|
||||
CMDBSource::Query('ROLLBACK');
|
||||
while ($iTransactionRetry > 0) {
|
||||
try {
|
||||
$iTransactionRetry--;
|
||||
if ($bIsTransactionEnabled) {
|
||||
CMDBSource::Query('START TRANSACTION');
|
||||
}
|
||||
|
||||
// First query built upon on the root class, because the ID must be created first
|
||||
$this->m_iKey = $this->DBInsertSingleTable($sRootClass);
|
||||
|
||||
// Then do the leaf class, if different from the root class
|
||||
if ($sClass != $sRootClass) {
|
||||
$this->DBInsertSingleTable($sClass);
|
||||
}
|
||||
|
||||
// Then do the other classes
|
||||
foreach (MetaModel::EnumParentClasses($sClass) as $sParentClass) {
|
||||
if ($sParentClass == $sRootClass) {
|
||||
continue;
|
||||
}
|
||||
$this->DBInsertSingleTable($sParentClass);
|
||||
}
|
||||
|
||||
$this->OnObjectKeyReady();
|
||||
|
||||
$this->DBWriteLinks();
|
||||
$this->WriteExternalAttributes();
|
||||
|
||||
// Write object creation history within the transaction
|
||||
$this->RecordObjCreation();
|
||||
|
||||
if ($bIsTransactionEnabled) {
|
||||
CMDBSource::Query('COMMIT');
|
||||
}
|
||||
break;
|
||||
}
|
||||
catch (Exception $e) {
|
||||
IssueLog::Error($e->getMessage());
|
||||
if ($bIsTransactionEnabled)
|
||||
{
|
||||
CMDBSource::Query('ROLLBACK');
|
||||
if (!CMDBSource::IsInsideTransaction() && CMDBSource::IsDeadlockException($e))
|
||||
{
|
||||
// Deadlock found when trying to get lock; try restarting transaction (only in main transaction)
|
||||
if ($iTransactionRetry > 0)
|
||||
{
|
||||
// wait and retry
|
||||
IssueLog::Error("Insert TRANSACTION Retrying...");
|
||||
usleep(random_int(1, 5) * 1000 * $iTransactionRetryDelay * ($iTransactionRetryCount - $iTransactionRetry));
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
IssueLog::Error("Insert Deadlock TRANSACTION prevention failed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$this->m_bIsInDB = true;
|
||||
@@ -2860,8 +2881,6 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
}
|
||||
|
||||
$this->RecordObjCreation();
|
||||
|
||||
return $this->m_iKey;
|
||||
}
|
||||
|
||||
@@ -3136,39 +3155,15 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
// 2 - Find mentioned objects
|
||||
$aMentionedObjects = array();
|
||||
foreach($aUpdatedLogAttCodes as $sAttCode)
|
||||
{
|
||||
foreach ($aUpdatedLogAttCodes as $sAttCode) {
|
||||
/** @var \ormCaseLog $oUpdatedCaseLog */
|
||||
$oUpdatedCaseLog = $this->Get($sAttCode);
|
||||
$aMentionMatches = array();
|
||||
// Note: As the sanitizer (or CKEditor autocomplete plugin? 🤔) removes data-* attributes from the hyperlink, we can't use the following (simpler) regexp: '/<a\s*([^>]*)data-object-class="([^"]*)"\s*data-object-id="([^"]*)">/i'
|
||||
// If we change the sanitizer, we might want to use this regexp as it's easier to read
|
||||
// Note 2: This is only working for backoffice URLs...
|
||||
$sAppRootUrlForRegExp = addcslashes(utils::GetAbsoluteUrlAppRoot(), '/&');
|
||||
preg_match_all("/\[([^\]]*)\]\({$sAppRootUrlForRegExp}[^\)]*\&class=([^\)\&]*)\&id=([\d]*)[^\)]*\)/i", $oUpdatedCaseLog->GetModifiedEntry(), $aMentionMatches);
|
||||
|
||||
foreach($aMentionMatches[0] as $iMatchIdx => $sCompleteMatch)
|
||||
{
|
||||
$sMatchedClass = $aMentionMatches[2][$iMatchIdx];
|
||||
$sMatchedId = $aMentionMatches[3][$iMatchIdx];
|
||||
|
||||
// Prepare array for matched class if not already present
|
||||
if(!array_key_exists($sMatchedClass, $aMentionedObjects))
|
||||
{
|
||||
$aMentionedObjects[$sMatchedClass] = array();
|
||||
}
|
||||
// Add matched ID if not already there
|
||||
if(!in_array($sMatchedId, $aMentionedObjects[$sMatchedClass]))
|
||||
{
|
||||
$aMentionedObjects[$sMatchedClass][] = $sMatchedId;
|
||||
}
|
||||
}
|
||||
$aMentionedObjects = array_merge_recursive($aMentionedObjects, utils::GetMentionedObjectsFromText($oUpdatedCaseLog->GetModifiedEntry()));
|
||||
}
|
||||
// 3 - Trigger for those objects
|
||||
foreach($aMentionedObjects as $sMentionedClass => $aMentionedIds)
|
||||
{
|
||||
foreach($aMentionedIds as $sMentionedId)
|
||||
{
|
||||
// TODO: This should be refactored and moved into the caselogs loop, otherwise, we won't be able to know which case log triggered the action.
|
||||
foreach ($aMentionedObjects as $sMentionedClass => $aMentionedIds) {
|
||||
foreach ($aMentionedIds as $sMentionedId) {
|
||||
/** @var \DBObject $oMentionedObject */
|
||||
$oMentionedObject = MetaModel::GetObject($sMentionedClass, $sMentionedId);
|
||||
// Important: Here the "$this->object()$" placeholder is actually the mentioned object and not the current object. The current object can be used through the $source->object()$ placeholder.
|
||||
@@ -3180,8 +3175,13 @@ abstract class DBObject implements iDisplay
|
||||
array(), $aParams);
|
||||
while ($oTrigger = $oSet->Fetch())
|
||||
{
|
||||
/** @var \Trigger $oTrigger */
|
||||
$oTrigger->DoActivate($aTriggerArgs);
|
||||
/** @var \TriggerOnObjectMention $oTrigger */
|
||||
try {
|
||||
$oTrigger->DoActivate($aTriggerArgs);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
utils::EnrichRaisedException($oTrigger, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3210,9 +3210,11 @@ abstract class DBObject implements iDisplay
|
||||
$bIsTransactionEnabled = MetaModel::GetConfig()->Get('db_core_transactions_enabled');
|
||||
if ($bIsTransactionEnabled)
|
||||
{
|
||||
$iIsTransactionRetryCount = MetaModel::GetConfig()->Get('db_core_transactions_retry_count');
|
||||
// TODO Deep clone this object before the transaction (to use it in case of rollback)
|
||||
// $iTransactionRetryCount = MetaModel::GetConfig()->Get('db_core_transactions_retry_count');
|
||||
$iTransactionRetryCount = 1;
|
||||
$iIsTransactionRetryDelay = MetaModel::GetConfig()->Get('db_core_transactions_retry_delay_ms');
|
||||
$iTransactionRetry = $iIsTransactionRetryCount;
|
||||
$iTransactionRetry = $iTransactionRetryCount;
|
||||
}
|
||||
while ($iTransactionRetry > 0)
|
||||
{
|
||||
@@ -3228,7 +3230,7 @@ abstract class DBObject implements iDisplay
|
||||
// Update the left & right indexes for each hierarchical key
|
||||
foreach ($aHierarchicalKeys as $sAttCode => $oAttDef)
|
||||
{
|
||||
$sTable = $sTable = MetaModel::DBGetTable(get_class($this), $sAttCode);
|
||||
$sTable = MetaModel::DBGetTable(get_class($this), $sAttCode);
|
||||
$sSQL = "SELECT `".$oAttDef->GetSQLRight()."` AS `right`, `".$oAttDef->GetSQLLeft()."` AS `left` FROM `$sTable` WHERE id=".$this->GetKey();
|
||||
$aRes = CMDBSource::QueryToArray($sSQL);
|
||||
$iMyLeft = $aRes[0]['left'];
|
||||
@@ -3236,8 +3238,7 @@ abstract class DBObject implements iDisplay
|
||||
$iDelta = $iMyRight - $iMyLeft + 1;
|
||||
MetaModel::HKTemporaryCutBranch($iMyLeft, $iMyRight, $oAttDef, $sTable);
|
||||
|
||||
if ($aDBChanges[$sAttCode] == 0)
|
||||
{
|
||||
if ($aDBChanges[$sAttCode] == 0) {
|
||||
// No new parent, insert completely at the right of the tree
|
||||
$sSQL = "SELECT max(`".$oAttDef->GetSQLRight()."`) AS max FROM `$sTable`";
|
||||
$aRes = CMDBSource::QueryToArray($sSQL);
|
||||
@@ -3280,38 +3281,29 @@ abstract class DBObject implements iDisplay
|
||||
$this->DBWriteLinks();
|
||||
$this->WriteExternalAttributes();
|
||||
|
||||
// following lines are resetting changes (so after this {@see DBObject::ListChanges()} won't return changes anymore)
|
||||
// new values are already in the object (call {@see DBObject::Get()} to get them)
|
||||
// call {@see DBObject::ListPreviousValuesForUpdatedAttributes()} to get changed fields and previous values
|
||||
$this->m_bDirty = false;
|
||||
$this->m_aTouchedAtt = array();
|
||||
$this->m_aModifiedAtt = array();
|
||||
|
||||
if (count($aChanges) != 0)
|
||||
{
|
||||
if (count($aChanges) != 0) {
|
||||
$this->RecordAttChanges($aChanges, $aOriginalValues);
|
||||
}
|
||||
|
||||
if ($bIsTransactionEnabled)
|
||||
{
|
||||
if ($bIsTransactionEnabled) {
|
||||
CMDBSource::Query('COMMIT');
|
||||
}
|
||||
break;
|
||||
}
|
||||
catch (MySQLException $e)
|
||||
{
|
||||
IssueLog::Error($e->getMessage());
|
||||
if ($bIsTransactionEnabled)
|
||||
{
|
||||
CMDBSource::Query('ROLLBACK');
|
||||
if ($e->getCode() == 1213)
|
||||
if (!CMDBSource::IsInsideTransaction() && CMDBSource::IsDeadlockException($e))
|
||||
{
|
||||
// Deadlock found when trying to get lock; try restarting transaction
|
||||
IssueLog::Error($e->getMessage());
|
||||
// Deadlock found when trying to get lock; try restarting transaction (only in main transaction)
|
||||
if ($iTransactionRetry > 0)
|
||||
{
|
||||
// wait and retry
|
||||
IssueLog::Error("Update TRANSACTION Retrying...");
|
||||
usleep(random_int(1, 5) * 1000 * $iIsTransactionRetryDelay * ($iIsTransactionRetryCount - $iTransactionRetry));
|
||||
usleep(random_int(1, 5) * 1000 * $iIsTransactionRetryDelay * ($iTransactionRetryCount - $iTransactionRetry));
|
||||
continue;
|
||||
}
|
||||
else
|
||||
@@ -3325,10 +3317,11 @@ abstract class DBObject implements iDisplay
|
||||
'id' => $this->GetKey(),
|
||||
'class' => get_class($this),
|
||||
'issues' => $aErrors
|
||||
));
|
||||
), $e);
|
||||
}
|
||||
catch (CoreCannotSaveObjectException $e)
|
||||
{
|
||||
IssueLog::Error($e->getMessage());
|
||||
if ($bIsTransactionEnabled)
|
||||
{
|
||||
CMDBSource::Query('ROLLBACK');
|
||||
@@ -3337,6 +3330,7 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
IssueLog::Error($e->getMessage());
|
||||
if ($bIsTransactionEnabled)
|
||||
{
|
||||
CMDBSource::Query('ROLLBACK');
|
||||
@@ -3350,13 +3344,20 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
}
|
||||
|
||||
// following lines are resetting changes (so after this {@see DBObject::ListChanges()} won't return changes anymore)
|
||||
// new values are already in the object (call {@see DBObject::Get()} to get them)
|
||||
// call {@see DBObject::ListPreviousValuesForUpdatedAttributes()} to get changed fields and previous values
|
||||
$this->m_bDirty = false;
|
||||
$this->m_aTouchedAtt = array();
|
||||
$this->m_aModifiedAtt = array();
|
||||
|
||||
try {
|
||||
// - TriggerOnObjectUpdate
|
||||
$aParams = array('class_list' => MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL));
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnObjectUpdate AS t WHERE t.target_class IN (:class_list)"),
|
||||
array(), $aParams);
|
||||
while ($oTrigger = $oSet->Fetch()) {
|
||||
/** @var \Trigger $oTrigger */
|
||||
/** @var \TriggerOnObjectUpdate $oTrigger */
|
||||
try {
|
||||
$oTrigger->DoActivate($this->ToArgs('this'));
|
||||
}
|
||||
@@ -3539,9 +3540,11 @@ abstract class DBObject implements iDisplay
|
||||
$bIsTransactionEnabled = MetaModel::GetConfig()->Get('db_core_transactions_enabled');
|
||||
if ($bIsTransactionEnabled)
|
||||
{
|
||||
$iIsTransactionRetryCount = MetaModel::GetConfig()->Get('db_core_transactions_retry_count');
|
||||
$iIsTransactionRetryDelay = MetaModel::GetConfig()->Get('db_core_transactions_retry_delay_ms');
|
||||
$iTransactionRetry = $iIsTransactionRetryCount;
|
||||
// TODO Deep clone this object before the transaction (to use it in case of rollback)
|
||||
// $iTransactionRetryCount = MetaModel::GetConfig()->Get('db_core_transactions_retry_count');
|
||||
$iTransactionRetryCount = 1;
|
||||
$iTransactionRetryDelay = MetaModel::GetConfig()->Get('db_core_transactions_retry_delay_ms');
|
||||
$iTransactionRetry = $iTransactionRetryCount;
|
||||
}
|
||||
while ($iTransactionRetry > 0)
|
||||
{
|
||||
@@ -3564,18 +3567,18 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
catch (MySQLException $e)
|
||||
{
|
||||
IssueLog::Error($e->getMessage());
|
||||
if ($bIsTransactionEnabled)
|
||||
{
|
||||
CMDBSource::Query('ROLLBACK');
|
||||
if ($e->getCode() == 1213)
|
||||
if (!CMDBSource::IsInsideTransaction() && CMDBSource::IsDeadlockException($e))
|
||||
{
|
||||
// Deadlock found when trying to get lock; try restarting transaction
|
||||
IssueLog::Error($e->getMessage());
|
||||
if ($iTransactionRetry > 0)
|
||||
{
|
||||
// wait and retry
|
||||
IssueLog::Error("Delete TRANSACTION Retrying...");
|
||||
usleep(random_int(1, 5) * 1000 * $iIsTransactionRetryDelay * ($iIsTransactionRetryCount - $iTransactionRetry));
|
||||
usleep(random_int(1, 5) * 1000 * $iTransactionRetryDelay * ($iTransactionRetryCount - $iTransactionRetry));
|
||||
continue;
|
||||
}
|
||||
else
|
||||
@@ -3728,16 +3731,22 @@ abstract class DBObject implements iDisplay
|
||||
|
||||
/**
|
||||
* Apply a stimulus (workflow)
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @param string $sStimulusCode
|
||||
* @param bool $bDoNotWrite
|
||||
*
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @param string $sStimulusCode
|
||||
* @param bool $bDoNotWrite if true we won't save the object !
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
*
|
||||
* @throws CoreException
|
||||
* @throws CoreUnexpectedValue
|
||||
*
|
||||
* @uses \AttributeStopWatch::Start
|
||||
* @uses \AttributeStopWatch::Stop
|
||||
* @uses \DBObject::DBWrite
|
||||
* @uses \TriggerOnStateLeave::DoActivate
|
||||
* @uses \TriggerOnStateEnter::DoActivate
|
||||
*/
|
||||
public function ApplyStimulus($sStimulusCode, $bDoNotWrite = false)
|
||||
{
|
||||
@@ -3852,17 +3861,14 @@ abstract class DBObject implements iDisplay
|
||||
if (in_array($sNewState, $oAttDef->GetStates()))
|
||||
{
|
||||
$oSW->Start($this, $oAttDef);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$oSW->Stop($this, $oAttDef);
|
||||
}
|
||||
$this->Set($sAttCode, $oSW);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$bDoNotWrite)
|
||||
{
|
||||
if (!$bDoNotWrite) {
|
||||
$this->DBWrite();
|
||||
}
|
||||
|
||||
@@ -3870,30 +3876,26 @@ abstract class DBObject implements iDisplay
|
||||
$aParams = array(
|
||||
'class_list' => MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL),
|
||||
'previous_state' => $sPreviousState,
|
||||
'new_state' => $sNewState);
|
||||
'new_state' => $sNewState,
|
||||
);
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnStateLeave AS t WHERE t.target_class IN (:class_list) AND t.state=:previous_state"), array(), $aParams);
|
||||
while ($oTrigger = $oSet->Fetch())
|
||||
{
|
||||
/** @var \Trigger $oTrigger */
|
||||
try
|
||||
{
|
||||
while ($oTrigger = $oSet->Fetch()) {
|
||||
/** @var \TriggerOnStateLeave $oTrigger */
|
||||
try {
|
||||
$oTrigger->DoActivate($this->ToArgs('this'));
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
catch (Exception $e) {
|
||||
utils::EnrichRaisedException($oTrigger, $e);
|
||||
}
|
||||
}
|
||||
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnStateEnter AS t WHERE t.target_class IN (:class_list) AND t.state=:new_state"), array(), $aParams);
|
||||
while ($oTrigger = $oSet->Fetch())
|
||||
{
|
||||
/** @var \Trigger $oTrigger */
|
||||
try{
|
||||
while ($oTrigger = $oSet->Fetch()) {
|
||||
/** @var \TriggerOnStateEnter $oTrigger */
|
||||
try {
|
||||
$oTrigger->DoActivate($this->ToArgs('this'));
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
catch (Exception $e) {
|
||||
utils::EnrichRaisedException($oTrigger, $e);
|
||||
}
|
||||
}
|
||||
@@ -4718,8 +4720,10 @@ abstract class DBObject implements iDisplay
|
||||
*/
|
||||
public function GetMasterReplica()
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod();
|
||||
$sOQL = "SELECT replica,datasource FROM SynchroReplica AS replica JOIN SynchroDataSource AS datasource ON replica.sync_source_id=datasource.id WHERE replica.dest_class = :dest_class AND replica.dest_id = :dest_id";
|
||||
$oReplicaSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), array() /* order by*/, array('dest_class' => get_class($this), 'dest_id' => $this->GetKey()));
|
||||
|
||||
return $oReplicaSet;
|
||||
}
|
||||
|
||||
@@ -5552,27 +5556,31 @@ abstract class DBObject implements iDisplay
|
||||
{
|
||||
$aFields = $oExpression->ListRequiredFields();
|
||||
$aArgs = array();
|
||||
foreach ($aFields as $sFieldDesc)
|
||||
{
|
||||
foreach ($aFields as $sFieldDesc) {
|
||||
$aFieldParts = explode('.', $sFieldDesc);
|
||||
if (count($aFieldParts) == 2)
|
||||
{
|
||||
if (count($aFieldParts) == 2) {
|
||||
$sClass = $aFieldParts[0];
|
||||
$sAttCode = $aFieldParts[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$sClass = get_class($this);
|
||||
$sAttCode = $aFieldParts[0];
|
||||
}
|
||||
if (get_class($this) != $sClass) continue;
|
||||
if (!MetaModel::IsValidAttCode(get_class($this), $sAttCode)) continue;
|
||||
if (get_class($this) != $sClass) {
|
||||
continue;
|
||||
}
|
||||
if (!MetaModel::IsValidAttCode(get_class($this), $sAttCode)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
$aSQLValues = $oAttDef->GetSQLValues($this->m_aCurrValues[$sAttCode]);
|
||||
$value = reset($aSQLValues);
|
||||
if ($oAttDef->IsNull($value)) {
|
||||
return '';
|
||||
}
|
||||
$aArgs[$sFieldDesc] = $value;
|
||||
}
|
||||
|
||||
return $oExpression->Evaluate($aArgs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,22 +63,23 @@ else
|
||||
|
||||
abstract class DBSearch
|
||||
{
|
||||
/** @internal */
|
||||
/** @internal */
|
||||
const JOIN_POINTING_TO = 0;
|
||||
/** @internal */
|
||||
/** @internal */
|
||||
const JOIN_REFERENCED_BY = 1;
|
||||
|
||||
protected $m_bNoContextParameters = false;
|
||||
/** @var array For {@see iQueryModifier} impl */
|
||||
protected $m_aModifierProperties = array();
|
||||
protected $m_bArchiveMode = false;
|
||||
protected $m_bShowObsoleteData = true;
|
||||
|
||||
/**
|
||||
* DBSearch constructor.
|
||||
*
|
||||
* @api
|
||||
* @see DBSearch::FromOQL()
|
||||
*/
|
||||
/**
|
||||
* DBSearch constructor.
|
||||
*
|
||||
* @api
|
||||
* @see DBSearch::FromOQL()
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->Init();
|
||||
@@ -280,62 +281,97 @@ abstract class DBSearch
|
||||
|
||||
abstract public function TranslateConditions($aTranslationData, $bMatchAll = true, $bMarkFieldsAsResolved = true);
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @return mixed
|
||||
*/
|
||||
/**
|
||||
* @internal
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function IsAny();
|
||||
|
||||
/**
|
||||
* @deprecated use ToOQL() instead
|
||||
* @internal
|
||||
* @return string
|
||||
*/
|
||||
public function Describe(){return 'deprecated - use ToOQL() instead';}
|
||||
/**
|
||||
* @deprecated use ToOQL() instead
|
||||
* @internal
|
||||
* @return string
|
||||
*/
|
||||
public function DescribeConditionPointTo($sExtKeyAttCode, $aPointingTo){return 'deprecated - use ToOQL() instead';}
|
||||
/**
|
||||
* @deprecated use ToOQL() instead
|
||||
* @internal
|
||||
* @return string
|
||||
*/
|
||||
public function DescribeConditionRefBy($sForeignClass, $sForeignExtKeyAttCode){return 'deprecated - use ToOQL() instead';}
|
||||
/**
|
||||
* @deprecated use ToOQL() instead
|
||||
* @internal
|
||||
* @return string
|
||||
*/
|
||||
public function DescribeConditionRelTo($aRelInfo){return 'deprecated - use ToOQL() instead';}
|
||||
/**
|
||||
* @deprecated use ToOQL() instead
|
||||
* @internal
|
||||
* @return string
|
||||
*/
|
||||
public function DescribeConditions(){return 'deprecated - use ToOQL() instead';}
|
||||
/**
|
||||
* @deprecated use ToOQL() instead
|
||||
* @internal
|
||||
* @return string
|
||||
*/
|
||||
public function __DescribeHTML(){return 'deprecated - use ToOQL() instead';}
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated use ToOQL() instead
|
||||
* @return string
|
||||
*/
|
||||
public function Describe()
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use ToOQL() instead');
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @return mixed
|
||||
*/
|
||||
return 'deprecated - use ToOQL() instead';
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated use ToOQL() instead
|
||||
* @return string
|
||||
*/
|
||||
public function DescribeConditionPointTo($sExtKeyAttCode, $aPointingTo)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use ToOQL() instead');
|
||||
|
||||
return 'deprecated - use ToOQL() instead';
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated use ToOQL() instead
|
||||
* @return string
|
||||
*/
|
||||
public function DescribeConditionRefBy($sForeignClass, $sForeignExtKeyAttCode)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use ToOQL() instead');
|
||||
|
||||
return 'deprecated - use ToOQL() instead';
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated use ToOQL() instead
|
||||
* @return string
|
||||
*/
|
||||
public function DescribeConditionRelTo($aRelInfo)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use ToOQL() instead');
|
||||
|
||||
return 'deprecated - use ToOQL() instead';
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated use ToOQL() instead
|
||||
* @return string
|
||||
*/
|
||||
public function DescribeConditions()
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use ToOQL() instead');
|
||||
|
||||
return 'deprecated - use ToOQL() instead';
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated use ToOQL() instead
|
||||
* @return string
|
||||
*/
|
||||
public function __DescribeHTML()
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use ToOQL() instead');
|
||||
|
||||
return 'deprecated - use ToOQL() instead';
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function ResetCondition();
|
||||
|
||||
/**
|
||||
* add $oExpression as a OR
|
||||
*
|
||||
* @api
|
||||
* @see DBSearch::AddConditionExpression()
|
||||
*
|
||||
* @param Expression $oExpression
|
||||
/**
|
||||
* add $oExpression as a OR
|
||||
*
|
||||
* @api
|
||||
* @see DBSearch::AddConditionExpression()
|
||||
*
|
||||
* @param Expression $oExpression
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
@@ -1004,10 +1040,8 @@ abstract class DBSearch
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a SQL query from the current search
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* Generate a SQL query from the current search
|
||||
*
|
||||
* @param array $aOrderBy Array of '[<classalias>.]attcode' => bAscending
|
||||
* @param array $aArgs
|
||||
* @param null $aAttToLoad
|
||||
@@ -1015,12 +1049,16 @@ abstract class DBSearch
|
||||
* @param int $iLimitCount
|
||||
* @param int $iLimitStart
|
||||
* @param bool $bGetCount
|
||||
* @param bool $bBeautifulSQL
|
||||
*
|
||||
* @return string
|
||||
* @throws CoreException
|
||||
* @throws Exception
|
||||
* @throws MissingQueryArgument
|
||||
* @throws \ConfigException
|
||||
* @throws \CoreException
|
||||
* @throws \MissingQueryArgument
|
||||
* @internal
|
||||
*
|
||||
*/
|
||||
public function MakeSelectQuery($aOrderBy = array(), $aArgs = array(), $aAttToLoad = null, $aExtendedDataSpec = null, $iLimitCount = 0, $iLimitStart = 0, $bGetCount = false)
|
||||
public function MakeSelectQuery($aOrderBy = array(), $aArgs = array(), $aAttToLoad = null, $aExtendedDataSpec = null, $iLimitCount = 0, $iLimitStart = 0, $bGetCount = false, $bBeautifulSQL = true)
|
||||
{
|
||||
// Check the order by specification, and prefix with the class alias
|
||||
// and make sure that the ordering columns are going to be selected
|
||||
@@ -1085,8 +1123,7 @@ abstract class DBSearch
|
||||
}
|
||||
try
|
||||
{
|
||||
// $bBeautifulSQL = self::$m_bTraceQueries || self::$m_bDebugQuery || self::$m_bIndentQueries;
|
||||
$sRes = $oSQLQuery->RenderSelect($aOrderSpec, $aScalarArgs, $iLimitCount, $iLimitStart, $bGetCount, true);
|
||||
$sRes = $oSQLQuery->RenderSelect($aOrderSpec, $aScalarArgs, $iLimitCount, $iLimitStart, $bGetCount, $bBeautifulSQL);
|
||||
if ($sClassAlias == '_itop_')
|
||||
{
|
||||
IssueLog::Info('SQL Query (_itop_): '.$sRes);
|
||||
|
||||
@@ -242,7 +242,7 @@ class DeletionPlan
|
||||
|
||||
public function SetDeletionIssues($oObject, $aIssues, $bSecurityIssue)
|
||||
{
|
||||
if (count($aIssues) > 0)
|
||||
if (count($aIssues ?? []) > 0)
|
||||
{
|
||||
$sClass = get_class($oObject);
|
||||
$iId = $oObject->GetKey();
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
use Combodo\iTop\Application\Helper\WebResourcesHelper;
|
||||
use Combodo\iTop\Application\UI\Base\Component\MedallionIcon\MedallionIcon;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Panel\Panel;
|
||||
use Combodo\iTop\Renderer\BlockRenderer;
|
||||
@@ -119,13 +120,12 @@ class DisplayableNode extends GraphNode
|
||||
$Alpha = 1.0;
|
||||
$oPdf->SetFillColor(200, 200, 200);
|
||||
$oPdf->setAlpha(1);
|
||||
|
||||
|
||||
$sIconUrl = $this->GetProperty('icon_url');
|
||||
$sIconPath = str_replace(utils::GetAbsoluteUrlModulesRoot(), APPROOT.'env-'.utils::GetCurrentEnvironment().'/', $sIconUrl);
|
||||
|
||||
if ($this->GetProperty('source'))
|
||||
{
|
||||
$oPdf->SetLineStyle(array('width' => 2*$fScale, 'cap' => 'round', 'join' => 'miter', 'dash' => 0, 'color' => array(204, 51, 51)));
|
||||
|
||||
if ($this->GetProperty('source')) {
|
||||
$oPdf->SetLineStyle(array('width' => 2 * $fScale, 'cap' => 'round', 'join' => 'miter', 'dash' => 0, 'color' => array(204, 51, 51)));
|
||||
$oPdf->Circle($this->x * $fScale, $this->y * $fScale, 16 * 1.25 * $fScale, 0, 360, 'D');
|
||||
}
|
||||
else if ($this->GetProperty('sink'))
|
||||
@@ -133,34 +133,30 @@ class DisplayableNode extends GraphNode
|
||||
$oPdf->SetLineStyle(array('width' => 2*$fScale, 'cap' => 'round', 'join' => 'miter', 'dash' => 0, 'color' => array(51, 51, 204)));
|
||||
$oPdf->Circle($this->x * $fScale, $this->y * $fScale, 16 * 1.25 * $fScale, 0, 360, 'D');
|
||||
}
|
||||
|
||||
if (!$this->GetProperty('is_reached'))
|
||||
{
|
||||
|
||||
if (!$this->GetProperty('is_reached')) {
|
||||
$sTempImageName = $this->CreateWhiteIcon($oGraph, $sIconPath);
|
||||
if ($sTempImageName != null)
|
||||
{
|
||||
$oPdf->Image($sTempImageName, ($this->x - 16)*$fScale, ($this->y - 16)*$fScale, 32*$fScale, 32*$fScale, 'PNG');
|
||||
if ($sTempImageName != null) {
|
||||
$oPdf->AddImage($sTempImageName, ($this->x - 16) * $fScale, ($this->y - 16) * $fScale, 32 * $fScale, 32 * $fScale, 'PNG');
|
||||
}
|
||||
$Alpha = 0.4;
|
||||
$oPdf->setAlpha($Alpha);
|
||||
}
|
||||
|
||||
$oPdf->Image($sIconPath, ($this->x - 16)*$fScale, ($this->y - 16)*$fScale, 32*$fScale, 32*$fScale);
|
||||
|
||||
|
||||
$oPdf->AddImage($sIconPath, ($this->x - 16) * $fScale, ($this->y - 16) * $fScale, 32 * $fScale, 32 * $fScale);
|
||||
|
||||
$aContextRootCauses = $this->GetProperty('context_root_causes');
|
||||
if (!is_null($aContextRootCauses))
|
||||
{
|
||||
if (!is_null($aContextRootCauses)) {
|
||||
$idx = 0;
|
||||
foreach($aContextRootCauses as $key => $aObjects)
|
||||
{
|
||||
$sgn = 2*($idx %2) -1;
|
||||
$coef = floor((1+$idx)/2) * $sgn;
|
||||
$alpha = $coef*pi()/4 - pi()/2;
|
||||
$x = $this->x * $fScale + cos($alpha) * 16*1.25 * $fScale;
|
||||
$y = $this->y * $fScale + sin($alpha) * 16*1.25 * $fScale;
|
||||
foreach ($aContextRootCauses as $key => $aObjects) {
|
||||
$sgn = 2 * ($idx % 2) - 1;
|
||||
$coef = floor((1 + $idx) / 2) * $sgn;
|
||||
$alpha = $coef * pi() / 4 - pi() / 2;
|
||||
$x = $this->x * $fScale + cos($alpha) * 16 * 1.25 * $fScale;
|
||||
$y = $this->y * $fScale + sin($alpha) * 16 * 1.25 * $fScale;
|
||||
$l = 32 * $fScale / 3;
|
||||
$sIconPath = APPROOT.'env-'.utils::GetCurrentEnvironment().'/'.$aContextDefs[$key]['icon'];
|
||||
$oPdf->Image($sIconPath, $x - $l/2, $y - $l/2, $l, $l);
|
||||
$oPdf->AddImage($sIconPath, $x - $l / 2, $y - $l / 2, $l, $l);
|
||||
$idx++;
|
||||
}
|
||||
}
|
||||
@@ -469,7 +465,7 @@ class DisplayableNode extends GraphNode
|
||||
{
|
||||
$aRootCauses[] = $oRootCause->GetHyperlink();
|
||||
}
|
||||
$sHtml .= '<p><img style="max-height: 24px; vertical-align:bottom;" src="'.utils::GetAbsoluteUrlModulesRoot().$aContext['icon'].'" title="'.htmlentities(Dict::S($aContext['dict'])).'"> '.implode(', ', $aRootCauses).'</p>';
|
||||
$sHtml .= '<p><img style="max-height: 24px; vertical-align:bottom;" class="ibo-class-icon ibo-is-small" src="'.utils::GetAbsoluteUrlModulesRoot().$aContext['icon'].'" title="'.htmlentities(Dict::S($aContext['dict'])).'"> '.implode(', ', $aRootCauses).'</p>';
|
||||
}
|
||||
$sHtml .= '<hr/>';
|
||||
}
|
||||
@@ -779,8 +775,8 @@ class DisplayableGroupNode extends DisplayableNode
|
||||
{
|
||||
$aBorderColor = array(200, 200, 200);
|
||||
}
|
||||
$oPdf->SetLineStyle(array('width' => 2*$fScale, 'cap' => 'round', 'join' => 'miter', 'dash' => 0, 'color' => $aBorderColor));
|
||||
|
||||
$oPdf->SetLineStyle(array('width' => 2 * $fScale, 'cap' => 'round', 'join' => 'miter', 'dash' => 0, 'color' => $aBorderColor));
|
||||
|
||||
$sIconUrl = $this->GetProperty('icon_url');
|
||||
$sIconPath = str_replace(utils::GetAbsoluteUrlModulesRoot(), APPROOT.'env-'.utils::GetCurrentEnvironment().'/', $sIconUrl);
|
||||
$oPdf->SetAlpha(1);
|
||||
@@ -794,13 +790,13 @@ class DisplayableGroupNode extends DisplayableNode
|
||||
{
|
||||
$oPdf->SetAlpha(0.4);
|
||||
}
|
||||
$oPdf->Image($sIconPath, ($this->x - 17)*$fScale, ($this->y - 17)*$fScale, 16*$fScale, 16*$fScale);
|
||||
$oPdf->Image($sIconPath, ($this->x + 1)*$fScale, ($this->y - 17)*$fScale, 16*$fScale, 16*$fScale);
|
||||
$oPdf->Image($sIconPath, ($this->x -8)*$fScale, ($this->y +1)*$fScale, 16*$fScale, 16*$fScale);
|
||||
$oPdf->AddImage($sIconPath, ($this->x - 17) * $fScale, ($this->y - 17) * $fScale, 16 * $fScale, 16 * $fScale);
|
||||
$oPdf->AddImage($sIconPath, ($this->x + 1) * $fScale, ($this->y - 17) * $fScale, 16 * $fScale, 16 * $fScale);
|
||||
$oPdf->AddImage($sIconPath, ($this->x - 8) * $fScale, ($this->y + 1) * $fScale, 16 * $fScale, 16 * $fScale);
|
||||
$oPdf->SetFontParams('', 24 * $fScale, '', true);
|
||||
$width = $oPdf->GetStringWidth($this->GetProperty('label'));
|
||||
$oPdf->SetTextColor(0, 0, 0);
|
||||
$oPdf->Text($this->x*$fScale - $width/2, ($this->y + 25)*$fScale, $this->GetProperty('label'));
|
||||
$oPdf->Text($this->x * $fScale - $width / 2, ($this->y + 25) * $fScale, $this->GetProperty('label'));
|
||||
}
|
||||
|
||||
public function GetTooltip($aContextDefs)
|
||||
@@ -810,7 +806,7 @@ class DisplayableGroupNode extends DisplayableNode
|
||||
$sHtml .= '<a href="#" onclick="$(\'.itop-simple-graph\').simple_graph(\'show_group\', \'relation_group_'.$iGroupIdx.'\');">'.Dict::Format('UI:RelationGroupNumber_N', (1+$iGroupIdx))."</a>";
|
||||
$sHtml .= '<hr/>';
|
||||
$sHtml .= '<table><tbody><tr>';
|
||||
$sHtml .= '<td style="vertical-align:top;padding-right: 0.5em;"><img src="'.$this->GetProperty('icon_url').'"></td><td style="vertical-align:top">'.MetaModel::GetName($this->GetObjectClass()).'<br/>';
|
||||
$sHtml .= '<td style="vertical-align:top;padding-right: 0.5em;"><img class="ibo-class-icon ibo-is-small" src="'.$this->GetProperty('icon_url').'"></td><td style="vertical-align:top">'.MetaModel::GetName($this->GetObjectClass()).'<br/>';
|
||||
$sHtml .= Dict::Format('UI_CountOfObjectsShort', $this->GetObjectCount()).'</td>';
|
||||
$sHtml .= '</tr></tbody></table>';
|
||||
return $sHtml;
|
||||
@@ -1185,7 +1181,7 @@ class DisplayableGraph extends SimpleGraph
|
||||
|
||||
return json_encode($aData);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sort class "codes" based on their localized name
|
||||
* @param string $sClass1
|
||||
@@ -1196,12 +1192,12 @@ class DisplayableGraph extends SimpleGraph
|
||||
{
|
||||
return strcasecmp(MetaModel::GetName($sClass1), MetaModel::GetName($sClass2));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Renders the graph in a PDF document: centered in the current page
|
||||
* @param PDFPage $oPage The PDFPage representing the PDF document to draw into
|
||||
* @param string $sComments An optional comment to display next to the graph (HTML entities will be escaped, \n replaced by <br/>)
|
||||
* @param string $sContextKey The key to fetch the queries in the configuration. Example: itop-tickets/relation_context/UserRequest/impacts/down
|
||||
* @param string $sContextKey The key to fetch the queries in the configuration. Example: itop-tickets/relation_context/UserRequest/impacts/down
|
||||
* @param float $xMin Left coordinate of the bounding box to display the graph
|
||||
* @param float $xMax Right coordinate of the bounding box to display the graph
|
||||
* @param float $yMin Top coordinate of the bounding box to display the graph
|
||||
@@ -1329,20 +1325,18 @@ class DisplayableGraph extends SimpleGraph
|
||||
$yPos = $yMin + $fPadding;
|
||||
$oPdf->SetFillColor(225, 225, 225);
|
||||
$oPdf->Cell($fIconSize + $fPadding + $fMaxWidth, $fIconSize + $fPadding, Dict::S('UI:Relation:Key'), 0 /* border */, 1 /* ln */, 'C', true /* fill */);
|
||||
$yPos += $fIconSize + 2*$fPadding;
|
||||
foreach($aClasses as $sClass => $sLabel)
|
||||
{
|
||||
$yPos += $fIconSize + 2 * $fPadding;
|
||||
foreach ($aClasses as $sClass => $sLabel) {
|
||||
$oPdf->SetX($xMin + $fIconSize + $fPadding);
|
||||
$oPdf->Cell(0, $fIconSize + 2*$fPadding, $sLabel, 0 /* border */, 1 /* ln */);
|
||||
$oPdf->Image($aIcons[$sClass], $xMin+1, $yPos, $fIconSize, $fIconSize);
|
||||
$yPos += $fIconSize + 2*$fPadding;
|
||||
$oPdf->Cell(0, $fIconSize + 2 * $fPadding, $sLabel, 0 /* border */, 1 /* ln */);
|
||||
$oPdf->AddImage($aIcons[$sClass], $xMin + 1, $yPos, $fIconSize, $fIconSize);
|
||||
$yPos += $fIconSize + 2 * $fPadding;
|
||||
}
|
||||
foreach($aContexts as $key => $sLabel)
|
||||
{
|
||||
foreach ($aContexts as $key => $sLabel) {
|
||||
$oPdf->SetX($xMin + $fIconSize + $fPadding);
|
||||
$oPdf->Cell(0, $fIconSize + 2*$fPadding, $sLabel, 0 /* border */, 1 /* ln */);
|
||||
$oPdf->Image($aContextIcons[$key], $xMin+1+$fIconSize*0.125, $yPos+$fIconSize*0.125, $fIconSize*0.75, $fIconSize*0.75);
|
||||
$yPos += $fIconSize + 2*$fPadding;
|
||||
$oPdf->Cell(0, $fIconSize + 2 * $fPadding, $sLabel, 0 /* border */, 1 /* ln */);
|
||||
$oPdf->AddImage($aContextIcons[$key], $xMin + 1 + $fIconSize * 0.125, $yPos + $fIconSize * 0.125, $fIconSize * 0.75, $fIconSize * 0.75);
|
||||
$yPos += $fIconSize + 2 * $fPadding;
|
||||
}
|
||||
$oPdf->Rect($xMin, $yMin, $fMaxWidth + $fIconSize + 3*$fPadding, $yMax - $yMin, 'D');
|
||||
|
||||
@@ -1453,9 +1447,9 @@ class DisplayableGraph extends SimpleGraph
|
||||
$sSftShort = Dict::S('UI:ElementsDisplayed');
|
||||
$sSearchToggle = Dict::S('UI:Search:Toggle');
|
||||
$oP->add("<div class=\"not-printable\">\n");
|
||||
$oUiSearchBlock = new Panel($sSftShort, [],Panel::ENUM_COLOR_CYAN, 'ds_flash');
|
||||
$oUiSearchBlock = new Panel($sSftShort, [],Panel::ENUM_COLOR_SCHEME_CYAN, 'ds_flash');
|
||||
$oUiSearchBlock->SetCSSClasses(["ibo-search-form-panel", "display_block"]);
|
||||
|
||||
$oUiSearchBlock->SetIsCollapsible(true);
|
||||
$oUiHtmlBlock = new Combodo\iTop\Application\UI\Base\Component\Html\Html(
|
||||
<<<EOF
|
||||
<div id="ds_flash" class="search_box ibo-display-graph--search-box">
|
||||
@@ -1506,11 +1500,8 @@ EOF
|
||||
|
||||
$sDirection = utils::ReadParam('d', 'horizontal');
|
||||
$iGroupingThreshold = utils::ReadParam('g', 5);
|
||||
|
||||
$oP->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/fraphael.js');
|
||||
$oP->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/jquery.contextMenu.css');
|
||||
$oP->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.contextMenu.js');
|
||||
$oP->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/simple_graph.js');
|
||||
|
||||
WebResourcesHelper::EnableSimpleGraphInWebPage($oP);
|
||||
try
|
||||
{
|
||||
$this->InitFromGraphviz();
|
||||
|
||||
@@ -24,6 +24,10 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
use Pelago\Emogrifier\CssInliner;
|
||||
use Pelago\Emogrifier\HtmlProcessor\CssToAttributeConverter;
|
||||
use Pelago\Emogrifier\HtmlProcessor\HtmlPruner;
|
||||
|
||||
Swift_Preferences::getInstance()->setCharset('UTF-8');
|
||||
|
||||
|
||||
@@ -335,8 +339,9 @@ class EMail
|
||||
{
|
||||
if (($sMimeType === 'text/html') && ($sCustomStyles !== null))
|
||||
{
|
||||
$emogrifier = new \Pelago\Emogrifier($sBody, $sCustomStyles);
|
||||
$sBody = $emogrifier->emogrify(); // Adds html/body tags if not already present
|
||||
$oDomDocument = CssInliner::fromHtml($sBody)->inlineCss($sCustomStyles)->getDomDocument();
|
||||
HtmlPruner::fromDomDocument($oDomDocument)->removeElementsWithDisplayNone();
|
||||
$sBody = CssToAttributeConverter::fromDomDocument($oDomDocument)->convertCssToVisualAttributes()->render(); // Adds html/body tags if not already present
|
||||
}
|
||||
$this->m_aData['body'] = array('body' => $sBody, 'mimeType' => $sMimeType);
|
||||
$this->m_oMessage->setBody($sBody, $sMimeType);
|
||||
|
||||
@@ -222,6 +222,10 @@ class EventIssue extends Event
|
||||
//
|
||||
$this->Set('page', @$GLOBALS['_SERVER']['SCRIPT_NAME']);
|
||||
|
||||
if (strlen($this->Get('userinfo')) == 0) {
|
||||
$this->Set('userinfo', UserRights::GetUserId());
|
||||
}
|
||||
|
||||
if (array_key_exists('_GET', $GLOBALS) && is_array($GLOBALS['_GET']))
|
||||
{
|
||||
$this->Set('arguments_get', $GLOBALS['_GET']);
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
*/
|
||||
|
||||
use PhpParser\Node\Expr\Assign;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Parser;
|
||||
use PhpParser\ParserFactory;
|
||||
use PhpParser\PrettyPrinter\Standard;
|
||||
|
||||
@@ -80,38 +82,49 @@ class iTopConfigParser
|
||||
* @param \PhpParser\Parser $oParser
|
||||
* @param $sConfig
|
||||
*
|
||||
* @return \Combodo\iTop\Config\Validator\ConfigNodesVisitor
|
||||
* @return void
|
||||
*/
|
||||
private function BrowseFile(\PhpParser\Parser $oParser, $sConfig)
|
||||
private function BrowseFile(Parser $oParser, $sConfig)
|
||||
{
|
||||
$prettyPrinter = new Standard();
|
||||
|
||||
try
|
||||
{
|
||||
try {
|
||||
$aNodes = $oParser->parse($sConfig);
|
||||
}
|
||||
catch (\Error $e)
|
||||
{
|
||||
catch (\Error $e) {
|
||||
$sMessage = Dict::Format('config-parse-error', $e->getMessage(), $e->getLine());
|
||||
$this->oException = new \Exception($sMessage, 0, $e);
|
||||
}
|
||||
|
||||
foreach ($aNodes as $oAssignation)
|
||||
{
|
||||
if (! $oAssignation instanceof Assign)
|
||||
{
|
||||
foreach ($aNodes as $sKey => $oNode) {
|
||||
// With PhpParser 3 we had an Assign node at root
|
||||
// In PhpParser 4 the root node is now an Expression
|
||||
|
||||
if (false === ($oNode instanceof \PhpParser\Node\Stmt\Expression)) {
|
||||
continue;
|
||||
}
|
||||
/** @var \PhpParser\Node\Stmt\Expression $oNode */
|
||||
|
||||
if (false === ($oNode->expr instanceof Assign)) {
|
||||
continue;
|
||||
}
|
||||
/** @var Assign $oAssignation */
|
||||
$oAssignation = $oNode->expr;
|
||||
|
||||
if (false === ($oAssignation->var instanceof Variable)) {
|
||||
continue;
|
||||
}
|
||||
if (false === ($oAssignation->expr instanceof PhpParser\Node\Expr\Array_)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$sCurrentRootVar = $oAssignation->var->name;
|
||||
if (!array_key_exists($sCurrentRootVar, $this->aVarsMap))
|
||||
{
|
||||
if (!array_key_exists($sCurrentRootVar, $this->aVarsMap)) {
|
||||
continue;
|
||||
}
|
||||
$aCurrentRootVarMap =& $this->aVarsMap[$sCurrentRootVar];
|
||||
|
||||
foreach ($oAssignation->expr->items as $oItem)
|
||||
{
|
||||
foreach ($oAssignation->expr->items as $oItem) {
|
||||
$sValue = $prettyPrinter->prettyPrintExpr($oItem->value);
|
||||
$aCurrentRootVarMap[$oItem->key->value] = $sValue;
|
||||
}
|
||||
|
||||
@@ -198,31 +198,29 @@ class InlineImage extends DBObject
|
||||
$sOQL = 'SELECT InlineImage WHERE temp_id = :temp_id';
|
||||
$oSearch = DBObjectSearch::FromOQL($sOQL);
|
||||
$oSet = new DBObjectSet($oSearch, array(), array('temp_id' => $sTempId));
|
||||
$aInlineImagesId = array();
|
||||
while($oInlineImage = $oSet->Fetch())
|
||||
{
|
||||
$aInlineImagesId[] = $oInlineImage->GetKey();
|
||||
$aInlineImagesId = array();
|
||||
while ($oInlineImage = $oSet->Fetch()) {
|
||||
$aInlineImagesId[] = $oInlineImage->GetKey();
|
||||
$oInlineImage->SetItem($oObject);
|
||||
$oInlineImage->Set('temp_id', '');
|
||||
$oInlineImage->DBUpdate();
|
||||
}
|
||||
IssueLog::Trace('FinalizeInlineImages (see $aInlineImagesId for the id list)', 'InlineImage', array(
|
||||
'$sObjectClass' => get_class($oObject),
|
||||
'$sTransactionId' => $iTransactionId,
|
||||
'$sTempId' => $sTempId,
|
||||
'$aInlineImagesId' => $aInlineImagesId,
|
||||
'$sUser' => UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
));
|
||||
IssueLog::Trace('FinalizeInlineImages (see $aInlineImagesId for the id list)', LogChannels::INLINE_IMAGE, array(
|
||||
'$sObjectClass' => get_class($oObject),
|
||||
'$sTransactionId' => $iTransactionId,
|
||||
'$sTempId' => $sTempId,
|
||||
'$aInlineImagesId' => $aInlineImagesId,
|
||||
'$sUser' => UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
));
|
||||
}
|
||||
else
|
||||
{
|
||||
IssueLog::Trace('FinalizeInlineImages "error" $iTransactionId is null', 'InlineImage', array(
|
||||
'$sObjectClass' => get_class($oObject),
|
||||
'$sTransactionId' => $iTransactionId,
|
||||
'$sUser' => UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
));
|
||||
else {
|
||||
IssueLog::Trace('FinalizeInlineImages "error" $iTransactionId is null', LogChannels::INLINE_IMAGE, array(
|
||||
'$sObjectClass' => get_class($oObject),
|
||||
'$sTransactionId' => $iTransactionId,
|
||||
'$sUser' => UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,7 +229,7 @@ class InlineImage extends DBObject
|
||||
*
|
||||
* @param string $sTempId
|
||||
*
|
||||
* @return void
|
||||
* @return bool True if cleaning was successful, false if anything aborted it
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreCannotSaveObjectException
|
||||
* @throws \CoreException
|
||||
@@ -241,8 +239,19 @@ class InlineImage extends DBObject
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public static function OnFormCancel($sTempId)
|
||||
public static function OnFormCancel($sTempId): bool
|
||||
{
|
||||
// Protection against unfortunate massive delete of inline images when a null temp ID is passed
|
||||
if (strlen($sTempId) === 0) {
|
||||
IssueLog::Trace('OnFormCancel "error" $sTempId is null or empty', LogChannels::INLINE_IMAGE, array(
|
||||
'$sTempId' => $sTempId,
|
||||
'$sUser' => UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Delete all "pending" InlineImages for this form
|
||||
$sOQL = 'SELECT InlineImage WHERE temp_id = :temp_id';
|
||||
$oSearch = DBObjectSearch::FromOQL($sOQL);
|
||||
@@ -253,12 +262,14 @@ class InlineImage extends DBObject
|
||||
$aInlineImagesId[] = $oInlineImage->GetKey();
|
||||
$oInlineImage->DBDelete();
|
||||
}
|
||||
IssueLog::Trace('OnFormCancel', 'InlineImage', array(
|
||||
'$sTempId' => $sTempId,
|
||||
'$aInlineImagesId' => $aInlineImagesId,
|
||||
'$sUser' => UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
));
|
||||
IssueLog::Trace('OnFormCancel', LogChannels::INLINE_IMAGE, array(
|
||||
'$sTempId' => $sTempId,
|
||||
'$aInlineImagesId' => $aInlineImagesId,
|
||||
'$sUser' => UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -579,7 +590,7 @@ JS
|
||||
oEditor.on( 'instanceReady', function() {
|
||||
if(!CKEDITOR.env.iOS && $('#'+oEditor.id+'_toolbox .ibo-vendors-ckeditor--toolbar-fullscreen-button').length == 0)
|
||||
{
|
||||
$('#'+oEditor.id+'_toolbox').append('<span class="ibo-vendors-ckeditor--toolbar-fullscreen-button" data-role="ibo-vendors-ckeditor--toolbar-fullscreen-button" title="$sToggleFullScreen" style="background-image:url(\\'$sAbsoluteUrlAppRoot/images/full-screen.png\\')"> </span>');
|
||||
$('#'+oEditor.id+'_toolbox').append('<span class="ibo-vendors-ckeditor--toolbar-fullscreen-button editor-fullscreen-button" data-role="ibo-vendors-ckeditor--toolbar-fullscreen-button" title="$sToggleFullScreen"> </span>');
|
||||
$('#'+oEditor.id+'_toolbox .ibo-vendors-ckeditor--toolbar-fullscreen-button').on('click', function() {
|
||||
oEditor.execCommand('maximize');
|
||||
if ($(this).closest('.cke_maximized').length != 0)
|
||||
@@ -608,17 +619,17 @@ JS
|
||||
*/
|
||||
protected function AfterInsert()
|
||||
{
|
||||
IssueLog::Trace(__METHOD__, 'InlineImage', array(
|
||||
'id' => $this->GetKey(),
|
||||
'expire' => $this->Get('expire'),
|
||||
'temp_id' => $this->Get('temp_id'),
|
||||
'item_class' => $this->Get('item_class'),
|
||||
'item_id' => $this->Get('item_id'),
|
||||
'item_org_id' => $this->Get('item_org_id'),
|
||||
'secret' => $this->Get('secret'),
|
||||
'user' => $sUser = UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
|
||||
IssueLog::Trace(__METHOD__, LogChannels::INLINE_IMAGE, array(
|
||||
'id' => $this->GetKey(),
|
||||
'expire' => $this->Get('expire'),
|
||||
'temp_id' => $this->Get('temp_id'),
|
||||
'item_class' => $this->Get('item_class'),
|
||||
'item_id' => $this->Get('item_id'),
|
||||
'item_org_id' => $this->Get('item_org_id'),
|
||||
'secret' => $this->Get('secret'),
|
||||
'user' => $sUser = UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
|
||||
));
|
||||
|
||||
parent::AfterInsert();
|
||||
@@ -629,17 +640,17 @@ JS
|
||||
*/
|
||||
protected function AfterUpdate()
|
||||
{
|
||||
IssueLog::Trace(__METHOD__, 'InlineImage', array(
|
||||
'id' => $this->GetKey(),
|
||||
'expire' => $this->Get('expire'),
|
||||
'temp_id' => $this->Get('temp_id'),
|
||||
'item_class' => $this->Get('item_class'),
|
||||
'item_id' => $this->Get('item_id'),
|
||||
'item_org_id' => $this->Get('item_org_id'),
|
||||
'secret' => $this->Get('secret'),
|
||||
'user' => $sUser = UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
|
||||
IssueLog::Trace(__METHOD__, LogChannels::INLINE_IMAGE, array(
|
||||
'id' => $this->GetKey(),
|
||||
'expire' => $this->Get('expire'),
|
||||
'temp_id' => $this->Get('temp_id'),
|
||||
'item_class' => $this->Get('item_class'),
|
||||
'item_id' => $this->Get('item_id'),
|
||||
'item_org_id' => $this->Get('item_org_id'),
|
||||
'secret' => $this->Get('secret'),
|
||||
'user' => $sUser = UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
|
||||
));
|
||||
|
||||
parent::AfterUpdate();
|
||||
@@ -650,17 +661,17 @@ JS
|
||||
*/
|
||||
protected function AfterDelete()
|
||||
{
|
||||
IssueLog::Trace(__METHOD__, 'InlineImage', array(
|
||||
'id' => $this->GetKey(),
|
||||
'expire' => $this->Get('expire'),
|
||||
'temp_id' => $this->Get('temp_id'),
|
||||
'item_class' => $this->Get('item_class'),
|
||||
'item_id' => $this->Get('item_id'),
|
||||
'item_org_id' => $this->Get('item_org_id'),
|
||||
'secret' => $this->Get('secret'),
|
||||
'user' => $sUser = UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
|
||||
IssueLog::Trace(__METHOD__, LogChannels::INLINE_IMAGE, array(
|
||||
'id' => $this->GetKey(),
|
||||
'expire' => $this->Get('expire'),
|
||||
'temp_id' => $this->Get('temp_id'),
|
||||
'item_class' => $this->Get('item_class'),
|
||||
'item_id' => $this->Get('item_id'),
|
||||
'item_org_id' => $this->Get('item_org_id'),
|
||||
'secret' => $this->Get('secret'),
|
||||
'user' => $sUser = UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
|
||||
));
|
||||
|
||||
parent::AfterDelete();
|
||||
|
||||
@@ -31,29 +31,38 @@ class ExecutionKPI
|
||||
static protected $m_bBlameCaller = false;
|
||||
static protected $m_sAllowedUser = '*';
|
||||
|
||||
static protected $m_aStats = array(); // Recurrent operations
|
||||
static protected $m_aExecData = array(); // One shot operations
|
||||
static protected $m_aStats = []; // Recurrent operations
|
||||
static protected $m_aExecData = []; // One shot operations
|
||||
/**
|
||||
* @var array[ExecutionKPI]
|
||||
*/
|
||||
static protected $m_aExecutionStack = []; // embedded execution stats
|
||||
|
||||
protected $m_fStarted = null;
|
||||
protected $m_fChildrenDuration = 0; // Count embedded
|
||||
protected $m_iInitialMemory = null;
|
||||
|
||||
static public function EnableDuration($iLevel)
|
||||
{
|
||||
if ($iLevel > 0)
|
||||
{
|
||||
if ($iLevel > 0) {
|
||||
self::$m_bEnabled_Duration = true;
|
||||
if ($iLevel > 1)
|
||||
{
|
||||
if ($iLevel > 1) {
|
||||
self::$m_bBlameCaller = true;
|
||||
} else {
|
||||
self::$m_bBlameCaller = false;
|
||||
}
|
||||
} else {
|
||||
self::$m_bEnabled_Duration = false;
|
||||
self::$m_bBlameCaller = false;
|
||||
}
|
||||
}
|
||||
|
||||
static public function EnableMemory($iLevel)
|
||||
{
|
||||
if ($iLevel > 0)
|
||||
{
|
||||
if ($iLevel > 0) {
|
||||
self::$m_bEnabled_Memory = true;
|
||||
} else {
|
||||
self::$m_bEnabled_Memory = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,16 +112,17 @@ class ExecutionKPI
|
||||
|
||||
$sTableStyle = 'background-color: #ccc; margin: 10px;';
|
||||
|
||||
self::Report("<hr/>");
|
||||
self::Report("<div style=\"background-color: grey; padding: 10px;\">");
|
||||
self::Report("<h3><a name=\"".md5($sExecId)."\">KPIs</a> - ".$_SERVER['REQUEST_URI']." (".$_SERVER['REQUEST_METHOD'].")</h3>");
|
||||
self::Report("<p>".date('Y-m-d H:i:s', $fItopStarted)."</p>");
|
||||
self::Report("<p>log_kpi_user_id: ".UserRights::GetUserId()."</p>");
|
||||
self::Report("<div>");
|
||||
self::Report("<table border=\"1\" style=\"$sTableStyle\">");
|
||||
self::Report("<thead>");
|
||||
self::Report(" <th>Operation</th><th>Begin</th><th>End</th><th>Duration</th><th>Memory start</th><th>Memory end</th><th>Memory peak</th>");
|
||||
self::Report("</thead>");
|
||||
$sHtml = "<hr/>";
|
||||
$sHtml .= "<div style=\"background-color: grey; padding: 10px;\">";
|
||||
$sHtml .= "<h3><a name=\"".md5($sExecId)."\">KPIs</a> - ".$_SERVER['REQUEST_URI']." (".$_SERVER['REQUEST_METHOD'].")</h3>";
|
||||
$oStarted = DateTime::createFromFormat('U.u', $fItopStarted);
|
||||
$sHtml .= "<p>".$oStarted->format('Y-m-d H:i:s.u')."</p>";
|
||||
$sHtml .= "<p>log_kpi_user_id: ".UserRights::GetUserId()."</p>";
|
||||
$sHtml .= "<div>";
|
||||
$sHtml .= "<table border=\"1\" style=\"$sTableStyle\">";
|
||||
$sHtml .= "<thead>";
|
||||
$sHtml .= " <th>Operation</th><th>Begin</th><th>End</th><th>Duration</th><th>Memory start</th><th>Memory end</th><th>Memory peak</th>";
|
||||
$sHtml .= "</thead>";
|
||||
foreach (self::$m_aExecData as $aOpStats)
|
||||
{
|
||||
$sOperation = $aOpStats['op'];
|
||||
@@ -134,12 +144,12 @@ class ExecutionKPI
|
||||
}
|
||||
}
|
||||
|
||||
self::Report("<tr>");
|
||||
self::Report(" <td>$sOperation</td><td>$sBegin</td><td>$sEnd</td><td>$sDuration</td><td>$sMemBegin</td><td>$sMemEnd</td><td>$sMemPeak</td>");
|
||||
self::Report("</tr>");
|
||||
$sHtml .= "<tr>";
|
||||
$sHtml .= " <td>$sOperation</td><td>$sBegin</td><td>$sEnd</td><td>$sDuration</td><td>$sMemBegin</td><td>$sMemEnd</td><td>$sMemPeak</td>";
|
||||
$sHtml .= "</tr>";
|
||||
}
|
||||
self::Report("</table>");
|
||||
self::Report("</div>");
|
||||
$sHtml .= "</table>";
|
||||
$sHtml .= "</div>";
|
||||
|
||||
$aConsolidatedStats = array();
|
||||
foreach (self::$m_aStats as $sOperation => $aOpStats)
|
||||
@@ -175,11 +185,11 @@ class ExecutionKPI
|
||||
);
|
||||
}
|
||||
|
||||
self::Report("<div>");
|
||||
self::Report("<table border=\"1\" style=\"$sTableStyle\">");
|
||||
self::Report("<thead>");
|
||||
self::Report(" <th>Operation</th><th>Count</th><th>Duration</th><th>Min</th><th>Max</th><th>Avg</th>");
|
||||
self::Report("</thead>");
|
||||
$sHtml .= "<div>";
|
||||
$sHtml .= "<table border=\"1\" style=\"$sTableStyle\">";
|
||||
$sHtml .= "<thead>";
|
||||
$sHtml .= " <th>Operation</th><th>Count</th><th>Duration</th><th>Min</th><th>Max</th><th>Avg</th>";
|
||||
$sHtml .= "</thead>";
|
||||
foreach ($aConsolidatedStats as $sOperation => $aOpStats)
|
||||
{
|
||||
$sOperation = '<a href="#'.md5($sExecId.$sOperation).'">'.$sOperation.'</a>';
|
||||
@@ -189,22 +199,24 @@ class ExecutionKPI
|
||||
$sMax = '<a href="#'.md5($sExecId.$aOpStats['max_args']).'">'.round($aOpStats['max'], 3).'</a>';
|
||||
$sAvg = round($aOpStats['avg'], 3);
|
||||
|
||||
self::Report("<tr>");
|
||||
self::Report(" <td>$sOperation</td><td>$sCount</td><td>$sDuration</td><td>$sMin</td><td>$sMax</td><td>$sAvg</td>");
|
||||
self::Report("</tr>");
|
||||
$sHtml .= "<tr>";
|
||||
$sHtml .= " <td>$sOperation</td><td>$sCount</td><td>$sDuration</td><td>$sMin</td><td>$sMax</td><td>$sAvg</td>";
|
||||
$sHtml .= "</tr>";
|
||||
}
|
||||
self::Report("</table>");
|
||||
self::Report("</div>");
|
||||
$sHtml .= "</table>";
|
||||
$sHtml .= "</div>";
|
||||
|
||||
self::Report("</div>");
|
||||
$sHtml .= "</div>";
|
||||
|
||||
self::Report("<p><a href=\"#end-".md5($sExecId)."\">Next page stats</a></p>");
|
||||
$sHtml .= "<p><a href=\"#end-".md5($sExecId)."\">Next page stats</a></p>";
|
||||
|
||||
$fSlowQueries = MetaModel::GetConfig()->Get('log_kpi_slow_queries');
|
||||
self::Report($sHtml);
|
||||
|
||||
// Report operation details
|
||||
foreach (self::$m_aStats as $sOperation => $aOpStats)
|
||||
{
|
||||
$sHtml = '';
|
||||
$bDisplayHeader = true;
|
||||
foreach ($aOpStats as $sArguments => $aEvents)
|
||||
{
|
||||
@@ -250,31 +262,59 @@ class ExecutionKPI
|
||||
if ($bDisplayHeader)
|
||||
{
|
||||
$sOperationHtml = '<a name="'.md5($sExecId.$sOperation).'">'.$sOperation.'</a>';
|
||||
self::Report("<h4>$sOperationHtml</h4>");
|
||||
self::Report("<table border=\"1\" style=\"$sTableStyle\">");
|
||||
self::Report("<thead>");
|
||||
self::Report(" <th>Operation details (+ blame caller if log_kpi_duration = 2)</th><th>Count</th><th>Duration</th><th>Min</th><th>Max</th>");
|
||||
self::Report("</thead>");
|
||||
$sHtml .= "<h4>$sOperationHtml</h4>";
|
||||
$sHtml .= "<table border=\"1\" style=\"$sTableStyle\">";
|
||||
$sHtml .= "<thead>";
|
||||
$sHtml .= " <th>Operation details (+ blame caller if log_kpi_duration = 2)</th><th>Count</th><th>Duration</th><th>Min</th><th>Max</th>";
|
||||
$sHtml .= "</thead>";
|
||||
$bDisplayHeader = false;
|
||||
}
|
||||
self::Report("<tr>");
|
||||
self::Report(" <td>$sHtmlArguments</td><td>$iCountInter</td><td>$sTotalInter</td><td>$sMinInter</td><td>$sMaxInter</td>");
|
||||
self::Report("</tr>");
|
||||
$sHtml .= "<tr>";
|
||||
$sHtml .= " <td>$sHtmlArguments</td><td>$iCountInter</td><td>$sTotalInter</td><td>$sMinInter</td><td>$sMaxInter</td>";
|
||||
$sHtml .= "</tr>";
|
||||
}
|
||||
}
|
||||
if (!$bDisplayHeader)
|
||||
{
|
||||
self::Report("</table>");
|
||||
self::Report("<p><a href=\"#".md5($sExecId)."\">Back to page stats</a></p>");
|
||||
$sHtml .= "</table>";
|
||||
$sHtml .= "<p><a href=\"#".md5($sExecId)."\">Back to page stats</a></p>";
|
||||
}
|
||||
self::Report($sHtml);
|
||||
}
|
||||
self::Report('<a name="end-'.md5($sExecId).'"> </a>');
|
||||
$sHtml = '<a name="end-'.md5($sExecId).'"> </a>';
|
||||
self::Report($sHtml);
|
||||
}
|
||||
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->ResetCounters();
|
||||
self::Push($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stack executions to remove children duration from stats
|
||||
*
|
||||
* @param \ExecutionKPI $oExecutionKPI
|
||||
*/
|
||||
private static function Push(ExecutionKPI $oExecutionKPI)
|
||||
{
|
||||
array_push(self::$m_aExecutionStack, $oExecutionKPI);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pop current child and count its duration in its parent
|
||||
*
|
||||
* @param float|int $fChildDuration
|
||||
*/
|
||||
private static function Pop(float $fChildDuration = 0)
|
||||
{
|
||||
array_pop(self::$m_aExecutionStack);
|
||||
// Update the parent's children duration
|
||||
$oPrevExecutionKPI = end(self::$m_aExecutionStack);
|
||||
if ($oPrevExecutionKPI) {
|
||||
$oPrevExecutionKPI->m_fChildrenDuration += $fChildDuration;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the duration since startup, and reset the counter for the next measure
|
||||
@@ -285,13 +325,12 @@ class ExecutionKPI
|
||||
|
||||
$aNewEntry = null;
|
||||
|
||||
if (self::$m_bEnabled_Duration)
|
||||
{
|
||||
if (self::$m_bEnabled_Duration) {
|
||||
$fStopped = MyHelpers::getmicrotime();
|
||||
$aNewEntry = array(
|
||||
'op' => $sOperationDesc,
|
||||
'op' => $sOperationDesc,
|
||||
'time_begin' => $this->m_fStarted - $fItopStarted,
|
||||
'time_end' => $fStopped - $fItopStarted,
|
||||
'time_end' => $fStopped - $fItopStarted,
|
||||
);
|
||||
// Reset for the next operation (if the object is recycled)
|
||||
$this->m_fStarted = $fStopped;
|
||||
@@ -323,31 +362,30 @@ class ExecutionKPI
|
||||
|
||||
public function ComputeStats($sOperation, $sArguments)
|
||||
{
|
||||
if (self::$m_bEnabled_Duration)
|
||||
{
|
||||
$fDuration = 0;
|
||||
if (self::$m_bEnabled_Duration) {
|
||||
$fStopped = MyHelpers::getmicrotime();
|
||||
$fDuration = $fStopped - $this->m_fStarted;
|
||||
if (self::$m_bBlameCaller)
|
||||
{
|
||||
$fSelfDuration = $fDuration - $this->m_fChildrenDuration;
|
||||
if (self::$m_bBlameCaller) {
|
||||
self::$m_aStats[$sOperation][$sArguments][] = array(
|
||||
'time' => $fDuration,
|
||||
'time' => $fSelfDuration,
|
||||
'callers' => MyHelpers::get_callstack(1),
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
self::$m_aStats[$sOperation][$sArguments][] = array(
|
||||
'time' => $fDuration
|
||||
'time' => $fSelfDuration,
|
||||
);
|
||||
}
|
||||
}
|
||||
self::Pop($fDuration);
|
||||
}
|
||||
|
||||
protected function ResetCounters()
|
||||
{
|
||||
if (self::$m_bEnabled_Duration)
|
||||
{
|
||||
$this->m_fStarted = MyHelpers::getmicrotime();
|
||||
$this->m_fStarted = microtime(true);
|
||||
}
|
||||
|
||||
if (self::$m_bEnabled_Memory)
|
||||
|
||||
@@ -399,10 +399,10 @@ class MonthlyRotatingLogFileNameBuilder extends RotatingLogFileNameBuilder
|
||||
*/
|
||||
protected function GetFileSuffix($oDate)
|
||||
{
|
||||
$sWeekYear = $oDate->format('o');
|
||||
$sWeekNumber = $oDate->format('m');
|
||||
$sMonthYear = $oDate->format('o');
|
||||
$sMonthNumber = $oDate->format('m');
|
||||
|
||||
return $sWeekYear.'-month'.$sWeekNumber;
|
||||
return $sMonthYear.'-month'.$sMonthNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -503,6 +503,7 @@ class FileLog
|
||||
protected function Write($sText, $sLevel = '', $sChannel = '', $aContext = array())
|
||||
{
|
||||
$sTextPrefix = empty($sLevel) ? '' : (str_pad($sLevel, 7).' | ');
|
||||
$sTextPrefix .= str_pad(UserRights::GetUserId(), 5)." | ";
|
||||
$sTextSuffix = empty($sChannel) ? '' : " | $sChannel";
|
||||
$sText = "{$sTextPrefix}{$sText}{$sTextSuffix}";
|
||||
$sLogFilePath = $this->oFileNameBuilder->GetLogFilePath();
|
||||
@@ -517,12 +518,9 @@ class FileLog
|
||||
{
|
||||
flock($hLogFile, LOCK_EX);
|
||||
$sDate = date('Y-m-d H:i:s');
|
||||
if (empty($aContext))
|
||||
{
|
||||
if (empty($aContext)) {
|
||||
fwrite($hLogFile, "$sDate | $sText\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$sContext = var_export($aContext, true);
|
||||
fwrite($hLogFile, "$sDate | $sText\n$sContext\n");
|
||||
}
|
||||
@@ -533,32 +531,54 @@ class FileLog
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Simple enum like class to factorize channels values as constants
|
||||
* Channels are used especially as parameters in {@see \LogAPI} methods
|
||||
*
|
||||
* @since 2.7.5 3.0.0 N°4012
|
||||
*/
|
||||
class LogChannels
|
||||
{
|
||||
public const CLI = 'CLI';
|
||||
public const CONSOLE = 'console';
|
||||
public const DEADLOCK = 'DeadLock';
|
||||
public const INLINE_IMAGE = 'InlineImage';
|
||||
public const PORTAL = 'portal';
|
||||
public const CMDB_SOURCE = 'cmdbsource';
|
||||
}
|
||||
|
||||
|
||||
abstract class LogAPI
|
||||
{
|
||||
const CHANNEL_DEFAULT = '';
|
||||
public const CHANNEL_DEFAULT = '';
|
||||
|
||||
const LEVEL_ERROR = 'Error';
|
||||
const LEVEL_WARNING = 'Warning';
|
||||
const LEVEL_INFO = 'Info';
|
||||
const LEVEL_OK = 'Ok';
|
||||
const LEVEL_DEBUG = 'Debug';
|
||||
const LEVEL_TRACE = 'Trace';
|
||||
public const LEVEL_ERROR = 'Error';
|
||||
public const LEVEL_WARNING = 'Warning';
|
||||
public const LEVEL_INFO = 'Info';
|
||||
public const LEVEL_OK = 'Ok';
|
||||
public const LEVEL_DEBUG = 'Debug';
|
||||
public const LEVEL_TRACE = 'Trace';
|
||||
/**
|
||||
* @var string default log level, can be overrided
|
||||
* @var string default log level
|
||||
* @see GetMinLogLevel
|
||||
* @used-by GetLevelDefault
|
||||
* @since 2.7.1 N°2977
|
||||
*/
|
||||
const LEVEL_DEFAULT = self::LEVEL_OK;
|
||||
public const LEVEL_DEFAULT = self::LEVEL_OK;
|
||||
|
||||
protected static $aLevelsPriority = array(
|
||||
self::LEVEL_ERROR => 400,
|
||||
self::LEVEL_ERROR => 400,
|
||||
self::LEVEL_WARNING => 300,
|
||||
self::LEVEL_INFO => 200,
|
||||
self::LEVEL_OK => 200,
|
||||
self::LEVEL_DEBUG => 100,
|
||||
self::LEVEL_TRACE => 50,
|
||||
self::LEVEL_INFO => 200,
|
||||
self::LEVEL_OK => 200,
|
||||
self::LEVEL_DEBUG => 100,
|
||||
self::LEVEL_TRACE => 50,
|
||||
);
|
||||
|
||||
/**
|
||||
* @var \Config attribute allowing to mock config in the tests
|
||||
*/
|
||||
protected static $m_oMockMetaModelConfig = null;
|
||||
|
||||
public static function Enable($sTargetFile)
|
||||
@@ -567,7 +587,10 @@ abstract class LogAPI
|
||||
static::$m_oFileLog = new FileLog($sTargetFile);
|
||||
}
|
||||
|
||||
public static function MockStaticObjects($oFileLog, $oMetaModelConfig=null)
|
||||
/**
|
||||
* @internal uses only for testing purpose.
|
||||
*/
|
||||
public static function MockStaticObjects($oFileLog, $oMetaModelConfig = null)
|
||||
{
|
||||
static::$m_oFileLog = $oFileLog;
|
||||
static::$m_oMockMetaModelConfig = $oMetaModelConfig;
|
||||
@@ -603,85 +626,132 @@ abstract class LogAPI
|
||||
static::Log(self::LEVEL_TRACE, $sMessage, $sChannel, $aContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \ConfigException if log wrongly configured
|
||||
*/
|
||||
public static function Log($sLevel, $sMessage, $sChannel = null, $aContext = array())
|
||||
{
|
||||
if (! static::$m_oFileLog)
|
||||
{
|
||||
if (!static::$m_oFileLog) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (! isset(self::$aLevelsPriority[$sLevel]))
|
||||
{
|
||||
if (!isset(self::$aLevelsPriority[$sLevel])) {
|
||||
IssueLog::Error("invalid log level '{$sLevel}'");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_null($sChannel))
|
||||
{
|
||||
if (is_null($sChannel)) {
|
||||
$sChannel = static::CHANNEL_DEFAULT;
|
||||
}
|
||||
|
||||
$sMinLogLevel = self::GetMinLogLevel($sChannel);
|
||||
|
||||
if ($sMinLogLevel === false || $sMinLogLevel === 'false')
|
||||
{
|
||||
if (!static::IsLogLevelEnabled($sLevel, $sChannel)) {
|
||||
return;
|
||||
}
|
||||
if (is_string($sMinLogLevel))
|
||||
{
|
||||
if (! isset(self::$aLevelsPriority[$sMinLogLevel]))
|
||||
{
|
||||
throw new Exception("invalid configuration for log_level '{$sMinLogLevel}' is not within the list: ".implode(',', array_keys(self::$aLevelsPriority)));
|
||||
}
|
||||
elseif (self::$aLevelsPriority[$sLevel] < self::$aLevelsPriority[$sMinLogLevel])
|
||||
{
|
||||
//priority too low regarding the conf, do not log this
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static::$m_oFileLog->$sLevel($sMessage, $sChannel, $aContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sChannel
|
||||
*
|
||||
* @return string one of the LEVEL_* const value
|
||||
* @uses \LogAPI::LEVEL_DEFAULT
|
||||
* @throws \ConfigException if log wrongly configured
|
||||
* @uses GetMinLogLevel
|
||||
*/
|
||||
private static function GetMinLogLevel($sChannel)
|
||||
final public static function IsLogLevelEnabled(string $sLevel, string $sChannel, string $sCode = 'log_level_min'): bool
|
||||
{
|
||||
$oConfig = (static::$m_oMockMetaModelConfig !== null) ? static::$m_oMockMetaModelConfig : \MetaModel::GetConfig();
|
||||
if (!$oConfig instanceof Config)
|
||||
{
|
||||
return static::LEVEL_DEFAULT;
|
||||
$sMinLogLevel = self::GetMinLogLevel($sChannel, $sCode);
|
||||
|
||||
if ($sMinLogLevel === false || $sMinLogLevel === 'false') {
|
||||
return false;
|
||||
}
|
||||
if (!is_string($sMinLogLevel)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$sLogLevelMin = $oConfig->Get('log_level_min');
|
||||
|
||||
if (empty($sLogLevelMin))
|
||||
{
|
||||
return static::LEVEL_DEFAULT;
|
||||
if (!isset(self::$aLevelsPriority[$sMinLogLevel])) {
|
||||
throw new ConfigException("invalid configuration for log_level '{$sMinLogLevel}' is not within the list: ".implode(',', array_keys(self::$aLevelsPriority)));
|
||||
} elseif (self::$aLevelsPriority[$sLevel] < self::$aLevelsPriority[$sMinLogLevel]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_array($sLogLevelMin))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sChannel
|
||||
* @param string $sCode
|
||||
*
|
||||
* @return string one of the LEVEL_* const value : the one configured it if exists, otherwise default log level for this channel
|
||||
* Config can be set :
|
||||
* * globally : `'log_level_min' => LogAPI::LEVEL_TRACE,`
|
||||
* * per channel :
|
||||
* ```
|
||||
* 'log_level_min' => [
|
||||
* '' => LogAPI::LEVEL_ERROR, // default log level for channels not listed below
|
||||
* 'InlineImage' => LogAPI::LEVEL_TRACE,
|
||||
* 'UserRequest' => LogAPI::LEVEL_TRACE
|
||||
* ],
|
||||
* ```
|
||||
*
|
||||
* @uses \LogAPI::GetConfig()
|
||||
* @uses `log_level_min` config parameter
|
||||
* @uses \LogAPI::GetLevelDefault
|
||||
*
|
||||
* @link https://www.itophub.io/wiki/page?id=3_0_0%3Aadmin%3Alog iTop log reference
|
||||
*/
|
||||
protected static function GetMinLogLevel($sChannel, $sCode = 'log_level_min')
|
||||
{
|
||||
$oConfig = static::GetConfig();
|
||||
if (!$oConfig instanceof Config) {
|
||||
return static::GetLevelDefault();
|
||||
}
|
||||
|
||||
$sLogLevelMin = $oConfig->Get($sCode);
|
||||
|
||||
if (empty($sLogLevelMin)) {
|
||||
return static::GetLevelDefault();
|
||||
}
|
||||
|
||||
if (!is_array($sLogLevelMin)) {
|
||||
return $sLogLevelMin;
|
||||
}
|
||||
|
||||
if (isset($sLogLevelMin[$sChannel]))
|
||||
{
|
||||
if (isset($sLogLevelMin[$sChannel])) {
|
||||
return $sLogLevelMin[$sChannel];
|
||||
}
|
||||
|
||||
if (isset($sLogLevelMin[static::CHANNEL_DEFAULT]))
|
||||
{
|
||||
return $sLogLevelMin[$sChannel];
|
||||
if (isset($sLogLevelMin[static::CHANNEL_DEFAULT])) {
|
||||
return $sLogLevelMin[static::CHANNEL_DEFAULT];
|
||||
}
|
||||
|
||||
return static::LEVEL_DEFAULT;
|
||||
// Even though the *self*::CHANNEL_DEFAULT is set to '' in the current class (LogAPI), the test below is necessary as the CHANNEL_DEFAULT constant can be (and is!) overloaded in derivated classes, don't remove this test to factorize it with the previous one.
|
||||
if (isset($sLogLevelMin[''])) {
|
||||
return $sLogLevelMin[''];
|
||||
}
|
||||
|
||||
return static::GetLevelDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
* @uses m_oMockMetaModelConfig if defined
|
||||
* @uses \MetaModel::GetConfig()
|
||||
*/
|
||||
protected static function GetConfig(): ?Config
|
||||
{
|
||||
return static::$m_oMockMetaModelConfig ?? \utils::GetConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* A method to override if default log level needs to be computed. Otherwise simply override the {@see LEVEL_DEFAULT} constant
|
||||
*
|
||||
* @used-by GetMinLogLevel
|
||||
* @uses \LogAPI::LEVEL_DEFAULT
|
||||
*
|
||||
* @since 3.0.0 N°3731
|
||||
*/
|
||||
protected static function GetLevelDefault(): string
|
||||
{
|
||||
return static::LEVEL_DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
class SetupLog extends LogAPI
|
||||
@@ -741,7 +811,7 @@ class DeadLockLog extends LogAPI
|
||||
return self::CHANNEL_WAIT_TIMEOUT;
|
||||
break;
|
||||
case 1213:
|
||||
return self::CHANNEL_DEADLOCK_FOUND;
|
||||
return self::CHANNEL_DEADLOCK_FOUND;
|
||||
break;
|
||||
default:
|
||||
return self::CHANNEL_DEFAULT;
|
||||
@@ -750,17 +820,238 @@ class DeadLockLog extends LogAPI
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $iMySQLErrNo will be converted to channel using {@link GetChannelFromMysqlErrorNo}
|
||||
* @param string $sLevel
|
||||
* @param string $sMessage
|
||||
* @param null $iMysqlErroNo
|
||||
* @param int $iMysqlErrorNumber will be converted to channel using {@link GetChannelFromMysqlErrorNo}
|
||||
* @param array $aContext
|
||||
*
|
||||
* @throws \Exception
|
||||
* @noinspection PhpParameterNameChangedDuringInheritanceInspection
|
||||
*
|
||||
* @since 2.7.1 method creation
|
||||
* @since 2.7.5 3.0.0 rename param names and fix phpdoc (thanks Hipska !)
|
||||
*/
|
||||
public static function Log($iMySQLErrNo, $sMessage, $iMysqlErroNo = null, $aContext = array())
|
||||
public static function Log($sLevel, $sMessage, $iMysqlErrorNumber = null, $aContext = array())
|
||||
{
|
||||
$sChannel = self::GetChannelFromMysqlErrorNo($iMysqlErroNo);
|
||||
parent::Log($iMySQLErrNo, $sMessage, $sChannel, $aContext);
|
||||
$sChannel = self::GetChannelFromMysqlErrorNo($iMysqlErrorNumber);
|
||||
parent::Log($sLevel, $sMessage, $sChannel, $aContext);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @since 3.0.0 N°3731
|
||||
*/
|
||||
class DeprecatedCallsLog extends LogAPI
|
||||
{
|
||||
public const ENUM_CHANNEL_PHP_METHOD = 'deprecated-php-method';
|
||||
public const ENUM_CHANNEL_PHP_LIBMETHOD = 'deprecated-php-libmethod';
|
||||
public const ENUM_CHANNEL_FILE = 'deprecated-file';
|
||||
public const CHANNEL_DEFAULT = self::ENUM_CHANNEL_PHP_METHOD;
|
||||
|
||||
public const LEVEL_DEFAULT = self::LEVEL_ERROR;
|
||||
|
||||
/** @var \FileLog we want our own instance ! */
|
||||
protected static $m_oFileLog = null;
|
||||
|
||||
/**
|
||||
* Indirection to {@see \LogAPI::IsLogLevelEnabled()} that is handling possible {@see ConfigException}
|
||||
*
|
||||
* @param string $sLevel
|
||||
* @param string $sChannel
|
||||
*
|
||||
* @return bool if exception occurs, then returns false
|
||||
*
|
||||
* @uses \LogAPI::IsLogLevelEnabled()
|
||||
*/
|
||||
protected static function IsLogLevelEnabledSafe($sLevel, $sChannel): bool
|
||||
{
|
||||
try {
|
||||
$bIsLogLevelEnabled = static::IsLogLevelEnabled(self::LEVEL_WARNING, self::ENUM_CHANNEL_PHP_LIBMETHOD);
|
||||
}
|
||||
catch (ConfigException $e) {
|
||||
$bIsLogLevelEnabled = false;
|
||||
}
|
||||
|
||||
return $bIsLogLevelEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $sTargetFile
|
||||
*
|
||||
* @uses \set_error_handler() to catch deprecated notices
|
||||
*
|
||||
* @since 3.0.0 N°3002 logs deprecated notices in called code
|
||||
*/
|
||||
public static function Enable($sTargetFile = null): void
|
||||
{
|
||||
if (empty($sTargetFile)) {
|
||||
$sTargetFile = APPROOT.'log/deprecated-calls.log';
|
||||
}
|
||||
parent::Enable($sTargetFile);
|
||||
|
||||
if (static::IsLogLevelEnabledSafe(self::LEVEL_WARNING, self::ENUM_CHANNEL_PHP_LIBMETHOD)) {
|
||||
set_error_handler([static::class, 'DeprecatedNoticesErrorHandler']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This will catch a message for all E_DEPRECATED and E_USER_DEPRECATED errors.
|
||||
* This handler is set in DeprecatedCallsLog::Enable
|
||||
*
|
||||
* @param int $errno
|
||||
* @param string $errstr
|
||||
* @param string $errfile
|
||||
* @param int $errline
|
||||
*
|
||||
* @return bool
|
||||
* @since 3.0.0 N°3002
|
||||
* @noinspection SpellCheckingInspection
|
||||
*/
|
||||
public static function DeprecatedNoticesErrorHandler(int $errno, string $errstr, string $errfile, int $errline): bool
|
||||
{
|
||||
if (
|
||||
(\E_USER_DEPRECATED !== $errno)
|
||||
&& (\E_DEPRECATED !== $errno)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (false === static::IsLogLevelEnabledSafe(self::LEVEL_WARNING, self::ENUM_CHANNEL_PHP_LIBMETHOD)) {
|
||||
// returns true so that nothing is throwned !
|
||||
return true;
|
||||
}
|
||||
|
||||
$aStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 4);
|
||||
$iStackDeprecatedMethodLevel = 2; // level 0 = current method, level 1 = @trigger_error, level 2 = method containing the `trigger_error` call
|
||||
$sDeprecatedObject = $aStack[$iStackDeprecatedMethodLevel]['class'];
|
||||
$sDeprecatedMethod = $aStack[$iStackDeprecatedMethodLevel]['function'];
|
||||
if (($sDeprecatedObject === __CLASS__) && ($sDeprecatedMethod === 'Log')) {
|
||||
// We are generating a trigger_error ourselves, we don't want to trace them !
|
||||
return false;
|
||||
}
|
||||
$sCallerFile = $aStack[$iStackDeprecatedMethodLevel]['file'];
|
||||
$sCallerLine = $aStack[$iStackDeprecatedMethodLevel]['line'];
|
||||
$sMessage = "Call to {$sDeprecatedObject}::{$sDeprecatedMethod} in {$sCallerFile}#L{$sCallerLine}";
|
||||
|
||||
$iStackCallerMethodLevel = $iStackDeprecatedMethodLevel + 1; // level 3 = caller of the deprecated method
|
||||
if (array_key_exists($iStackCallerMethodLevel, $aStack)) {
|
||||
$sCallerObject = $aStack[$iStackCallerMethodLevel]['class'] ?? null;
|
||||
$sCallerMethod = $aStack[$iStackCallerMethodLevel]['function'] ?? null;
|
||||
$sMessage .= ' (';
|
||||
if (!is_null($sCallerObject)) {
|
||||
$sMessage .= "{$sCallerObject}::{$sCallerMethod}";
|
||||
} else {
|
||||
$sCallerMethodFile = $aStack[$iStackCallerMethodLevel]['file'];
|
||||
$sCallerMethodLine = $aStack[$iStackCallerMethodLevel]['line'];
|
||||
if (!is_null($sCallerMethod)) {
|
||||
$sMessage .= "call to {$sCallerMethod}() in {$sCallerMethodFile}#L{$sCallerMethodLine}";
|
||||
} else {
|
||||
$sMessage .= "{$sCallerMethodFile}#L{$sCallerMethodLine}";
|
||||
}
|
||||
}
|
||||
$sMessage .= ')';
|
||||
}
|
||||
|
||||
if (!empty($errstr)) {
|
||||
$sMessage .= ' : '.$errstr;
|
||||
}
|
||||
|
||||
static::Warning($sMessage, self::ENUM_CHANNEL_PHP_LIBMETHOD);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected static function GetLevelDefault(): string
|
||||
{
|
||||
if (utils::IsDevelopmentEnvironment()) {
|
||||
return static::LEVEL_DEBUG;
|
||||
}
|
||||
|
||||
return static::LEVEL_DEFAULT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \ConfigException
|
||||
* @link https://www.php.net/debug_backtrace
|
||||
* @uses \debug_backtrace()
|
||||
*/
|
||||
public static function NotifyDeprecatedFile(?string $sAdditionalMessage = null): void
|
||||
{
|
||||
if (!static::IsLogLevelEnabled(self::LEVEL_WARNING, self::ENUM_CHANNEL_FILE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$aStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
|
||||
$sDeprecatedFile = $aStack[0]['file'];
|
||||
if (array_key_exists(1, $aStack)) {
|
||||
$sCallerFile = $aStack[1]['file'];
|
||||
$sCallerLine = $aStack[1]['line'];
|
||||
} else {
|
||||
$sCallerFile = 'N/A';
|
||||
$sCallerLine = 'N/A';
|
||||
}
|
||||
|
||||
$sMessage = "{$sCallerFile} L{$sCallerLine} including/requiring {$sDeprecatedFile}";
|
||||
|
||||
if (!is_null($sAdditionalMessage)) {
|
||||
$sMessage .= ' : '.$sAdditionalMessage;
|
||||
}
|
||||
|
||||
static::Warning($sMessage, static::ENUM_CHANNEL_FILE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $sAdditionalMessage
|
||||
*
|
||||
* @link https://www.php.net/debug_backtrace
|
||||
* @uses \debug_backtrace()
|
||||
*/
|
||||
public static function NotifyDeprecatedPhpMethod(?string $sAdditionalMessage = null): void
|
||||
{
|
||||
try {
|
||||
if (!static::IsLogLevelEnabled(self::LEVEL_WARNING, self::ENUM_CHANNEL_PHP_METHOD)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (ConfigException $e) {
|
||||
return;
|
||||
}
|
||||
|
||||
$aStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
|
||||
$iStackDeprecatedMethodLevel = 1; // level 0 = current method, level 1 = method containing the `NotifyDeprecatedPhpMethod` call
|
||||
$sDeprecatedObject = $aStack[$iStackDeprecatedMethodLevel]['class'];
|
||||
$sDeprecatedMethod = $aStack[$iStackDeprecatedMethodLevel]['function'];
|
||||
$sCallerFile = $aStack[$iStackDeprecatedMethodLevel]['file'];
|
||||
$sCallerLine = $aStack[$iStackDeprecatedMethodLevel]['line'];
|
||||
$sMessage = "Call to {$sDeprecatedObject}::{$sDeprecatedMethod} in {$sCallerFile}#L{$sCallerLine}";
|
||||
|
||||
$iStackCallerMethodLevel = $iStackDeprecatedMethodLevel + 1; // level 2 = caller of the deprecated method
|
||||
if (array_key_exists($iStackCallerMethodLevel, $aStack)) {
|
||||
$sCallerObject = $aStack[$iStackCallerMethodLevel]['class'];
|
||||
$sCallerMethod = $aStack[$iStackCallerMethodLevel]['function'];
|
||||
$sMessage .= " ({$sCallerObject}::{$sCallerMethod})";
|
||||
}
|
||||
|
||||
if (!is_null($sAdditionalMessage)) {
|
||||
$sMessage .= ' : '.$sAdditionalMessage;
|
||||
}
|
||||
|
||||
static::Warning($sMessage, self::ENUM_CHANNEL_PHP_METHOD);
|
||||
}
|
||||
|
||||
public static function Log($sLevel, $sMessage, $sChannel = null, $aContext = array()): void
|
||||
{
|
||||
if (true === utils::IsDevelopmentEnvironment()) {
|
||||
trigger_error($sMessage, E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
try {
|
||||
parent::Log($sLevel, $sMessage, $sChannel, $aContext);
|
||||
}
|
||||
catch (ConfigException $e) {
|
||||
// nothing much we can do... and we don't want to crash the caller !
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -769,6 +1060,7 @@ class LogFileRotationProcess implements iScheduledProcess
|
||||
{
|
||||
/**
|
||||
* Cannot get this list from anywhere as log file name is provided by the caller using LogAPI::Enable
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
const LOGFILES_TO_ROTATE = array(
|
||||
@@ -832,3 +1124,160 @@ class LogFileRotationProcess implements iScheduledProcess
|
||||
throw new ProcessException(self::class.' : The configured filename builder is invalid (log_filename_builder_impl="'.$sLogFileNameBuilder.'")');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log exceptions using dedicated API and logic.
|
||||
*
|
||||
* Please use {@see ExceptionLog::LogException()} to log exceptions
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
class ExceptionLog extends LogAPI
|
||||
{
|
||||
const CHANNEL_DEFAULT = 'Exception';
|
||||
const CONTEXT_EXCEPTION = '__exception';
|
||||
|
||||
private static $oLastEventIssue = null;
|
||||
|
||||
protected static $m_oFileLog = null;
|
||||
|
||||
/**
|
||||
* This method should be used to write logs.
|
||||
*
|
||||
* As it encapsulate the operations performed using the Exception, you should prefer it to the standard API inherited from LogApi `ExceptionLog::Error($oException->getMessage(), get_class($oException), ['__exception' => $oException]);`
|
||||
* The parameter order is not standard, but in our use case, the resulting API is way more convenient this way.
|
||||
*/
|
||||
public static function LogException(Exception $oException, $aContext = array(), $sLevel = self::LEVEL_WARNING)
|
||||
{
|
||||
if (empty($aContext[self::CONTEXT_EXCEPTION])) {
|
||||
$aContext[self::CONTEXT_EXCEPTION] = $oException;
|
||||
}
|
||||
|
||||
if (empty($aContext['exception class'])) {
|
||||
$aContext['exception class'] = get_class($oException);
|
||||
}
|
||||
|
||||
if (empty($aContext['file'])) {
|
||||
$aContext['file'] = $oException->getFile();
|
||||
}
|
||||
|
||||
if (empty($aContext['line'])) {
|
||||
$aContext['line'] =$oException->getLine();
|
||||
}
|
||||
|
||||
self::Log($sLevel, $oException->getMessage(), get_class($oException), $aContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @throws \ConfigException if log wrongly configured
|
||||
*/
|
||||
public static function Log($sLevel, $sMessage, $sClass = null, $aContext = array())
|
||||
{
|
||||
if (!static::$m_oFileLog) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isset(self::$aLevelsPriority[$sLevel])) {
|
||||
IssueLog::Error("invalid log level '{$sLevel}'");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
$sChannel = self::FindClassChannel($sClass);
|
||||
if (static::IsLogLevelEnabled($sLevel, $sChannel)) {
|
||||
static::$m_oFileLog->$sLevel($sMessage, $sChannel, array_diff_key($aContext, [self::CONTEXT_EXCEPTION => null])); //The exception should not be included in the error.log because of its verbosity.
|
||||
}
|
||||
|
||||
$sDbChannel = self::FindClassChannel($sClass, 'log_level_min.write_in_db');
|
||||
if (static::IsLogLevelEnabled($sLevel, $sDbChannel, 'log_level_min.write_in_db')) {
|
||||
self::WriteToDb($aContext);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected static function FindClassChannel($sClass, $sCode = 'log_level_min')
|
||||
{
|
||||
$oConfig = static::GetConfig();
|
||||
if (!$oConfig instanceof Config) {
|
||||
return static::GetLevelDefault();
|
||||
}
|
||||
|
||||
$sLogLevelMin = $oConfig->Get($sCode);
|
||||
|
||||
if (empty($sLogLevelMin)) {
|
||||
return $sClass;
|
||||
}
|
||||
|
||||
if (!is_array($sLogLevelMin)) {
|
||||
return $sClass;
|
||||
}
|
||||
|
||||
$sParentClass = $sClass;
|
||||
while (
|
||||
(!isset($sLogLevelMin[$sParentClass]))
|
||||
&&
|
||||
($sParentClass !== false)
|
||||
)
|
||||
{
|
||||
$sParentClass = get_parent_class($sParentClass);
|
||||
}
|
||||
|
||||
if (isset($sLogLevelMin[$sParentClass])) {
|
||||
return $sParentClass;
|
||||
}
|
||||
|
||||
return $sClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public static function Enable($sTargetFile = null)
|
||||
{
|
||||
if (empty($sTargetFile))
|
||||
{
|
||||
$sTargetFile = APPROOT.'log/error.log';
|
||||
}
|
||||
parent::Enable($sTargetFile);
|
||||
}
|
||||
|
||||
private static function WriteToDb(array $aContext): void
|
||||
{
|
||||
$oContextException = $aContext[self::CONTEXT_EXCEPTION];
|
||||
unset($aContext[self::CONTEXT_EXCEPTION]);
|
||||
|
||||
if (MetaModel::IsLogEnabledIssue()) {
|
||||
if (MetaModel::IsValidClass('EventIssue')) {
|
||||
try {
|
||||
self::$oLastEventIssue = new EventIssue();
|
||||
|
||||
$sIssue = ($oContextException instanceof CoreException) ? $oContextException->GetIssue() : 'PHP Exception';
|
||||
$sErrorStackTrace = ($oContextException instanceof CoreException) ? $oContextException->getFullStackTraceAsString() : $oContextException->getTraceAsString();
|
||||
$aContextData = ($oContextException instanceof CoreException) ? $oContextException->getContextData() : [];
|
||||
|
||||
|
||||
self::$oLastEventIssue->Set('message', $oContextException->getMessage());
|
||||
self::$oLastEventIssue->Set('userinfo', '');
|
||||
self::$oLastEventIssue->Set('issue', $sIssue);
|
||||
self::$oLastEventIssue->Set('impact', '');
|
||||
self::$oLastEventIssue->Set('callstack', $sErrorStackTrace);
|
||||
self::$oLastEventIssue->Set('data', array_merge($aContextData, $aContext));
|
||||
self::$oLastEventIssue->DBInsertNoReload();
|
||||
}
|
||||
catch (Exception $e) {
|
||||
IssueLog::Error("Failed to log issue into the DB");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Used by the tests
|
||||
*/
|
||||
private static function GetLastEventIssue()
|
||||
{
|
||||
return self::$oLastEventIssue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
//
|
||||
|
||||
use Combodo\iTop\Core\MetaModel\FriendlyNameType;
|
||||
|
||||
require_once APPROOT.'core/modulehandler.class.inc.php';
|
||||
require_once APPROOT.'core/querymodifier.class.inc.php';
|
||||
require_once APPROOT.'core/metamodelmodifier.inc.php';
|
||||
@@ -443,10 +445,10 @@ abstract class MetaModel
|
||||
|
||||
/**
|
||||
* @param string $sClass
|
||||
* @param bool $bImgTag
|
||||
* @param string $sMoreStyles
|
||||
* @param bool $bImgTag Whether to surround the icon URL with an HTML IMG tag or not
|
||||
* @param string $sMoreStyles Additional inline CSS style to add to the IMG tag. Only used if $bImgTag is set to true
|
||||
*
|
||||
* @return string
|
||||
* @return string Absolute URL the class icon
|
||||
* @throws \CoreException
|
||||
*/
|
||||
final public static function GetClassIcon($sClass, $bImgTag = true, $sMoreStyles = '')
|
||||
@@ -457,7 +459,7 @@ abstract class MetaModel
|
||||
if (array_key_exists('style', self::$m_aClassParams[$sClass])) {
|
||||
/** @var ormStyle $oStyle */
|
||||
$oStyle = self::$m_aClassParams[$sClass]['style'];
|
||||
$sIcon = $oStyle->GetIcon();
|
||||
$sIcon = $oStyle->GetIconAsAbsUrl();
|
||||
}
|
||||
if (strlen($sIcon) == 0) {
|
||||
$sParentClass = self::GetParentPersistentClass($sClass);
|
||||
@@ -492,7 +494,7 @@ abstract class MetaModel
|
||||
$oStyle = new ormStyle("ibo-class-style--$sClass", "ibo-class-style-alt--$sClass");
|
||||
}
|
||||
|
||||
if ((strlen($oStyle->GetMainColor()) > 0) && (strlen($oStyle->GetComplementaryColor()) > 0) && (strlen($oStyle->GetIcon()) > 0)) {
|
||||
if ((strlen($oStyle->GetMainColor()) > 0) && (strlen($oStyle->GetComplementaryColor()) > 0) && (strlen($oStyle->GetIconAsRelPath()) > 0)) {
|
||||
// all the parameters are set, no need to search in the parent classes
|
||||
return $oStyle;
|
||||
}
|
||||
@@ -510,10 +512,10 @@ abstract class MetaModel
|
||||
$oStyle->SetComplementaryColor($oParentStyle->GetComplementaryColor());
|
||||
$oStyle->SetAltStyleClass($oParentStyle->GetAltStyleClass());
|
||||
}
|
||||
if (strlen($oStyle->GetIcon()) == 0) {
|
||||
$oStyle->SetIcon($oParentStyle->GetIcon());
|
||||
if (strlen($oStyle->GetIconAsRelPath()) == 0) {
|
||||
$oStyle->SetIcon($oParentStyle->GetIconAsRelPath());
|
||||
}
|
||||
if ((strlen($oStyle->GetMainColor()) > 0) && (strlen($oStyle->GetComplementaryColor()) > 0) && (strlen($oStyle->GetIcon()) > 0)) {
|
||||
if ((strlen($oStyle->GetMainColor()) > 0) && (strlen($oStyle->GetComplementaryColor()) > 0) && (strlen($oStyle->GetIconAsRelPath()) > 0)) {
|
||||
// all the parameters are set, no need to search in the parent classes
|
||||
return $oStyle;
|
||||
}
|
||||
@@ -521,7 +523,7 @@ abstract class MetaModel
|
||||
$sParentClass = self::GetParentPersistentClass($sParentClass);
|
||||
}
|
||||
|
||||
if ((strlen($oStyle->GetMainColor()) == 0) && (strlen($oStyle->GetComplementaryColor()) == 0) && (strlen($oStyle->GetIcon()) == 0)) {
|
||||
if ((strlen($oStyle->GetMainColor()) == 0) && (strlen($oStyle->GetComplementaryColor()) == 0) && (strlen($oStyle->GetIconAsRelPath()) == 0)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -752,17 +754,56 @@ abstract class MetaModel
|
||||
|
||||
/**
|
||||
* @param string $sClass
|
||||
* @param string $sType {@see \Combodo\iTop\Core\MetaModel\FriendlyNameType}
|
||||
*
|
||||
* @return array
|
||||
* @throws \CoreException
|
||||
* @throws \DictExceptionMissingString
|
||||
*
|
||||
* @since 3.0.0 N°580 New $sType parameter
|
||||
*/
|
||||
final public static function GetNameSpec($sClass)
|
||||
final public static function GetNameSpec($sClass, $sType = FriendlyNameType::SHORT)
|
||||
{
|
||||
self::_check_subclass($sClass);
|
||||
$nameRawSpec = self::$m_aClassParams[$sClass]["name_attcode"];
|
||||
|
||||
switch ($sType) {
|
||||
case FriendlyNameType::COMPLEMENTARY:
|
||||
if (!isset(self::$m_aClassParams[$sClass]["complementary_name_attcode"])) {
|
||||
return [$sClass, []];
|
||||
}
|
||||
$nameRawSpec = self::$m_aClassParams[$sClass]["complementary_name_attcode"];
|
||||
$sDictName = 'ComplementaryName';
|
||||
break;
|
||||
case FriendlyNameType::LONG:
|
||||
$nameRawSpec = self::$m_aClassParams[$sClass]["name_attcode"];
|
||||
if (!isset(self::$m_aClassParams[$sClass]["complementary_name_attcode"])) {
|
||||
return self::GetNameSpec($sClass, FriendlyNameType::SHORT);
|
||||
}
|
||||
$complementaryNameRawSpec = self::$m_aClassParams[$sClass]["complementary_name_attcode"];
|
||||
if (is_array($nameRawSpec)) {
|
||||
if (is_array($complementaryNameRawSpec)) {
|
||||
$nameRawSpec = merge($nameRawSpec, $complementaryNameRawSpec);
|
||||
} elseif (!empty($nameRawSpec)) {
|
||||
$nameRawSpec = merge($nameRawSpec, [$complementaryNameRawSpec]);
|
||||
}
|
||||
} elseif (empty($nameRawSpec)) {
|
||||
$nameRawSpec = $complementaryNameRawSpec;
|
||||
} else {
|
||||
if (is_array($complementaryNameRawSpec)) {
|
||||
$nameRawSpec = merge([$nameRawSpec], $complementaryNameRawSpec);
|
||||
} elseif (!empty($nameRawSpec)) {
|
||||
$nameRawSpec = [$nameRawSpec, $complementaryNameRawSpec];
|
||||
}
|
||||
}
|
||||
$sDictName = 'LongName';
|
||||
break;
|
||||
default:
|
||||
$nameRawSpec = self::$m_aClassParams[$sClass]["name_attcode"];
|
||||
$sDictName = 'Name';
|
||||
}
|
||||
|
||||
if (is_array($nameRawSpec)) {
|
||||
$sFormat = Dict::S("Class:$sClass/Name", '');
|
||||
$sFormat = Dict::S("Class:$sClass/$sDictName", '');
|
||||
if (strlen($sFormat) == 0) {
|
||||
// Default to "%1$s %2$s..."
|
||||
for ($i = 1; $i <= count($nameRawSpec); $i++) {
|
||||
@@ -774,80 +815,103 @@ abstract class MetaModel
|
||||
}
|
||||
}
|
||||
|
||||
return array($sFormat, $nameRawSpec);
|
||||
}
|
||||
elseif (empty($nameRawSpec))
|
||||
{
|
||||
return array($sClass, array());
|
||||
}
|
||||
else
|
||||
{
|
||||
return [$sFormat, $nameRawSpec];
|
||||
} elseif (empty($nameRawSpec)) {
|
||||
return [$sClass, []];
|
||||
} else {
|
||||
// string -> attcode
|
||||
return array('%1$s', array($nameRawSpec));
|
||||
return ['%1$s', [$nameRawSpec]];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sClass
|
||||
*
|
||||
* @return array
|
||||
* @param string $sClass
|
||||
* @param bool $bWithAttributeDefinition
|
||||
* @param string $sType {@see \Combodo\iTop\Core\MetaModel\FriendlyNameType}
|
||||
*
|
||||
* @return array of attribute codes used by friendlyname
|
||||
* @throws \CoreException
|
||||
* @throws \DictExceptionMissingString
|
||||
* @since 3.0.0
|
||||
*/
|
||||
final static public function GetComplementAttributeSpec($sClass)
|
||||
final public static function GetNameAttributes(string $sClass, $bWithAttributeDefinition = false, $sType = FriendlyNameType::SHORT): array
|
||||
{
|
||||
self::_check_subclass($sClass);
|
||||
if (!isset(self::$m_aClassParams[$sClass]["name_complement_for_select"]))
|
||||
{
|
||||
$sParentClass = static::GetParentClass($sClass);
|
||||
if (is_null($sParentClass)) {
|
||||
return array($sClass, array());
|
||||
$aNameAttCodes = [];
|
||||
if ($sType == FriendlyNameType::SHORT || FriendlyNameType::LONG) {
|
||||
$rawNameAttCodes = self::$m_aClassParams[$sClass]["name_attcode"];
|
||||
if (!is_array($rawNameAttCodes)) {
|
||||
if (self::IsValidAttCode($sClass, $rawNameAttCodes)) {
|
||||
$aNameAttCodes[] = $rawNameAttCodes;
|
||||
}
|
||||
} else {
|
||||
return static::GetComplementAttributeSpec($sParentClass);
|
||||
$aNameAttCodes = $rawNameAttCodes;
|
||||
}
|
||||
}
|
||||
$nameRawSpec = self::$m_aClassParams[$sClass]["name_complement_for_select"];
|
||||
if (is_array($nameRawSpec))
|
||||
{
|
||||
$sFormat = Dict::S("Class:$sClass/ComplementForSelect", '');
|
||||
if (strlen($sFormat) == 0)
|
||||
{
|
||||
// Default to "%1$s %2$s..."
|
||||
for($i = 1; $i <= count($nameRawSpec); $i++)
|
||||
{
|
||||
if (empty($sFormat))
|
||||
{
|
||||
$sFormat .= '%'.$i.'$s';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sFormat .= ' %'.$i.'$s';
|
||||
if ($sType == FriendlyNameType::COMPLEMENTARY || FriendlyNameType::LONG) {
|
||||
$rawNameAttCodes = self::$m_aClassParams[$sClass]["complementary_name_attcode"];
|
||||
if (!isEmpty($rawNameAttCodes)) {
|
||||
if (!is_array($rawNameAttCodes)) {
|
||||
if (self::IsValidAttCode($sClass, $rawNameAttCodes)) {
|
||||
$aNameAttCodes[] = array_merge($aNameAttCodes, [$rawNameAttCodes]);
|
||||
}
|
||||
} else {
|
||||
$aNameAttCodes = array_merge($rawNameAttCodes, $rawNameAttCodes);
|
||||
}
|
||||
}
|
||||
return array($sFormat, $nameRawSpec);
|
||||
}
|
||||
elseif (empty($nameRawSpec))
|
||||
{
|
||||
return array($sClass, array());
|
||||
}
|
||||
else
|
||||
{
|
||||
// string -> attcode
|
||||
return array('%1$s', array($nameRawSpec));
|
||||
if ($bWithAttributeDefinition) {
|
||||
$aResults = [];
|
||||
foreach ($aNameAttCodes as $sAttCode) {
|
||||
$aResults[$sAttCode] = self::GetAttributeDef($sClass, $sAttCode);
|
||||
}
|
||||
|
||||
return $aResults;
|
||||
}
|
||||
|
||||
return $aNameAttCodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sClass
|
||||
* @param false $bWithAttributeDefinition
|
||||
*
|
||||
* @return array of attributes to always reload in tables
|
||||
* @throws \CoreException
|
||||
* @since 3.0.0
|
||||
*/
|
||||
final public static function GetAttributesToAlwaysLoadInTables(string $sClass, $bWithAttributeDefinition = false): array
|
||||
{
|
||||
$aResults = [];
|
||||
foreach (self::GetAttributesList($sClass) as $sAttCode) {
|
||||
$oAttDef = self::GetAttributeDef($sClass, $sAttCode);
|
||||
if ($oAttDef->AlwaysLoadInTables()) {
|
||||
if ($bWithAttributeDefinition) {
|
||||
$aResults[$sAttCode] = $oAttDef;
|
||||
} else {
|
||||
$aResults[] = $sAttCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $aResults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the friendly name expression for a given class
|
||||
*
|
||||
* @param string $sClass
|
||||
* @param string $sType {@see \Combodo\iTop\Core\MetaModel\FriendlyNameType}
|
||||
*
|
||||
* @return Expression
|
||||
* @throws \CoreException
|
||||
* @throws \DictExceptionMissingString
|
||||
*
|
||||
* @since 3.0.0 N°580 New $sType parameter
|
||||
*/
|
||||
final public static function GetNameExpression($sClass)
|
||||
final public static function GetNameExpression($sClass, $sType = FriendlyNameType::SHORT)
|
||||
{
|
||||
$aNameSpec = self::GetNameSpec($sClass);
|
||||
$aNameSpec = self::GetNameSpec($sClass, $sType);
|
||||
$sFormat = $aNameSpec[0];
|
||||
$aAttributes = $aNameSpec[1];
|
||||
|
||||
@@ -879,14 +943,17 @@ abstract class MetaModel
|
||||
|
||||
/**
|
||||
* @param string $sClass
|
||||
* @param string $sType {@see \Combodo\iTop\Core\MetaModel\FriendlyNameType}
|
||||
*
|
||||
* @return string The friendly name IIF it is equivalent to a single attribute
|
||||
* @throws \CoreException
|
||||
* @throws \DictExceptionMissingString
|
||||
*
|
||||
* @since 3.0.0 N°580 New $sType parameter
|
||||
*/
|
||||
final public static function GetFriendlyNameAttributeCode($sClass)
|
||||
final public static function GetFriendlyNameAttributeCode($sClass, $sType = FriendlyNameType::SHORT)
|
||||
{
|
||||
$aNameSpec = self::GetNameSpec($sClass);
|
||||
$aNameSpec = self::GetNameSpec($sClass, $sType);
|
||||
$sFormat = trim($aNameSpec[0]);
|
||||
$aAttributes = $aNameSpec[1];
|
||||
if (($sFormat != '') && ($sFormat != '%1$s')) {
|
||||
@@ -902,13 +969,16 @@ abstract class MetaModel
|
||||
/**
|
||||
* Returns the list of attributes composing the friendlyname
|
||||
*
|
||||
* @param $sClass
|
||||
* @param string $sClass
|
||||
* @param string $sType {@see \Combodo\iTop\Core\MetaModel\FriendlyNameType}
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 3.0.0 N°580 New $sType parameter
|
||||
*/
|
||||
final public static function GetFriendlyNameAttributeCodeList($sClass)
|
||||
final public static function GetFriendlyNameAttributeCodeList($sClass, $sType = FriendlyNameType::SHORT)
|
||||
{
|
||||
$aNameSpec = self::GetNameSpec($sClass);
|
||||
$aNameSpec = self::GetNameSpec($sClass, $sType);
|
||||
$aAttributes = $aNameSpec[1];
|
||||
|
||||
return $aAttributes;
|
||||
@@ -1077,7 +1147,8 @@ abstract class MetaModel
|
||||
{
|
||||
self::_check_subclass($sClass);
|
||||
$oAtt = self::GetAttributeDef($sClass, $sAttCode);
|
||||
return $oAtt->GetPrerequisiteAttributes();
|
||||
|
||||
return $oAtt->GetPrerequisiteAttributes($sClass);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1419,7 +1490,10 @@ abstract class MetaModel
|
||||
*/
|
||||
final public static function GetFiltersList($sClass)
|
||||
{
|
||||
// cannot notify depreciation for now as this is still MASSIVELY used in iTop core !
|
||||
//DeprecatedCallsLog::NotifyDeprecatedPhpMethod('do not use : dead code, will be removed in the future');
|
||||
self::_check_subclass($sClass);
|
||||
|
||||
return array_keys(self::$m_aFilterDefs[$sClass]);
|
||||
}
|
||||
|
||||
@@ -1526,6 +1600,8 @@ abstract class MetaModel
|
||||
*/
|
||||
final public static function IsValidFilterCode($sClass, $sFilterCode)
|
||||
{
|
||||
// cannot notify depreciation for now as this is still MASSIVELY used in iTop core !
|
||||
//DeprecatedCallsLog::NotifyDeprecatedPhpMethod('do not use : dead code, will be removed in the future');
|
||||
if (!array_key_exists($sClass, self::$m_aFilterDefs)) {
|
||||
return false;
|
||||
}
|
||||
@@ -1837,7 +1913,10 @@ abstract class MetaModel
|
||||
*/
|
||||
public static function GetClassFilterDefs($sClass)
|
||||
{
|
||||
// cannot notify depreciation for now as this is still MASSIVELY used in iTop core !
|
||||
//DeprecatedCallsLog::NotifyDeprecatedPhpMethod('do not use : dead code, will be removed in the future');
|
||||
self::_check_subclass($sClass);
|
||||
|
||||
return self::$m_aFilterDefs[$sClass];
|
||||
}
|
||||
|
||||
@@ -1852,6 +1931,7 @@ abstract class MetaModel
|
||||
*/
|
||||
final public static function GetClassFilterDef($sClass, $sFilterCode)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('do not use : dead code, will be removed in the future');
|
||||
self::_check_subclass($sClass);
|
||||
if (!array_key_exists($sFilterCode, self::$m_aFilterDefs[$sClass])) {
|
||||
throw new CoreException("Unknown filter code '$sFilterCode' for class '$sClass'");
|
||||
@@ -1871,9 +1951,9 @@ abstract class MetaModel
|
||||
*/
|
||||
public static function GetFilterLabel($sClass, $sFilterCode)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('do not use : dead code, will be removed in the future');
|
||||
$oFilter = self::GetClassFilterDef($sClass, $sFilterCode);
|
||||
if ($oFilter)
|
||||
{
|
||||
if ($oFilter) {
|
||||
return $oFilter->GetLabel();
|
||||
}
|
||||
|
||||
@@ -1890,11 +1970,12 @@ abstract class MetaModel
|
||||
*/
|
||||
public static function GetFilterDescription($sClass, $sFilterCode)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('do not use : dead code, will be removed in the future');
|
||||
$oFilter = self::GetClassFilterDef($sClass, $sFilterCode);
|
||||
if ($oFilter)
|
||||
{
|
||||
if ($oFilter) {
|
||||
return $oFilter->GetDescription();
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -1908,11 +1989,12 @@ abstract class MetaModel
|
||||
*/
|
||||
public static function GetFilterOperators($sClass, $sFilterCode)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('do not use : dead code, will be removed in the future');
|
||||
$oFilter = self::GetClassFilterDef($sClass, $sFilterCode);
|
||||
if ($oFilter)
|
||||
{
|
||||
if ($oFilter) {
|
||||
return $oFilter->GetOperators();
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
@@ -1926,9 +2008,9 @@ abstract class MetaModel
|
||||
*/
|
||||
public static function GetFilterLooseOperator($sClass, $sFilterCode)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('do not use : dead code, will be removed in the future');
|
||||
$oFilter = self::GetClassFilterDef($sClass, $sFilterCode);
|
||||
if ($oFilter)
|
||||
{
|
||||
if ($oFilter) {
|
||||
return $oFilter->GetLooseOperator();
|
||||
}
|
||||
|
||||
@@ -1946,9 +2028,9 @@ abstract class MetaModel
|
||||
*/
|
||||
public static function GetFilterOpDescription($sClass, $sFilterCode, $sOpCode)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('do not use : dead code, will be removed in the future');
|
||||
$oFilter = self::GetClassFilterDef($sClass, $sFilterCode);
|
||||
if ($oFilter)
|
||||
{
|
||||
if ($oFilter) {
|
||||
return $oFilter->GetOpDescription($sOpCode);
|
||||
}
|
||||
|
||||
@@ -1963,6 +2045,8 @@ abstract class MetaModel
|
||||
*/
|
||||
public static function GetFilterHTMLInput($sFilterCode)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('do not use : dead code, will be removed in the future');
|
||||
|
||||
return "<INPUT name=\"$sFilterCode\">";
|
||||
}
|
||||
|
||||
@@ -2137,24 +2221,21 @@ abstract class MetaModel
|
||||
*/
|
||||
public static function EnumRelations($sClass = '')
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('Use EnumRelationsEx instead');
|
||||
$aResult = array_keys(self::$m_aRelationInfos);
|
||||
if (!empty($sClass))
|
||||
{
|
||||
if (!empty($sClass)) {
|
||||
// Return only the relations that have a meaning (i.e. for which at least one query is defined)
|
||||
// for the specified class
|
||||
$aClassRelations = array();
|
||||
foreach($aResult as $sRelCode)
|
||||
{
|
||||
foreach ($aResult as $sRelCode) {
|
||||
$aQueriesDown = self::EnumRelationQueries($sClass, $sRelCode);
|
||||
if (count($aQueriesDown) > 0)
|
||||
{
|
||||
if (count($aQueriesDown) > 0) {
|
||||
$aClassRelations[] = $sRelCode;
|
||||
}
|
||||
// Temporary patch: until the impact analysis GUI gets rewritten,
|
||||
// let's consider that "depends on" is equivalent to "impacts/up"
|
||||
// The current patch has been implemented in DBObject and MetaModel
|
||||
if ($sRelCode == 'impacts')
|
||||
{
|
||||
if ($sRelCode == 'impacts') {
|
||||
$aQueriesUp = self::EnumRelationQueries($sClass, 'impacts', false);
|
||||
if (count($aQueriesUp) > 0)
|
||||
{
|
||||
@@ -5048,7 +5129,7 @@ abstract class MetaModel
|
||||
foreach($aErrors as $sClass => $aMessages)
|
||||
{
|
||||
echo "<p>Wrong declaration for class <b>$sClass</b></p>\n";
|
||||
echo "<ul class=\"treeview\">\n";
|
||||
echo "<ul >\n";
|
||||
$i = 0;
|
||||
foreach($aMessages as $sMsg)
|
||||
{
|
||||
@@ -5512,7 +5593,7 @@ abstract class MetaModel
|
||||
$aSugFix[$sClass]['id'][] = "ALTER TABLE `$sTable` ADD $sKeyFieldDefinition";
|
||||
if (!$bTableToCreate)
|
||||
{
|
||||
$aAlterTableItems[$sTable][$sKeyField] = "ADD $sKeyFieldDefinition";
|
||||
$aAlterTableItems[$sTable]['field'][$sKeyField] = "ADD $sKeyFieldDefinition";
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -5525,7 +5606,7 @@ abstract class MetaModel
|
||||
$aSugFix[$sClass]['id'][] = "ALTER TABLE `$sTable`, DROP PRIMARY KEY, ADD PRIMARY key(`$sKeyField`)";
|
||||
if (!$bTableToCreate)
|
||||
{
|
||||
$aAlterTableItems[$sTable][$sKeyField] = "CHANGE `$sKeyField` $sKeyFieldDefinition";
|
||||
$aAlterTableItems[$sTable]['field'][$sKeyField] = "CHANGE `$sKeyField` $sKeyFieldDefinition";
|
||||
}
|
||||
}
|
||||
if (self::IsAutoIncrementKey($sClass) && !CMDBSource::IsAutoIncrement($sTable, $sKeyField))
|
||||
@@ -5534,7 +5615,7 @@ abstract class MetaModel
|
||||
$aSugFix[$sClass]['id'][] = "ALTER TABLE `$sTable` CHANGE `$sKeyField` $sKeyFieldDefinition";
|
||||
if (!$bTableToCreate)
|
||||
{
|
||||
$aAlterTableItems[$sTable][$sKeyField] = "CHANGE `$sKeyField` $sKeyFieldDefinition";
|
||||
$aAlterTableItems[$sTable]['field'][$sKeyField] = "CHANGE `$sKeyField` $sKeyFieldDefinition";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5597,7 +5678,7 @@ abstract class MetaModel
|
||||
}
|
||||
else
|
||||
{
|
||||
$aAlterTableItems[$sTable][$sField] = "ADD $sFieldDefinition";
|
||||
$aAlterTableItems[$sTable]['field'][$sField] = "ADD $sFieldDefinition";
|
||||
$aAdditionalRequests = self::GetAdditionalRequestAfterAlter($sClass, $sTable, $sField);
|
||||
if (!empty($aAdditionalRequests))
|
||||
{
|
||||
@@ -5641,7 +5722,7 @@ abstract class MetaModel
|
||||
}
|
||||
else
|
||||
{
|
||||
$aAlterTableItems[$sTable][] = "ADD $sIndexType `$sIndexName` ($sColumns)";
|
||||
$aAlterTableItems[$sTable]['index'][] = "ADD $sIndexType `$sIndexName` ($sColumns)";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5682,7 +5763,7 @@ abstract class MetaModel
|
||||
if (CMDBSource::HasIndex($sTable, $sField))
|
||||
{
|
||||
$aSugFix[$sClass][$sAttCode][] = "ALTER TABLE `$sTable` DROP INDEX `$sIndexName`";
|
||||
$aAlterTableItems[$sTable][] = "DROP INDEX `$sIndexName`";
|
||||
$aAlterTableItems[$sTable]['index'][] = "DROP INDEX `$sIndexName`";
|
||||
}
|
||||
$sSugFixAfterChange = "ALTER TABLE `$sTable` ADD $sIndexType `$sIndexName` ($sColumns)";
|
||||
$sAlterTableItemsAfterChange = "ADD $sIndexType `$sIndexName` ($sColumns)";
|
||||
@@ -5696,7 +5777,7 @@ abstract class MetaModel
|
||||
{
|
||||
$aErrors[$sClass][$sAttCode][] = "field '$sField' in table '$sTable' has a wrong type: found <code>$sActualFieldSpec</code> while expecting <code>$sDBFieldSpec</code>";
|
||||
$aSugFix[$sClass][$sAttCode][] = "ALTER TABLE `$sTable` CHANGE `$sField` $sFieldDefinition";
|
||||
$aAlterTableItems[$sTable][$sField] = "CHANGE `$sField` $sFieldDefinition";
|
||||
$aAlterTableItems[$sTable]['field'][$sField] = "CHANGE `$sField` $sFieldDefinition";
|
||||
}
|
||||
|
||||
// Create indexes (external keys only... so far)
|
||||
@@ -5711,7 +5792,7 @@ abstract class MetaModel
|
||||
}
|
||||
else
|
||||
{
|
||||
$aAlterTableItems[$sTable][] = $sAlterTableItemsAfterChange;
|
||||
$aAlterTableItems[$sTable]['index'][] = $sAlterTableItemsAfterChange;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5771,9 +5852,12 @@ abstract class MetaModel
|
||||
{
|
||||
$aAlterTableItems[$sTable] = array();
|
||||
}
|
||||
array_unshift($aAlterTableItems[$sTable], "DROP INDEX `$sIndexId`");
|
||||
if (isset($aAlterTableItems[$sTable]['index']))
|
||||
{
|
||||
array_unshift($aAlterTableItems[$sTable]['index'], "DROP INDEX `$sIndexId`");
|
||||
}
|
||||
}
|
||||
$aAlterTableItems[$sTable][] = "ADD INDEX `$sIndexId` ($sColumns)";
|
||||
$aAlterTableItems[$sTable]['index'][] = "ADD INDEX `$sIndexId` ($sColumns)";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5791,7 +5875,7 @@ abstract class MetaModel
|
||||
// without specifying the value of this unknown column
|
||||
$sFieldDefinition = "`$sField` ".CMDBSource::GetFieldType($sTable, $sField).' NULL';
|
||||
$aSugFix[$sClass][$sAttCode][] = "ALTER TABLE `$sTable` CHANGE `$sField` $sFieldDefinition";
|
||||
$aAlterTableItems[$sTable][$sField] = "CHANGE `$sField` $sFieldDefinition";
|
||||
$aAlterTableItems[$sTable]['field'][$sField] = "CHANGE `$sField` $sFieldDefinition";
|
||||
}
|
||||
$aSugFix[$sClass][$sAttCode][] = "-- Recommended action: ALTER TABLE `$sTable` DROP `$sField`";
|
||||
}
|
||||
@@ -5810,7 +5894,10 @@ abstract class MetaModel
|
||||
{
|
||||
$aAlterTableItems[$sTable] = array();
|
||||
}
|
||||
array_unshift($aAlterTableItems[$sTable], "DROP INDEX `$sIndexId`");
|
||||
if (isset($aAlterTableItems[$sTable]['index']))
|
||||
{
|
||||
array_unshift($aAlterTableItems[$sTable]['index'], "DROP INDEX `$sIndexId`");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5842,8 +5929,16 @@ abstract class MetaModel
|
||||
}
|
||||
foreach ($aAlterTableItems as $sTable => $aChangeList)
|
||||
{
|
||||
$sChangeList = implode(', ', $aChangeList);
|
||||
$aCondensedQueries[] = "ALTER TABLE `$sTable` $sChangeList";
|
||||
if (isset($aAlterTableItems[$sTable]['field']))
|
||||
{
|
||||
$sChangeList = implode(', ', $aChangeList['field']);
|
||||
$aCondensedQueries[] = "ALTER TABLE `$sTable` $sChangeList";
|
||||
}
|
||||
if (isset($aAlterTableItems[$sTable]['index']))
|
||||
{
|
||||
$sChangeList = implode(', ', $aChangeList['index']);
|
||||
$aCondensedQueries[] = "ALTER TABLE `$sTable` $sChangeList";
|
||||
}
|
||||
// Add request right after the ALTER TABLE
|
||||
if (isset($aPostTableAlteration[$sTable]))
|
||||
{
|
||||
@@ -5893,6 +5988,7 @@ abstract class MetaModel
|
||||
$aSugFix[$sClass]['*'][] = "DROP VIEW `$sView`";
|
||||
}
|
||||
}
|
||||
|
||||
return array($aErrors, $aSugFix);
|
||||
}
|
||||
|
||||
@@ -6428,6 +6524,7 @@ abstract class MetaModel
|
||||
*/
|
||||
public static function LoadConfig($oConfiguration, $bAllowCache = false)
|
||||
{
|
||||
$oKPI = new ExecutionKPI();
|
||||
self::$m_oConfig = $oConfiguration;
|
||||
|
||||
// N°2478 utils has his own private attribute
|
||||
@@ -6438,8 +6535,7 @@ abstract class MetaModel
|
||||
// Set log ASAP
|
||||
if (self::$m_oConfig->GetLogGlobal())
|
||||
{
|
||||
if (self::$m_oConfig->GetLogIssue())
|
||||
{
|
||||
if (self::$m_oConfig->GetLogIssue()) {
|
||||
self::$m_bLogIssue = true;
|
||||
IssueLog::Enable(APPROOT.'log/error.log');
|
||||
}
|
||||
@@ -6448,6 +6544,8 @@ abstract class MetaModel
|
||||
|
||||
ToolsLog::Enable(APPROOT.'log/tools.log');
|
||||
DeadLockLog::Enable();
|
||||
DeprecatedCallsLog::Enable();
|
||||
ExceptionLog::Enable();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -6504,6 +6602,7 @@ abstract class MetaModel
|
||||
|
||||
$sSource = self::$m_oConfig->Get('db_name');
|
||||
$sTablePrefix = self::$m_oConfig->Get('db_subname');
|
||||
$oKPI->ComputeAndReport('Load config');
|
||||
|
||||
if (self::$m_bUseAPCCache)
|
||||
{
|
||||
@@ -6875,7 +6974,7 @@ abstract class MetaModel
|
||||
* @param int $iKey id value of the object to retrieve
|
||||
* @param bool $bMustBeFound see throws ArchivedObjectException
|
||||
* @param bool $bAllowAllData if true then user rights will be bypassed - use with care!
|
||||
* @param null $aModifierProperties
|
||||
* @param array $aModifierProperties properties for {@see iQueryModifier} impl
|
||||
*
|
||||
* @return \DBObject null if : (the object is not found) or (archive mode disabled and object is archived and
|
||||
* $bMustBeFound=false)
|
||||
@@ -6895,12 +6994,9 @@ abstract class MetaModel
|
||||
|
||||
if (!utils::IsArchiveMode() && $oObject->IsArchived())
|
||||
{
|
||||
if ($bMustBeFound)
|
||||
{
|
||||
if ($bMustBeFound) {
|
||||
throw new ArchivedObjectException("The object $sClass::$iKey is archived");
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -6908,6 +7004,35 @@ abstract class MetaModel
|
||||
return $oObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sClass
|
||||
* @param int $iKey
|
||||
*
|
||||
* @return bool True if the object of $sClass and $iKey exists in the DB -no matter the current user restrictions-, false otherwise meaning:
|
||||
* - It could be in memory for now and is not persisted yet
|
||||
* - It is neither in memory nor DB
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLQueryHasNoResultException
|
||||
* @since 3.0.0 N°4173
|
||||
*/
|
||||
public static function IsObjectInDB(string $sClass, int $iKey): bool
|
||||
{
|
||||
// Note: We take the root class to ensure that there is a corresponding table in the DB
|
||||
// as some intermediate classes can have no table in the DB.
|
||||
$sRootClass = MetaModel::GetRootClass($sClass);
|
||||
|
||||
$sTable = MetaModel::DBGetTable($sRootClass);
|
||||
$sKeyCol = MetaModel::DBGetKey($sRootClass);
|
||||
$sEscapedKey = CMDBSource::Quote($iKey);
|
||||
|
||||
$sQuery = "SELECT count(*) FROM `{$sTable}` WHERE `{$sKeyCol}` = {$sEscapedKey}";
|
||||
$iCount = (int) CMDBSource::QueryToScalar($sQuery);
|
||||
|
||||
return $iCount === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for the specified class and id. If the object is archived it will be returned anyway (this is for pre-2.4
|
||||
* module compatibility, see N.1108)
|
||||
@@ -7108,19 +7233,6 @@ abstract class MetaModel
|
||||
return $oRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 2.7.0 N°1627, use ItopCounter::incRootClass($sClass) instead
|
||||
*
|
||||
* @param string $sClass
|
||||
*
|
||||
* @return int
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public static function GetNextKey($sClass)
|
||||
{
|
||||
return ItopCounter::IncClass($sClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletion of records, bypassing {@link DBObject::DBDelete} !!!
|
||||
* It is NOT recommended to use this shortcut
|
||||
@@ -7141,9 +7253,9 @@ abstract class MetaModel
|
||||
*/
|
||||
public static function BulkDelete(DBObjectSearch $oFilter)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('do not use : dead code, will be removed in the future');
|
||||
$sSQL = $oFilter->MakeDeleteQuery();
|
||||
if (!self::DBIsReadOnly())
|
||||
{
|
||||
if (!self::DBIsReadOnly()) {
|
||||
CMDBSource::Query($sSQL);
|
||||
}
|
||||
}
|
||||
@@ -7162,10 +7274,10 @@ abstract class MetaModel
|
||||
{
|
||||
// $aValues is an array of $sAttCode => $value
|
||||
$sSQL = $oFilter->MakeUpdateQuery($aValues);
|
||||
if (!self::DBIsReadOnly())
|
||||
{
|
||||
if (!self::DBIsReadOnly()) {
|
||||
CMDBSource::Query($sSQL);
|
||||
}
|
||||
|
||||
return CMDBSource::AffectedRows();
|
||||
}
|
||||
|
||||
@@ -7606,14 +7718,18 @@ abstract class MetaModel
|
||||
/**
|
||||
* @param string $sClass
|
||||
* @param string $sAttCode
|
||||
* @param string $sValue
|
||||
* @param string|null $sValue Code of the state value, can be null if allowed by the attribute definition
|
||||
*
|
||||
* @return \ormStyle|null
|
||||
* @throws \Exception
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public static function GetEnumStyle(string $sClass, string $sAttCode, string $sValue = ''): ?ormStyle
|
||||
public static function GetEnumStyle(string $sClass, string $sAttCode, ?string $sValue = ''): ?ormStyle
|
||||
{
|
||||
if (strlen($sAttCode) === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$oAttDef = self::GetAttributeDef($sClass, $sAttCode);
|
||||
if (!$oAttDef instanceof AttributeEnum) {
|
||||
throw new CoreException("MetaModel::GetEnumStyle() Attribute $sAttCode of class $sClass is not an AttributeEnum\n");
|
||||
|
||||
@@ -242,6 +242,8 @@ class iTopMutex
|
||||
*
|
||||
* @throws \Exception
|
||||
* @throws \MySQLException
|
||||
*
|
||||
* @since 2.7.5 3.0.0 N°3968 specify `wait_timeout` for the mutex dedicated connection
|
||||
*/
|
||||
public function InitMySQLSession()
|
||||
{
|
||||
@@ -254,10 +256,36 @@ class iTopMutex
|
||||
|
||||
$this->hDBLink = CMDBSource::GetMysqliInstance($sServer, $sUser, $sPwd, $sSource, $bTlsEnabled, $sTlsCA, false);
|
||||
|
||||
if (!$this->hDBLink)
|
||||
{
|
||||
if (!$this->hDBLink) {
|
||||
throw new Exception("Could not connect to the DB server (host=$sServer, user=$sUser): ".mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno().')');
|
||||
}
|
||||
|
||||
// Make sure that the server variable `wait_timeout` is at least 86400 seconds for this connection,
|
||||
// since the lock will be released if/when the connection times out.
|
||||
// Source https://dev.mysql.com/doc/refman/5.7/en/locking-functions.html :
|
||||
// > A lock obtained with GET_LOCK() is released explicitly by executing RELEASE_LOCK() or implicitly when your session terminates
|
||||
//
|
||||
// BEWARE: If you want to check the value of this variable, when run from an interactive console `SHOW VARIABLES LIKE 'wait_timeout'`
|
||||
// will actually returns the value of the variable `interactive_timeout` which may be quite different.
|
||||
$sSql = "SHOW VARIABLES LIKE 'wait_timeout'";
|
||||
$result = mysqli_query($this->hDBLink, $sSql);
|
||||
if (!$result) {
|
||||
throw new Exception("Failed to issue MySQL query '".$sSql."': ".mysqli_error($this->hDBLink).' (mysql errno: '.mysqli_errno($this->hDBLink).')');
|
||||
}
|
||||
if ($aRow = mysqli_fetch_array($result, MYSQLI_BOTH)) {
|
||||
$iTimeout = (int)$aRow[1];
|
||||
} else {
|
||||
mysqli_free_result($result);
|
||||
throw new Exception("No result for query '".$sSql."'");
|
||||
}
|
||||
mysqli_free_result($result);
|
||||
|
||||
if ($iTimeout < 86400) {
|
||||
$result = mysqli_query($this->hDBLink, 'SET SESSION wait_timeout=86400');
|
||||
if ($result === false) {
|
||||
throw new Exception("Failed to issue MySQL query '".$sSql."': ".mysqli_error($this->hDBLink).' (mysql errno: '.mysqli_errno($this->hDBLink).')');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -117,6 +117,9 @@ abstract class Expression {
|
||||
*/
|
||||
public function Render(&$aArgs = null, $bRetrofitParams = false)
|
||||
{
|
||||
// cannot notify depreciation for now as this is still MASSIVELY used in iTop core !
|
||||
//DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use RenderExpression');
|
||||
|
||||
return $this->RenderExpression(false, $aArgs, $bRetrofitParams);
|
||||
}
|
||||
|
||||
|
||||
@@ -597,7 +597,7 @@ static public $yy_action = array(
|
||||
** defined, then do no error processing.
|
||||
*/
|
||||
const YYNOCODE = 119;
|
||||
const YYSTACKDEPTH = 100;
|
||||
const YYSTACKDEPTH = 1000;
|
||||
const YYNSTATE = 175;
|
||||
const YYNRULE = 125;
|
||||
const YYERRORSYMBOL = 76;
|
||||
@@ -1175,6 +1175,10 @@ static public $yy_action = array(
|
||||
}
|
||||
/* Here code is inserted which will execute if the parser
|
||||
** stack ever overflows */
|
||||
#line 30 "..\oql-parser.y"
|
||||
|
||||
throw new OQLParserStackOverFlowException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol);
|
||||
#line 1186 "..\oql-parser.php"
|
||||
return;
|
||||
}
|
||||
$yytos = new OQLParser_yyStackEntry;
|
||||
@@ -1474,116 +1478,116 @@ static public $yy_action = array(
|
||||
** function yy_r0($yymsp){ ... } // User supplied code
|
||||
** #line <lineno> <thisfile>
|
||||
*/
|
||||
#line 29 "..\oql-parser.y"
|
||||
#line 37 "..\oql-parser.y"
|
||||
function yy_r0(){ $this->my_result = $this->yystack[$this->yyidx + 0]->minor; }
|
||||
#line 1483 "..\oql-parser.php"
|
||||
#line 33 "..\oql-parser.y"
|
||||
#line 1488 "..\oql-parser.php"
|
||||
#line 41 "..\oql-parser.y"
|
||||
function yy_r3(){
|
||||
$this->_retvalue = new OqlUnionQuery($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor);
|
||||
}
|
||||
#line 1488 "..\oql-parser.php"
|
||||
#line 40 "..\oql-parser.y"
|
||||
#line 1493 "..\oql-parser.php"
|
||||
#line 48 "..\oql-parser.y"
|
||||
function yy_r5(){
|
||||
$this->_retvalue = new OqlObjectQuery($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor, array($this->yystack[$this->yyidx + -2]->minor));
|
||||
}
|
||||
#line 1493 "..\oql-parser.php"
|
||||
#line 43 "..\oql-parser.y"
|
||||
#line 1498 "..\oql-parser.php"
|
||||
#line 51 "..\oql-parser.y"
|
||||
function yy_r6(){
|
||||
$this->_retvalue = new OqlObjectQuery($this->yystack[$this->yyidx + -4]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor, array($this->yystack[$this->yyidx + -2]->minor));
|
||||
}
|
||||
#line 1498 "..\oql-parser.php"
|
||||
#line 47 "..\oql-parser.y"
|
||||
#line 1503 "..\oql-parser.php"
|
||||
#line 55 "..\oql-parser.y"
|
||||
function yy_r7(){
|
||||
$this->_retvalue = new OqlObjectQuery($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + -4]->minor);
|
||||
}
|
||||
#line 1503 "..\oql-parser.php"
|
||||
#line 50 "..\oql-parser.y"
|
||||
#line 1508 "..\oql-parser.php"
|
||||
#line 58 "..\oql-parser.y"
|
||||
function yy_r8(){
|
||||
$this->_retvalue = new OqlObjectQuery($this->yystack[$this->yyidx + -4]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + -6]->minor);
|
||||
}
|
||||
#line 1508 "..\oql-parser.php"
|
||||
#line 55 "..\oql-parser.y"
|
||||
#line 1513 "..\oql-parser.php"
|
||||
#line 63 "..\oql-parser.y"
|
||||
function yy_r9(){
|
||||
$this->_retvalue = array($this->yystack[$this->yyidx + 0]->minor);
|
||||
}
|
||||
#line 1513 "..\oql-parser.php"
|
||||
#line 58 "..\oql-parser.y"
|
||||
#line 1518 "..\oql-parser.php"
|
||||
#line 66 "..\oql-parser.y"
|
||||
function yy_r10(){
|
||||
array_push($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor);
|
||||
$this->_retvalue = $this->yystack[$this->yyidx + -2]->minor;
|
||||
}
|
||||
#line 1519 "..\oql-parser.php"
|
||||
#line 63 "..\oql-parser.y"
|
||||
#line 1524 "..\oql-parser.php"
|
||||
#line 71 "..\oql-parser.y"
|
||||
function yy_r11(){ $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; }
|
||||
#line 1522 "..\oql-parser.php"
|
||||
#line 64 "..\oql-parser.y"
|
||||
#line 1527 "..\oql-parser.php"
|
||||
#line 72 "..\oql-parser.y"
|
||||
function yy_r12(){ $this->_retvalue = null; }
|
||||
#line 1525 "..\oql-parser.php"
|
||||
#line 66 "..\oql-parser.y"
|
||||
#line 1530 "..\oql-parser.php"
|
||||
#line 74 "..\oql-parser.y"
|
||||
function yy_r13(){
|
||||
// insert the join statement on top of the existing list
|
||||
array_unshift($this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor);
|
||||
// and return the updated array
|
||||
$this->_retvalue = $this->yystack[$this->yyidx + 0]->minor;
|
||||
}
|
||||
#line 1533 "..\oql-parser.php"
|
||||
#line 72 "..\oql-parser.y"
|
||||
#line 1538 "..\oql-parser.php"
|
||||
#line 80 "..\oql-parser.y"
|
||||
function yy_r14(){
|
||||
$this->_retvalue = Array($this->yystack[$this->yyidx + 0]->minor);
|
||||
}
|
||||
#line 1538 "..\oql-parser.php"
|
||||
#line 78 "..\oql-parser.y"
|
||||
#line 1543 "..\oql-parser.php"
|
||||
#line 86 "..\oql-parser.y"
|
||||
function yy_r16(){
|
||||
// create an array with one single item
|
||||
$this->_retvalue = new OqlJoinSpec($this->yystack[$this->yyidx + -4]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor);
|
||||
}
|
||||
#line 1544 "..\oql-parser.php"
|
||||
#line 83 "..\oql-parser.y"
|
||||
#line 1549 "..\oql-parser.php"
|
||||
#line 91 "..\oql-parser.y"
|
||||
function yy_r17(){
|
||||
// create an array with one single item
|
||||
$this->_retvalue = new OqlJoinSpec($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor);
|
||||
}
|
||||
#line 1550 "..\oql-parser.php"
|
||||
#line 88 "..\oql-parser.y"
|
||||
function yy_r18(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, '=', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1553 "..\oql-parser.php"
|
||||
#line 89 "..\oql-parser.y"
|
||||
function yy_r19(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'BELOW', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1556 "..\oql-parser.php"
|
||||
#line 90 "..\oql-parser.y"
|
||||
function yy_r20(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'BELOW_STRICT', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1559 "..\oql-parser.php"
|
||||
#line 91 "..\oql-parser.y"
|
||||
function yy_r21(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_BELOW', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1562 "..\oql-parser.php"
|
||||
#line 92 "..\oql-parser.y"
|
||||
function yy_r22(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_BELOW_STRICT', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1565 "..\oql-parser.php"
|
||||
#line 93 "..\oql-parser.y"
|
||||
function yy_r23(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'ABOVE', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1568 "..\oql-parser.php"
|
||||
#line 94 "..\oql-parser.y"
|
||||
function yy_r24(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'ABOVE_STRICT', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1571 "..\oql-parser.php"
|
||||
#line 95 "..\oql-parser.y"
|
||||
function yy_r25(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_ABOVE', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1574 "..\oql-parser.php"
|
||||
#line 1555 "..\oql-parser.php"
|
||||
#line 96 "..\oql-parser.y"
|
||||
function yy_r26(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_ABOVE_STRICT', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1577 "..\oql-parser.php"
|
||||
function yy_r18(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, '=', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1558 "..\oql-parser.php"
|
||||
#line 97 "..\oql-parser.y"
|
||||
function yy_r19(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'BELOW', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1561 "..\oql-parser.php"
|
||||
#line 98 "..\oql-parser.y"
|
||||
function yy_r27(){ $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; }
|
||||
#line 1580 "..\oql-parser.php"
|
||||
function yy_r20(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'BELOW_STRICT', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1564 "..\oql-parser.php"
|
||||
#line 99 "..\oql-parser.y"
|
||||
function yy_r21(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_BELOW', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1567 "..\oql-parser.php"
|
||||
#line 100 "..\oql-parser.y"
|
||||
function yy_r22(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_BELOW_STRICT', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1570 "..\oql-parser.php"
|
||||
#line 101 "..\oql-parser.y"
|
||||
function yy_r23(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'ABOVE', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1573 "..\oql-parser.php"
|
||||
#line 102 "..\oql-parser.y"
|
||||
function yy_r24(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'ABOVE_STRICT', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1576 "..\oql-parser.php"
|
||||
#line 103 "..\oql-parser.y"
|
||||
function yy_r31(){ $this->_retvalue = new FunctionOqlExpression($this->yystack[$this->yyidx + -3]->minor, $this->yystack[$this->yyidx + -1]->minor); }
|
||||
#line 1583 "..\oql-parser.php"
|
||||
function yy_r25(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_ABOVE', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1579 "..\oql-parser.php"
|
||||
#line 104 "..\oql-parser.y"
|
||||
function yy_r32(){ $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor; }
|
||||
#line 1586 "..\oql-parser.php"
|
||||
#line 105 "..\oql-parser.y"
|
||||
function yy_r33(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1589 "..\oql-parser.php"
|
||||
function yy_r26(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_ABOVE_STRICT', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1582 "..\oql-parser.php"
|
||||
#line 106 "..\oql-parser.y"
|
||||
function yy_r27(){ $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; }
|
||||
#line 1585 "..\oql-parser.php"
|
||||
#line 111 "..\oql-parser.y"
|
||||
function yy_r31(){ $this->_retvalue = new FunctionOqlExpression($this->yystack[$this->yyidx + -3]->minor, $this->yystack[$this->yyidx + -1]->minor); }
|
||||
#line 1588 "..\oql-parser.php"
|
||||
#line 112 "..\oql-parser.y"
|
||||
function yy_r32(){ $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor; }
|
||||
#line 1591 "..\oql-parser.php"
|
||||
#line 113 "..\oql-parser.y"
|
||||
function yy_r33(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1594 "..\oql-parser.php"
|
||||
#line 119 "..\oql-parser.y"
|
||||
function yy_r37(){
|
||||
if ($this->yystack[$this->yyidx + -1]->minor == 'MATCHES')
|
||||
{
|
||||
@@ -1594,44 +1598,44 @@ static public $yy_action = array(
|
||||
$this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor);
|
||||
}
|
||||
}
|
||||
#line 1601 "..\oql-parser.php"
|
||||
#line 128 "..\oql-parser.y"
|
||||
#line 1606 "..\oql-parser.php"
|
||||
#line 136 "..\oql-parser.y"
|
||||
function yy_r42(){
|
||||
$this->_retvalue = new ListOqlExpression($this->yystack[$this->yyidx + -1]->minor);
|
||||
}
|
||||
#line 1606 "..\oql-parser.php"
|
||||
#line 131 "..\oql-parser.y"
|
||||
#line 1611 "..\oql-parser.php"
|
||||
#line 139 "..\oql-parser.y"
|
||||
function yy_r43(){
|
||||
$this->_retvalue = new NestedQueryOqlExpression($this->yystack[$this->yyidx + -1]->minor);
|
||||
}
|
||||
#line 1611 "..\oql-parser.php"
|
||||
#line 146 "..\oql-parser.y"
|
||||
#line 1616 "..\oql-parser.php"
|
||||
#line 154 "..\oql-parser.y"
|
||||
function yy_r47(){
|
||||
$this->_retvalue = array();
|
||||
}
|
||||
#line 1616 "..\oql-parser.php"
|
||||
#line 157 "..\oql-parser.y"
|
||||
#line 1621 "..\oql-parser.php"
|
||||
#line 165 "..\oql-parser.y"
|
||||
function yy_r51(){ $this->_retvalue = new IntervalOqlExpression($this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1619 "..\oql-parser.php"
|
||||
#line 170 "..\oql-parser.y"
|
||||
#line 1624 "..\oql-parser.php"
|
||||
#line 178 "..\oql-parser.y"
|
||||
function yy_r61(){ $this->_retvalue = new ScalarOqlExpression($this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1622 "..\oql-parser.php"
|
||||
#line 172 "..\oql-parser.y"
|
||||
#line 1627 "..\oql-parser.php"
|
||||
#line 180 "..\oql-parser.y"
|
||||
function yy_r63(){ $this->_retvalue = new ScalarOqlExpression(null); }
|
||||
#line 1625 "..\oql-parser.php"
|
||||
#line 174 "..\oql-parser.y"
|
||||
#line 1630 "..\oql-parser.php"
|
||||
#line 182 "..\oql-parser.y"
|
||||
function yy_r64(){ $this->_retvalue = new FieldOqlExpression($this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1628 "..\oql-parser.php"
|
||||
#line 175 "..\oql-parser.y"
|
||||
#line 1633 "..\oql-parser.php"
|
||||
#line 183 "..\oql-parser.y"
|
||||
function yy_r65(){ $this->_retvalue = new FieldOqlExpression($this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -2]->minor); }
|
||||
#line 1631 "..\oql-parser.php"
|
||||
#line 176 "..\oql-parser.y"
|
||||
#line 1636 "..\oql-parser.php"
|
||||
#line 184 "..\oql-parser.y"
|
||||
function yy_r66(){ $this->_retvalue=$this->yystack[$this->yyidx + 0]->minor; }
|
||||
#line 1634 "..\oql-parser.php"
|
||||
#line 179 "..\oql-parser.y"
|
||||
#line 1639 "..\oql-parser.php"
|
||||
#line 187 "..\oql-parser.y"
|
||||
function yy_r67(){ $this->_retvalue = new VariableOqlExpression(substr($this->yystack[$this->yyidx + 0]->minor, 1)); }
|
||||
#line 1637 "..\oql-parser.php"
|
||||
#line 181 "..\oql-parser.y"
|
||||
#line 1642 "..\oql-parser.php"
|
||||
#line 189 "..\oql-parser.y"
|
||||
function yy_r68(){
|
||||
if ($this->yystack[$this->yyidx + 0]->minor[0] == '`')
|
||||
{
|
||||
@@ -1643,22 +1647,22 @@ static public $yy_action = array(
|
||||
}
|
||||
$this->_retvalue = new OqlName($name, $this->m_iColPrev);
|
||||
}
|
||||
#line 1650 "..\oql-parser.php"
|
||||
#line 192 "..\oql-parser.y"
|
||||
#line 1655 "..\oql-parser.php"
|
||||
#line 200 "..\oql-parser.y"
|
||||
function yy_r69(){$this->_retvalue=(int)$this->yystack[$this->yyidx + 0]->minor; }
|
||||
#line 1653 "..\oql-parser.php"
|
||||
#line 193 "..\oql-parser.y"
|
||||
#line 1658 "..\oql-parser.php"
|
||||
#line 201 "..\oql-parser.y"
|
||||
function yy_r70(){$this->_retvalue=(int)-$this->yystack[$this->yyidx + 0]->minor; }
|
||||
#line 1656 "..\oql-parser.php"
|
||||
#line 194 "..\oql-parser.y"
|
||||
#line 1661 "..\oql-parser.php"
|
||||
#line 202 "..\oql-parser.y"
|
||||
function yy_r71(){$this->_retvalue=new OqlHexValue($this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1659 "..\oql-parser.php"
|
||||
#line 195 "..\oql-parser.y"
|
||||
#line 1664 "..\oql-parser.php"
|
||||
#line 203 "..\oql-parser.y"
|
||||
function yy_r72(){$this->_retvalue=stripslashes(substr($this->yystack[$this->yyidx + 0]->minor, 1, strlen($this->yystack[$this->yyidx + 0]->minor) - 2)); }
|
||||
#line 1662 "..\oql-parser.php"
|
||||
#line 198 "..\oql-parser.y"
|
||||
#line 1667 "..\oql-parser.php"
|
||||
#line 206 "..\oql-parser.y"
|
||||
function yy_r73(){$this->_retvalue=$this->yystack[$this->yyidx + 0]->minor; }
|
||||
#line 1665 "..\oql-parser.php"
|
||||
#line 1670 "..\oql-parser.php"
|
||||
|
||||
/**
|
||||
* placeholder for the left hand side in a reduce operation.
|
||||
@@ -1759,6 +1763,10 @@ static public $yy_action = array(
|
||||
}
|
||||
/* Here code is inserted which will be executed whenever the
|
||||
** parser fails */
|
||||
#line 33 "..\oql-parser.y"
|
||||
|
||||
throw new OQLParserParseFailureException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol);
|
||||
#line 1775 "..\oql-parser.php"
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1772,8 +1780,8 @@ static public $yy_action = array(
|
||||
{
|
||||
#line 25 "..\oql-parser.y"
|
||||
|
||||
throw new OQLParserException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol, $this->tokenName($yymajor), $TOKEN);
|
||||
#line 1781 "..\oql-parser.php"
|
||||
throw new OQLParserSyntaxErrorException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol, $this->tokenName($yymajor), $TOKEN);
|
||||
#line 1791 "..\oql-parser.php"
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1940,19 +1948,47 @@ throw new OQLParserException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCo
|
||||
} while ($yymajor != self::YYNOCODE && $this->yyidx >= 0);
|
||||
}
|
||||
}
|
||||
#line 263 "..\oql-parser.y"
|
||||
#line 271 "..\oql-parser.y"
|
||||
|
||||
|
||||
class OQLParserException extends OQLException
|
||||
{
|
||||
public function __construct($sIssue, $sInput, $iLine, $iCol, $sTokenValue)
|
||||
{
|
||||
parent::__construct($sIssue, $sInput, $iLine, $iCol, $sTokenValue);
|
||||
}
|
||||
}
|
||||
|
||||
class OQLParserSyntaxErrorException extends OQLParserException
|
||||
{
|
||||
public function __construct($sInput, $iLine, $iCol, $sTokenName, $sTokenValue)
|
||||
{
|
||||
$sIssue = "Unexpected token $sTokenName";
|
||||
|
||||
|
||||
parent::__construct($sIssue, $sInput, $iLine, $iCol, $sTokenValue);
|
||||
}
|
||||
}
|
||||
|
||||
class OQLParserStackOverFlowException extends OQLParserException
|
||||
{
|
||||
public function __construct($sInput, $iLine, $iCol)
|
||||
{
|
||||
$sIssue = "Stack overflow";
|
||||
|
||||
parent::__construct($sIssue, $sInput, $iLine, $iCol, '');
|
||||
}
|
||||
}
|
||||
|
||||
class OQLParserParseFailureException extends OQLParserException
|
||||
{
|
||||
public function __construct($sInput, $iLine, $iCol)
|
||||
{
|
||||
$sIssue = "Unexpected token $sTokenName";
|
||||
|
||||
parent::__construct($sIssue, $sInput, $iLine, $iCol, '');
|
||||
}
|
||||
}
|
||||
|
||||
class OQLParser extends OQLParserRaw
|
||||
{
|
||||
// dirty, but working for us (no other mean to get the final result :-(
|
||||
@@ -2005,4 +2041,4 @@ class OQLParser extends OQLParserRaw
|
||||
}
|
||||
}
|
||||
|
||||
#line 2014 "..\oql-parser.php"
|
||||
#line 2052 "..\oql-parser.php"
|
||||
|
||||
@@ -23,7 +23,15 @@ later : solve the 2 remaining shift-reduce conflicts (JOIN)
|
||||
%name OQLParser_
|
||||
%declare_class {class OQLParserRaw}
|
||||
%syntax_error {
|
||||
throw new OQLParserException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol, $this->tokenName($yymajor), $TOKEN);
|
||||
throw new OQLParserSyntaxErrorException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol, $this->tokenName($yymajor), $TOKEN);
|
||||
}
|
||||
/* Bug N°4052 Parser stack size too small for huge OQL requests */
|
||||
%stack_size 1000
|
||||
%stack_overflow {
|
||||
throw new OQLParserStackOverFlowException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol);
|
||||
}
|
||||
%parse_failure {
|
||||
throw new OQLParserParseFailureException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol);
|
||||
}
|
||||
|
||||
result ::= union(X). { $this->my_result = X; }
|
||||
@@ -263,15 +271,43 @@ func_name(A) ::= F_INET_NTOA(X). { A=X; }
|
||||
%code {
|
||||
|
||||
class OQLParserException extends OQLException
|
||||
{
|
||||
public function __construct($sIssue, $sInput, $iLine, $iCol, $sTokenValue)
|
||||
{
|
||||
parent::__construct($sIssue, $sInput, $iLine, $iCol, $sTokenValue);
|
||||
}
|
||||
}
|
||||
|
||||
class OQLParserSyntaxErrorException extends OQLParserException
|
||||
{
|
||||
public function __construct($sInput, $iLine, $iCol, $sTokenName, $sTokenValue)
|
||||
{
|
||||
$sIssue = "Unexpected token $sTokenName";
|
||||
|
||||
|
||||
parent::__construct($sIssue, $sInput, $iLine, $iCol, $sTokenValue);
|
||||
}
|
||||
}
|
||||
|
||||
class OQLParserStackOverFlowException extends OQLParserException
|
||||
{
|
||||
public function __construct($sInput, $iLine, $iCol)
|
||||
{
|
||||
$sIssue = "Stack overflow";
|
||||
|
||||
parent::__construct($sIssue, $sInput, $iLine, $iCol, '');
|
||||
}
|
||||
}
|
||||
|
||||
class OQLParserParseFailureException extends OQLParserException
|
||||
{
|
||||
public function __construct($sInput, $iLine, $iCol)
|
||||
{
|
||||
$sIssue = "Unexpected token $sTokenName";
|
||||
|
||||
parent::__construct($sIssue, $sInput, $iLine, $iCol, '');
|
||||
}
|
||||
}
|
||||
|
||||
class OQLParser extends OQLParserRaw
|
||||
{
|
||||
// dirty, but working for us (no other mean to get the final result :-(
|
||||
|
||||
@@ -1 +1 @@
|
||||
2020-09-29
|
||||
2021-06-03
|
||||
@@ -55,7 +55,7 @@ class OQLClassTreeOptimizer
|
||||
$sJoinedClass = $oJoin->GetOOQLClassNode()->GetNodeClass();
|
||||
$sExtKeyAttCode = $oJoin->GetLeftField();
|
||||
$oExtKeyAttDef = MetaModel::GetAttributeDef($oCurrentClassNode->GetNodeClass(), $sExtKeyAttCode);
|
||||
if ($sJoinedClass == $oExtKeyAttDef->GetTargetClass()) {
|
||||
if (($oExtKeyAttDef instanceof AttributeExternalKey) && ($sJoinedClass == $oExtKeyAttDef->GetTargetClass())) {
|
||||
// The join is not used, remove from tree
|
||||
$oCurrentClassNode->RemoveJoin($sLeftKey, $index);
|
||||
}
|
||||
|
||||
@@ -7,41 +7,50 @@
|
||||
/**
|
||||
* Class ormStyle
|
||||
*
|
||||
* @since 3.0
|
||||
* @since 3.0.0
|
||||
*/
|
||||
class ormStyle
|
||||
{
|
||||
/** @var string */
|
||||
/** @var string|null */
|
||||
protected $sMainColor;
|
||||
/** @var string */
|
||||
/** @var string|null */
|
||||
protected $sComplementaryColor;
|
||||
/** @var string CSS class with color and background-color */
|
||||
/** @var string|null CSS class with color and background-color */
|
||||
protected $sStyleClass;
|
||||
/** @var string CSS class with only color */
|
||||
/** @var string|null CSS class with only color */
|
||||
protected $sAltStyleClass;
|
||||
/** @var string */
|
||||
/** @var string|null */
|
||||
protected $sDecorationClasses;
|
||||
/** @var string */
|
||||
/** @var string|null Relative path (from current environment) to the icon */
|
||||
protected $sIcon;
|
||||
|
||||
/**
|
||||
* ormStyle constructor.
|
||||
*
|
||||
* @param string $sStyleClass
|
||||
* @param string $sAltStyleClass
|
||||
* @param string|null $sStyleClass
|
||||
* @param string|null $sAltStyleClass
|
||||
* @param string|null $sMainColor
|
||||
* @param string|null $sComplementaryColor
|
||||
* @param string|null $sDecorationClasses
|
||||
* @param string|null $sIcon
|
||||
*/
|
||||
public function __construct(string $sStyleClass, string $sAltStyleClass, string $sMainColor = null, string $sComplementaryColor = null, string $sDecorationClasses = null, string $sIcon = null)
|
||||
public function __construct(?string $sStyleClass = null, ?string $sAltStyleClass = null, ?string $sMainColor = null, ?string $sComplementaryColor = null, ?string $sDecorationClasses = null, ?string $sIcon = null)
|
||||
{
|
||||
$this->sMainColor = $sMainColor;
|
||||
$this->sComplementaryColor = $sComplementaryColor;
|
||||
$this->sStyleClass = $sStyleClass;
|
||||
$this->sAltStyleClass = $sAltStyleClass;
|
||||
$this->sDecorationClasses = $sDecorationClasses;
|
||||
$this->sIcon = $sIcon;
|
||||
$this->SetMainColor($sMainColor);
|
||||
$this->SetComplementaryColor($sComplementaryColor);
|
||||
$this->SetStyleClass($sStyleClass);
|
||||
$this->SetAltStyleClass($sAltStyleClass);
|
||||
$this->SetDecorationClasses($sDecorationClasses);
|
||||
$this->SetIcon($sIcon);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see static::$sMainColor
|
||||
* @return bool
|
||||
*/
|
||||
public function HasMainColor(): bool
|
||||
{
|
||||
return strlen($this->sMainColor) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,10 +68,19 @@ class ormStyle
|
||||
*/
|
||||
public function SetMainColor(?string $sMainColor)
|
||||
{
|
||||
$this->sMainColor = $sMainColor;
|
||||
$this->sMainColor = (strlen($sMainColor) === 0) ? null : $sMainColor;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see static::$sComplementaryColor
|
||||
* @return bool
|
||||
*/
|
||||
public function HasComplementaryColor(): bool
|
||||
{
|
||||
return strlen($this->sComplementaryColor) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
@@ -78,14 +96,33 @@ class ormStyle
|
||||
*/
|
||||
public function SetComplementaryColor(?string $sComplementaryColor)
|
||||
{
|
||||
$this->sComplementaryColor = $sComplementaryColor;
|
||||
$this->sComplementaryColor = (strlen($sComplementaryColor) === 0) ? null : $sComplementaryColor;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see static::$sMainColor
|
||||
* @see static::$sComplementaryColor
|
||||
* @return bool
|
||||
*/
|
||||
public function HasAtLeastOneColor(): bool
|
||||
{
|
||||
return $this->HasMainColor() || $this->HasComplementaryColor();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see static::$sStyleClass
|
||||
* @return bool
|
||||
*/
|
||||
public function HasStyleClass(): bool
|
||||
{
|
||||
return strlen($this->sStyleClass) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function GetStyleClass(): string
|
||||
public function GetStyleClass(): ?string
|
||||
{
|
||||
return $this->sStyleClass;
|
||||
}
|
||||
@@ -95,16 +132,25 @@ class ormStyle
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function SetStyleClass(string $sStyleClass)
|
||||
public function SetStyleClass(?string $sStyleClass)
|
||||
{
|
||||
$this->sStyleClass = $sStyleClass;
|
||||
$this->sStyleClass = (strlen($sStyleClass) === 0) ? null : $sStyleClass;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see static::$sAltStyleClass
|
||||
* @return bool
|
||||
*/
|
||||
public function HasAltStyleClass(): bool
|
||||
{
|
||||
return strlen($this->sAltStyleClass) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function GetAltStyleClass(): string
|
||||
public function GetAltStyleClass(): ?string
|
||||
{
|
||||
return $this->sAltStyleClass;
|
||||
}
|
||||
@@ -114,12 +160,21 @@ class ormStyle
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function SetAltStyleClass(string $sAltStyleClass)
|
||||
public function SetAltStyleClass(?string $sAltStyleClass)
|
||||
{
|
||||
$this->sAltStyleClass = $sAltStyleClass;
|
||||
$this->sAltStyleClass = (strlen($sAltStyleClass) === 0) ? null : $sAltStyleClass;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see static::$sDecorationClasses
|
||||
* @return bool
|
||||
*/
|
||||
public function HasDecorationClasses(): bool
|
||||
{
|
||||
return strlen($this->sDecorationClasses) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
@@ -135,16 +190,17 @@ class ormStyle
|
||||
*/
|
||||
public function SetDecorationClasses(?string $sDecorationClasses)
|
||||
{
|
||||
$this->sDecorationClasses = $sDecorationClasses;
|
||||
$this->sDecorationClasses = (strlen($sDecorationClasses) === 0) ? null : $sDecorationClasses;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @see static::$sIcon
|
||||
* @return bool
|
||||
*/
|
||||
public function GetIcon(): ?string
|
||||
public function HasIcon(): bool
|
||||
{
|
||||
return $this->sIcon;
|
||||
return strlen($this->sIcon) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,8 +210,30 @@ class ormStyle
|
||||
*/
|
||||
public function SetIcon(?string $sIcon)
|
||||
{
|
||||
$this->sIcon = $sIcon;
|
||||
$this->sIcon = (strlen($sIcon) === 0) ? null : $sIcon;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see static::$sIcon
|
||||
* @return string|null Relative path (from the current environment) of the icon
|
||||
*/
|
||||
public function GetIconAsRelPath(): ?string
|
||||
{
|
||||
return $this->sIcon;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see static::$sIcon
|
||||
* @return string|null Absolute URL of the icon
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function GetIconAsAbsUrl(): ?string
|
||||
{
|
||||
if (is_null($this->sIcon)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return utils::GetAbsoluteUrlModulesRoot().$this->sIcon;
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,12 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
use Combodo\iTop\Application\UI\Base\Component\CollapsibleSection\CollapsibleSectionUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
|
||||
use Combodo\iTop\Application\UI\Base\iUIBlock;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlockUIBlockFactory;
|
||||
use Combodo\iTop\Renderer\BlockRenderer;
|
||||
|
||||
define('CASELOG_VISIBLE_ITEMS', 2);
|
||||
define('CASELOG_SEPARATOR', "\n".'========== %1$s : %2$s (%3$d) ============'."\n\n");
|
||||
|
||||
@@ -168,7 +174,6 @@ class ormCaseLog {
|
||||
return $aEntries;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a "plain text" version of the log (equivalent to $this->m_sLog) where all the HTML markup from the 'html' entries have been removed
|
||||
* @return string
|
||||
@@ -201,6 +206,15 @@ class ormCaseLog {
|
||||
{
|
||||
return ($this->m_sLog === null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int The number of entries in this log
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function GetEntryCount(): int
|
||||
{
|
||||
return count($this->m_aIndex);
|
||||
}
|
||||
|
||||
public function ClearModifiedFlag()
|
||||
{
|
||||
@@ -389,7 +403,7 @@ class ormCaseLog {
|
||||
{
|
||||
$bPrintableVersion = (utils::ReadParam('printable', '0') == '1');
|
||||
|
||||
$sHtml = '<table style="width:100%;table-layout:fixed"><tr><td>'; // Use table-layout:fixed to force the with to be independent from the actual content
|
||||
$oBlock = UIContentBlockUIBlockFactory::MakeStandard(null, ['ibo-caselog-list']);
|
||||
$iPos = 0;
|
||||
$aIndex = $this->m_aIndex;
|
||||
if (($bEditMode) && (count($aIndex) > 0) && $this->m_bModified)
|
||||
@@ -403,20 +417,16 @@ class ormCaseLog {
|
||||
{
|
||||
if (!$bPrintableVersion && ($index < count($aIndex) - CASELOG_VISIBLE_ITEMS))
|
||||
{
|
||||
$sOpen = '';
|
||||
$sDisplay = 'style="display:none;"';
|
||||
$bIsOpen = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sOpen = ' open';
|
||||
$sDisplay = '';
|
||||
$bIsOpen = true;
|
||||
}
|
||||
$iPos += $aIndex[$index]['separator_length'];
|
||||
$sTextEntry = substr($this->m_sLog, $iPos, $aIndex[$index]['text_length']);
|
||||
$sCSSClass= 'caselog_entry_html';
|
||||
if (!array_key_exists('format', $aIndex[$index]) || ($aIndex[$index]['format'] == 'text'))
|
||||
{
|
||||
$sCSSClass= 'caselog_entry';
|
||||
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
|
||||
if (!is_null($aTransfoHandler))
|
||||
{
|
||||
@@ -433,7 +443,6 @@ class ormCaseLog {
|
||||
}
|
||||
$iPos += $aIndex[$index]['text_length'];
|
||||
|
||||
$sEntry = '<div class="caselog_header'.$sOpen.'">';
|
||||
// Workaround: PHP < 5.3 cannot unserialize correctly DateTime objects,
|
||||
// therefore we have changed the format. To preserve the compatibility with existing
|
||||
// installations of iTop, both format are allowed:
|
||||
@@ -456,14 +465,11 @@ class ormCaseLog {
|
||||
$sDate = '';
|
||||
}
|
||||
}
|
||||
$sEntry .= sprintf(Dict::S('UI:CaseLog:Header_Date_UserName'), $sDate, $aIndex[$index]['user_name']);
|
||||
$sEntry .= '</div>';
|
||||
$sEntry .= '<div class="'.$sCSSClass.'"'.$sDisplay.'>';
|
||||
$sEntry .= $sTextEntry;
|
||||
$sEntry .= '</div>';
|
||||
$sHtml = $sHtml.$sEntry;
|
||||
$oCollapsibleBlock = CollapsibleSectionUIBlockFactory::MakeStandard( sprintf(Dict::S('UI:CaseLog:Header_Date_UserName'), $sDate, $aIndex[$index]['user_name']));
|
||||
$oCollapsibleBlock->AddSubBlock(new Html($sTextEntry));
|
||||
$oCollapsibleBlock->SetOpenedByDefault($bIsOpen);
|
||||
$oBlock->AddSubBlock($oCollapsibleBlock);
|
||||
}
|
||||
|
||||
// Process the case of an eventual remainder (quick migration of AttributeText fields)
|
||||
if ($iPos < (strlen($this->m_sLog) - 1))
|
||||
{
|
||||
@@ -477,48 +483,71 @@ class ormCaseLog {
|
||||
|
||||
if (count($this->m_aIndex) == 0)
|
||||
{
|
||||
$sHtml .= '<div class="caselog_entry open">';
|
||||
$sHtml .= $sTextEntry;
|
||||
$sHtml .= '</div>';
|
||||
$oCollapsibleBlock = CollapsibleSectionUIBlockFactory::MakeStandard( '');
|
||||
$oCollapsibleBlock->AddSubBlock(new Html($sTextEntry));
|
||||
$oCollapsibleBlock->SetOpenedByDefault(true);
|
||||
$oBlock->AddSubBlock($oCollapsibleBlock);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!$bPrintableVersion && (count($this->m_aIndex) - CASELOG_VISIBLE_ITEMS > 0))
|
||||
{
|
||||
$sOpen = '';
|
||||
$sDisplay = 'style="display:none;"';
|
||||
$bIsOpen = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sOpen = ' open';
|
||||
$sDisplay = '';
|
||||
$bIsOpen = true;
|
||||
}
|
||||
$sHtml .= '<div class="caselog_header'.$sOpen.'">';
|
||||
$sHtml .= Dict::S('UI:CaseLog:InitialValue');
|
||||
$sHtml .= '</div>';
|
||||
$sHtml .= '<div class="caselog_entry"'.$sDisplay.'>';
|
||||
$sHtml .= $sTextEntry;
|
||||
$sHtml .= '</div>';
|
||||
$oCollapsibleBlock = CollapsibleSectionUIBlockFactory::MakeStandard( Dict::S('UI:CaseLog:InitialValue'));
|
||||
$oCollapsibleBlock->AddSubBlock(new Html($sTextEntry));
|
||||
$oCollapsibleBlock->SetOpenedByDefault($bIsOpen);
|
||||
}
|
||||
}
|
||||
$sHtml .= '</td></tr></table>';
|
||||
return $sHtml;
|
||||
$oBlockRenderer = new BlockRenderer($oBlock);
|
||||
$sHtml = $oBlockRenderer->RenderHtml();
|
||||
$sScript = $oBlockRenderer->RenderJsInlineRecursively($oBlock,iUIBlock::ENUM_JS_TYPE_ON_READY);
|
||||
if ($sScript!=''){
|
||||
if ($oP == null) {
|
||||
$sScript = '<script>'.$sScript.'</script>';
|
||||
$sHtml .= $sScript;
|
||||
} else {
|
||||
$oP->add_ready_script($sScript);
|
||||
}
|
||||
}
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a new entry to the log or merge the given text into the currently modified entry
|
||||
* Add a new entry to the log or merge the given text into the currently modified entry
|
||||
* and updates the internal index
|
||||
* @param $sText string The text of the new entry
|
||||
*
|
||||
* @param string $sText The text of the new entry
|
||||
* @param string $sOnBehalfOf Display this name instead of current user name
|
||||
* @param null|int $iOnBehalfOfId Use this UserId to author this Entry. If $sOnBehalfOf equals '', it'll be replaced by this User friendlyname
|
||||
*
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \OQLException
|
||||
*
|
||||
* @since 3.0.0 New $iOnBehalfOfId parameter
|
||||
* @since 3.0.0 May throw \ArchivedObjectException exception
|
||||
*/
|
||||
public function AddLogEntry($sText, $sOnBehalfOf = '')
|
||||
public function AddLogEntry(string $sText, $sOnBehalfOf = '', $iOnBehalfOfId = null)
|
||||
{
|
||||
$sText = HTMLSanitizer::Sanitize($sText);
|
||||
$sDate = date(AttributeDateTime::GetInternalFormat());
|
||||
if ($sOnBehalfOf == '')
|
||||
{
|
||||
if ($sOnBehalfOf == '' && $iOnBehalfOfId === null) {
|
||||
$sOnBehalfOf = UserRights::GetUserFriendlyName();
|
||||
$iUserId = UserRights::GetUserId();
|
||||
}
|
||||
elseif ($iOnBehalfOfId !== null) {
|
||||
$iUserId = $iOnBehalfOfId;
|
||||
/* @var User $oUser */
|
||||
$oUser = MetaModel::GetObject('User', $iUserId, false, true);
|
||||
if ($oUser !== null && $sOnBehalfOf === '') {
|
||||
$sOnBehalfOf = $oUser->GetFriendlyName();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$iUserId = null;
|
||||
@@ -553,7 +582,6 @@ class ormCaseLog {
|
||||
$this->m_bModified = true;
|
||||
}
|
||||
|
||||
|
||||
public function AddLogEntryFromJSON($oJson, $bCheckUserId = true)
|
||||
{
|
||||
if (isset($oJson->user_id))
|
||||
@@ -630,7 +658,6 @@ class ormCaseLog {
|
||||
$this->m_bModified = true;
|
||||
}
|
||||
|
||||
|
||||
public function GetModifiedEntry($sFormat = 'text')
|
||||
{
|
||||
$sModifiedEntry = '';
|
||||
@@ -709,4 +736,3 @@ class ormCaseLog {
|
||||
return $sText;
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -118,7 +118,8 @@ class ormDocument
|
||||
else
|
||||
{
|
||||
$data = $this->GetData();
|
||||
$sResult = htmlentities($this->GetFileName(), ENT_QUOTES, 'UTF-8').' [ '.$this->GetMimeType().', size: '.strlen($data).' byte(s) ]<br/>';
|
||||
$sSize = utils::BytesToFriendlyFormat(strlen($data));
|
||||
$sResult = htmlentities($this->GetFileName(), ENT_QUOTES, 'UTF-8').' ('.$sSize.')<br/>';
|
||||
}
|
||||
return $sResult;
|
||||
}
|
||||
|
||||
@@ -153,7 +153,9 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
||||
*/
|
||||
public function AddObject(DBObject $oObject, $sClassAlias = '')
|
||||
{
|
||||
$this->AddItem($oObject);
|
||||
// cannot notify depreciation for now as this is still MASSIVELY used in iTop core !
|
||||
//DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use \ormLinkSet::AddItem() instead');
|
||||
$this->AddItem($oObject);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -258,16 +260,13 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
||||
*/
|
||||
public function ToArray($bWithId = true)
|
||||
{
|
||||
$aRet = array();
|
||||
foreach($this as $oItem)
|
||||
{
|
||||
if ($bWithId)
|
||||
{
|
||||
$aRet[$oItem->GetKey()] = $oItem;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aRet[] = $oItem;
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use foreach($this as $oItem){} instead');
|
||||
$aRet = array();
|
||||
foreach ($this as $oItem) {
|
||||
if ($bWithId) {
|
||||
$aRet[$oItem->GetKey()] = $oItem;
|
||||
} else {
|
||||
$aRet[] = $oItem;
|
||||
}
|
||||
}
|
||||
return $aRet;
|
||||
@@ -804,6 +803,7 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
||||
$oLinkSearch = $this->GetFilter();
|
||||
if ($oAttDef->IsIndirect())
|
||||
{
|
||||
$oLinkSearch->RenameAlias($oLinkSearch->GetClassAlias(), self::LINK_ALIAS);
|
||||
$sExtKeyToRemote = $oAttDef->GetExtKeyToRemote();
|
||||
/** @var \AttributeExternalKey $oLinkingAttDef */
|
||||
$oLinkingAttDef = MetaModel::GetAttributeDef($this->sClass, $sExtKeyToRemote);
|
||||
@@ -811,12 +811,12 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
||||
// N°2334 add pointed class (SELECT L,R) to have all fields (lnk + remote) in display
|
||||
// the pointed class is always present in the search, as generated by \AttributeLinkedSet::GetDefaultValue
|
||||
$sTargetClass = $oLinkingAttDef->GetTargetClass();
|
||||
$oRemoteClassSearch = new DBObjectSearch($sTargetClass);
|
||||
$oRemoteClassSearch = new DBObjectSearch($sTargetClass, self::REMOTE_ALIAS);
|
||||
|
||||
if (!$bShowObsolete && MetaModel::IsObsoletable($sTargetClass))
|
||||
{
|
||||
$oNotObsolete = new BinaryExpression(
|
||||
new FieldExpression('obsolescence_flag', $sTargetClass),
|
||||
new FieldExpression('obsolescence_flag', self::REMOTE_ALIAS),
|
||||
'=',
|
||||
new ScalarExpression(0)
|
||||
);
|
||||
@@ -826,7 +826,7 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
||||
if (!utils::IsArchiveMode() && MetaModel::IsArchivable($sTargetClass))
|
||||
{
|
||||
$oNotArchived = new BinaryExpression(
|
||||
new FieldExpression('archive_flag', $sTargetClass),
|
||||
new FieldExpression('archive_flag', self::REMOTE_ALIAS),
|
||||
'=',
|
||||
new ScalarExpression(0)
|
||||
);
|
||||
@@ -834,9 +834,14 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
||||
$oRemoteClassSearch->AddConditionExpression($oNotArchived);
|
||||
}
|
||||
|
||||
$oLinkSearch->AddCondition_PointingTo($oRemoteClassSearch, $sExtKeyToRemote);
|
||||
$oLinkSearch->RenameAlias($oLinkSearch->GetClassAlias(), self::LINK_ALIAS);
|
||||
$oLinkSearch->RenameAlias($sTargetClass, self::REMOTE_ALIAS);
|
||||
$aReAliasingMap = [];
|
||||
$oLinkSearch->AddCondition_PointingTo($oRemoteClassSearch, $sExtKeyToRemote, TREE_OPERATOR_EQUALS, $aReAliasingMap);
|
||||
if (array_key_exists(self::REMOTE_ALIAS, $aReAliasingMap)) {
|
||||
// If 'Remote' alias has been renamed, change it back.
|
||||
if ($aReAliasingMap[self::REMOTE_ALIAS][0] != self::REMOTE_ALIAS) {
|
||||
$oLinkSearch->RenameAlias($aReAliasingMap[self::REMOTE_ALIAS][0], self::REMOTE_ALIAS);
|
||||
}
|
||||
}
|
||||
$oLinkSearch->SetSelectedClasses([self::LINK_ALIAS, self::REMOTE_ALIAS]);
|
||||
}
|
||||
$oLinkSet = new DBObjectSet($oLinkSearch);
|
||||
|
||||
@@ -55,27 +55,27 @@ class PDFBulkExport extends HTMLBulkExport
|
||||
|
||||
//page format
|
||||
$oSelectFormat = SelectUIBlockFactory::MakeForSelectWithLabel("page_size", Dict::S('Core:BulkExport:PDFPageSize'));
|
||||
$oSelectFormat->SetBeforeInput(false);
|
||||
$oSelectFormat->GetInput()->AddCSSClass('ibo-input-checkbox');
|
||||
$oSelectFormat->SetIsLabelBefore(false);
|
||||
//$oSelectFormat->AddCSSClass('ibo-input-checkbox');
|
||||
$oFieldSetFormat->AddSubBlock($oSelectFormat);
|
||||
|
||||
$aPossibleFormat = ['A3', 'A4', 'Letter'];
|
||||
$sDefaultFormat = 'A4';
|
||||
foreach ($aPossibleFormat as $sVal) {
|
||||
$oSelectFormat->GetInput()->AddSubBlock(SelectOptionUIBlockFactory::MakeForSelectOption($sVal, htmlentities(Dict::S('Core:BulkExport:PageSize-'.$sVal), ENT_QUOTES, 'UTF-8'), ($sVal == $sDefaultFormat)));
|
||||
$oSelectFormat->AddSubBlock(SelectOptionUIBlockFactory::MakeForSelectOption($sVal, htmlentities(Dict::S('Core:BulkExport:PageSize-'.$sVal), ENT_QUOTES, 'UTF-8'), ($sVal == $sDefaultFormat)));
|
||||
}
|
||||
$oFieldSetFormat->AddSubBlock(new Html('</br>'));
|
||||
|
||||
$oSelectOrientation = SelectUIBlockFactory::MakeForSelectWithLabel("page_size",
|
||||
$oSelectOrientation = SelectUIBlockFactory::MakeForSelectWithLabel("page_orientation",
|
||||
Dict::S('Core:BulkExport:PDFPageOrientation'));
|
||||
$oSelectOrientation->SetBeforeInput(false);
|
||||
$oSelectOrientation->GetInput()->AddCSSClass('ibo-input-checkbox');
|
||||
$oSelectOrientation->SetIsLabelBefore(false);
|
||||
//$oSelectOrientation->AddCSSClass('ibo-input-checkbox');
|
||||
$oFieldSetFormat->AddSubBlock($oSelectOrientation);
|
||||
|
||||
$aPossibleOrientation = ['P', 'L'];
|
||||
$sDefaultOrientation = 'L';
|
||||
foreach ($aPossibleOrientation as $sVal) {
|
||||
$oSelectOrientation->GetInput()->AddSubBlock(SelectOptionUIBlockFactory::MakeForSelectOption($sVal, htmlentities(Dict::S('Core:BulkExport:PageOrientation-'.$sVal), ENT_QUOTES, 'UTF-8'), ($sVal == $sDefaultOrientation)));
|
||||
$oSelectOrientation->AddSubBlock(SelectOptionUIBlockFactory::MakeForSelectOption($sVal, htmlentities(Dict::S('Core:BulkExport:PageOrientation-'.$sVal), ENT_QUOTES, 'UTF-8'), ($sVal == $sDefaultOrientation)));
|
||||
}
|
||||
|
||||
//date format
|
||||
@@ -86,15 +86,15 @@ class PDFBulkExport extends HTMLBulkExport
|
||||
|
||||
$sDefaultFormat = htmlentities((string)AttributeDateTime::GetFormat(), ENT_QUOTES, 'UTF-8');
|
||||
$sExample = htmlentities(date((string)AttributeDateTime::GetFormat()), ENT_QUOTES, 'UTF-8');
|
||||
$oRadioDefault = InputUIBlockFactory::MakeForInputWithLabel(Dict::Format('Core:BulkExport:DateTimeFormatDefault_Example', $sDefaultFormat, $sExample), "pdf_custom_date_time_format", "default", "pdf_date_time_format_default", "radio");
|
||||
$oRadioDefault = InputUIBlockFactory::MakeForInputWithLabel(Dict::Format('Core:BulkExport:DateTimeFormatDefault_Example', $sDefaultFormat, $sExample), "pdf_date_format_radio", "default", "pdf_date_time_format_default", "radio");
|
||||
$oRadioDefault->GetInput()->SetIsChecked(($sDateTimeFormat == (string)AttributeDateTime::GetFormat()));
|
||||
$oRadioDefault->SetBeforeInput(false);
|
||||
$oRadioDefault->GetInput()->AddCSSClass('ibo-input-checkbox');
|
||||
$oFieldSetDate->AddSubBlock($oRadioDefault);
|
||||
$oFieldSetDate->AddSubBlock(new Html('</br>'));
|
||||
|
||||
$sFormatInput = '<input type="text" size="15" name="date_format" id="excel_custom_date_time_format" title="" value="'.htmlentities($sDateTimeFormat, ENT_QUOTES, 'UTF-8').'"/>';
|
||||
$oRadioCustom = InputUIBlockFactory::MakeForInputWithLabel(Dict::Format('Core:BulkExport:DateTimeFormatCustom_Format', $sFormatInput), "pdf_custom_date_time_format", "custom", "pdf_date_time_format_custom", "radio");
|
||||
$sFormatInput = '<input type="text" size="15" name="date_format" id="pdf_custom_date_time_format" title="" value="'.htmlentities($sDateTimeFormat, ENT_QUOTES, 'UTF-8').'"/>';
|
||||
$oRadioCustom = InputUIBlockFactory::MakeForInputWithLabel(Dict::Format('Core:BulkExport:DateTimeFormatCustom_Format', $sFormatInput), "pdf_date_format_radio", "custom", "pdf_date_time_format_custom", "radio");
|
||||
$oRadioCustom->GetInput()->SetIsChecked($sDateTimeFormat !== (string)AttributeDateTime::GetFormat());
|
||||
$oRadioCustom->SetBeforeInput(false);
|
||||
$oRadioCustom->GetInput()->AddCSSClass('ibo-input-checkbox');
|
||||
@@ -184,9 +184,8 @@ EOF
|
||||
$sData = parent::GetFooter();
|
||||
|
||||
// We need a lot of time for the PDF conversion
|
||||
set_time_limit(60*10); // 10 minutes max ???
|
||||
|
||||
require_once(APPROOT.'application/pdfpage.class.inc.php');
|
||||
set_time_limit(60 * 10); // 10 minutes max ???
|
||||
|
||||
$oPage = new PDFPage(Dict::Format('Core:BulkExportOf_Class', MetaModel::GetName($this->oSearch->GetClass())), $this->aStatusInfo['page_size'], $this->aStatusInfo['page_orientation']);
|
||||
$oPDF = $oPage->get_tcpdf();
|
||||
$oPDF->SetFontSize(8);
|
||||
|
||||
@@ -31,29 +31,25 @@ class PluginManager
|
||||
|
||||
/**
|
||||
* @param string $sInterface
|
||||
* @param bool $bCanInstantiatePlugins internal use, let this value to true
|
||||
* @param string|null $sFilterInstanceOf [optional] if given, only instance of this string will be returned
|
||||
* @param bool $bCanInstantiatePlugins internal use, let this value to true
|
||||
*
|
||||
* @return array classes=>instance implementing the given interface
|
||||
*/
|
||||
public function EnumPlugins($sInterface, $sFilterInstanceOf = null, $bCanInstantiatePlugins = true)
|
||||
{
|
||||
$aPlugins = array();
|
||||
if (array_key_exists($sInterface, self::$m_aExtensionClasses))
|
||||
{
|
||||
$aAllPlugins = array_values(self::$m_aExtensionClasses[$sInterface]);
|
||||
if (array_key_exists($sInterface, self::$m_aExtensionClasses)) {
|
||||
$aAllPlugins = self::$m_aExtensionClasses[$sInterface];
|
||||
|
||||
if (is_null($sFilterInstanceOf))
|
||||
{
|
||||
if (is_null($sFilterInstanceOf)) {
|
||||
return $aAllPlugins;
|
||||
};
|
||||
|
||||
$aPlugins = array();
|
||||
foreach ($aAllPlugins as $instance)
|
||||
{
|
||||
if ($instance instanceof $sFilterInstanceOf)
|
||||
{
|
||||
$aPlugins[] = $instance;
|
||||
foreach ($aAllPlugins as $sPluginClass => $instance) {
|
||||
if ($instance instanceof $sFilterInstanceOf) {
|
||||
$aPlugins[$sPluginClass] = $instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -388,15 +388,18 @@ class SQLObjectQuery extends SQLQuery
|
||||
{
|
||||
if (count($this->__aSelectedIdFields) > 0)
|
||||
{
|
||||
$aCountFields = array();
|
||||
foreach ($this->__aSelectedIdFields as $sFieldExpr)
|
||||
{
|
||||
$aCountFields[] = "COALESCE($sFieldExpr, 0)"; // Null values are excluded from the count
|
||||
$aCountFields = [];
|
||||
$aCountI = [];
|
||||
$i = 0;
|
||||
foreach ($this->__aSelectedIdFields as $sFieldExpr) {
|
||||
$aCountFields[] = "COALESCE($sFieldExpr, 0) AS idCount$i"; // Null values are excluded from the count
|
||||
$aCountI[] = 'idCount'.$i++;
|
||||
}
|
||||
$sCountFields = implode(', ', $aCountFields);
|
||||
$sCountI = implode('+ ', $aCountI);
|
||||
// Count can be limited for performance reason, in this case the total amount is not important,
|
||||
// we only need to know if the number of entries is greater than a certain amount.
|
||||
$sSQL = "SELECT COUNT(*) AS COUNT FROM (SELECT$sLineSep DISTINCT $sCountFields $sLineSep FROM $sFrom$sLineSep WHERE $sWhere $sLimit) AS _alderaan_";
|
||||
$sSQL = "SELECT COUNT(*) AS COUNT FROM (SELECT$sLineSep DISTINCT $sCountFields $sLineSep FROM $sFrom$sLineSep WHERE $sWhere $sLimit) AS _alderaan_ WHERE $sCountI>0";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -286,19 +286,16 @@ abstract class TabularBulkExport extends BulkExport
|
||||
$aSampleRow = array();
|
||||
foreach($aAuthorizedClasses as $sAlias => $sClass)
|
||||
{
|
||||
if (count($aAuthorizedClasses) > 1 )
|
||||
{
|
||||
if (count($aAuthorizedClasses) > 1) {
|
||||
$sShortAlias = $sAlias.'.';
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$sShortAlias = '';
|
||||
}
|
||||
|
||||
foreach ($aAllAttCodes[$sAlias] as $sAttCodeEx)
|
||||
{
|
||||
$oObj = $aRow[$sAlias];
|
||||
$aSampleRow[$sShortAlias.$sAttCodeEx] = $oObj ? $this->GetSampleData($oObj, $sAttCodeEx) : '';
|
||||
if (isset($aAllAttCodes[$sAlias])) {
|
||||
foreach ($aAllAttCodes[$sAlias] as $sAttCodeEx) {
|
||||
$oObj = $aRow[$sAlias];
|
||||
$aSampleRow[$sShortAlias.$sAttCodeEx] = $oObj ? $this->GetSampleData($oObj, $sAttCodeEx) : '';
|
||||
}
|
||||
}
|
||||
}
|
||||
$aSampleData[] = $aSampleRow;
|
||||
|
||||
@@ -558,7 +558,7 @@ class TriggerOnObjectUpdate extends TriggerOnObject
|
||||
* Class TriggerOnObjectMention
|
||||
*
|
||||
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
|
||||
* @since 2.7.0
|
||||
* @since 3.0.0
|
||||
*/
|
||||
class TriggerOnObjectMention extends TriggerOnObject
|
||||
{
|
||||
@@ -584,37 +584,11 @@ class TriggerOnObjectMention extends TriggerOnObject
|
||||
MetaModel::Init_InheritAttributes();
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('description', 'target_class', 'filter', 'action_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('details', array('description', 'context', 'target_class', 'filter', 'action_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class')); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
MetaModel::Init_SetZListItems('standard_search', array('description', 'target_class')); // Criteria of the std search form
|
||||
}
|
||||
|
||||
// TODO 3.0.0: Clean this up. What was the intention?
|
||||
// public function IsTargetObject($iObjectId, $aChanges = array())
|
||||
// {
|
||||
// if (!parent::IsTargetObject($iObjectId, $aChanges))
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// // Check the attribute
|
||||
// $oAttCodeSet = $this->Get('target_attcodes');
|
||||
// $aAttCodes = $oAttCodeSet->GetValues();
|
||||
// if (empty($aAttCodes))
|
||||
// {
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// foreach($aAttCodes as $sAttCode)
|
||||
// {
|
||||
// if (array_key_exists($sAttCode, $aChanges))
|
||||
// {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
<?php
|
||||
|
||||
use Combodo\iTop\Application\Helper\Session;
|
||||
|
||||
define('UR_ALLOWED_NO', 0);
|
||||
define('UR_ALLOWED_YES', 1);
|
||||
define('UR_ALLOWED_DEPENDS', 2);
|
||||
@@ -318,7 +321,14 @@ abstract class User extends cmdbAbstractObject
|
||||
{
|
||||
if (MetaModel::IsValidAttCode(get_class($this), 'contactid') && ($this->Get('contactid') != 0))
|
||||
{
|
||||
$this->oContactObject = MetaModel::GetObject('Contact', $this->Get('contactid'));
|
||||
$this->oContactObject = null;
|
||||
// The User Contact is generally a Person, so try it first
|
||||
if (MetaModel::IsValidClass('Person')) {
|
||||
$this->oContactObject = MetaModel::GetObject('Person', $this->Get('contactid'), false);
|
||||
}
|
||||
if (is_null($this->oContactObject)) {
|
||||
$this->oContactObject = MetaModel::GetObject('Contact', $this->Get('contactid'));
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this->oContactObject;
|
||||
@@ -333,77 +343,135 @@ abstract class User extends cmdbAbstractObject
|
||||
{
|
||||
parent::DoCheckToWrite();
|
||||
|
||||
// Note: This MUST be factorized later: declare unique keys (set of columns) in the data model
|
||||
$oAddon = UserRights::GetModuleInstance();
|
||||
$aChanges = $this->ListChanges();
|
||||
if (array_key_exists('login', $aChanges))
|
||||
{
|
||||
if (strcasecmp($this->Get('login'), $this->GetOriginal('login')) !== 0)
|
||||
{
|
||||
if (array_key_exists('login', $aChanges)) {
|
||||
// Check login uniqueness
|
||||
if (strcasecmp($this->Get('login'), $this->GetOriginal('login')) !== 0) {
|
||||
$sNewLogin = $aChanges['login'];
|
||||
$oSearch = DBObjectSearch::FromOQL_AllData("SELECT User WHERE login = :newlogin");
|
||||
if (!$this->IsNew())
|
||||
{
|
||||
if (!$this->IsNew()) {
|
||||
$oSearch->AddCondition('id', $this->GetKey(), '!=');
|
||||
}
|
||||
$oSet = new DBObjectSet($oSearch, array(), array('newlogin' => $sNewLogin));
|
||||
if ($oSet->Count() > 0)
|
||||
{
|
||||
if ($oSet->Count() > 0) {
|
||||
$this->m_aCheckIssues[] = Dict::Format('Class:User/Error:LoginMustBeUnique', $sNewLogin);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check that this user has at least one profile assigned when profiles have changed
|
||||
if (array_key_exists('profile_list', $aChanges))
|
||||
{
|
||||
$oSet = $this->Get('profile_list');
|
||||
if ($oSet->Count() == 0)
|
||||
{
|
||||
$this->m_aCheckIssues[] = Dict::S('Class:User/Error:AtLeastOneProfileIsNeeded');
|
||||
|
||||
// A User cannot disable himself
|
||||
if ($this->IsCurrentUser()) {
|
||||
if (isset($aChanges['status']) && ($this->Get('status') == 'disabled')) {
|
||||
$this->m_aCheckIssues[] = Dict::S('Class:User/Error:StatusChangeIsNotAllowed');
|
||||
}
|
||||
}
|
||||
|
||||
// Check that this user has at least one profile assigned when profiles have changed
|
||||
if (array_key_exists('profile_list', $aChanges)) {
|
||||
/** @var \DBObjectSet $oSet */
|
||||
$oSet = $this->Get('profile_list');
|
||||
if ($oSet->Count() == 0) {
|
||||
$this->m_aCheckIssues[] = Dict::S('Class:User/Error:AtLeastOneProfileIsNeeded');
|
||||
}
|
||||
|
||||
// A user cannot add to themself a profile denying the access to the backoffice
|
||||
$aForbiddenProfiles = PortalDispatcherData::GetData('backoffice')['deny'];
|
||||
if ($this->IsCurrentUser()) {
|
||||
$oSet->Rewind();
|
||||
$aProfiles = [];
|
||||
while ($oUserProfile = $oSet->Fetch()) {
|
||||
$sProfile = $oUserProfile->Get('profile');
|
||||
if (in_array($sProfile, $aForbiddenProfiles)) {
|
||||
$this->m_aCheckIssues[] = Dict::Format('Class:User/Error:ProfileNotAllowed', $sProfile);
|
||||
}
|
||||
$aProfiles[$oUserProfile->Get('profileid')] = $sProfile;
|
||||
}
|
||||
|
||||
if (!in_array(ADMIN_PROFILE_NAME, $aProfiles)) {
|
||||
// Check if the user is yet allowed to modify Users
|
||||
if (method_exists($oAddon, 'ResetCache')) {
|
||||
$aCurrentProfiles = Session::Get('profile_list');
|
||||
// Set the current profiles into a session variable (not yet in the database)
|
||||
Session::Set('profile_list', $aProfiles);
|
||||
|
||||
$oAddon->ResetCache();
|
||||
if (!$oAddon->IsActionAllowed($this, 'User', UR_ACTION_MODIFY, null)) {
|
||||
$this->m_aCheckIssues[] = Dict::S('Class:User/Error:CurrentProfilesHaveInsufficientRights');
|
||||
}
|
||||
$oAddon->ResetCache();
|
||||
|
||||
if (is_null($aCurrentProfiles)) {
|
||||
Session::IsSet('profile_list');
|
||||
} else {
|
||||
Session::Set('profile_list', $aCurrentProfiles);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only administrators can manage administrators
|
||||
if (UserRights::IsAdministrator($this) && !UserRights::IsAdministrator())
|
||||
{
|
||||
if (UserRights::IsAdministrator($this) && !UserRights::IsAdministrator()) {
|
||||
$this->m_aCheckIssues[] = Dict::S('UI:Login:Error:AccessRestricted');
|
||||
}
|
||||
|
||||
if (!UserRights::IsAdministrator())
|
||||
{
|
||||
$oUser = UserRights::GetUserObject();
|
||||
$oAddon = UserRights::GetModuleInstance();
|
||||
if (!is_null($oUser) && method_exists($oAddon, 'GetUserOrgs'))
|
||||
{
|
||||
if ((empty($this->GetOriginal('contactid')) && !($this->IsNew())) || empty($this->Get('contactid')))
|
||||
{
|
||||
$this->m_aCheckIssues[] = Dict::S('Class:User/Error:PersonIsMandatory');
|
||||
// A contact is mandatory (an administrator can bypass it but not for himself)
|
||||
if ((!UserRights::IsAdministrator() || $this->IsCurrentUser())
|
||||
&& !$this->IsNew()
|
||||
&& isset($aChanges['contactid'])
|
||||
&& empty($this->Get('contactid'))) {
|
||||
$this->m_aCheckIssues[] = Dict::S('Class:User/Error:PersonIsMandatory');
|
||||
}
|
||||
|
||||
// Allowed orgs must contains the user org (if any)
|
||||
if (!empty($this->Get('org_id')) && !UserRights::IsAdministrator($this)) {
|
||||
// Get the user org and all its parent orgs
|
||||
$aUserOrgs = [$this->Get('org_id')];
|
||||
$sHierarchicalKeyCode = MetaModel::IsHierarchicalClass('Organization');
|
||||
if ($sHierarchicalKeyCode !== false) {
|
||||
$sOrgQuery = 'SELECT Org FROM Organization AS Org JOIN Organization AS Root ON Org.'.$sHierarchicalKeyCode.' ABOVE Root.id WHERE Root.id = :id';
|
||||
$oOrgSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData($sOrgQuery), [], ['id' => $this->Get('org_id')]);
|
||||
while ($aRow = $oOrgSet->FetchAssoc()) {
|
||||
$oOrg = $aRow['Org'];
|
||||
$aUserOrgs[] = $oOrg->GetKey();
|
||||
}
|
||||
else
|
||||
{
|
||||
$aOrgs = $oAddon->GetUserOrgs($oUser, '');
|
||||
if (count($aOrgs) > 0)
|
||||
{
|
||||
// Check that the modified User belongs to one of our organization
|
||||
if (!in_array($this->GetOriginal('org_id'), $aOrgs) && !in_array($this->Get('org_id'), $aOrgs))
|
||||
{
|
||||
$this->m_aCheckIssues[] = Dict::S('Class:User/Error:UserOrganizationNotAllowed');
|
||||
}
|
||||
// Check users with restricted organizations when allowed organizations have changed
|
||||
if ($this->IsNew() || array_key_exists('allowed_org_list', $aChanges))
|
||||
{
|
||||
$oSet = $this->get('allowed_org_list');
|
||||
if ($oSet->Count() == 0)
|
||||
{
|
||||
$this->m_aCheckIssues[] = Dict::S('Class:User/Error:AtLeastOneOrganizationIsNeeded');
|
||||
}
|
||||
else
|
||||
{
|
||||
$aModifiedLinks = $oSet->ListModifiedLinks();
|
||||
foreach ($aModifiedLinks as $oLink)
|
||||
{
|
||||
if (!in_array($oLink->Get('allowed_org_id'), $aOrgs))
|
||||
{
|
||||
$this->m_aCheckIssues[] = Dict::S('Class:User/Error:OrganizationNotAllowed');
|
||||
}
|
||||
}
|
||||
// Check the allowed orgs list
|
||||
$oSet = $this->get('allowed_org_list');
|
||||
if ($oSet->Count() > 0) {
|
||||
$bFound = false;
|
||||
while ($oOrg = $oSet->Fetch()) {
|
||||
if (in_array($oOrg->Get('allowed_org_id'), $aUserOrgs)) {
|
||||
$bFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$bFound) {
|
||||
$this->m_aCheckIssues[] = Dict::S('Class:User/Error:AllowedOrgsMustContainUserOrg');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!UserRights::IsAdministrator()) {
|
||||
$oUser = UserRights::GetUserObject();
|
||||
if (!is_null($oUser) && method_exists($oAddon, 'GetUserOrgs')) {
|
||||
$aOrgs = $oAddon->GetUserOrgs($oUser, ''); // Modifier allowed orgs
|
||||
if (count($aOrgs) > 0) {
|
||||
// Check that the modified User belongs to one of our organization
|
||||
if (!in_array($this->GetOriginal('org_id'), $aOrgs) && !in_array($this->Get('org_id'), $aOrgs)) {
|
||||
$this->m_aCheckIssues[] = Dict::S('Class:User/Error:UserOrganizationNotAllowed');
|
||||
}
|
||||
// Check users with restricted organizations when allowed organizations have changed
|
||||
if ($this->IsNew() || array_key_exists('allowed_org_list', $aChanges)) {
|
||||
$oSet = $this->get('allowed_org_list');
|
||||
if ($oSet->Count() == 0) {
|
||||
$this->m_aCheckIssues[] = Dict::S('Class:User/Error:AtLeastOneOrganizationIsNeeded');
|
||||
} else {
|
||||
$aModifiedLinks = $oSet->ListModifiedLinks();
|
||||
foreach ($aModifiedLinks as $oLink) {
|
||||
if (!in_array($oLink->Get('allowed_org_id'), $aOrgs)) {
|
||||
$this->m_aCheckIssues[] = Dict::S('Class:User/Error:OrganizationNotAllowed');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -413,14 +481,26 @@ abstract class User extends cmdbAbstractObject
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function DoCheckToDelete(&$oDeletionPlan)
|
||||
{
|
||||
parent::DoCheckToDelete($oDeletionPlan);
|
||||
|
||||
// A user cannot suppress himself
|
||||
if ($this->IsCurrentUser()) {
|
||||
$this->m_bSecurityIssue = true;
|
||||
$this->m_aDeleteIssues[] = Dict::S('UI:Delete:NotAllowedToDelete');
|
||||
}
|
||||
}
|
||||
|
||||
function GetGrantAsHtml($sClass, $iAction)
|
||||
{
|
||||
if (UserRights::IsActionAllowed($sClass, $iAction, null, $this))
|
||||
{
|
||||
if (UserRights::IsActionAllowed($sClass, $iAction, null, $this)) {
|
||||
return '<span style="background-color: #ddffdd;">'.Dict::S('UI:UserManagement:ActionAllowed:Yes').'</span>';
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
return '<span style="background-color: #ffdddd;">'.Dict::S('UI:UserManagement:ActionAllowed:No').'</span>';
|
||||
}
|
||||
}
|
||||
@@ -528,6 +608,19 @@ abstract class User extends cmdbAbstractObject
|
||||
}
|
||||
parent::DBDeleteSingleObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws \OQLException
|
||||
* @since 3.0.0
|
||||
*/
|
||||
protected function IsCurrentUser(): bool
|
||||
{
|
||||
if (is_null(UserRights::GetUserId())) {
|
||||
return false;
|
||||
}
|
||||
return UserRights::GetUserId() == $this->GetKey();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -757,10 +850,10 @@ class UserRights
|
||||
}
|
||||
self::$m_oUser = $oUser;
|
||||
|
||||
if (isset($_SESSION['impersonate_user']))
|
||||
if (Session::IsSet('impersonate_user'))
|
||||
{
|
||||
self::$m_oRealUser = self::$m_oUser;
|
||||
self::$m_oUser = self::FindUser($_SESSION['impersonate_user']);
|
||||
self::$m_oUser = self::FindUser(Session::Get('impersonate_user'));
|
||||
}
|
||||
|
||||
Dict::SetUserLanguage(self::GetUserLanguage());
|
||||
@@ -864,15 +957,15 @@ class UserRights
|
||||
{
|
||||
$bRet = false;
|
||||
}
|
||||
elseif (isset($_SESSION['archive_allowed']))
|
||||
elseif (Session::IsSet('archive_allowed'))
|
||||
{
|
||||
$bRet = $_SESSION['archive_allowed'];
|
||||
$bRet = Session::Get('archive_allowed');
|
||||
}
|
||||
else
|
||||
{
|
||||
// As of now, anybody can switch to the archive mode as soon as there is an archivable class
|
||||
$bRet = (count(MetaModel::EnumArchivableClasses()) > 0);
|
||||
$_SESSION['archive_allowed'] = $bRet;
|
||||
Session::Set('archive_allowed', $bRet);
|
||||
}
|
||||
return $bRet;
|
||||
}
|
||||
@@ -958,7 +1051,7 @@ class UserRights
|
||||
// Do impersonate!
|
||||
self::$m_oUser = $oUser;
|
||||
Dict::SetUserLanguage(self::GetUserLanguage());
|
||||
$_SESSION['impersonate_user'] = $sLogin;
|
||||
Session::Set('impersonate_user', $sLogin);
|
||||
self::_ResetSessionCache();
|
||||
}
|
||||
}
|
||||
@@ -974,7 +1067,7 @@ class UserRights
|
||||
{
|
||||
self::$m_oUser = self::$m_oRealUser;
|
||||
Dict::SetUserLanguage(self::GetUserLanguage());
|
||||
unset($_SESSION['impersonate_user']);
|
||||
Session::Unset('impersonate_user');
|
||||
self::_ResetSessionCache();
|
||||
}
|
||||
}
|
||||
@@ -1011,6 +1104,30 @@ class UserRights
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Person $oPerson Person we try to match against Users contact (also Person objects)
|
||||
* @param bool $bMustBeUnique If true, return null when 2+ Users matching this Person were found. Otherwise return the first one
|
||||
*
|
||||
* @return \DBObject|null
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static function GetUserFromPerson(Person $oPerson, bool $bMustBeUnique = true): ?DBObject
|
||||
{
|
||||
$sUserSearch = 'SELECT User WHERE contactid = :id';
|
||||
$oUserSearch = DBObjectSearch::FromOQL($sUserSearch);
|
||||
$oUserSearch->AllowAllData();
|
||||
$oUserSet = new DBObjectSet($oUserSearch, array(), array('id' => $oPerson->GetKey()));
|
||||
if($oUserSet->Count() > 0 && !($oUserSet->Count() > 1 && $bMustBeUnique)){
|
||||
return $oUserSet->Fetch();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
@@ -1086,17 +1203,26 @@ class UserRights
|
||||
// Then check if the user has a contact attached and if it has an picture defined
|
||||
$sContactId = UserRights::GetContactId($sLogin);
|
||||
if (!empty($sContactId)) {
|
||||
$oContact = MetaModel::GetObject('Contact', $sContactId, false, true);
|
||||
$oContact = null;
|
||||
// Picture if generally for Person, so try it first
|
||||
if (MetaModel::IsValidClass('Person')) {
|
||||
$oContact = MetaModel::GetObject('Person', $sContactId, false, true);
|
||||
}
|
||||
if (is_null($oContact)) {
|
||||
$oContact = MetaModel::GetObject('Contact', $sContactId, false, true);
|
||||
}
|
||||
$sContactClass = get_class($oContact);
|
||||
|
||||
// Check that Contact object still exists and that Contact class has a picture attribute
|
||||
if (!is_null($oContact) && MetaModel::IsValidAttCode($sContactClass, static::DEFAULT_CONTACT_PICTURE_ATTCODE)) {
|
||||
// - Try to get the semantic image attribute, or try to fallback on the default one if none defined
|
||||
$sContactPictureAttCode = MetaModel::HasImageAttributeCode($sContactClass) ? MetaModel::GetImageAttributeCode($sContactClass) : static::DEFAULT_CONTACT_PICTURE_ATTCODE;
|
||||
if (!is_null($oContact) && MetaModel::IsValidAttCode($sContactClass, $sContactPictureAttCode)) {
|
||||
/** @var \ormDocument $oPicture */
|
||||
$oPicture = $oContact->Get(static::DEFAULT_CONTACT_PICTURE_ATTCODE);
|
||||
$oPicture = $oContact->Get($sContactPictureAttCode);
|
||||
if ($oPicture->IsEmpty()) {
|
||||
if ($bAllowDefaultPicture === true) {
|
||||
/** @var \AttributeImage $oAttDef */
|
||||
$oAttDef = MetaModel::GetAttributeDef($sContactClass, static::DEFAULT_CONTACT_PICTURE_ATTCODE);
|
||||
$oAttDef = MetaModel::GetAttributeDef($sContactClass, $sContactPictureAttCode);
|
||||
$sPictureUrl = $oAttDef->Get('default_image');
|
||||
} else {
|
||||
$sPictureUrl = null;
|
||||
@@ -1104,19 +1230,21 @@ class UserRights
|
||||
} else {
|
||||
if (ContextTag::Check(ContextTag::TAG_PORTAL)) {
|
||||
$sSignature = $oPicture->GetSignature();
|
||||
$sPictureUrl = utils::GetAbsoluteUrlAppRoot().'pages/exec.php/object/document/display/'.$sContactClass.'/'.$oContact->GetKey().'/'.static::DEFAULT_CONTACT_PICTURE_ATTCODE.'?cache=86400&s='.$sSignature.'&exec_module=itop-portal-base&exec_page=index.php&portal_id='.PORTAL_ID;
|
||||
$sPictureUrl = utils::GetAbsoluteUrlAppRoot().'pages/exec.php/object/document/display/'.$sContactClass.'/'.$oContact->GetKey().'/'.$sContactPictureAttCode.'?cache=86400&s='.$sSignature.'&exec_module=itop-portal-base&exec_page=index.php&portal_id='.PORTAL_ID;
|
||||
} else {
|
||||
$sPictureUrl = $oPicture->GetDisplayURL($sContactClass, $oContact->GetKey(), static::DEFAULT_CONTACT_PICTURE_ATTCODE);
|
||||
$sPictureUrl = $oPicture->GetDisplayURL($sContactClass, $oContact->GetKey(), $sContactPictureAttCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // If no contact, check if user has a placeholder in they preferences
|
||||
else {
|
||||
}
|
||||
// If no contact & empty login, check if current user has a placeholder in they preferences
|
||||
elseif ('' === $sLogin) {
|
||||
$sPlaceholderPictureFilename = appUserPreferences::GetPref($sUserPicturePlaceholderPrefKey, null, static::GetUserId($sLogin));
|
||||
if (!empty($sPlaceholderPictureFilename)) {
|
||||
$sPictureUrl = utils::GetAbsoluteUrlAppRoot().$sUserPicturesFolder.$sPlaceholderPictureFilename;
|
||||
}
|
||||
}
|
||||
// Else, no contact and no login, then it's for an unknown origin (system, extension, ...)
|
||||
|
||||
// Update cache
|
||||
static::$m_aCacheContactPictureAbsUrl[$sLogin] = $sPictureUrl;
|
||||
@@ -1646,9 +1774,9 @@ class UserRights
|
||||
elseif ((self::$m_oUser !== null) && ($oUser->GetKey() == self::$m_oUser->GetKey()))
|
||||
{
|
||||
// Data about the current user can be found into the session data
|
||||
if (array_key_exists('profile_list', $_SESSION))
|
||||
if (Session::IsSet('profile_list'))
|
||||
{
|
||||
$aProfiles = $_SESSION['profile_list'];
|
||||
$aProfiles = Session::Get('profile_list');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1683,11 +1811,6 @@ class UserRights
|
||||
self::$m_aAdmins = array();
|
||||
self::$m_aPortalUsers = array();
|
||||
}
|
||||
if (!isset($_SESSION) && !utils::IsModeCLI())
|
||||
{
|
||||
session_name('itop-'.md5(APPROOT));
|
||||
session_start();
|
||||
}
|
||||
self::_ResetSessionCache();
|
||||
if (self::$m_oAddOn)
|
||||
{
|
||||
@@ -1722,7 +1845,7 @@ class UserRights
|
||||
self::$m_aCacheUsers = array('internal' => array(), 'external' => array());
|
||||
}
|
||||
|
||||
if (!isset(self::$m_aCacheUsers[$sAuthentication][$sLogin]))
|
||||
if (!array_key_exists($sLogin, self::$m_aCacheUsers[$sAuthentication]))
|
||||
{
|
||||
switch($sAuthentication)
|
||||
{
|
||||
@@ -1772,10 +1895,7 @@ class UserRights
|
||||
public static function _InitSessionCache()
|
||||
{
|
||||
// Cache data about the current user into the session
|
||||
if (isset($_SESSION))
|
||||
{
|
||||
$_SESSION['profile_list'] = self::ListProfiles();
|
||||
}
|
||||
Session::Set('profile_list', self::ListProfiles());
|
||||
|
||||
$oConfig = MetaModel::GetConfig();
|
||||
$bSessionIdRegeneration = $oConfig->Get('regenerate_session_id_enabled');
|
||||
@@ -1797,14 +1917,8 @@ class UserRights
|
||||
|
||||
public static function _ResetSessionCache()
|
||||
{
|
||||
if (isset($_SESSION['profile_list']))
|
||||
{
|
||||
unset($_SESSION['profile_list']);
|
||||
}
|
||||
if (isset($_SESSION['archive_allowed']))
|
||||
{
|
||||
unset($_SESSION['archive_allowed']);
|
||||
}
|
||||
Session::Unset('profile_list');
|
||||
Session::Unset('archive_allowed');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user