mirror of
https://github.com/Combodo/iTop.git
synced 2026-03-22 01:14:11 +01:00
Compare commits
613 Commits
support/3.
...
feature/88
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b80aa236f2 | ||
|
|
6226ac8746 | ||
|
|
e661e0bdbb | ||
|
|
1d52665012 | ||
|
|
4aa0c19183 | ||
|
|
809d4cd665 | ||
|
|
0c3bc7f35b | ||
|
|
778c16da86 | ||
|
|
981d5c6263 | ||
|
|
4ed21dc21a | ||
|
|
1f4a2f0f56 | ||
|
|
bf7a756714 | ||
|
|
428d2c6356 | ||
|
|
8982f7e0e3 | ||
|
|
bb8a09d8e2 | ||
|
|
901f8f2a7b | ||
|
|
ba6fff801b | ||
|
|
50098bad73 | ||
|
|
65c9145b10 | ||
|
|
437296eab9 | ||
|
|
7cf6f65265 | ||
|
|
491f55c7bd | ||
|
|
7b44ec23a1 | ||
|
|
f00b8d529c | ||
|
|
af44ffd0ad | ||
|
|
c63e01e27b | ||
|
|
1438006861 | ||
|
|
700470dd29 | ||
|
|
497fc4978a | ||
|
|
6826ab509d | ||
|
|
41194ce9ab | ||
|
|
0e5bece5b6 | ||
|
|
e987ea72bc | ||
|
|
c8371f1c19 | ||
|
|
740f83f43e | ||
|
|
14791bf6b4 | ||
|
|
062d543b26 | ||
|
|
8cece0f0fd | ||
|
|
18871566d2 | ||
|
|
b565b700a9 | ||
|
|
3846087dc1 | ||
|
|
4f96246cbe | ||
|
|
ddd3dcdaf2 | ||
|
|
82ad9d4317 | ||
|
|
6dc48ad36d | ||
|
|
84476a869f | ||
|
|
684f829581 | ||
|
|
d1e6334629 | ||
|
|
047ba8c6c7 | ||
|
|
f609010d7f | ||
|
|
9330b5ec9a | ||
|
|
18b85d4080 | ||
|
|
7214e5b43a | ||
|
|
3689a83953 | ||
|
|
a715ce1292 | ||
|
|
17d85fbb3f | ||
|
|
5e59aff74f | ||
|
|
9e1e81ccc1 | ||
|
|
c57c8d2af3 | ||
|
|
8a436f9760 | ||
|
|
2e77713772 | ||
|
|
975c554e91 | ||
|
|
ac6f642052 | ||
|
|
cf9ab2a83c | ||
|
|
3ff3dcba54 | ||
|
|
657fc912bf | ||
|
|
5ae5221f6f | ||
|
|
b15ca2fbc9 | ||
|
|
cb382eab4e | ||
|
|
9723cde24c | ||
|
|
7ae49e2cf4 | ||
|
|
cb13a7a5b4 | ||
|
|
9618e47045 | ||
|
|
d84506ea9e | ||
|
|
13239c2751 | ||
|
|
8b30e36dd1 | ||
|
|
80b290ab88 | ||
|
|
81b20ee583 | ||
|
|
a5545b0084 | ||
|
|
d72e861dfe | ||
|
|
92385273ff | ||
|
|
61c25f85e7 | ||
|
|
b1cf2ec137 | ||
|
|
7549ded51d | ||
|
|
38683c20b1 | ||
|
|
81791dd253 | ||
|
|
e77e0eec9f | ||
|
|
f5ddbbbe0e | ||
|
|
6811a82e1a | ||
|
|
960133c0df | ||
|
|
544c4ae888 | ||
|
|
9ee18c2f36 | ||
|
|
43a10e6944 | ||
|
|
bf8269fee1 | ||
|
|
c99995563e | ||
|
|
7af1cdebfa | ||
|
|
a9fb5cd182 | ||
|
|
9010504902 | ||
|
|
2625b1ab54 | ||
|
|
b967338c48 | ||
|
|
10fbea5f4d | ||
|
|
e49b8fc480 | ||
|
|
4a415fdb59 | ||
|
|
5232694c04 | ||
|
|
2a87d0aa15 | ||
|
|
a103c458cb | ||
|
|
9a895a7fbd | ||
|
|
f5011bb200 | ||
|
|
29c75f626b | ||
|
|
0562563cbb | ||
|
|
40068bd913 | ||
|
|
874a5fd2ce | ||
|
|
1ec139782e | ||
|
|
056dce4d78 | ||
|
|
063bb9680e | ||
|
|
9b1395db03 | ||
|
|
8fd9eb6a84 | ||
|
|
1142bf327c | ||
|
|
278496eaf6 | ||
|
|
77ba0b398f | ||
|
|
04ca7bf603 | ||
|
|
8f8ac46f55 | ||
|
|
07b904ee1b | ||
|
|
5aee7f4722 | ||
|
|
292701b71c | ||
|
|
533b57ab99 | ||
|
|
be8d348b25 | ||
|
|
6c7a98fe3d | ||
|
|
ec2203229b | ||
|
|
2f699d355a | ||
|
|
da4457f5b4 | ||
|
|
97848cea4f | ||
|
|
2ccd883c9a | ||
|
|
eabd68ff77 | ||
|
|
2b493787b1 | ||
|
|
d74c850621 | ||
|
|
4e575e17a3 | ||
|
|
94d6eca0c1 | ||
|
|
355da8ec0a | ||
|
|
5f006c45db | ||
|
|
a9bc4973b4 | ||
|
|
e93d909a38 | ||
|
|
14346e0895 | ||
|
|
6985e366c2 | ||
|
|
cf57549370 | ||
|
|
46aaeb4301 | ||
|
|
7ddc593869 | ||
|
|
affed69999 | ||
|
|
695f357054 | ||
|
|
0088580798 | ||
|
|
f6298370a7 | ||
|
|
bd6ccc55f8 | ||
|
|
3a497524dc | ||
|
|
4c1da328ce | ||
|
|
64a216e0f6 | ||
|
|
d5754fc568 | ||
|
|
5851b2e1ff | ||
|
|
803ebfbe33 | ||
|
|
d12c8a183c | ||
|
|
7bbd4b4726 | ||
|
|
c8754725b0 | ||
|
|
a3c911e93e | ||
|
|
38e0fb27f1 | ||
|
|
4b4197d910 | ||
|
|
c5189b7d33 | ||
|
|
b0288bc08e | ||
|
|
52097f5e12 | ||
|
|
ccb1ca9d79 | ||
|
|
44290db312 | ||
|
|
65e49e2139 | ||
|
|
b1bf89807d | ||
|
|
39c59f46fd | ||
|
|
025af923ea | ||
|
|
c2aecad9d2 | ||
|
|
986e900a99 | ||
|
|
858b12abaa | ||
|
|
bd93879ac4 | ||
|
|
133a4a8533 | ||
|
|
1163a20cdd | ||
|
|
a7bc4bd411 | ||
|
|
6f3fddf9bf | ||
|
|
470f145747 | ||
|
|
2852d8ce49 | ||
|
|
7e9a95fe93 | ||
|
|
a07f66c061 | ||
|
|
c49ceae75e | ||
|
|
80244e2797 | ||
|
|
4da975cb64 | ||
|
|
8980f627e9 | ||
|
|
5ce1788d4a | ||
|
|
38fe31c9fc | ||
|
|
ec61b52238 | ||
|
|
072596a53b | ||
|
|
160bfd714b | ||
|
|
3b51124f1c | ||
|
|
19fa836758 | ||
|
|
1c5cb1547f | ||
|
|
8d58372074 | ||
|
|
e98c6637ac | ||
|
|
7d2a9d0bfc | ||
|
|
762d1162ca | ||
|
|
95dbe4c859 | ||
|
|
ec6adee9c1 | ||
|
|
baa8bba926 | ||
|
|
8dc5411717 | ||
|
|
f1d448fd78 | ||
|
|
a4490e2b5f | ||
|
|
efb7831a5a | ||
|
|
9fadbb5eb1 | ||
|
|
93dba0644d | ||
|
|
ec324bb28e | ||
|
|
dc8f521b12 | ||
|
|
bf6277fcd2 | ||
|
|
886db5d6ad | ||
|
|
0e8ddf990c | ||
|
|
346a8eadec | ||
|
|
6948c594c2 | ||
|
|
301a7a92a0 | ||
|
|
73bb80ebea | ||
|
|
1e674d7bdc | ||
|
|
1225ee1e78 | ||
|
|
a91de9fb36 | ||
|
|
71b3a415a6 | ||
|
|
b97c7433c8 | ||
|
|
11fc958a7b | ||
|
|
a151e40b75 | ||
|
|
5780f26817 | ||
|
|
9a690861a3 | ||
|
|
343f3286b8 | ||
|
|
37fc1a5723 | ||
|
|
7a09b3effc | ||
|
|
4f6d514694 | ||
|
|
8f8b65a71d | ||
|
|
49e72e83fe | ||
|
|
f0685e33e1 | ||
|
|
42f391472b | ||
|
|
30ef2735b6 | ||
|
|
bbff0b72d3 | ||
|
|
2dffab9ca0 | ||
|
|
1cbfa4f1b1 | ||
|
|
5f85757630 | ||
|
|
56d3ac668c | ||
|
|
0a3b02bf45 | ||
|
|
74ebbc5fa4 | ||
|
|
a762b6a2bb | ||
|
|
32ef639ce1 | ||
|
|
8647a76dbf | ||
|
|
47cd8bce31 | ||
|
|
86c677b2ca | ||
|
|
960129316d | ||
|
|
1fa50f695d | ||
|
|
692cf4f635 | ||
|
|
95aa444ee6 | ||
|
|
f5de808c7c | ||
|
|
fc388313d7 | ||
|
|
d544ee5498 | ||
|
|
ce187550f6 | ||
|
|
5d15a08824 | ||
|
|
8135316119 | ||
|
|
868c0ae836 | ||
|
|
d03d4fce5f | ||
|
|
aa55c2b30f | ||
|
|
9d3b46b919 | ||
|
|
01b4dbba71 | ||
|
|
72ac4096c1 | ||
|
|
19559b08a7 | ||
|
|
346564ca0e | ||
|
|
1bf53bae2a | ||
|
|
29ce042916 | ||
|
|
83539d6d4c | ||
|
|
e6a7b926f6 | ||
|
|
b30e053236 | ||
|
|
afd96a0f49 | ||
|
|
a77765ec7b | ||
|
|
64b4b03ea9 | ||
|
|
a797878b17 | ||
|
|
1fa0f7bdd9 | ||
|
|
f718b4173d | ||
|
|
e057c0f081 | ||
|
|
5a49fc7654 | ||
|
|
6fca659c9d | ||
|
|
eacd08f31e | ||
|
|
5f7d8f6cc0 | ||
|
|
cbb4281a37 | ||
|
|
d7a8d335d5 | ||
|
|
bd1d447677 | ||
|
|
bb405d5173 | ||
|
|
4723fc885c | ||
|
|
06dcae1dd1 | ||
|
|
e03033ce52 | ||
|
|
19eae916f0 | ||
|
|
596e26a96f | ||
|
|
ef42a49009 | ||
|
|
51e5f1e7de | ||
|
|
9cffd17e19 | ||
|
|
e9f16935b6 | ||
|
|
e7488b2c89 | ||
|
|
8ac4086e71 | ||
|
|
6b5273fa1c | ||
|
|
9b1e854bf7 | ||
|
|
926700856d | ||
|
|
ab93d59a77 | ||
|
|
c70d62a51e | ||
|
|
82bc2f33f2 | ||
|
|
0d5ff261fe | ||
|
|
374b35f78a | ||
|
|
f5c29edee4 | ||
|
|
8fb450a6d4 | ||
|
|
311c5d0d51 | ||
|
|
f90bd81e15 | ||
|
|
a10e547420 | ||
|
|
2519456c98 | ||
|
|
9371bc6d7b | ||
|
|
58e964fb8c | ||
|
|
57760528c9 | ||
|
|
561baf5771 | ||
|
|
f281fa7b14 | ||
|
|
6ae8a72a35 | ||
|
|
694711b3f4 | ||
|
|
01d3f707e4 | ||
|
|
4e28856015 | ||
|
|
8bb456df53 | ||
|
|
4886e2a7dc | ||
|
|
674dfebb0d | ||
|
|
e8ecc85828 | ||
|
|
04bd8cc5ce | ||
|
|
764eddd9f8 | ||
|
|
015ff8f179 | ||
|
|
9b651c2451 | ||
|
|
bb16f4ad57 | ||
|
|
7b3023ccce | ||
|
|
e6a3a95ff9 | ||
|
|
80e413c370 | ||
|
|
69fd9048fd | ||
|
|
7254bb7a2f | ||
|
|
7a4d29d561 | ||
|
|
c54909f2a3 | ||
|
|
23af649d7f | ||
|
|
88756a443a | ||
|
|
4a78f8fb43 | ||
|
|
6c8388ea5e | ||
|
|
75520bfaf9 | ||
|
|
3a4d9e64f8 | ||
|
|
209e30f7ef | ||
|
|
052e2a1a42 | ||
|
|
3aadccc2e8 | ||
|
|
5ae2fdee94 | ||
|
|
380512dcbe | ||
|
|
8e2a68887c | ||
|
|
eb1ecff7d8 | ||
|
|
8141723869 | ||
|
|
8cb701bda3 | ||
|
|
a8f3c3054b | ||
|
|
c5fb4227bf | ||
|
|
1b29746806 | ||
|
|
f98ad6ccab | ||
|
|
fb9c317256 | ||
|
|
0f1f2875ff | ||
|
|
a88b4ad06b | ||
|
|
172fda2157 | ||
|
|
4f0c45004a | ||
|
|
60de41e6fd | ||
|
|
cbbb4cdb8c | ||
|
|
8c5b919e72 | ||
|
|
47c05b75d0 | ||
|
|
2147ae9105 | ||
|
|
4a99afae3b | ||
|
|
119dcf9c97 | ||
|
|
b7d14ca48e | ||
|
|
37cd12fb21 | ||
|
|
ffb61503dc | ||
|
|
fbda0e38e1 | ||
|
|
bfd8fb1c23 | ||
|
|
c6760371ba | ||
|
|
8c704951e1 | ||
|
|
018f7808db | ||
|
|
24c23628d6 | ||
|
|
1d3c71fd10 | ||
|
|
e8d059fa77 | ||
|
|
3b4a9dc368 | ||
|
|
244ae33ad6 | ||
|
|
c1eb605195 | ||
|
|
0ee1818f12 | ||
|
|
31758cbe2b | ||
|
|
a4a1fa4ec9 | ||
|
|
c32e186133 | ||
|
|
3d325caa06 | ||
|
|
22141aba7b | ||
|
|
9b066b3b88 | ||
|
|
7ea7f5a967 | ||
|
|
38041b25fb | ||
|
|
3adeea8172 | ||
|
|
10c3641307 | ||
|
|
af9b03cfa5 | ||
|
|
806a2f0597 | ||
|
|
0cb9f7ab4f | ||
|
|
dc6c894cb8 | ||
|
|
3fa9b68882 | ||
|
|
0514f889d3 | ||
|
|
890841a818 | ||
|
|
aed71a6335 | ||
|
|
c910d35e25 | ||
|
|
0586a77043 | ||
|
|
b4b032693a | ||
|
|
054fd8798b | ||
|
|
35a7d907cd | ||
|
|
4c66cfe6e3 | ||
|
|
4360ad6502 | ||
|
|
841a2474af | ||
|
|
33b6f2cbc4 | ||
|
|
4680b6a497 | ||
|
|
c5727d366f | ||
|
|
a749a4654e | ||
|
|
3c8a71f39f | ||
|
|
ed7411294e | ||
|
|
aaecce7574 | ||
|
|
480e2a0fb5 | ||
|
|
a3a0f33ad4 | ||
|
|
089da40941 | ||
|
|
ce0d17e042 | ||
|
|
aba737ab48 | ||
|
|
a7c5d9e92a | ||
|
|
b2a377d7f1 | ||
|
|
1d65234073 | ||
|
|
38065791da | ||
|
|
086d2f0ba4 | ||
|
|
377e06d99a | ||
|
|
37cf1b2901 | ||
|
|
d9f280dc56 | ||
|
|
b207f9ea02 | ||
|
|
2690f8171e | ||
|
|
7136ac38d6 | ||
|
|
d4b5e98514 | ||
|
|
0ede0719c5 | ||
|
|
06124a0d94 | ||
|
|
d19d88e5dc | ||
|
|
b2d8b0bfe4 | ||
|
|
095ac3dfa5 | ||
|
|
a86edc69ac | ||
|
|
21bc4cdc9b | ||
|
|
b682ed7ad9 | ||
|
|
5e823d1f16 | ||
|
|
d32949d1d3 | ||
|
|
06c26c99bb | ||
|
|
22e599598a | ||
|
|
d666050e0c | ||
|
|
8018c586cd | ||
|
|
e73911c458 | ||
|
|
7e79b27823 | ||
|
|
5fa9ef05a6 | ||
|
|
f239b658e6 | ||
|
|
141c22ff67 | ||
|
|
23651ae510 | ||
|
|
45a1efe2ae | ||
|
|
06c135aab1 | ||
|
|
2203694b3f | ||
|
|
f7ab9646d0 | ||
|
|
a4959cad15 | ||
|
|
1c0a7e9bd9 | ||
|
|
cc46761e34 | ||
|
|
e1c3e7a3a6 | ||
|
|
baf85e7a80 | ||
|
|
147aad9221 | ||
|
|
41e0d39a5a | ||
|
|
91496b08bd | ||
|
|
b3d20effd6 | ||
|
|
2e0b9bd6b4 | ||
|
|
e497304b9d | ||
|
|
4b3e9432df | ||
|
|
1d67d50a20 | ||
|
|
f0c37bd69e | ||
|
|
21513f8df0 | ||
|
|
eb73b6b2f5 | ||
|
|
504378261b | ||
|
|
a5ceddc40a | ||
|
|
0fb23c4e5b | ||
|
|
40c7559905 | ||
|
|
edda622060 | ||
|
|
f1694707ee | ||
|
|
97ec9666ed | ||
|
|
7b91e9e164 | ||
|
|
ff0f1d8ce9 | ||
|
|
1e3c425e81 | ||
|
|
b2b5aeb08b | ||
|
|
01ab3be8d3 | ||
|
|
0d7ca88cc2 | ||
|
|
4fee774fe5 | ||
|
|
0d9f348e93 | ||
|
|
1ea0018a50 | ||
|
|
3bec8fe7d2 | ||
|
|
b2e3fb6354 | ||
|
|
7cedc501f7 | ||
|
|
296c5bd3d2 | ||
|
|
a2bddea1e2 | ||
|
|
53cd9f8b5f | ||
|
|
b344826c44 | ||
|
|
a0dc68ab9a | ||
|
|
22f2b5d0e5 | ||
|
|
c411900efd | ||
|
|
592aa93c40 | ||
|
|
6c02dd75fa | ||
|
|
39853d4949 | ||
|
|
2404f5d00f | ||
|
|
f84746274f | ||
|
|
45ce68e16e | ||
|
|
1aef576403 | ||
|
|
c3582f0aff | ||
|
|
8c4c0ff5e1 | ||
|
|
7b6334d447 | ||
|
|
bfd9be8280 | ||
|
|
96e1388dde | ||
|
|
fd64be1dcd | ||
|
|
64974a2c54 | ||
|
|
e363692822 | ||
|
|
d5d93ed147 | ||
|
|
c3547f29d0 | ||
|
|
989ba7bbe7 | ||
|
|
ac90d8036f | ||
|
|
644da707d2 | ||
|
|
86b48b8980 | ||
|
|
69c8791fc5 | ||
|
|
8b35679fcf | ||
|
|
cb8af0a02b | ||
|
|
0b442a2e1a | ||
|
|
f98e92b255 | ||
|
|
eca1836bd8 | ||
|
|
17e8cad005 | ||
|
|
48e89dd27e | ||
|
|
e9ef48b79c | ||
|
|
ab9df22e76 | ||
|
|
84f2ecd80f | ||
|
|
98f946c871 | ||
|
|
452d97f2d0 | ||
|
|
3f0e46230f | ||
|
|
fad3259290 | ||
|
|
5fd8678a3a | ||
|
|
86df9ac035 | ||
|
|
d4b342a35d | ||
|
|
8c87f5a384 | ||
|
|
984f676d6e | ||
|
|
0f213b08a6 | ||
|
|
3f997b416f | ||
|
|
f88e231780 | ||
|
|
fff95fcebd | ||
|
|
de17664c49 | ||
|
|
2b1826d2e6 | ||
|
|
b8d8ec640d | ||
|
|
e2c01e9e5d | ||
|
|
a55efdff18 | ||
|
|
3c1af200f8 | ||
|
|
dc8f676f75 | ||
|
|
a160f2e212 | ||
|
|
c1ce4f9a9f | ||
|
|
cceb6809e7 | ||
|
|
48b559472e | ||
|
|
cddc452693 | ||
|
|
0904a21e3f | ||
|
|
e800946898 | ||
|
|
e189034436 | ||
|
|
4590013adc | ||
|
|
66230199f8 | ||
|
|
1f1a2b660f | ||
|
|
b8b35194c9 | ||
|
|
4f36369ea1 | ||
|
|
33a906f11a | ||
|
|
82d11eeb47 | ||
|
|
fdef394070 | ||
|
|
a3a5630e88 | ||
|
|
9f7fd6a3a1 | ||
|
|
112a6f9942 | ||
|
|
60110a4302 | ||
|
|
4426e2adfe | ||
|
|
0028b3cf17 | ||
|
|
4867810ebe | ||
|
|
6b2476d220 | ||
|
|
b8bf66031b | ||
|
|
2596a150bf | ||
|
|
142d6c8993 | ||
|
|
a3309365fc | ||
|
|
9dcdb1bc8c | ||
|
|
2275daf3b0 | ||
|
|
d145cb508a | ||
|
|
7028fd21bc | ||
|
|
51adfd0254 | ||
|
|
6e2ed188eb | ||
|
|
2a3516d593 | ||
|
|
e5a60beba0 | ||
|
|
2804076bf6 | ||
|
|
f63abd9b6f | ||
|
|
4ebdc9d6ed | ||
|
|
6e364a0ab9 | ||
|
|
3233b9776f | ||
|
|
905bbf9118 | ||
|
|
d70e5dff45 | ||
|
|
c4fc0ed982 | ||
|
|
320922a13d | ||
|
|
9514977ce2 | ||
|
|
62d08dce56 | ||
|
|
71e36d264e | ||
|
|
7dc1672b24 | ||
|
|
50e40879de | ||
|
|
6cc24e475a | ||
|
|
4232284dae | ||
|
|
6958fd0965 | ||
|
|
1f8726669b | ||
|
|
47d65931e6 | ||
|
|
bfbb046b10 | ||
|
|
8b256caa6c | ||
|
|
f96d28a0a8 | ||
|
|
d3b9965283 | ||
|
|
f03d731b1d | ||
|
|
291c513d3b | ||
|
|
8935de0809 |
@@ -1,99 +0,0 @@
|
||||
# Phpdoc dokuwiki template
|
||||
This directory contains a template for rendering iTop phpdoc as dokuwiki pages.
|
||||
|
||||
|
||||
Conventional tags that you should use:
|
||||
* `@internal` : exclude from the documentation.
|
||||
* `@api` : it means that a method is an api, thus it may be interacted with.
|
||||
* `@see` : it points to another documented method
|
||||
* `@link` : external url
|
||||
* if you point to another page of the wiki, please use relative links.
|
||||
* `@example` : let you provide example of code
|
||||
* `@param`, `@return`, `@throws`, ...
|
||||
|
||||
|
||||
## Special instructions
|
||||
|
||||
Some iTop specific tags were added :
|
||||
* `@api-advanced`: it means that a method is an `@api` but mark it also as "complex" to use
|
||||
* `@overwritable-hook`: used to mark a method as "designed to be extended"
|
||||
* `@extension-hook`: not used for now
|
||||
* `@phpdoc-tuning-exclude-inherited`: once this tag is present on a class, it's inherited methods won't be showed.
|
||||
|
||||
|
||||
### known limitations:
|
||||
#### `@see` tags must be very specific:
|
||||
* always prefix class members (attributes or methods) with `ClassName::` (do not use self)
|
||||
* for methods always suffix them with `()`,
|
||||
* do not reference variables since they are not documented. If you have to, always prefix them with `$`
|
||||
|
||||
examples:
|
||||
```
|
||||
/**
|
||||
* @see DBObject
|
||||
* @see DBObject::Get()
|
||||
* @see DBObject::$foo
|
||||
*/
|
||||
```
|
||||
|
||||
#### Do not use inline tags, they do not work properly, example:
|
||||
```
|
||||
/**
|
||||
* This is a texts with an inline tag {@see [FQSEN] [<description>]} it must never be used
|
||||
*/
|
||||
```
|
||||
|
||||
#### The `@example` tag must respect this very precise syntax
|
||||
* the sentence in the first line (next to the tag) is the title, it must be enclosed by double quotes
|
||||
* the following lines are the sample code.
|
||||
* 💔 since we simply hack the official tag, this syntax must be respected carefully 💔
|
||||
example:
|
||||
```
|
||||
/**
|
||||
* @example "This is the title of the multiline example"
|
||||
* $foo = DBObject::Get('foo');
|
||||
* DBObject::Set('foo', ++$foo);
|
||||
*/
|
||||
```
|
||||
|
||||
## How content is included into the documentation
|
||||
|
||||
**For a class** those requirements have to be respected:
|
||||
- the file containing the class must be listed in `/phpdoc/files/file[]` of `.doc/phpdoc-objects-manipulation.dist.xml`
|
||||
- the class **must not** have the tag `@internal`
|
||||
- the class **must** have at least one of: `@api`, `@api-advanced`, `@overwritable-hook`, `@extension-hook`
|
||||
|
||||
Then, **for a method** of an eligible class:
|
||||
- **public** methods **must** have at least one of: `@api`, `@api-advanced`, `@overwritable-hook`, `@extension-hook`
|
||||
- **protected** methods **must** have at least one of: `@overwritable-hook`, `@extension-hook`
|
||||
- **private** methods are **always excluded**
|
||||
|
||||
**Class properties** and **constants** are never documented (this is subject to change).
|
||||
|
||||
|
||||
|
||||
|
||||
## A note about the rendering engine
|
||||
|
||||
: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
|
||||
|
||||
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).
|
||||
* the generated files have to be placed under an arbitrary directory of `[/path/to/dokuwiki]/data/pages`.
|
||||
* the html has to be activated [config:htmlok](https://www.dokuwiki.org/config:htmlok)
|
||||
* the generated files have to be in lowercase
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/bin/sh -x
|
||||
|
||||
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
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/bin/sh -x
|
||||
|
||||
rm -rf /tmp/phpdoc-twig-cache/ && rm -rf ../data/phpdocumentor/output/objects-manipulation/ && rm -rf ../data/phpdocumentor/temp/objects-manipulation/ && ./vendor/bin/phpdoc -c ./phpdoc-objects-manipulation.dist.xml -vvv
|
||||
|
||||
|
||||
# now wee need to lowercase every generated file because dokuwiki can't handle uppercase
|
||||
cd ../data/phpdocumentor/output/objects-manipulation/ && for i in $( ls | grep [A-Z] ); do mv -i $i `echo $i | tr 'A-Z' 'a-z'`; done
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"require-dev": {
|
||||
"phpdocumentor/phpdocumentor": "~2",
|
||||
"jms/serializer": "1.7.*"
|
||||
}
|
||||
}
|
||||
3015
.doc/composer.lock
generated
3015
.doc/composer.lock
generated
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 983 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.4 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.0 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 4.8 MiB |
@@ -1,84 +0,0 @@
|
||||
# iTop version history
|
||||
|
||||
```mermaid
|
||||
%%{init: { 'logLevel': 'debug', 'theme': 'base', 'themeVariables': {
|
||||
'git0': 'lawngreen',
|
||||
'git3': 'dodgerblue',
|
||||
'git4': 'grey',
|
||||
'git5': 'grey',
|
||||
'git6': 'grey',
|
||||
'git7': 'grey'
|
||||
}, 'gitGraph': {'showBranches': true,'mainBranchName': 'develop','rotateCommitLabel': true}} }%%
|
||||
gitGraph
|
||||
commit id: "2016-07-06" tag: "2.3.0" type: HIGHLIGHT
|
||||
branch support/2.3 order: 900
|
||||
commit id: "2016-07-08" tag: "2.3.1"
|
||||
commit id: "2016-12-22" tag: "2.3.3"
|
||||
commit id: "2017-04-14" tag: "2.3.4"
|
||||
checkout develop
|
||||
commit id: "2017-07-12" tag: "2.4.0-beta" type: REVERSE
|
||||
commit id: "2017-11-16" tag: "2.4.0" type: HIGHLIGHT
|
||||
branch support/2.4 order: 890
|
||||
commit id: "2018-02-14" tag: "2.4.1"
|
||||
checkout develop
|
||||
commit id: "2018-04-25" tag: "2.5.0-beta" type: REVERSE
|
||||
checkout support/2.4
|
||||
commit id: "2018-06-14" tag: "2.4.2"
|
||||
checkout develop
|
||||
commit id: "2018-06-27" tag: "2.5.0" type: HIGHLIGHT
|
||||
branch support/2.5 order: 880
|
||||
checkout develop
|
||||
commit id: "2019-01-09" tag: "2.6.0" type: HIGHLIGHT
|
||||
branch support/2.6 order: 870
|
||||
commit id: "2019-03-28" tag: "2.6.1"
|
||||
checkout develop
|
||||
commit id: "2019-12-18" tag: "2.7.0-beta" type: REVERSE
|
||||
checkout support/2.5
|
||||
commit id: "2020-01-22" tag: "2.5.4"
|
||||
checkout support/2.6
|
||||
commit id: "2020-01-23" tag: "2.6.3"
|
||||
checkout develop
|
||||
commit id: "2020-01-29" tag: "2.7.0-beta2" type: REVERSE
|
||||
commit id: "2020-04-01" tag: "2.7.0-1" type: HIGHLIGHT
|
||||
checkout support/2.6
|
||||
commit id: "2020-04-22" tag: "2.6.4"
|
||||
checkout develop
|
||||
branch support/2.7 order: 860
|
||||
commit id: "2020-06-26" tag: "2.7.1"
|
||||
checkout support/2.7
|
||||
commit id: "2020-12-09" tag: "2.7.3"
|
||||
commit id: "2021-03-31" tag: "2.7.4"
|
||||
checkout develop
|
||||
commit id: "2021-04-06" tag: "3.0.0-beta" type: REVERSE
|
||||
checkout support/2.7
|
||||
commit id: "2021-07-05" tag: "2.7.5"
|
||||
checkout develop
|
||||
commit id: "2021-07-05." tag: "3.0.0-beta2" type: REVERSE
|
||||
checkout support/2.7
|
||||
commit id: "2021-12-17" tag: "2.7.6"
|
||||
checkout develop
|
||||
commit id: "2022-01-04" tag: "3.0.0" type: HIGHLIGHT
|
||||
branch support/3.0 order: 850
|
||||
commit id: "2022-04-08" tag: "3.0.1"
|
||||
checkout support/2.7
|
||||
commit id: "2022-07-11" tag: "2.7.7"
|
||||
checkout support/3.0
|
||||
commit id: "2022-09-12" tag: "3.0.2-1"
|
||||
checkout develop
|
||||
checkout support/2.7
|
||||
commit id: "2022-12-28" tag: "2.7.8"
|
||||
checkout support/3.0
|
||||
commit id: "2023-04-12" tag: "3.0.3"
|
||||
checkout develop
|
||||
commit id: "2023-06-19" tag: "3.1.0-beta" type: REVERSE
|
||||
commit id: "2023-07-26" tag: "3.1.0-1" type: HIGHLIGHT
|
||||
branch support/3.1 order: 840
|
||||
checkout support/3.1
|
||||
commit id: "2023-08-09" tag: "3.1.0-2"
|
||||
checkout support/2.7
|
||||
commit id: "2023-08-10" tag: "2.7.9"
|
||||
checkout support/3.1
|
||||
commit id: "2023-12-20" tag: "3.1.1"
|
||||
```
|
||||
|
||||
To learn more, check the [iTop community versions history on the official wiki](https://www.itophub.io/wiki/page?id=latest:release:start).
|
||||
@@ -1,20 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<phpdoc>
|
||||
<title><![CDATA[iTop extensions]]></title>
|
||||
|
||||
<parser>
|
||||
<target>../data/phpdocumentor/temp/extensions</target>
|
||||
</parser>
|
||||
|
||||
<transformer>
|
||||
<target>../data/phpdocumentor/output/extensions</target>
|
||||
</transformer>
|
||||
|
||||
<transformations>
|
||||
<template name="phpdoc-templates/combodo-wiki"/>
|
||||
</transformations>
|
||||
|
||||
<files>
|
||||
<file>../application/applicationextension.inc.php</file>
|
||||
</files>
|
||||
</phpdoc>
|
||||
@@ -1,58 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<phpdoc>
|
||||
|
||||
<!--
|
||||
/**
|
||||
The documentation of this file can be found here : https://docs.phpdoc.org/references/configuration.html
|
||||
it has to be completed by the CLI parameters documentation which is more comprehensive: https://docs.phpdoc.org/references/commands/project_run.html#usage
|
||||
|
||||
usage:
|
||||
vendor/bin/phpdoc -c phpdoc-objects-manipulation.dist.xml
|
||||
|
||||
*/
|
||||
-->
|
||||
|
||||
<title><![CDATA[iTop's objects manipulation API]]></title>
|
||||
|
||||
<parser>
|
||||
<default-package-name>iTopORM</default-package-name>
|
||||
<target>../data/phpdocumentor/temp/objects-manipulation</target>
|
||||
<visibility>public,protected</visibility>
|
||||
<markers>
|
||||
<!--<item>TODO</item>-->
|
||||
<!--<item>FIXME</item>-->
|
||||
</markers>
|
||||
<extensions>
|
||||
<extension>php</extension>
|
||||
</extensions>
|
||||
</parser>
|
||||
|
||||
<transformer>
|
||||
<target>../data/phpdocumentor/output/objects-manipulation</target>
|
||||
</transformer>
|
||||
|
||||
<transformations>
|
||||
<template name="phpdoc-templates/combodo-wiki"/>
|
||||
</transformations>
|
||||
|
||||
<!--<logging>-->
|
||||
<!--<level>warn</level>-->
|
||||
<!--<paths>-->
|
||||
<!--<!–<default>data/phpdocumentor/log/objects-manipulation/{DATE}.log</default>–>-->
|
||||
<!--<!–<errors>data/phpdocumentor/log/objects-manipulation/{DATE}.errors.log</errors>–>-->
|
||||
|
||||
<!--<default>{APP_ROOT}/data/log/{DATE}.log</default>-->
|
||||
<!--<errors>{APP_ROOT}/data/log/{DATE}.errors.log</errors>-->
|
||||
<!--</paths>-->
|
||||
<!--</logging>-->
|
||||
|
||||
<files>
|
||||
<file>../core/dbobject.class.php</file>
|
||||
<file>../core/dbobjectsearch.class.php</file>
|
||||
<file>../core/metamodel.class.php</file>
|
||||
<file>../core/dbobjectset.class.php</file>
|
||||
<file>../core/dbsearch.class.php</file>
|
||||
<file>../core/dbunionsearch.class.php</file>
|
||||
</files>
|
||||
|
||||
</phpdoc>
|
||||
@@ -1,136 +0,0 @@
|
||||
{% extends 'layout.txt.twig' %}
|
||||
|
||||
{% block content %}
|
||||
<wrap button>[[start|🔙 Back]]</wrap>
|
||||
|
||||
{% if node.tags['internal'] is defined %}
|
||||
====== {{ node.name }} ======
|
||||
<WRAP alert>This class is "internal", and thus is not documented!</WRAP>
|
||||
{% elseif node.tags['api'] is not defined and node.tags['api-advanced'] is not defined and node.tags['overwritable-hook'] is not defined and node.tags['extension-hook'] is not defined %}
|
||||
====== {{ node.name }} ======
|
||||
<WRAP alert>This class is neither "api", "api-advanced", "overwritable-hook" or "extension-hook", and thus is not documented!</WRAP>
|
||||
{% else %}
|
||||
|
||||
====== {{ node.name }} ======
|
||||
|
||||
{% if node.deprecated %}<wrap danger>deprecated</wrap>{% endif %}
|
||||
{% if node.abstract %}<wrap warning>abstract</wrap>{% endif %}
|
||||
{% if node.final %}<wrap notice>final</wrap>{% endif %}
|
||||
{% include 'includes/wrap-tags.txt.twig' with {structure:node, wrap: 'safety', wrapTags: ['api', 'api-advanced', 'overwritable-hook', 'extension-hook']} %}
|
||||
|
||||
|
||||
|
||||
{% if node.deprecated %}
|
||||
=== **<del>Deprecated</del>**===
|
||||
//{{ node.tags.deprecated[0].description }}//
|
||||
{% endif %}
|
||||
|
||||
|
||||
== {{ node.summary|replace({"\n":""})|raw }} ==
|
||||
<html>{{ node.description|markdown|raw }}</html>
|
||||
|
||||
|
||||
{% include 'includes/code-examples.txt.twig' with {structure:node, title_level: '====='} %}
|
||||
|
||||
|
||||
{% set class = node.parent %}
|
||||
{% block hierarchy_element %}
|
||||
|
||||
{% if class and class.name is defined and class.name|trim != '' %}
|
||||
==== parent ====
|
||||
{% set child = class %}
|
||||
{% set class = class.parent %}
|
||||
{{ block('hierarchy_element') }}
|
||||
[[{{ child.name }}|{{ child.name }}]]
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% for interface in node.interfaces|sort_asc %}
|
||||
{% if loop.first %}
|
||||
==== Implements ====
|
||||
{% endif %}
|
||||
{% if loop.length > 1 %} * {% endif %}{{ interface.fullyQualifiedStructuralElementName ?: interface }}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
{% for trait in node.usedTraits|sort_asc %}
|
||||
{% if loop.first %}
|
||||
==== Uses traits ====
|
||||
{% endif %}
|
||||
{% if loop.length > 1 %} * {% endif %}{{ trait.fullyQualifiedStructuralElementName ?: trait }}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
{% include 'includes/see-also.txt.twig' with {structure:node, title_level: '==='} %}
|
||||
|
||||
{% include 'includes/tags.txt.twig' with {structure:node, title_level: '=====', blacklist: ['link', 'see', 'abstract', 'example', 'method', 'property', 'property-read', 'property-write', 'package', 'subpackage', 'phpdoc-tuning-exclude-inherited', 'api', 'api-advanced', 'overwritable-hook', 'extension-hook', 'copyright', 'license', 'code-example']} %}
|
||||
|
||||
{% set methods = node.inheritedMethods.merge(node.methods.merge(node.magicMethods)) %}
|
||||
{% include 'includes/tag-synthesys.txt.twig' with {methods:methods, tag:'api'} %}
|
||||
{% include 'includes/tag-synthesys.txt.twig' with {methods:methods, tag:'api-advanced'} %}
|
||||
{% include 'includes/tag-synthesys.txt.twig' with {methods:methods, tag:'overwritable-hook'} %}
|
||||
{% include 'includes/tag-synthesys.txt.twig' with {methods:methods, tag:'extension-hook'} %}
|
||||
|
||||
|
||||
{% include 'includes/code-examples.txt.twig' with {structure:node, title_level: '=====', sub_title_level: '=='} %}
|
||||
|
||||
<WRAP clear />
|
||||
|
||||
{% for method in methods|sort_asc
|
||||
if method.visibility == 'public'
|
||||
and (
|
||||
method.tags['api'] is defined
|
||||
or method.tags['api-advanced'] is defined
|
||||
or method.tags['overwritable-hook'] is defined
|
||||
or method.tags['extension-hook'] is defined
|
||||
)
|
||||
and (
|
||||
node.tags['phpdoc-tuning-exclude-inherited'] is not defined
|
||||
or method.parent.name == node.name
|
||||
)
|
||||
%}
|
||||
{%- if loop.first %}
|
||||
===== Public methods =====
|
||||
{% endif %}
|
||||
{{ block('method') }}
|
||||
{% endfor %}
|
||||
{% for method in methods|sort_asc if method.visibility == 'protected' and (method.tags['overwritable-hook'] is defined or method.tags['extension-hook'] is defined) %}
|
||||
{%- if loop.first %}
|
||||
===== Protected methods =====
|
||||
{% endif %}
|
||||
{{ block('method') }}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{% set constants = node.inheritedConstants.merge(node.constants) %}
|
||||
{% if constants|length > 0 %}
|
||||
===== Constants =====
|
||||
{% for constant in constants|sort_asc %}
|
||||
{{ block('constant') }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
{#{% set properties = node.inheritedProperties.merge(node.properties.merge(node.magicProperties)) %}#}
|
||||
{#{% for property in properties|sort_asc if property.visibility == 'public' %}#}
|
||||
{#{%- if loop.first %}#}
|
||||
{#===== Public properties =====#}
|
||||
{#{% endif %}#}
|
||||
{#{{ block('property') }}#}
|
||||
{#{% endfor %}#}
|
||||
{#{% for property in properties|sort_asc if property.visibility == 'protected' %}#}
|
||||
{#{%- if loop.first %}#}
|
||||
{#===== Protected properties =====#}
|
||||
{#{% endif %}#}
|
||||
{#{{ block('property') }}#}
|
||||
{#{% endfor %}#}
|
||||
|
||||
|
||||
{%- endif %} {#{% elseif node.tags['xxx'] is not defined and ... #}
|
||||
|
||||
<wrap button>[[start|🔙 Back]]</wrap>
|
||||
{% endblock %}
|
||||
@@ -1,31 +0,0 @@
|
||||
{% block constant %}
|
||||
|
||||
<WRAP group box >
|
||||
<WRAP twothirds column >
|
||||
==== {{ constant.name }} ====
|
||||
</WRAP>{# twothirds column#}
|
||||
|
||||
<WRAP third column>
|
||||
{% if constant.deprecated %}<wrap danger>deprecated</wrap> {% endif %}
|
||||
{% if (node.parent is not null and constant.parent.fullyQualifiedStructuralElementName != node.fullyQualifiedStructuralElementName) %}<wrap notice>inherited</wrap> {% endif %}
|
||||
</WRAP>{# third column#}
|
||||
|
||||
== {{ constant.summary|replace({"\n":""})|raw }} ==
|
||||
<html>{{ constant.description|markdown|raw }}</html>
|
||||
|
||||
{% if constant.deprecated %}
|
||||
=== Deprecated ===
|
||||
{{ constant.tags.deprecated[0].description|raw }}
|
||||
{% endif %}
|
||||
|
||||
{% include 'includes/inherited-from.txt.twig' with {structure:constant} %}
|
||||
|
||||
{% include 'includes/see-also.txt.twig' with {structure:constant, title_level: '=='} %}
|
||||
|
||||
{% include 'includes/uses.txt.twig' with {structure:constant, title_level: '=='} %}
|
||||
|
||||
{% include 'includes/tags.txt.twig' with {structure:constant, title_level: '==', blacklist: ['link', 'see', 'var', 'deprecated', 'uses', 'package', 'subpackage', 'todo', 'code-example']} %}
|
||||
|
||||
</WRAP>{# group #}
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,95 +0,0 @@
|
||||
{% block method %}
|
||||
|
||||
|
||||
<WRAP group box >
|
||||
<WRAP twothirds column >
|
||||
==== {{ method.name }} ====
|
||||
</WRAP>{# twothirds column#}
|
||||
<WRAP third column >
|
||||
{% include 'includes/wrap-tags.txt.twig' with {structure:method, wrap: 'safety', wrapTags: ['api', 'api-advanced', 'overwritable-hook', 'extension-hook']} %}
|
||||
{% if method.deprecated %}<wrap danger>deprecated</wrap> {% endif %}
|
||||
{% if (node.parent is not null and method.parent.fullyQualifiedStructuralElementName != node.fullyQualifiedStructuralElementName) %}<wrap notice>inherited</wrap> {% endif %}
|
||||
{% if method.abstract %}<wrap warning>abstract</wrap> {% endif %}
|
||||
{% if method.final %}<wrap notice>final</wrap> {% endif %}
|
||||
<wrap notice>{{ method.visibility }}</wrap>
|
||||
{% if method.static %}<wrap warning>static</wrap> {% endif %}
|
||||
</WRAP>{# third column#}
|
||||
|
||||
|
||||
== {{ method.summary|replace({"\n":""})|raw }} ==
|
||||
<html>{{ method.description|markdown|raw }}</html>
|
||||
|
||||
<code php>{% if method.abstract %}abstract {% endif %}{% if method.final %}final {% endif %}{{ method.visibility }} {% if method.static %}static {% endif %}{{ method.name }}({% for argument in method.arguments %}{{ argument.isVariadic ? '...' }}{{ argument.name }}{{ argument.default ? (' = '~argument.default)|raw }}{% if not loop.last %}, {% endif %}{% endfor %})</code>
|
||||
|
||||
<WRAP twothirds column >
|
||||
|
||||
|
||||
=== Parameters ===
|
||||
{% if method.arguments|length > 0 -%}
|
||||
^ types ^ name ^ default ^ description ^
|
||||
{% for argument in method.arguments -%}
|
||||
| **<nowiki>{{ argument.types|join('|')|raw }}</nowiki>** | {{ argument.name }} {{ argument.isVariadic ? '<small style="color: gray">variadic</small>' }} | <nowiki>{{ argument.default|raw }}</nowiki> | {{ argument.description|trim|replace("\n", ' ')|raw }} |{{ "\r\n" }}
|
||||
{%- endfor %}
|
||||
{% else %}
|
||||
//none//
|
||||
{% endif %}
|
||||
|
||||
|
||||
{#=== Parameters ===#}
|
||||
{#{% if method.arguments|length > 0 -%}#}
|
||||
{#{% for argument in method.arguments -%}#}
|
||||
{#== {{ argument.name }} ==#}
|
||||
|
||||
|
||||
{#{% set varDesc %}#}
|
||||
{#<span style="margin:0 10px; 0 20px; font-weight: bold;">{{ argument.types|join('|') }}</span>#}
|
||||
{#{{ argument.isVariadic ? '<small style="color: gray">variadic</small>' }}#}
|
||||
{#{{ argument.description|raw }}#}
|
||||
{#{% endset %}#}
|
||||
{#<html>{{ varDesc|markdown|raw }}</html>#}
|
||||
{#{%- endfor %}#}
|
||||
{#{% else %}#}
|
||||
{#<wrap tip>This method has no parameter</wrap>#}
|
||||
{#{% endif %}#}
|
||||
|
||||
|
||||
{% if method.response and method.response.types|join() != 'void' %}
|
||||
=== Returns ===
|
||||
<html>{{ ('**' ~ method.response.types|join('|')|trim ~ '** ' ~ method.response.description)|markdown|raw }}</html>
|
||||
{% endif %}
|
||||
|
||||
</WRAP>{# twothirds column#}
|
||||
|
||||
<WRAP third column >
|
||||
|
||||
{% if method.tags.throws|length > 0 or method.tags.throw|length > 0 %}
|
||||
=== Throws ===
|
||||
{% for exception in method.tags.throws -%}
|
||||
{% if loop.length > 1 %} * {% endif %}''{{ exception.types|join('|')|raw }}'' <nowiki>{{ exception.description|raw }}</nowiki>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% include 'includes/inherited-from.txt.twig' with {structure:method} %}
|
||||
|
||||
{% include 'includes/see-also.txt.twig' with {structure:method, title_level: '==='} %}
|
||||
|
||||
{% include 'includes/uses.txt.twig' with {structure:method, title_level: '==='} %}
|
||||
|
||||
{% include 'includes/used-by.txt.twig' with {structure:method, title_level: '==='} %}
|
||||
|
||||
{% include 'includes/tags-with-description.txt.twig' with {structure:method, title_level: '===', WRAP: 'info', tagsWithDescription: ['api', 'api-advanced', 'overwritable-hook', 'extension-hook']} %}
|
||||
|
||||
{% include 'includes/tags.txt.twig' with {structure:method, title_level: '===', blacklist: ['todo', 'link', 'see', 'abstract', 'example', 'param', 'return', 'access', 'deprecated', 'throws', 'throw', 'uses', 'api', 'api-advanced', 'overwritable-hook', 'extension-hook', 'used-by', 'inheritdoc', 'code-example']} %}
|
||||
|
||||
</WRAP>{# third column#}
|
||||
|
||||
|
||||
|
||||
{% include 'includes/code-examples.txt.twig' with {structure:method, title_level: '==='} %}
|
||||
|
||||
</WRAP>{# group #}
|
||||
|
||||
|
||||
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,49 +0,0 @@
|
||||
{% block property %}
|
||||
|
||||
<WRAP group box>
|
||||
<WRAP twothirds column >
|
||||
==== ${{ property.name }} ====
|
||||
</WRAP>{# twothirds column#}
|
||||
|
||||
<WRAP third column>
|
||||
{% if property.deprecated %}<wrap danger>deprecated</wrap> {% endif %}
|
||||
{% if (node.parent is not null and property.parent.fullyQualifiedStructuralElementName != node.fullyQualifiedStructuralElementName) %}<wrap notice>inherited</wrap> {% endif %}
|
||||
</WRAP>{# third column#}
|
||||
|
||||
|
||||
|
||||
|
||||
== {{ property.summary|replace({"\n":""})|raw }} ==
|
||||
<html>{{ property.description|markdown|raw }}</html>
|
||||
{% if property.var.0.description %}<html>{{ property.var.0.description|markdown|raw }}</html>{% endif %}
|
||||
|
||||
|
||||
|
||||
{#{% if property.types %}#}
|
||||
{#== Type ==#}
|
||||
{#{% for type in property.types %}#}
|
||||
{#{% if loop.length > 1 %} * {% endif %}{{ type|raw }} : {{ type.description|raw }}#}
|
||||
{#{% endfor %}#}
|
||||
{#{{ property.types|join('|')|raw }}#}
|
||||
{#{% endif %}#}
|
||||
|
||||
|
||||
{% if property.deprecated %}
|
||||
== Deprecated ==
|
||||
{{ property.tags.deprecated[0].description }}
|
||||
{% endif %}
|
||||
|
||||
{% include 'includes/inherited-from.txt.twig' with {structure:property} %}
|
||||
|
||||
{% include 'includes/see-also.txt.twig' with {structure:property, title_level: '=='} %}
|
||||
|
||||
{% include 'includes/uses.txt.twig' with {structure:property, title_level: ''} %}
|
||||
|
||||
{% include 'includes/tags.txt.twig' with {structure:property, title_level: '==', blacklist: ['link', 'see', 'access', 'var', 'deprecated', 'uses', 'todo', 'code-example']} %}
|
||||
|
||||
|
||||
<code php>{{ property.visibility }} ${{ property.name }}{% if property.types %} : {{ property.types|join('|')|raw }}{% endif %}</code>
|
||||
|
||||
</WRAP>{# group #}
|
||||
|
||||
{% endblock %}
|
||||
@@ -1 +0,0 @@
|
||||
{{ node.source|raw }}
|
||||
@@ -1,122 +0,0 @@
|
||||
{% extends 'layout.txt.twig' %}
|
||||
|
||||
{% block javascripts %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{#<section class="row-fluid">#}
|
||||
{#<div class="span2 sidebar">#}
|
||||
{#{% set namespace = project.namespace %}#}
|
||||
{#{{ block('sidebarNamespaces') }}#}
|
||||
{#</div>#}
|
||||
{#</section>#}
|
||||
{#<section class="row-fluid">#}
|
||||
====== {{ node.path|split('/')|slice(0,-1)|join('/') }}{{ node.name }} ======
|
||||
{{ node.summary }}
|
||||
<html>{{ node.description|markdown|raw }}</html>
|
||||
|
||||
{% if node.traits|length > 0 %}
|
||||
|
||||
===== Traits =====
|
||||
{% for trait in node.traits %}
|
||||
<tr>
|
||||
<td>{{ trait|raw }}</td>
|
||||
<td><em>{{ trait.summary }}</em></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% if node.interfaces|length > 0 %}
|
||||
===== Interfaces =====
|
||||
{% for interface in node.interfaces %}
|
||||
<tr>
|
||||
<td>{{ interface|raw }}</td>
|
||||
<td><em>{{ interface.summary }}</em></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% if node.classes|length > 0 %}
|
||||
===== Classes =====
|
||||
{% for class in node.classes %}
|
||||
{{ class|raw }}
|
||||
<em>{{ class.summary }}</em>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% if node.package is not empty and node.package != '\\' %}
|
||||
===== Package =====
|
||||
{{ node.subpackage ? (node.package ~ '\\' ~ node.subpackage) : node.package }}
|
||||
{% endif %}
|
||||
|
||||
{% for tagName,tags in node.tags if tagName in ['link', 'see'] %}
|
||||
{% if loop.first %}
|
||||
===== See also =====
|
||||
{% endif %}
|
||||
{% for tag in tags %}
|
||||
<dd><a href="{{ tag.reference ?: tag.link }}"><div class="namespace-wrapper">{{ tag.description ?: tag.reference }}</div></a></dd>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
||||
<h2>Tags</h2>
|
||||
<table class="table table-condensed">
|
||||
{% for tagName,tags in node.tags if tagName not in ['link', 'see', 'package', 'subpackage'] %}
|
||||
<tr>
|
||||
<th>
|
||||
{{ tagName }}
|
||||
</th>
|
||||
<td>
|
||||
{% for tag in tags %}
|
||||
{{ tag.description|markdown|raw }}
|
||||
{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><td colspan="2"><em>None found</em></td></tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
{% if node.constants|length > 0 %}
|
||||
<div class="row-fluid">
|
||||
<section class="span8 content file">
|
||||
<h2>Constants</h2>
|
||||
</section>
|
||||
<aside class="span4 detailsbar"></aside>
|
||||
</div>
|
||||
|
||||
{% for constant in node.constants %}
|
||||
{{ block('constant') }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% if node.functions|length > 0 %}
|
||||
<div class="row-fluid">
|
||||
<section class="span8 content file">
|
||||
<h2>Functions</h2>
|
||||
</section>
|
||||
<aside class="span4 detailsbar"></aside>
|
||||
</div>
|
||||
|
||||
{% for method in node.functions %}
|
||||
{{ block('method') }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div id="source-view" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="source-view-label" aria-hidden="true">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3 id="source-view-label">{{ node.file.name }}</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<pre data-src="{{ path('files/' ~ node.path ~ '.txt')|raw }}" class="language-php line-numbers"></pre>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,42 +0,0 @@
|
||||
{% extends 'layout.html.twig' %}
|
||||
|
||||
{% block stylesheets %}
|
||||
<link href="{{ path('css/jquery.iviewer.css') }}" rel="stylesheet" media="all"/>
|
||||
<style>
|
||||
#viewer {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
.wrapper {
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block javascripts %}
|
||||
<script src="{{ path('js/jquery.mousewheel.js') }}" type="text/javascript"></script>
|
||||
<script src="{{ path('js/jquery.iviewer.js') }}" type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
$(window).resize(function(){
|
||||
$("#viewer").height($(window).height() - 100);
|
||||
});
|
||||
|
||||
$(document).ready(function() {
|
||||
$("#viewer").iviewer({src: '{{ path('graphs/classes.svg') }}', zoom_animation: false});
|
||||
$('#viewer img').bind('dragstart', function(event){
|
||||
event.preventDefault();
|
||||
});
|
||||
$(window).resize();
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
<div class="wrapper">
|
||||
<div id="viewer" class="viewer"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,5 +0,0 @@
|
||||
# Fixes a vulnerability in CentOS: http://stackoverflow.com/questions/20533279/prevent-php-from-parsing-non-php-files-such-as-somefile-php-txt
|
||||
<FilesMatch \.php\.txt$>
|
||||
RemoveHandler .php
|
||||
ForceType text/plain
|
||||
</FilesMatch>
|
||||
@@ -1,34 +0,0 @@
|
||||
{% if title_level is not defined %}
|
||||
{%- set title_level = '==' -%}
|
||||
{% endif %}
|
||||
|
||||
{% if sub_title_level is not defined %}
|
||||
{%- set sub_title_level = title_level|slice(1) -%}
|
||||
{% endif %}
|
||||
{% if sub_title_level == '=' %}
|
||||
{%- set sub_title_level = '' -%}
|
||||
{% endif %}
|
||||
|
||||
{#{% for tagName,tags in structure.tags if tagName in ['code-example'] %}#}
|
||||
{#{% if loop.first %}#}
|
||||
{#{{title_level}} Examples {{title_level}}#}
|
||||
{#{% endif %}#}
|
||||
{#{% for tag in tags %}#}
|
||||
{#{%- set descToken = tag.description|split("\n", 2) -%}#}
|
||||
{#{%- set title = descToken[0] -%}#}
|
||||
{#{%- set code = descToken[1] -%}#}
|
||||
{#{{sub_title_level}} {{ title }} {{sub_title_level}}#}
|
||||
{#<code php>{{ code|raw }}</code>#}
|
||||
{#{% endfor %}#}
|
||||
{#{% endfor %}#}
|
||||
|
||||
|
||||
{% for tagName,tags in structure.tags if tagName in ['example'] %}
|
||||
{% if loop.first %}
|
||||
{{title_level}} Examples {{title_level}}
|
||||
{% endif %}
|
||||
{% for tag in tags %}
|
||||
{{ sub_title_level }} {{ tag.filePath|escape }}{{ sub_title_level }}
|
||||
<code php>{{ tag.description|raw }}</code>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
@@ -1,12 +0,0 @@
|
||||
{% if title_level is not defined %}
|
||||
{% set title_level='' %}
|
||||
{% endif %}
|
||||
|
||||
{% if (node.parent is null) %}
|
||||
{{title_level}} File {{ structure.path }} {{title_level}}
|
||||
{% endif %}
|
||||
|
||||
{% if (node.parent is not null and structure.parent.fullyQualifiedStructuralElementName != node.fullyQualifiedStructuralElementName) %}
|
||||
{{title_level}} Inherited from {{title_level}}
|
||||
[[{{structure.parent}}|{{structure.parent}}]]
|
||||
{% endif %}
|
||||
@@ -1,26 +0,0 @@
|
||||
{% for structure in structures|sort_asc if structure.tags['internal'] is not defined and (structure.tags['api'] is defined or structure.tags['api-advanced'] is defined or structure.tags['overwritable-hook'] is defined or structure.tags['extension-hook'] is defined ) %}
|
||||
{#{{ structure|raw }}#}
|
||||
|
||||
{% set structureName = structure|trim('\\', 'left') %}
|
||||
|
||||
<WRAP group box>
|
||||
<WRAP twothirds column >
|
||||
==== {{ structureName }} ====
|
||||
</WRAP>{# twothirds column#}
|
||||
|
||||
<WRAP third column>
|
||||
{% if structure.deprecated %}<wrap danger>deprecated</wrap>{% endif %}
|
||||
{% if structure.abstract %}<wrap warning>abstract</wrap>{% endif %}
|
||||
{% if structure.final %}<wrap notice>final</wrap>{% endif %}
|
||||
{% if (node.parent is not null and structure.parent.fullyQualifiedStructuralElementName != node.fullyQualifiedStructuralElementName) %}<wrap notice>inherited</wrap> {% endif %}
|
||||
{% include 'includes/wrap-tags.txt.twig' with {structure:structure, wrap: 'safety', wrapTags: ['api', 'api-advanced', 'overwritable-hook', 'extension-hook']} %}
|
||||
</WRAP>{# third column#}
|
||||
|
||||
|
||||
{{ structure.summary|raw }}
|
||||
[[{{structureName}}|More information]]
|
||||
|
||||
</WRAP>{# group #}
|
||||
|
||||
{% endfor %}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
{% if title_level is not defined %}
|
||||
{%- set title_level='==' -%}
|
||||
{% endif %}
|
||||
{% for tagName,tags in structure.tags if tagName in ['link', 'see'] %}
|
||||
{% if loop.first %}
|
||||
{{title_level}} See also {{title_level}}
|
||||
{% endif %}
|
||||
{% for tag in tags %}
|
||||
{%- set linkTag = tag.reference|trim('\\', 'left') -%}
|
||||
{% if not('()' in linkTag or '$' in linkTag or node.name in linkTag or '::' in linkTag ) %}
|
||||
{%- set linkTag = linkTag|lower -%}
|
||||
{% elseif node.name~'::' in linkTag %}
|
||||
{%- set linkTag = linkTag|replace({(node.name~'::'): '#'})|lower -%}
|
||||
{% elseif '::' in linkTag -%}
|
||||
{%- set linkTag = linkTag|replace({'::': '#'})|lower -%}
|
||||
{% else %}
|
||||
{%- set linkTag = '#' ~ linkTag|lower -%}
|
||||
{%- endif %}
|
||||
|
||||
{% if loop.length > 1 %} * {% endif %}{% if tag.reference is not empty -%}
|
||||
[[{{linkTag}}|{{ (tag.reference)|trim('\\', 'left') }}]] {% if tag.description|trim is not empty %}: {{ tag.description|trim('\\', 'left') }} {% endif %}
|
||||
{%- else -%}
|
||||
{#{{ tag.description|trim('\\', 'left') }}#}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
@@ -1,56 +0,0 @@
|
||||
{% if tag is not defined -%}
|
||||
{# Do not display @api if @api-advanced is also present #}
|
||||
{%- set tag = "api" -%}
|
||||
{%- endif %}
|
||||
|
||||
{% if hidden_by is not defined -%}
|
||||
{# Do not display @api if @api-advanced is also present #}
|
||||
{%- set hidden_by = {"api" : "api-advanced"} -%}
|
||||
{%- endif %}
|
||||
|
||||
|
||||
|
||||
{% for method in methods|sort_asc
|
||||
if (method.visibility == 'public')
|
||||
and (
|
||||
method.tags[tag] is defined
|
||||
and (
|
||||
hidden_by[tag] is not defined or method.tags[hidden_by[tag]] is not defined
|
||||
)
|
||||
)
|
||||
%}
|
||||
{%- if loop.first %}
|
||||
{% if tag == 'api' %}
|
||||
===== API synthesis =====
|
||||
<WRAP>
|
||||
List of the public API methods.
|
||||
When manipulating {{ node.name }}, You can call those methods:
|
||||
</WRAP>
|
||||
{% elseif tag == 'api-advanced' %}
|
||||
===== Advanced API synthesis =====
|
||||
<WRAP>
|
||||
List of advanced API methods
|
||||
Beware they usage is recommended to advanced users only.
|
||||
</WRAP>
|
||||
{% elseif tag == 'overwritable-hook' %}
|
||||
===== overwritable-hook synthesis =====
|
||||
<WRAP >When inheriting from {{ node.name }},
|
||||
you can overwrite those methods in order to add custom logic:
|
||||
</WRAP>
|
||||
{% elseif tag == 'extension-hook' %}
|
||||
===== extension-hook synthesis =====
|
||||
<WRAP >
|
||||
When inheriting from {{ node.name }},
|
||||
you can extend the behaviour of iTop by implementing:
|
||||
</WRAP>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% set sanitizedMethod = method|trim('\\', 'left')|replace({(node.name~'::'): ''}) %}
|
||||
{% if '::' in sanitizedMethod -%}
|
||||
{%- if node.tags['phpdoc-tuning-exclude-inherited'] is not defined %}
|
||||
* [[{{sanitizedMethod|replace({'::': '#'})|lower}}|↪{{sanitizedMethod}}]] — {{ method.summary|replace({"\n":""})|raw }}
|
||||
{% endif %}
|
||||
{%- else %}
|
||||
* [[#{{sanitizedMethod}}|{{sanitizedMethod}}]] — {{ method.summary|replace({"\n":""})|raw }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
@@ -1,20 +0,0 @@
|
||||
{% if title_level is not defined %}
|
||||
{% set title_level = '==' %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
{%- for tagName,tags in structure.tags if tagName in tagsWithDescription -%}
|
||||
{%- for tag in tags -%}
|
||||
{%- if tag.description is not empty -%}
|
||||
{%- if WRAP is defined -%}
|
||||
<WRAP {{WRAP}}>
|
||||
{%- endif -%}
|
||||
{{title_level}} {{ tagName }} {{title_level}}
|
||||
{{ tag.description|escape }}
|
||||
{%- if WRAP is defined -%}
|
||||
</WRAP>
|
||||
{%- endif -%}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{%- endfor -%}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
{% if title_level is not defined %}
|
||||
{% set title_level='=====' %}
|
||||
{% endif %}
|
||||
|
||||
{% if blacklist is not defined %}
|
||||
{% set blacklist =['link', 'see', 'abstract', 'example', 'method', 'property', 'property-read', 'property-write', 'package', 'subpackage', 'api', 'api-advanced', 'todo', 'code-example'] %}
|
||||
{% endif %}
|
||||
{% if hidden_by is not defined -%}
|
||||
{# Do not display @api if @api-advanced is also present #}
|
||||
{%- set hidden_by = {"api" : "api-advanced"} -%}
|
||||
{%- endif %}
|
||||
|
||||
{#^ {% for tagName,tags in structure.tags if tagName not in blacklist -%}#}
|
||||
{#{{ tagName }} ^#}
|
||||
{#{%- endfor %}#}
|
||||
|
||||
{% for tagName,tags in structure.tags if tagName not in blacklist and (hidden_by[tagName] is not defined or structure.tags[hidden_by[tagName]] is not defined) %}
|
||||
{%- if loop.first %}
|
||||
{{title_level}} Tags {{title_level}}
|
||||
{% endif %}
|
||||
^ {{ tagName }} | {% for tag in tags %}{{ tag.version ? tag.version ~ ' ' : '' }}{{ tag.description}}{% endfor %} |
|
||||
{% endfor %}
|
||||
@@ -1,24 +0,0 @@
|
||||
{% if title_level is not defined %}
|
||||
{% set title_level='' %}
|
||||
{% endif %}
|
||||
{% for tagName,tags in structure.tags if tagName in ['used-by'] %}
|
||||
{% if loop.first %}
|
||||
{{title_level}} Used by {{title_level}}
|
||||
{% endif %}
|
||||
{% for tag in tags %}
|
||||
{% if loop.length > 1 %} * {% endif %}{{ tag.reference ?: tag.link }} : {{ tag.description ?: tag.reference }}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{#{% for tagName,tags in method.tags if tagName in ['uses'] %}#}
|
||||
{#{% if loop.first %}#}
|
||||
{#<dt>Uses</dt>#}
|
||||
{#{% endif %}#}
|
||||
{#{% for tag in tags %}#}
|
||||
{#<dd>{{ tag.reference|raw }}</dd>#}
|
||||
{#{% endfor %}#}
|
||||
{#{% endfor %}#}
|
||||
@@ -1,24 +0,0 @@
|
||||
{% if title_level is not defined %}
|
||||
{% set title_level='' %}
|
||||
{% endif %}
|
||||
{% for tagName,tags in structure.tags if tagName in ['uses'] %}
|
||||
{% if loop.first %}
|
||||
{{title_level}} Uses {{title_level}}
|
||||
{% endif %}
|
||||
{% for tag in tags %}
|
||||
{% if loop.length > 1 %} * {% endif %}{{ tag.reference ?: tag.link }} : {{ tag.description ?: tag.reference }}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{#{% for tagName,tags in method.tags if tagName in ['uses'] %}#}
|
||||
{#{% if loop.first %}#}
|
||||
{#<dt>Uses</dt>#}
|
||||
{#{% endif %}#}
|
||||
{#{% for tag in tags %}#}
|
||||
{#<dd>{{ tag.reference|raw }}</dd>#}
|
||||
{#{% endfor %}#}
|
||||
{#{% endfor %}#}
|
||||
@@ -1,11 +0,0 @@
|
||||
{% if wrap is not defined -%}
|
||||
{% set wrap = 'notice' %}
|
||||
{%- endif -%}
|
||||
{% if hidden_by is not defined -%}
|
||||
{# Do not display @api if @api-advanced is also present #}
|
||||
{%- set hidden_by = {"api" : "api-advanced"} -%}
|
||||
{%- endif %}
|
||||
|
||||
{%- for tagName,tags in structure.tags if tagName in wrapTags and (hidden_by[tagName] is not defined or structure.tags[hidden_by[tagName]] is not defined) %}
|
||||
<wrap {{wrap}}>{{tagName}}</wrap>
|
||||
{% endfor %}
|
||||
@@ -1,121 +0,0 @@
|
||||
{% extends 'layout.txt.twig' %}
|
||||
|
||||
{% block content %}
|
||||
<wrap button>[[start|🔙 Back]]</wrap>
|
||||
|
||||
{% if node.tags['internal'] is defined %}
|
||||
====== {{ node.name }} ======
|
||||
<WRAP alert>This interface is "internal", and thus is not documented!</WRAP>
|
||||
{% elseif node.tags['api'] is not defined and node.tags['api-advanced'] is not defined and node.tags['overwritable-hook'] is not defined and node.tags['extension-hook'] is not defined %}
|
||||
====== {{ node.name }} ======
|
||||
<WRAP alert>This interface is neither "api", "overwritable-hook" or "extension-hook", and thus is not documented!</WRAP>
|
||||
{% else %}
|
||||
|
||||
====== {{ node.name }} ======
|
||||
|
||||
{% if node.deprecated %}<wrap danger>deprecated</wrap>{% endif %}
|
||||
{% if node.abstract %}<wrap warning>abstract</wrap>{% endif %}
|
||||
{% if node.final %}<wrap notice>final</wrap>{% endif %}
|
||||
{% include 'includes/wrap-tags.txt.twig' with {structure:node, wrap: 'safety', wrapTags: ['api', 'api-advanced', 'overwritable-hook', 'extension-hook']} %}
|
||||
|
||||
|
||||
|
||||
{% if node.deprecated %}
|
||||
=== **<del>Deprecated</del>**===
|
||||
//{{ node.tags.deprecated[0].description }}//
|
||||
{% endif %}
|
||||
|
||||
|
||||
== {{ node.summary|replace({"\n":""})|raw }} ==
|
||||
<html>{{ node.description|markdown|raw }}</html>
|
||||
|
||||
|
||||
{% include 'includes/code-examples.txt.twig' with {structure:node, title_level: '====='} %}
|
||||
|
||||
|
||||
{% set class = node.parent %}
|
||||
{% block hierarchy_element %}
|
||||
|
||||
{% if class and class.name is defined and class.name|trim != '' %}
|
||||
==== parent ====
|
||||
{% set child = class %}
|
||||
{% set class = class.parent %}
|
||||
{{ block('hierarchy_element') }}
|
||||
[[{{ child.name }}|{{ child.name }}]]
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% for interface in node.interfaces|sort_asc %}
|
||||
{% if loop.first %}
|
||||
==== Implements ====
|
||||
{% endif %}
|
||||
{% if loop.length > 1 %} * {% endif %}{{ interface.fullyQualifiedStructuralElementName ?: interface }}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
{% for trait in node.usedTraits|sort_asc %}
|
||||
{% if loop.first %}
|
||||
==== Uses traits ====
|
||||
{% endif %}
|
||||
{% if loop.length > 1 %} * {% endif %}{{ trait.fullyQualifiedStructuralElementName ?: trait }}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
{% include 'includes/see-also.txt.twig' with {structure:node, title_level: '==='} %}
|
||||
|
||||
{% include 'includes/tags.txt.twig' with {structure:node, title_level: '=====', blacklist: ['link', 'see', 'abstract', 'example', 'method', 'property', 'property-read', 'property-write', 'package', 'subpackage', 'phpdoc-tuning-exclude-inherited', 'api', 'api-advanced', 'overwritable-hook', 'extension-hook', 'copyright', 'license', 'code-example']} %}
|
||||
|
||||
|
||||
{% set methods = node.inheritedMethods.merge(node.methods) %}
|
||||
{% include 'includes/tag-synthesys.txt.twig' with {methods:methods, tag:'api'} %}
|
||||
{% include 'includes/tag-synthesys.txt.twig' with {methods:methods, tag:'api-advanced'} %}
|
||||
{% include 'includes/tag-synthesys.txt.twig' with {methods:methods, tag:'overwritable-hook'} %}
|
||||
{% include 'includes/tag-synthesys.txt.twig' with {methods:methods, tag:'extension-hook'} %}
|
||||
|
||||
<WRAP clear />
|
||||
|
||||
|
||||
{% for method in methods|sort_asc if method.visibility == 'public' %}
|
||||
{%- if loop.first %}
|
||||
===== Public methods =====
|
||||
{% endif %}
|
||||
{{ block('method') }}
|
||||
{% endfor %}
|
||||
{% for method in methods|sort_asc if method.visibility == 'protected' %}
|
||||
{%- if loop.first %}
|
||||
===== Protected methods =====
|
||||
{% endif %}
|
||||
{{ block('method') }}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
|
||||
{% set constants = node.inheritedConstants.merge(node.constants) %}
|
||||
{% if constants|length > 0 %}
|
||||
===== Constants =====
|
||||
{% for constant in constants|sort_asc %}
|
||||
{{ block('constant') }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
{#{% set properties = node.inheritedProperties.merge(node.properties) %}#}
|
||||
{#{% for property in properties|sort_asc if property.visibility == 'public' %}#}
|
||||
{#{%- if loop.first %}#}
|
||||
{#===== Public properties =====#}
|
||||
{#{% endif %}#}
|
||||
{#{{ block('property') }}#}
|
||||
{#{% endfor %}#}
|
||||
{#{% for property in properties|sort_asc if property.visibility == 'protected' %}#}
|
||||
{#{%- if loop.first %}#}
|
||||
{#===== Protected properties =====#}
|
||||
{#{% endif %}#}
|
||||
{#{{ block('property') }}#}
|
||||
{#{% endfor %}#}
|
||||
|
||||
|
||||
{%- endif %} {#{% elseif node.tags['xxx'] is not defined and ... #}
|
||||
|
||||
<wrap button>[[start|🔙 Back]]</wrap>
|
||||
{% endblock %}
|
||||
@@ -1,5 +0,0 @@
|
||||
{% use 'elements/constant.txt.twig' %}
|
||||
{% use 'elements/property.txt.twig' %}
|
||||
{% use 'elements/method.txt.twig' %}
|
||||
|
||||
{% block content %}{% endblock %}
|
||||
@@ -1,51 +0,0 @@
|
||||
{% extends 'layout.txt.twig' %}
|
||||
|
||||
{% block content %}
|
||||
{% set namespace = project.namespace %}
|
||||
{{ block('sidebarNamespaces') }}
|
||||
|
||||
{#{{ node.parent|raw }}#}
|
||||
{#====== {{ node.parent.fullyQualifiedStructuralElementName }}{{ node.name }} ======#}
|
||||
|
||||
{% if node.children|length > 0 %}
|
||||
=====Namespaces=====
|
||||
{% include 'includes/namespace-structure-toc.html.twig' with {structures: node.children} %}
|
||||
----
|
||||
{% endif %}
|
||||
|
||||
{% if node.traits|length > 0 %}
|
||||
===== Traits =====
|
||||
{% include 'includes/namespace-structure-toc.html.twig' with {structures: node.traits} %}
|
||||
----
|
||||
{%- endif %}
|
||||
|
||||
{% if node.interfaces|length > 0 %}
|
||||
===== Interfaces =====
|
||||
{% include 'includes/namespace-structure-toc.html.twig' with {structures: node.interfaces} %}
|
||||
----
|
||||
{% endif %}
|
||||
|
||||
{% if node.classes|length > 0 %}
|
||||
===== Classes =====
|
||||
{% include 'includes/namespace-structure-toc.html.twig' with {structures: node.classes} %}
|
||||
----
|
||||
{% endif %}
|
||||
|
||||
|
||||
|
||||
{#{% if node.constants|length > 0 %}#}
|
||||
{#===== Constants =====#}
|
||||
{#{% for constant in node.constants|sort_asc %}#}
|
||||
{# {{ block('constant') }}#}
|
||||
{#{% endfor %}#}
|
||||
{#{% endif %}#}
|
||||
|
||||
{#{% if node.functions|length > 0 %}#}
|
||||
{#===== Functions =====#}
|
||||
|
||||
{#{% for method in node.functions|sort_asc %}#}
|
||||
{# {{ block('method') }}#}
|
||||
{#{% endfor %}#}
|
||||
{#{% endif %}#}
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,49 +0,0 @@
|
||||
|
||||
====== Deprecated elements ======
|
||||
|
||||
{#{% for element in project.indexes.elements if element.deprecated %}#}
|
||||
{#{% if element.file.path != previousPath %}#}
|
||||
{#<li><a href="#{{ element.file.path }}"><i class="icon-file"></i> {{ element.file.path }}</a></li>#}
|
||||
{#{% endif %}#}
|
||||
{#{% set previousPath = element.file.path %}#}
|
||||
{#{% endfor %}#}
|
||||
|
||||
{% for element in project.indexes.elements if element.deprecated %}
|
||||
{% if element.file.path != previousPath %}
|
||||
{% if previousPath %}
|
||||
</WRAP>{# group #}
|
||||
{% endif %}
|
||||
{#<a name="{{ element.file.path }}" id="{{ element.file.path }}"></a>#}
|
||||
===== {{ element.file.path }} ({{ element.tags.deprecated.count }} found)=====
|
||||
|
||||
<WRAP group >
|
||||
<WRAP third column >
|
||||
Element
|
||||
</WRAP>{# third column#}
|
||||
<WRAP third column >
|
||||
Line
|
||||
</WRAP>{# third column#}
|
||||
<WRAP third column >
|
||||
Description
|
||||
</WRAP>{# third column#}
|
||||
|
||||
{% endif %}
|
||||
{% for tag in element.tags.deprecated %}
|
||||
<WRAP group >
|
||||
<WRAP third column >
|
||||
{{ element.fullyQualifiedStructuralElementName }}
|
||||
</WRAP>{# third column#}
|
||||
<WRAP third column >
|
||||
{{ element.line }}
|
||||
</WRAP>{# third column#}
|
||||
<WRAP third column >
|
||||
{{ tag.description }}
|
||||
</WRAP>{# third column#}
|
||||
|
||||
{% endfor %}
|
||||
</WRAP>{# group #}
|
||||
{% set previousPath = element.file.path %}
|
||||
{% else %}
|
||||
<WRAP info>No deprecated elements have been found in this project.</WRAP>
|
||||
{% endfor %}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<template>
|
||||
<author>Bruno DA SILVA</author>
|
||||
<email>contact [at] combodo.com</email>
|
||||
<version>1.0.0</version>
|
||||
<copyright>Combodo 2018</copyright>
|
||||
<description><![CDATA[
|
||||
|
||||
Forked from the clean theme of https://github.com/phpDocumentor/phpDocumentor2 provided under the MIT licence.
|
||||
The original work is copyright "Mike van Riel".
|
||||
|
||||
------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
To improve performance you can add the following to your .htaccess:
|
||||
|
||||
<ifModule mod_deflate.c>
|
||||
<filesMatch "\.(js|css|html)$">
|
||||
SetOutputFilter DEFLATE
|
||||
</filesMatch>
|
||||
</ifModule>
|
||||
]]></description>
|
||||
<transformations>
|
||||
<transformation writer="twig" query="namespace" source="templates/combodo-wiki/namespace.txt.twig" artifact="start.txt"/>
|
||||
<transformation writer="twig" query="indexes.classes" source="templates/combodo-wiki/class.txt.twig" artifact="{{name}}.txt"/>
|
||||
<transformation writer="twig" query="indexes.interfaces" source="templates/combodo-wiki/interface.txt.twig" artifact="{{name}}.txt" />
|
||||
</transformations>
|
||||
</template>
|
||||
43
.github/workflows/action.yml
vendored
Normal file
43
.github/workflows/action.yml
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
name: Add PRs to Combodo PRs Dashboard
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
|
||||
jobs:
|
||||
add-to-project:
|
||||
name: Add PR to Combodo Project
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check if author is a member of the organization
|
||||
id: check-membership
|
||||
run: |
|
||||
ORG="Combodo"
|
||||
AUTHOR=$(jq -r .pull_request.user.login "$GITHUB_EVENT_PATH")
|
||||
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: token ${{ secrets.PR_AUTOMATICALLY_ADD_TO_PROJECT }}" \
|
||||
"https://api.github.com/orgs/$ORG/members/$AUTHOR")
|
||||
if [ "$RESPONSE" == "404" ]; then
|
||||
echo "project_url=https://github.com/orgs/Combodo/projects/5" >> $GITHUB_ENV
|
||||
echo "is_member=false" >> $GITHUB_ENV
|
||||
else
|
||||
echo "project_url=https://github.com/orgs/Combodo/projects/4" >> $GITHUB_ENV
|
||||
echo "is_member=true" >> $GITHUB_ENV
|
||||
|
||||
fi
|
||||
|
||||
- name: Add internal tag if member
|
||||
if: env.is_member == 'true'
|
||||
run: |
|
||||
curl -X POST -H "Authorization: token ${{ secrets.PR_AUTOMATICALLY_ADD_TO_PROJECT }}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
https://api.github.com/repos/Combodo/iTop/issues/${{ github.event.pull_request.number }}/labels \
|
||||
-d '{"labels":["internal"]}'
|
||||
env:
|
||||
is_member: ${{ env.is_member }}
|
||||
|
||||
- name: Add PR to the appropriate project
|
||||
uses: actions/add-to-project@v1.0.2
|
||||
with:
|
||||
project-url: ${{ env.project_url }}
|
||||
github-token: ${{ secrets.PR_AUTOMATICALLY_ADD_TO_PROJECT }}
|
||||
@@ -25,7 +25,7 @@ if (false === file_exists($sTcPdfRootFolder)) {
|
||||
echo $sCurrentScriptFileName.": No TCPDF lib detected, exiting !\n";
|
||||
return;
|
||||
}
|
||||
$sTcPdfFontsFolder = $sTcPdfRootFolder.'/Fonts/';
|
||||
$sTcPdfFontsFolder = $sTcPdfRootFolder.'/fonts/';
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -53,12 +53,12 @@ switch ($sDependenciesHandlerCode) {
|
||||
|
||||
// Start handler
|
||||
$oDependenciesHandler = new $sDependenciesHandlerFQCN();
|
||||
$aDeniedButStillPresent = $oDependenciesHandler->ListDeniedButStillPresentFoldersAbsPaths();
|
||||
$aDeniedButStillPresent = $oDependenciesHandler->ListDeniedButStillPresentFilesAbsPaths();
|
||||
|
||||
echo "\n";
|
||||
foreach ($aDeniedButStillPresent as $sDir)
|
||||
{
|
||||
if (false === $oDependenciesHandler::IsQuestionnableFolder($sDir))
|
||||
if (false === $oDependenciesHandler::IsQuestionnableFile($sDir))
|
||||
{
|
||||
echo "ERROR found INVALID denied test dir: '$sDir'\n";
|
||||
throw new \RuntimeException("$sDir is in the denied list but doesn't comply with the rule (see IsQuestionnableFolder method)");
|
||||
@@ -70,7 +70,12 @@ foreach ($aDeniedButStillPresent as $sDir)
|
||||
}
|
||||
|
||||
try {
|
||||
SetupUtils::rrmdir($sDir);
|
||||
if(is_dir($sDir)){
|
||||
SetupUtils::rrmdir($sDir);
|
||||
}
|
||||
else{
|
||||
unlink($sDir);
|
||||
}
|
||||
echo "✔️ Remove denied test dir: '$sDir'\n";
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
@@ -80,10 +85,10 @@ foreach ($aDeniedButStillPresent as $sDir)
|
||||
|
||||
|
||||
$aAllowedAndDeniedDirs = array_merge(
|
||||
$oDependenciesHandler->ListAllowedFoldersAbsPaths(),
|
||||
$oDependenciesHandler->ListDeniedFoldersAbsPaths()
|
||||
$oDependenciesHandler->ListAllowedFilesAbsPaths(),
|
||||
$oDependenciesHandler->ListDeniedFilesAbsPaths()
|
||||
);
|
||||
$aExistingDirs = $oDependenciesHandler->ListAllFoldersAbsPaths();
|
||||
$aExistingDirs = $oDependenciesHandler->ListAllFilesAbsPaths();
|
||||
$aMissing = array_diff($aExistingDirs, $aAllowedAndDeniedDirs);
|
||||
if (false === empty($aMissing)) {
|
||||
echo "Some new tests dirs exists !\n"
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
<p align="center"><a href="https://www.combodo.com/itop-193" target="_blank">
|
||||
<img src="https://www.combodo.com/logos/logo-itop-baseline.svg" width=350>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="/images/logos/logo-itop-baseline-light.svg">
|
||||
<source media="(prefers-color-scheme: light)" srcset="/images/logos/logo-itop-baseline-dark.svg">
|
||||
<img src="/images/logos/logo-itop-baseline-light.svg" width="350" alt="Logo iTop with baseline" />
|
||||
</picture>
|
||||
</a></p>
|
||||
|
||||
|
||||
@@ -106,6 +110,7 @@ We would like to give a special thank you 🤗 to the people from the community
|
||||
- Raenker, Martin
|
||||
- Roháč, Richard (a.k.a [@RohacRichard](https://github.com/RohacRichard))
|
||||
- Rosenke, Stephan
|
||||
- Rossi, Tommaso (a.k.a [@tomrss](https://www.github.com/tomrss))
|
||||
- Rudner, Björn (a.k.a [@rudnerbjoern](https://github.com/rudnerbjoern))
|
||||
- Šafránek, Jaroslav (a.k.a [jkcinik](https://sourceforge.net/u/jkcinik/profile/) on SourceForge)
|
||||
- Seki, Shoji
|
||||
@@ -115,6 +120,7 @@ We would like to give a special thank you 🤗 to the people from the community
|
||||
- Tarjányi, Csaba (a.k.a [@tacsaby](https://github.com/tacsaby))
|
||||
- Tulio, Marco
|
||||
- Turrubiates, Miguel
|
||||
- Vlk, Karel (a.k.a [@vlk-charles](https://www.github.com/vlk-charles))
|
||||
|
||||
### Aliases
|
||||
|
||||
|
||||
@@ -94,15 +94,15 @@ class URP_Profiles extends UserRightsBaseClassGUI
|
||||
$bGrant = $oUserRights->GetProfileActionGrant($this->GetKey(), $sClass, $sAction);
|
||||
if (is_null($bGrant))
|
||||
{
|
||||
return '<span style="background-color: #ffdddd;">'.Dict::S('UI:UserManagement:ActionAllowed:No').'</span>';
|
||||
return '<span class="ibo-user-rights ibo-is-failure">'.Dict::S('UI:UserManagement:ActionAllowed:No').'</span>';
|
||||
}
|
||||
elseif ($bGrant)
|
||||
{
|
||||
return '<span style="background-color: #ddffdd;">'.Dict::S('UI:UserManagement:ActionAllowed:Yes').'</span>';
|
||||
return '<span class="ibo-user-rights ibo-is-success">'.Dict::S('UI:UserManagement:ActionAllowed:Yes').'</span>';
|
||||
}
|
||||
else
|
||||
{
|
||||
return '<span style="background-color: #ffdddd;">'.Dict::S('UI:UserManagement:ActionAllowed:No').'</span>';
|
||||
return '<span class="ibo-user-rights ibo-is-failure">'.Dict::S('UI:UserManagement:ActionAllowed:No').'</span>';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -825,49 +825,38 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
||||
{
|
||||
// We are protected by GetSelectFilter: the object set contains objects allowed or shared for reading
|
||||
// We have to answer NO for objects shared for reading purposes
|
||||
if (self::HasSharing())
|
||||
{
|
||||
$aClassProps = SharedObject::GetSharedClassProperties($sClass);
|
||||
if ($aClassProps)
|
||||
{
|
||||
// This class is shared, GetSelectFilter may allow some objects for read only
|
||||
// But currently we are checking wether the objects might be written...
|
||||
// Let's exclude the objects based on the relevant criteria
|
||||
if (self::HasSharing() && SharedObject::GetSharedClassProperties($sClass)) {
|
||||
// This class is shared, GetSelectFilter may allow some objects for read only
|
||||
// But currently we are checking whether the objects might be written...
|
||||
// Let's exclude the objects based on the relevant criteria
|
||||
|
||||
$sOrgAttCode = self::GetOwnerOrganizationAttCode($sClass);
|
||||
if (!is_null($sOrgAttCode))
|
||||
{
|
||||
$aUserOrgs = $this->GetUserOrgs($oUser, $sClass);
|
||||
if (!is_null($aUserOrgs) && count($aUserOrgs) > 0)
|
||||
{
|
||||
$iCountNO = 0;
|
||||
$iCountYES = 0;
|
||||
$oInstanceSet->Rewind();
|
||||
while($oObject = $oInstanceSet->Fetch())
|
||||
{
|
||||
$iOrg = $oObject->Get($sOrgAttCode);
|
||||
if (in_array($iOrg, $aUserOrgs))
|
||||
{
|
||||
$iCountYES++;
|
||||
}
|
||||
else
|
||||
{
|
||||
$iCountNO++;
|
||||
}
|
||||
}
|
||||
if ($iCountNO == 0)
|
||||
{
|
||||
$iPermission = UR_ALLOWED_YES;
|
||||
}
|
||||
elseif ($iCountYES == 0)
|
||||
{
|
||||
$iPermission = UR_ALLOWED_NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
$iPermission = UR_ALLOWED_DEPENDS;
|
||||
// Use $oInstanceSet only if sClass is the main class
|
||||
if (!is_a($oInstanceSet->GetClass(), $sClass, true)) {
|
||||
/** @var \DBObjectSet $oInstanceSet */
|
||||
throw new CoreException(__FUNCTION__.': Expecting object set to be of class '.$sClass.' but it is of class '.$oInstanceSet->GetClass(), ['OQL_Query' => $oInstanceSet->GetFilter()->ToOQL(), 'classes' => $oInstanceSet->GetSelectedClasses()]);
|
||||
}
|
||||
$sOrgAttCode = self::GetOwnerOrganizationAttCode($sClass);
|
||||
if (!is_null($sOrgAttCode)) {
|
||||
$aUserOrgs = $this->GetUserOrgs($oUser, $sClass);
|
||||
if (!is_null($aUserOrgs) && count($aUserOrgs) > 0) {
|
||||
$iCountNO = 0;
|
||||
$iCountYES = 0;
|
||||
$oInstanceSet->Rewind();
|
||||
while ($oObject = $oInstanceSet->Fetch()) {
|
||||
$iOrg = $oObject->Get($sOrgAttCode);
|
||||
if (in_array($iOrg, $aUserOrgs)) {
|
||||
$iCountYES++;
|
||||
} else {
|
||||
$iCountNO++;
|
||||
}
|
||||
}
|
||||
if ($iCountNO == 0) {
|
||||
$iPermission = UR_ALLOWED_YES;
|
||||
} elseif ($iCountYES == 0) {
|
||||
$iPermission = UR_ALLOWED_NO;
|
||||
} else {
|
||||
$iPermission = UR_ALLOWED_DEPENDS;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -982,4 +971,3 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
||||
|
||||
UserRights::SelectModule('UserRightsProfile');
|
||||
|
||||
?>
|
||||
|
||||
@@ -250,11 +250,11 @@ class URP_Profiles extends UserRightsBaseClassGUI
|
||||
$iGrant = $oUserRights->GetProfileActionGrant($this->GetKey(), $sClass, $sAction);
|
||||
if (!is_null($iGrant))
|
||||
{
|
||||
return '<span style="background-color: #ddffdd;">'.Dict::S('UI:UserManagement:ActionAllowed:Yes').'</span>';
|
||||
return '<span class="ibo-user-rights ibo-is-success">'.Dict::S('UI:UserManagement:ActionAllowed:Yes').'</span>';
|
||||
}
|
||||
else
|
||||
{
|
||||
return '<span style="background-color: #ffdddd;">'.Dict::S('UI:UserManagement:ActionAllowed:No').'</span>';
|
||||
return '<span class="ibo-user-rights ibo-is-failure">'.Dict::S('UI:UserManagement:ActionAllowed:No').'</span>';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -82,11 +82,11 @@ class URP_Profiles extends UserRightsBaseClass
|
||||
$oGrant = $oUserRights->GetClassActionGrant($this->GetKey(), $sClass, $sAction);
|
||||
if (is_object($oGrant) && ($oGrant->Get('permission') == 'yes'))
|
||||
{
|
||||
return '<span style="background-color: #ddffdd;">'.Dict::S('UI:UserManagement:ActionAllowed:Yes').'</span>';
|
||||
return '<span class="ibo-user-rights ibo-is-success">'.Dict::S('UI:UserManagement:ActionAllowed:Yes').'</span>';
|
||||
}
|
||||
else
|
||||
{
|
||||
return '<span style="background-color: #ffdddd;">'.Dict::S('UI:UserManagement:ActionAllowed:No').'</span>';
|
||||
return '<span class="ibo-user-rights ibo-is-failure">'.Dict::S('UI:UserManagement:ActionAllowed:No').'</span>';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,331 +0,0 @@
|
||||
<?php
|
||||
namespace Html2Text;
|
||||
|
||||
if (!function_exists('mb_split'))
|
||||
{
|
||||
function mb_split($pattern, $subject, $limit = -1)
|
||||
{
|
||||
return preg_split('/'.$pattern.'/', $subject, $limit);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Replace all occurrences of the search string with the replacement string.
|
||||
*
|
||||
* @author Sean Murphy <sean@iamseanmurphy.com>
|
||||
* @copyright Copyright 2012 Sean Murphy. All rights reserved.
|
||||
* @license http://creativecommons.org/publicdomain/zero/1.0/
|
||||
* @link http://php.net/manual/function.str-replace.php
|
||||
*
|
||||
* @param mixed $search
|
||||
* @param mixed $replace
|
||||
* @param mixed $subject
|
||||
* @param int $count
|
||||
* @return mixed
|
||||
*/
|
||||
function mb_str_replace($search, $replace, $subject, &$count = 0) {
|
||||
if (!is_array($subject)) {
|
||||
// Normalize $search and $replace so they are both arrays of the same length
|
||||
$searches = is_array($search) ? array_values($search) : array($search);
|
||||
$replacements = is_array($replace) ? array_values($replace) : array($replace);
|
||||
$replacements = array_pad($replacements, count($searches), '');
|
||||
foreach ($searches as $key => $search) {
|
||||
$parts = mb_split(preg_quote($search), $subject);
|
||||
if (is_array($parts))
|
||||
{
|
||||
$count += count($parts) - 1;
|
||||
$subject = implode($replacements[$key], $parts);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Call mb_str_replace for each subject in array, recursively
|
||||
foreach ($subject as $key => $value) {
|
||||
$subject[$key] = mb_str_replace($search, $replace, $value, $count);
|
||||
}
|
||||
}
|
||||
return $subject;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2010 Jevon Wright and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* or
|
||||
*
|
||||
* LGPL which is available at http://www.gnu.org/licenses/lgpl.html
|
||||
*
|
||||
*
|
||||
* Contributors:
|
||||
* Jevon Wright - initial API and implementation
|
||||
* Denis Flaven - some fixes for properly handling UTF-8 characters
|
||||
****************************************************************************/
|
||||
|
||||
class Html2Text {
|
||||
|
||||
/**
|
||||
* Tries to convert the given HTML into a plain text format - best suited for
|
||||
* e-mail display, etc.
|
||||
*
|
||||
* <p>In particular, it tries to maintain the following features:
|
||||
* <ul>
|
||||
* <li>Links are maintained, with the 'href' copied over
|
||||
* <li>Information in the <head> is lost
|
||||
* </ul>
|
||||
*
|
||||
* @param string html the input HTML
|
||||
* @return string the HTML converted, as best as possible, to text
|
||||
* @throws Html2TextException if the HTML could not be loaded as a {@link DOMDocument}
|
||||
*/
|
||||
static function convert($html) {
|
||||
// replace with spaces
|
||||
|
||||
$html = str_replace(" ", " ", $html);
|
||||
$html = mb_str_replace("\xc2\xa0", " ", $html); // DO NOT USE str_replace since it breaks the "à" character which is \xc3 \xa0 in UTF-8
|
||||
|
||||
$html = static::fixNewlines($html);
|
||||
|
||||
$doc = new \DOMDocument();
|
||||
if (!@$doc->loadHTML('<?xml encoding="UTF-8">'.$html)) // Forces the UTF-8 character set for HTML fragments
|
||||
{
|
||||
throw new Html2TextException("Could not load HTML - badly formed?", $html);
|
||||
}
|
||||
|
||||
$output = static::iterateOverNode($doc);
|
||||
|
||||
// remove leading and trailing spaces on each line
|
||||
$output = preg_replace("/[ \t]*\n[ \t]*/im", "\n", $output);
|
||||
$output = preg_replace("/ *\t */im", "\t", $output);
|
||||
|
||||
// remove unnecessary empty lines
|
||||
$output = preg_replace("/\n\n\n*/im", "\n\n", $output);
|
||||
|
||||
// remove leading and trailing whitespace
|
||||
$output = trim($output);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unify newlines; in particular, \r\n becomes \n, and
|
||||
* then \r becomes \n. This means that all newlines (Unix, Windows, Mac)
|
||||
* all become \ns.
|
||||
*
|
||||
* @param string text text with any number of \r, \r\n and \n combinations
|
||||
* @return string the fixed text
|
||||
*/
|
||||
static function fixNewlines($text) {
|
||||
// replace \r\n to \n
|
||||
$text = str_replace("\r\n", "\n", $text);
|
||||
// remove \rs
|
||||
$text = str_replace("\r", "\n", $text);
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
static function nextChildName($node) {
|
||||
// get the next child
|
||||
$nextNode = $node->nextSibling;
|
||||
while ($nextNode != null) {
|
||||
if ($nextNode instanceof \DOMElement) {
|
||||
break;
|
||||
}
|
||||
$nextNode = $nextNode->nextSibling;
|
||||
}
|
||||
$nextName = null;
|
||||
if ($nextNode instanceof \DOMElement && $nextNode != null) {
|
||||
$nextName = strtolower($nextNode->nodeName);
|
||||
}
|
||||
|
||||
return $nextName;
|
||||
}
|
||||
|
||||
static function prevChildName($node) {
|
||||
// get the previous child
|
||||
$nextNode = $node->previousSibling;
|
||||
while ($nextNode != null) {
|
||||
if ($nextNode instanceof \DOMElement) {
|
||||
break;
|
||||
}
|
||||
$nextNode = $nextNode->previousSibling;
|
||||
}
|
||||
$nextName = null;
|
||||
if ($nextNode instanceof \DOMElement && $nextNode != null) {
|
||||
$nextName = strtolower($nextNode->nodeName);
|
||||
}
|
||||
|
||||
return $nextName;
|
||||
}
|
||||
|
||||
static function iterateOverNode($node) {
|
||||
if ($node instanceof \DOMText) {
|
||||
// Replace whitespace characters with a space (equivilant to \s)
|
||||
return preg_replace("/[\\t\\n\\f\\r ]+/im", " ", $node->wholeText);
|
||||
}
|
||||
if ($node instanceof \DOMDocumentType) {
|
||||
// ignore
|
||||
return "";
|
||||
}
|
||||
|
||||
$nextName = static::nextChildName($node);
|
||||
$prevName = static::prevChildName($node);
|
||||
|
||||
$name = strtolower($node->nodeName);
|
||||
|
||||
// start whitespace
|
||||
switch ($name) {
|
||||
case "hr":
|
||||
return "---------------------------------------------------------------\n";
|
||||
|
||||
case "style":
|
||||
case "head":
|
||||
case "title":
|
||||
case "meta":
|
||||
case "script":
|
||||
// ignore these tags
|
||||
return "";
|
||||
|
||||
case "h1":
|
||||
case "h2":
|
||||
case "h3":
|
||||
case "h4":
|
||||
case "h5":
|
||||
case "h6":
|
||||
case "ol":
|
||||
case "ul":
|
||||
// add two newlines, second line is added below
|
||||
$output = "\n";
|
||||
break;
|
||||
|
||||
case "td":
|
||||
case "th":
|
||||
// add tab char to separate table fields
|
||||
$output = "\t";
|
||||
break;
|
||||
|
||||
case "tr":
|
||||
case "p":
|
||||
case "div":
|
||||
// add one line
|
||||
$output = "\n";
|
||||
break;
|
||||
|
||||
case "li":
|
||||
$output = "- ";
|
||||
break;
|
||||
|
||||
default:
|
||||
// print out contents of unknown tags
|
||||
$output = "";
|
||||
break;
|
||||
}
|
||||
|
||||
// debug
|
||||
//$output .= "[$name,$nextName]";
|
||||
|
||||
if (isset($node->childNodes)) {
|
||||
for ($i = 0; $i < $node->childNodes->length; $i++) {
|
||||
$n = $node->childNodes->item($i);
|
||||
|
||||
$text = static::iterateOverNode($n);
|
||||
|
||||
$output .= $text;
|
||||
}
|
||||
}
|
||||
|
||||
// end whitespace
|
||||
switch ($name) {
|
||||
case "h1":
|
||||
case "h2":
|
||||
case "h3":
|
||||
case "h4":
|
||||
case "h5":
|
||||
case "h6":
|
||||
$output .= "\n";
|
||||
break;
|
||||
|
||||
case "p":
|
||||
case "br":
|
||||
// add one line
|
||||
if ($nextName != "div")
|
||||
$output .= "\n";
|
||||
break;
|
||||
|
||||
case "div":
|
||||
// add one line only if the next child isn't a div
|
||||
if ($nextName != "div" && $nextName != null)
|
||||
$output .= "\n";
|
||||
break;
|
||||
|
||||
case "a":
|
||||
// links are returned in [text](link) format
|
||||
$href = $node->getAttribute("href");
|
||||
|
||||
$output = trim($output);
|
||||
|
||||
// remove double [[ ]] s from linking images
|
||||
if (substr($output, 0, 1) == "[" && substr($output, -1) == "]") {
|
||||
$output = substr($output, 1, strlen($output) - 2);
|
||||
|
||||
// for linking images, the title of the <a> overrides the title of the <img>
|
||||
if ($node->getAttribute("title")) {
|
||||
$output = $node->getAttribute("title");
|
||||
}
|
||||
}
|
||||
|
||||
// if there is no link text, but a title attr
|
||||
if (!$output && $node->getAttribute("title")) {
|
||||
$output = $node->getAttribute("title");
|
||||
}
|
||||
|
||||
if ($href == null) {
|
||||
// it doesn't link anywhere
|
||||
if ($node->getAttribute("name") != null) {
|
||||
$output = "[$output]";
|
||||
}
|
||||
} else {
|
||||
if ($href == $output || $href == "mailto:$output" || $href == "http://$output" || $href == "https://$output") {
|
||||
// link to the same address: just use link
|
||||
$output;
|
||||
} else {
|
||||
// replace it
|
||||
if ($output) {
|
||||
$output = "[$output]($href)";
|
||||
} else {
|
||||
// empty string
|
||||
$output = $href;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// does the next node require additional whitespace?
|
||||
switch ($nextName) {
|
||||
case "h1": case "h2": case "h3": case "h4": case "h5": case "h6":
|
||||
$output .= "\n";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case "img":
|
||||
if ($node->getAttribute("title")) {
|
||||
$output = "[" . $node->getAttribute("title") . "]";
|
||||
} elseif ($node->getAttribute("alt")) {
|
||||
$output = "[" . $node->getAttribute("alt") . "]";
|
||||
} else {
|
||||
$output = "";
|
||||
}
|
||||
break;
|
||||
|
||||
case "li":
|
||||
$output .= "\n";
|
||||
break;
|
||||
|
||||
default:
|
||||
// do nothing
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
<?php
|
||||
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2010 Jevon Wright and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* or
|
||||
*
|
||||
* LGPL which is available at http://www.gnu.org/licenses/lgpl.html
|
||||
*
|
||||
*
|
||||
* Contributors:
|
||||
* Jevon Wright - initial API and implementation
|
||||
****************************************************************************/
|
||||
|
||||
namespace Html2Text;
|
||||
|
||||
class Html2TextException extends \Exception {
|
||||
var $more_info;
|
||||
|
||||
public function __construct($message = "", $more_info = "") {
|
||||
parent::__construct($message);
|
||||
$this->more_info = $more_info;
|
||||
}
|
||||
}
|
||||
@@ -195,16 +195,31 @@ class ApplicationContext
|
||||
/**
|
||||
* Returns the context as string with the format name1=value1&name2=value2....
|
||||
* @return string The context as a string to be appended to an href property
|
||||
*
|
||||
*/
|
||||
public function GetForLink()
|
||||
public function GetForLink(bool $bWithLeadingAmpersand = false)
|
||||
{
|
||||
// If there are no parameters, return an empty string
|
||||
if(empty($this->aValues)){
|
||||
return '';
|
||||
}
|
||||
|
||||
// Build the query string with ampersand separated parameters
|
||||
$aParams = array();
|
||||
foreach($this->aValues as $sName => $sValue)
|
||||
{
|
||||
$aParams[] = "c[$sName]".'='.urlencode($sValue);
|
||||
}
|
||||
return implode("&", $aParams);
|
||||
$sReturnValue = implode('&', $aParams);
|
||||
|
||||
// add the leading ampersand if requested
|
||||
if($bWithLeadingAmpersand){
|
||||
$sReturnValue = '&' . $sReturnValue;
|
||||
}
|
||||
|
||||
return $sReturnValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.0.0 N°2534 - dashboard: bug with autorefresh that deactivates filtering on organisation
|
||||
* Returns the params as c[menu]:..., c[org_id]:....
|
||||
@@ -382,7 +397,7 @@ class ApplicationContext
|
||||
$sUrl = call_user_func(array($sUrlMakerClass, 'MakeObjectUrl'), $sObjClass, $sObjKey);
|
||||
if (utils::StrLen($sUrl) > 0) {
|
||||
if ($bWithNavigationContext) {
|
||||
return $sUrl."&".$oAppContext->GetForLink();
|
||||
return $sUrl.$oAppContext->GetForLink(true);
|
||||
} else {
|
||||
return $sUrl;
|
||||
}
|
||||
|
||||
@@ -64,13 +64,13 @@ interface iLoginFSMExtension extends iLoginExtension
|
||||
* If a page is displayed, the action must exit at this point
|
||||
* if LoginWebPage::LOGIN_FSM_RETURN_ERROR is returned $iErrorCode must be set
|
||||
* if LoginWebPage::LOGIN_FSM_RETURN_OK is returned then the login is OK and terminated
|
||||
* if LoginWebPage::LOGIN_FSM_RETURN_IGNORE is returned then the FSM will proceed to next plugin or state
|
||||
* if LoginWebPage::LOGIN_FSM_CONTINUE is returned then the FSM will proceed to next plugin or state
|
||||
*
|
||||
* @api
|
||||
* @param string $sLoginState (see LoginWebPage::LOGIN_STATE_...)
|
||||
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
|
||||
*
|
||||
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
|
||||
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_CONTINUE
|
||||
*/
|
||||
public function LoginAction($sLoginState, &$iErrorCode);
|
||||
}
|
||||
@@ -83,7 +83,7 @@ interface iLoginFSMExtension extends iLoginExtension
|
||||
* * If a page is displayed, the action must exit at this point
|
||||
* * if LoginWebPage::LOGIN_FSM_RETURN_ERROR is returned $iErrorCode must be set
|
||||
* * if LoginWebPage::LOGIN_FSM_RETURN_OK is returned then the login is OK and terminated
|
||||
* * if LoginWebPage::LOGIN_FSM_RETURN_IGNORE is returned then the FSM will proceed to next plugin or to next state
|
||||
* * if LoginWebPage::LOGIN_FSM_CONTINUE is returned then the FSM will proceed to next plugin or to next state
|
||||
*
|
||||
* @api
|
||||
* @package LoginExtensibilityAPI
|
||||
@@ -136,7 +136,7 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
|
||||
* @api
|
||||
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
|
||||
*
|
||||
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
|
||||
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_CONTINUE
|
||||
*/
|
||||
protected function OnStart(&$iErrorCode)
|
||||
{
|
||||
@@ -150,7 +150,7 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
|
||||
* @api
|
||||
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
|
||||
*
|
||||
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
|
||||
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_CONTINUE
|
||||
*/
|
||||
protected function OnModeDetection(&$iErrorCode)
|
||||
{
|
||||
@@ -167,7 +167,7 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
|
||||
* @api
|
||||
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
|
||||
*
|
||||
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
|
||||
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_CONTINUE
|
||||
*/
|
||||
protected function OnReadCredentials(&$iErrorCode)
|
||||
{
|
||||
@@ -181,7 +181,7 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
|
||||
* @api
|
||||
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
|
||||
*
|
||||
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
|
||||
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_CONTINUE
|
||||
*/
|
||||
protected function OnCheckCredentials(&$iErrorCode)
|
||||
{
|
||||
@@ -192,7 +192,7 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
|
||||
* @api
|
||||
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
|
||||
*
|
||||
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
|
||||
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_CONTINUE
|
||||
*/
|
||||
protected function OnCredentialsOK(&$iErrorCode)
|
||||
{
|
||||
@@ -203,7 +203,7 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
|
||||
* @api
|
||||
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
|
||||
*
|
||||
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
|
||||
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_CONTINUE
|
||||
*/
|
||||
protected function OnUsersOK(&$iErrorCode)
|
||||
{
|
||||
@@ -214,7 +214,7 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
|
||||
* @api
|
||||
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
|
||||
*
|
||||
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
|
||||
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_CONTINUE
|
||||
*/
|
||||
protected function OnConnected(&$iErrorCode)
|
||||
{
|
||||
@@ -225,7 +225,7 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
|
||||
* @api
|
||||
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
|
||||
*
|
||||
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
|
||||
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_CONTINUE
|
||||
*/
|
||||
protected function OnError(&$iErrorCode)
|
||||
{
|
||||
@@ -1711,6 +1711,11 @@ interface iRestServiceProvider
|
||||
public function ExecOperation($sVersion, $sVerb, $aParams);
|
||||
}
|
||||
|
||||
interface iRestInputSanitizer
|
||||
{
|
||||
public function SanitizeJsonInput(string $sJsonInput): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Minimal REST response structure. Derive this structure to add response data and error codes.
|
||||
*
|
||||
@@ -1802,6 +1807,14 @@ class RestResult
|
||||
* @api
|
||||
*/
|
||||
public $message;
|
||||
|
||||
/**
|
||||
* Sanitize the content of this result to hide sensitive information
|
||||
*/
|
||||
public function SanitizeContent()
|
||||
{
|
||||
// The default implementation does nothing
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -311,7 +311,7 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
{
|
||||
$sParams .= $sName.'='.urlencode($value).'&'; // Always add a trailing &
|
||||
}
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/'.$oObj->GetUIPage().'?'.$sParams.'class='.get_class($oObj).'&id='.$oObj->getKey().'&'.$oAppContext->GetForLink().'&a=1';
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/'.$oObj->GetUIPage().'?'.$sParams.'class='.get_class($oObj).'&id='.$oObj->getKey().$oAppContext->GetForLink(true).'&a=1';
|
||||
$oPage->add_early_script(<<<JS
|
||||
if (!sessionStorage.getItem('$sSessionStorageKey'))
|
||||
{
|
||||
@@ -1113,8 +1113,10 @@ HTML
|
||||
}
|
||||
|
||||
// Note: DisplayBareHeader is called before adding $oObjectDetails to the page, so it can inject HTML before it through $oPage.
|
||||
/** @var iTopWebPage $oPage */
|
||||
/** @var \iTopWebPage $oPage */
|
||||
$oKPI = new ExecutionKPI();
|
||||
$aHeadersBlocks = $this->DisplayBareHeader($oPage, $bEditMode);
|
||||
$oKPI->ComputeStatsForExtension($this, 'DisplayBareHeader');
|
||||
if (false === empty($aHeadersBlocks['subtitle'])) {
|
||||
$oObjectDetails->AddSubTitleBlocks($aHeadersBlocks['subtitle']);
|
||||
}
|
||||
@@ -1127,8 +1129,12 @@ HTML
|
||||
$oPage->AddTabContainer(OBJECT_PROPERTIES_TAB, '', $oObjectDetails);
|
||||
$oPage->SetCurrentTabContainer(OBJECT_PROPERTIES_TAB);
|
||||
$oPage->SetCurrentTab('UI:PropertiesTab');
|
||||
$oKPI = new ExecutionKPI();
|
||||
$this->DisplayBareProperties($oPage, $bEditMode);
|
||||
$oKPI->ComputeStatsForExtension($this, 'DisplayBareProperties');
|
||||
$oKPI = new ExecutionKPI();
|
||||
$this->DisplayBareRelations($oPage, $bEditMode);
|
||||
$oKPI->ComputeStatsForExtension($this, 'DisplayBareRelations');
|
||||
|
||||
|
||||
// Note: Adding the JS snippet which enables the image upload should have been done directly by the ActivityPanel which would have kept the independance principle
|
||||
@@ -1331,7 +1337,7 @@ HTML
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$aHeader['friendlyname'] = ['label' => MetaModel::GetName($sClassName)];
|
||||
foreach ($aList[$sAlias] as $sAttCodeEx => $oAttDef) {
|
||||
$sColLabel = $bLocalize ? MetaModel::GetLabel($sClassName, $sAttCodeEx) : $sAttCodeEx;
|
||||
|
||||
@@ -1352,6 +1358,7 @@ HTML
|
||||
$aRow = [];
|
||||
foreach ($aAuthorizedClasses as $sAlias => $sClassName) {
|
||||
$oObj = $aObjects[$sAlias];
|
||||
$aRow["friendlyname"] = $oObj->Get('friendlyname');
|
||||
foreach ($aList[$sAlias] as $sAttCodeEx => $oAttDef) {
|
||||
if (is_null($oObj)) {
|
||||
$aRow[$oAttDef->GetCode()] = '';
|
||||
@@ -3065,7 +3072,7 @@ JS
|
||||
$oPage->add($oAppContext->GetForForm());
|
||||
|
||||
// Hook the cancel button via jQuery so that it can be unhooked easily as well if needed
|
||||
$sDefaultUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search_form&class='.$sClass.'&'.$oAppContext->GetForLink();
|
||||
$sDefaultUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search_form&class='.$sClass.$oAppContext->GetForLink(true);
|
||||
|
||||
$sCancelButtonOnClickScript = "let fOnClick{$this->m_iFormId}CancelButton = ";
|
||||
if(isset($aExtraParams['js_handlers']['cancel_button_on_click'])){
|
||||
@@ -3073,7 +3080,7 @@ JS
|
||||
} else {
|
||||
$sCancelButtonOnClickScript .= "function() { BackToDetails('$sClass', $iKey, '$sDefaultUrl', $sJSToken)};";
|
||||
}
|
||||
$sCancelButtonOnClickScript .= "$('#form_{$this->m_iFormId} button.cancel').on('click', fOnClick{$this->m_iFormId}CancelButton);";
|
||||
$sCancelButtonOnClickScript .= "$('#form_{$this->m_iFormId} button.cancel').on('click.navigation.itop', fOnClick{$this->m_iFormId}CancelButton);";
|
||||
$oPage->add_ready_script($sCancelButtonOnClickScript);
|
||||
|
||||
$iFieldsCount = count($aFieldsMap);
|
||||
@@ -3439,8 +3446,18 @@ EOF
|
||||
}
|
||||
$sInputType = '';
|
||||
$sInputId = 'att_'.$iFieldIndex;
|
||||
$value = $this->Get($sAttCode);
|
||||
$sDisplayValue = $this->GetEditValue($sAttCode);
|
||||
if ($oAttDef instanceof AttributeDateTime && !$oAttDef->IsNullAllowed() && $value === $oAttDef->GetNullValue()) {
|
||||
$value = $oAttDef->GetDefaultValue($this);
|
||||
if ($value !== $oAttDef->GetNullValue()) {
|
||||
// Set default date
|
||||
$this->Set($sAttCode, $value);
|
||||
$sDisplayValue = $this->GetEditValue($sAttCode);
|
||||
}
|
||||
}
|
||||
$sHTMLValue = cmdbAbstractObject::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef,
|
||||
$this->Get($sAttCode), $this->GetEditValue($sAttCode), $sInputId, '', $iExpectCode,
|
||||
$value, $sDisplayValue, $sInputId, '', $iExpectCode,
|
||||
$aArgs, true, $sInputType);
|
||||
$aAttrib = array(
|
||||
'label' => '<span>'.$oAttDef->GetLabel().'</span>',
|
||||
@@ -4570,6 +4587,8 @@ HTML;
|
||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
||||
{
|
||||
$sExtensionClass = get_class($oExtensionInstance);
|
||||
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnDBInsert()");
|
||||
$oKPI = new ExecutionKPI();
|
||||
$oExtensionInstance->OnDBInsert($oNewObj, self::GetCurrentChange());
|
||||
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBInsert');
|
||||
@@ -4651,7 +4670,22 @@ HTML;
|
||||
return $oDeletionPlan;
|
||||
}
|
||||
|
||||
protected function PostDeleteActions(): void
|
||||
final protected function PreDeleteActions(): void
|
||||
{
|
||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
||||
{
|
||||
$sExtensionClass = get_class($oExtensionInstance);
|
||||
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnDBDelete()");
|
||||
$oKPI = new ExecutionKPI();
|
||||
$oExtensionInstance->OnDBDelete($this, self::GetCurrentChange());
|
||||
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBDelete');
|
||||
}
|
||||
|
||||
parent::PreDeleteActions();
|
||||
}
|
||||
|
||||
final protected function PostDeleteActions(): void
|
||||
{
|
||||
parent::PostDeleteActions();
|
||||
}
|
||||
@@ -4665,6 +4699,8 @@ HTML;
|
||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
||||
{
|
||||
$sExtensionClass = get_class($oExtensionInstance);
|
||||
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnDBDelete()");
|
||||
$oKPI = new ExecutionKPI();
|
||||
$oExtensionInstance->OnDBDelete($this, self::GetCurrentChange());
|
||||
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBDelete');
|
||||
@@ -4686,6 +4722,7 @@ HTML;
|
||||
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
||||
{
|
||||
$sExtensionClass = get_class($oExtensionInstance);
|
||||
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnIsModified()");
|
||||
$oKPI = new ExecutionKPI();
|
||||
$bIsModified = $oExtensionInstance->OnIsModified($this);
|
||||
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnIsModified');
|
||||
@@ -4745,6 +4782,8 @@ HTML;
|
||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
||||
{
|
||||
$sExtensionClass = get_class($oExtensionInstance);
|
||||
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnCheckToWrite()");
|
||||
$oKPI = new ExecutionKPI();
|
||||
$aNewIssues = $oExtensionInstance->OnCheckToWrite($this);
|
||||
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnCheckToWrite');
|
||||
@@ -4795,6 +4834,8 @@ HTML;
|
||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
||||
{
|
||||
$sExtensionClass = get_class($oExtensionInstance);
|
||||
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnCheckToDelete()");
|
||||
$oKPI = new ExecutionKPI();
|
||||
$aNewIssues = $oExtensionInstance->OnCheckToDelete($this);
|
||||
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnCheckToDelete');
|
||||
@@ -5899,14 +5940,14 @@ JS
|
||||
*
|
||||
* @since 3.1.0
|
||||
*/
|
||||
final protected function FireEventCheckToWrite(): void
|
||||
final protected function FireEventCheckToWrite(?string $sStimulusBeingApplied): void
|
||||
{
|
||||
$this->FireEvent(EVENT_DB_CHECK_TO_WRITE, ['is_new' => $this->IsNew()]);
|
||||
$this->FireEvent(EVENT_DB_CHECK_TO_WRITE, ['is_new' => $this->IsNew(), 'stimulus_applied' => $sStimulusBeingApplied]);
|
||||
}
|
||||
|
||||
final protected function FireEventBeforeWrite()
|
||||
final protected function FireEventBeforeWrite(?string $sStimulusBeingApplied)
|
||||
{
|
||||
$this->FireEvent(EVENT_DB_BEFORE_WRITE, ['is_new' => $this->IsNew()]);
|
||||
$this->FireEvent(EVENT_DB_BEFORE_WRITE, ['is_new' => $this->IsNew(), 'stimulus_applied' => $sStimulusBeingApplied]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -5918,11 +5959,11 @@ JS
|
||||
* @throws \CoreException
|
||||
* @since 3.1.0
|
||||
*/
|
||||
final protected function FireEventAfterWrite(array $aChanges, bool $bIsNew): void
|
||||
final protected function FireEventAfterWrite(array $aChanges, bool $bIsNew, ?string $sStimulusBeingApplied): void
|
||||
{
|
||||
$this->NotifyAttachedObjectsOnLinkClassModification();
|
||||
$this->RemoveObjectAwaitingEventDbLinksChanged(get_class($this), $this->GetKey());
|
||||
$this->FireEvent(EVENT_DB_AFTER_WRITE, ['is_new' => $bIsNew, 'changes' => $aChanges]);
|
||||
$this->FireEvent(EVENT_DB_AFTER_WRITE, ['is_new' => $bIsNew, 'changes' => $aChanges, 'stimulus_applied' => $sStimulusBeingApplied]);
|
||||
}
|
||||
|
||||
//////////////
|
||||
@@ -6063,7 +6104,9 @@ JS
|
||||
// We want to avoid launching the listener twice, first here, and secondly after saving the Ticket in the listener
|
||||
// By disabling the event to be fired, we can remove the current object from the attribute !
|
||||
$oObject = MetaModel::GetObject($sClass, $sId, false);
|
||||
self::FireEventDbLinksChangedForObject($oObject);
|
||||
if (!is_null($oObject)) {
|
||||
self::FireEventDbLinksChangedForObject($oObject);
|
||||
}
|
||||
self::RemoveObjectAwaitingEventDbLinksChanged($sClass, $sId);
|
||||
}
|
||||
|
||||
@@ -6071,13 +6114,11 @@ JS
|
||||
{
|
||||
self::SetEventDBLinksChangedBlocked(true);
|
||||
// N°6408 The object can have been deleted
|
||||
if (!is_null($oObject)) {
|
||||
$oObject->FireEvent(EVENT_DB_LINKS_CHANGED);
|
||||
$oObject->FireEvent(EVENT_DB_LINKS_CHANGED);
|
||||
|
||||
// Update the object if needed
|
||||
if (count($oObject->ListChanges()) !== 0) {
|
||||
$oObject->DBUpdate();
|
||||
}
|
||||
// Update the object if needed
|
||||
if (count($oObject->ListChanges()) !== 0) {
|
||||
$oObject->DBUpdate();
|
||||
}
|
||||
cmdbAbstractObject::SetEventDBLinksChangedBlocked(false);
|
||||
}
|
||||
@@ -6155,9 +6196,9 @@ JS
|
||||
* @inheritDoc
|
||||
* @throws \CoreException
|
||||
*/
|
||||
final protected function FireEventComputeValues(): void
|
||||
final protected function FireEventComputeValues(?string $sStimulusBeingApplied): void
|
||||
{
|
||||
$this->FireEvent(EVENT_DB_COMPUTE_VALUES);
|
||||
$this->FireEvent(EVENT_DB_COMPUTE_VALUES, ['is_new' => $this->IsNew(), 'stimulus_applied' => $sStimulusBeingApplied]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -524,9 +524,7 @@ EOF
|
||||
*/
|
||||
public function Render($oPage, $bEditMode = false, $aExtraParams = array(), $bCanEdit = true)
|
||||
{
|
||||
if (!array_key_exists('dashboard_div_id', $aExtraParams)) {
|
||||
$aExtraParams['dashboard_div_id'] = utils::Sanitize($this->GetId(), '', 'element_identifier');
|
||||
}
|
||||
$aExtraParams['dashboard_div_id'] = utils::Sanitize($aExtraParams['dashboard_div_id'] ?? null, $this->GetId(), utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER);
|
||||
|
||||
/** @var \DashboardLayoutMultiCol $oLayout */
|
||||
$oLayout = new $this->sLayoutClass();
|
||||
@@ -1052,7 +1050,7 @@ EOF
|
||||
$sSelectorHtml .= '</div>';
|
||||
|
||||
$sFile = addslashes($this->GetDefinitionFile());
|
||||
$sReloadURL = $this->GetReloadURL();
|
||||
$sReloadURL = json_encode($this->GetReloadURL());
|
||||
|
||||
$bFromDashboardPage = isset($aAjaxParams['from_dashboard_page']) ? isset($aAjaxParams['from_dashboard_page']) : false;
|
||||
if ($bFromDashboardPage) {
|
||||
@@ -1141,7 +1139,6 @@ JS
|
||||
->AddCSSClass('ibo-action-button');
|
||||
|
||||
$oToolbar->AddSubBlock($oActionButton);
|
||||
|
||||
$aActions = array();
|
||||
$sFile = addslashes(utils::LocalPath($this->sDefinitionFile));
|
||||
$sJSExtraParams = json_encode($aExtraParams);
|
||||
@@ -1166,7 +1163,7 @@ JS
|
||||
$oToolbar->AddSubBlock($oActionButton)
|
||||
->AddSubBlock($oActionsMenu);
|
||||
|
||||
$sReloadURL = $this->GetReloadURL();
|
||||
$sReloadURL = json_encode($this->GetReloadURL());
|
||||
$oPage->add_script(
|
||||
<<<EOF
|
||||
function EditDashboard(sId, sDashboardFile, aExtraParams)
|
||||
@@ -1266,15 +1263,14 @@ EOF
|
||||
$sOkButtonLabel = Dict::S('UI:Button:Save');
|
||||
$sCancelButtonLabel = Dict::S('UI:Button:Cancel');
|
||||
|
||||
$sId = utils::HtmlEntities($this->sId);
|
||||
$sLayoutClass = utils::HtmlEntities($this->sLayoutClass);
|
||||
$sId = json_encode($this->sId);
|
||||
$sLayoutClass = json_encode($this->sLayoutClass);
|
||||
$sAutoReload = $this->bAutoReload ? 'true' : 'false';
|
||||
$sAutoReloadSec = (string) $this->iAutoReloadSec;
|
||||
$sTitle = utils::HtmlEntities($this->sTitle);
|
||||
$sFile = utils::HtmlEntities($this->GetDefinitionFile());
|
||||
$sFileForJS = json_encode($this->GetDefinitionFile());
|
||||
$sTitle = json_encode($this->sTitle);
|
||||
$sFile = json_encode($this->GetDefinitionFile());
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php';
|
||||
$sReloadURL = $this->GetReloadURL();
|
||||
$sReloadURL = json_encode($this->GetReloadURL());
|
||||
|
||||
$sExitConfirmationMessage = addslashes(Dict::S('UI:NavigateAwayConfirmationMessage'));
|
||||
$sCancelConfirmationMessage = addslashes(Dict::S('UI:CancelConfirmationMessage'));
|
||||
@@ -1328,15 +1324,15 @@ $('#dashboard_editor').dialog({
|
||||
});
|
||||
|
||||
$('#dashboard_editor .ui-layout-center').runtimedashboard({
|
||||
dashboard_id: '$sId',
|
||||
layout_class: '$sLayoutClass',
|
||||
title: '$sTitle',
|
||||
dashboard_id: $sId,
|
||||
layout_class: $sLayoutClass,
|
||||
title: $sTitle,
|
||||
auto_reload: $sAutoReload,
|
||||
auto_reload_sec: $sAutoReloadSec,
|
||||
submit_to: '$sUrl',
|
||||
submit_parameters: {operation: 'save_dashboard', file: {$sFileForJS}, extra_params: $sJSExtraParams, reload_url: '$sReloadURL'},
|
||||
submit_parameters: {operation: 'save_dashboard', file: $sFile, extra_params: $sJSExtraParams, reload_url: '$sReloadURL'},
|
||||
render_to: '$sUrl',
|
||||
render_parameters: {operation: 'render_dashboard', file: {$sFileForJS}, extra_params: $sJSExtraParams, reload_url: '$sReloadURL'},
|
||||
render_parameters: {operation: 'render_dashboard', file: $sFile, extra_params: $sJSExtraParams, reload_url: '$sReloadURL'},
|
||||
new_dashlet_parameters: {operation: 'new_dashlet'}
|
||||
});
|
||||
|
||||
|
||||
@@ -2138,7 +2138,7 @@ class DashletHeaderDynamic extends Dashlet
|
||||
$oSet = new DBObjectSet($oFilter);
|
||||
$iCount = $oSet->Count();
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sHyperlink = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&'.$oAppContext->GetForLink().'&filter='.rawurlencode($oFilter->serialize());
|
||||
$sHyperlink = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search'.$oAppContext->GetForLink(true).'&filter='.rawurlencode($oFilter->serialize());
|
||||
$oSubTitle->AddHtml('<a class="summary" href="'.$sHyperlink.'">'.Dict::Format(str_replace('_', ':', $sSubtitle), $iCount).'</a>');
|
||||
|
||||
return $oPanel;
|
||||
|
||||
@@ -185,6 +185,11 @@
|
||||
<decoration_classes>fas fa-cog</decoration_classes>
|
||||
</style>
|
||||
</menu>
|
||||
<menu id="Integrations" xsi:type="TemplateMenuNode" _delta="define">
|
||||
<rank>50</rank>
|
||||
<parent>ConfigurationTools</parent>
|
||||
<template_file/>
|
||||
</menu>
|
||||
<menu id="DataSources" xsi:type="OQLMenuNode" _delta="define">
|
||||
<rank>20</rank>
|
||||
<parent>ConfigurationTools</parent>
|
||||
@@ -233,6 +238,10 @@ The object can be modified.]]></description>
|
||||
<description>Creation flag</description>
|
||||
<type>boolean</type>
|
||||
</event_datum>
|
||||
<event_datum id="stimulus_applied">
|
||||
<description>Life cycle stimulus applied (null if not within a transition)</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
<event_datum id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
@@ -258,6 +267,10 @@ Call $this->AddCheckWarning($sWarningMessage) to display a warning.
|
||||
<description>Creation flag</description>
|
||||
<type>boolean</type>
|
||||
</event_datum>
|
||||
<event_datum id="stimulus_applied">
|
||||
<description>Life cycle stimulus applied (null if not within a transition)</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
<event_datum id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
@@ -285,6 +298,10 @@ The modifications can be propagated to other objects.]]></description>
|
||||
<description><![CDATA[For updates, the list of changes done during this operation]]></description>
|
||||
<type>array</type>
|
||||
</event_datum>
|
||||
<event_datum id="stimulus_applied">
|
||||
<description>Life cycle stimulus applied (null if not within a transition)</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
<event_datum id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
@@ -415,6 +432,14 @@ The only action allowed is to deny transitions with $this->DenyTransition($sTran
|
||||
<description>The object inserted</description>
|
||||
<type>DBObject</type>
|
||||
</event_datum>
|
||||
<event_datum id="is_new">
|
||||
<description>Creation flag</description>
|
||||
<type>boolean</type>
|
||||
</event_datum>
|
||||
<event_datum id="stimulus_applied">
|
||||
<description>Life cycle stimulus applied (null if not within a transition)</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
<event_datum id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
|
||||
@@ -706,7 +706,7 @@ class DisplayBlock
|
||||
if ($bDoSearch)
|
||||
{
|
||||
// Keep the table_id identifying this table if we're performing a search
|
||||
$sTableId = utils::ReadParam('_table_id_', null, false, 'raw_data');
|
||||
$sTableId = utils::ReadParam('_table_id_', null, false, utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER);
|
||||
if ($sTableId != null)
|
||||
{
|
||||
$aExtraParams['table_id'] = $sTableId;
|
||||
@@ -1124,7 +1124,7 @@ JS
|
||||
$oSingleGroupByValueFilter->SetShowObsoleteData($this->m_bShowObsoleteData);
|
||||
}
|
||||
$sHyperlink = utils::GetAbsoluteUrlAppRoot()
|
||||
.'pages/UI.php?operation=search&'.$oAppContext->GetForLink()
|
||||
.'pages/UI.php?operation=search'.$oAppContext->GetForLink(true)
|
||||
.'&filter='.rawurlencode($oSingleGroupByValueFilter->serialize());
|
||||
$aCounts[$sStateValue] = ['link' => $sHyperlink, 'label' => $aCounts[$sStateValue]];
|
||||
}
|
||||
@@ -1232,7 +1232,7 @@ JS
|
||||
$iCount = $this->m_oSet->Count();
|
||||
$sClassLabel = MetaModel::GetName($sClass);
|
||||
$sClassIconUrl = MetaModel::GetClassIcon($sClass, false);
|
||||
$sHyperlink = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&'.$oAppContext->GetForLink().'&filter='.rawurlencode($this->m_oFilter->serialize());
|
||||
$sHyperlink = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search'.$oAppContext->GetForLink(true).'&filter='.rawurlencode($this->m_oFilter->serialize());
|
||||
|
||||
$aExtraParams['query_params'] = $this->m_oFilter->GetInternalParams();
|
||||
$aRefreshParams = [
|
||||
@@ -1241,7 +1241,7 @@ JS
|
||||
];
|
||||
|
||||
if (UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY)) {
|
||||
$sCreateActionUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=new&class='.$sClass.'&'.$oAppContext->GetForLink();
|
||||
$sCreateActionUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=new&class='.$sClass.$oAppContext->GetForLink(true);
|
||||
$sCreateActionLabel = Dict::Format('UI:Button:Create');
|
||||
$oBlock = DashletFactory::MakeForDashletBadge($sClassIconUrl, $sHyperlink, $iCount, $sClassLabel, $sCreateActionUrl,
|
||||
$sCreateActionLabel, $aRefreshParams);
|
||||
@@ -1289,7 +1289,7 @@ JS
|
||||
|
||||
$aData = array();
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sParams = $oAppContext->GetForLink();
|
||||
$sParams = $oAppContext->GetForLink(true);
|
||||
foreach ($aGroupBy as $iRow => $iCount) {
|
||||
// Build the search for this subset
|
||||
$oSubsetSearch = $this->m_oFilter->DeepClone();
|
||||
@@ -1304,7 +1304,7 @@ JS
|
||||
|
||||
$aData[] = array(
|
||||
'group' => $aLabels[$iRow],
|
||||
'value' => "<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=search&dosearch=1&$sParams&filter=$sFilter\">$iCount</a>"
|
||||
'value' => "<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=search&dosearch=1$sParams&filter=$sFilter\">$iCount</a>"
|
||||
); // TO DO: add the context information
|
||||
}
|
||||
$aAttribs = array(
|
||||
@@ -1636,7 +1636,7 @@ JS
|
||||
$sGroupByExpr = isset($aExtraParams['group_by_expr']) ? '¶ms[group_by_expr]='.$aExtraParams['group_by_expr'] : '';
|
||||
$sFilter = $this->m_oFilter->serialize(false, $aQueryParams);
|
||||
$oContext = new ApplicationContext();
|
||||
$sContextParam = $oContext->GetForLink();
|
||||
$sContextParam = $oContext->GetForLink(true);
|
||||
$sAggregationFunction = isset($aExtraParams['aggregation_function']) ? $aExtraParams['aggregation_function'] : '';
|
||||
$sAggregationAttr = isset($aExtraParams['aggregation_attribute']) ? $aExtraParams['aggregation_attribute'] : '';
|
||||
$sLimit = isset($aExtraParams['limit']) ? $aExtraParams['limit'] : '';
|
||||
@@ -1644,7 +1644,7 @@ JS
|
||||
$sOrderDirection = isset($aExtraParams['order_direction']) ? $aExtraParams['order_direction'] : '';
|
||||
|
||||
if (isset($aExtraParams['group_by_label'])) {
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart¶ms[group_by]=$sGroupBy{$sGroupByExpr}¶ms[group_by_label]={$aExtraParams['group_by_label']}¶ms[chart_type]=$sChartType¶ms[currentId]=$sChartId{$iChartCounter}¶ms[order_direction]=$sOrderDirection¶ms[order_by]=$sOrderBy¶ms[limit]=$sLimit¶ms[aggregation_function]=$sAggregationFunction¶ms[aggregation_attribute]=$sAggregationAttr&id=$sChartId{$iChartCounter}&filter=".rawurlencode($sFilter).'&'.$sContextParam;
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart¶ms[group_by]=$sGroupBy{$sGroupByExpr}¶ms[group_by_label]={$aExtraParams['group_by_label']}¶ms[chart_type]=$sChartType¶ms[currentId]=$sChartId{$iChartCounter}¶ms[order_direction]=$sOrderDirection¶ms[order_by]=$sOrderBy¶ms[limit]=$sLimit¶ms[aggregation_function]=$sAggregationFunction¶ms[aggregation_attribute]=$sAggregationAttr&id=$sChartId{$iChartCounter}&filter=".rawurlencode($sFilter).$sContextParam;
|
||||
} else {
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart¶ms[group_by]=$sGroupBy{$sGroupByExpr}¶ms[chart_type]=$sChartType¶ms[currentId]=$sChartId{$iChartCounter}¶ms[order_direction]=$sOrderDirection¶ms[order_by]=$sOrderBy¶ms[limit]=$sLimit¶ms[aggregation_function]=$sAggregationFunction¶ms[aggregation_attribute]=$sAggregationAttr&id=$sChartId{$iChartCounter}&filter=".rawurlencode($sFilter).'&'.$sContextParam;
|
||||
}
|
||||
@@ -1681,24 +1681,24 @@ JS
|
||||
$oBlock = null;
|
||||
$sJSURLs = '';
|
||||
|
||||
$oContext = new ApplicationContext();
|
||||
$sContextParam = $oContext->GetForLink(true);
|
||||
|
||||
if (isset($aExtraParams['group_by'])) {
|
||||
$this->MakeGroupByQuery($aExtraParams, $oGroupByExp, $sGroupByLabel, $aGroupBy, $sAggregationFunction, $sFctVar, $sAggregationAttr, $sSql);
|
||||
$aRes = CMDBSource::QueryToArray($sSql);
|
||||
$oContext = new ApplicationContext();
|
||||
$sContextParam = $oContext->GetForLink();
|
||||
/** @var AttributeDefinition $oGroupByAttDef */
|
||||
$oGroupByAttDef = $aGroupBy["grouped_by_1"]->GetAttDef();
|
||||
|
||||
|
||||
|
||||
$iTotalCount = 0;
|
||||
$aURLs = array();
|
||||
|
||||
foreach ($aRes as $iRow => $aRow) {
|
||||
$sValue = $aRow['grouped_by_1'];
|
||||
$sPlainTextValue = $oGroupByAttDef->GetValueLabel($sValue);
|
||||
$sHtmlValue = utils::EscapeHtml($sPlainTextValue);
|
||||
$sHtmlValue = $oGroupByExp->MakeValueLabel($this->m_oFilter, $sValue, $sValue);
|
||||
$iTotalCount += $aRow['_itop_count_'];
|
||||
$aValues[] = array(
|
||||
'label' => $sPlainTextValue,
|
||||
'label' => html_entity_decode(strip_tags($sHtmlValue), ENT_QUOTES, 'UTF-8'),
|
||||
'label_html' => $sHtmlValue,
|
||||
'value' => (float)$aRow[$sFctVar],
|
||||
);
|
||||
@@ -1708,14 +1708,14 @@ JS
|
||||
$oSubsetSearch = $this->m_oFilter->DeepClone();
|
||||
$oCondition = new BinaryExpression($oGroupByExp, '=', new ScalarExpression($sValue));
|
||||
$oSubsetSearch->AddConditionExpression($oCondition);
|
||||
$aURLs[] = utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=search&format=html&filter=".rawurlencode($oSubsetSearch->serialize()).'&'.$sContextParam;
|
||||
$aURLs[] = utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=search&format=html&filter=".rawurlencode($oSubsetSearch->serialize()).$sContextParam;
|
||||
}
|
||||
$sJSURLs = json_encode($aURLs);
|
||||
}
|
||||
if (isset($aExtraParams['group_by_label'])) {
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart¶ms[group_by]=$aExtraParams[group_by]¶ms[group_by_label]={$aExtraParams['group_by_label']}¶ms[chart_type]=$sChartType¶ms[currentId]=$aExtraParams[currentId]¶ms[order_direction]=$aExtraParams[order_direction]¶ms[order_by]=$aExtraParams[order_by]¶ms[limit]=$aExtraParams[limit]¶ms[aggregation_function]=$sAggregationFunction¶ms[aggregation_attribute]=$sAggregationAttr&id=$sId&filter=".rawurlencode($this->m_oFilter->ToOQL()).'&'.$sContextParam;
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart¶ms[group_by]=$aExtraParams[group_by]¶ms[group_by_label]={$aExtraParams['group_by_label']}¶ms[chart_type]=$sChartType¶ms[currentId]=$aExtraParams[currentId]¶ms[order_direction]=$aExtraParams[order_direction]¶ms[order_by]=$aExtraParams[order_by]¶ms[limit]=$aExtraParams[limit]¶ms[aggregation_function]=$sAggregationFunction¶ms[aggregation_attribute]=$sAggregationAttr&id=$sId&filter=".rawurlencode($this->m_oFilter->ToOQL()).$sContextParam;
|
||||
} else {
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart¶ms[group_by]=$aExtraParams[group_by]¶ms[chart_type]=$sChartType¶ms[currentId]=$aExtraParams[currentId]¶ms[order_direction]=$aExtraParams[order_direction]¶ms[order_by]=$aExtraParams[order_by]¶ms[limit]=$aExtraParams[limit]¶ms[aggregation_function]=$sAggregationFunction¶ms[aggregation_attribute]=$sAggregationAttr&id=$sId&filter=".rawurlencode($this->m_oFilter->ToOQL()).'&'.$sContextParam;
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart¶ms[group_by]=$aExtraParams[group_by]¶ms[chart_type]=$sChartType¶ms[currentId]=$aExtraParams[currentId]¶ms[order_direction]=$aExtraParams[order_direction]¶ms[order_by]=$aExtraParams[order_by]¶ms[limit]=$aExtraParams[limit]¶ms[aggregation_function]=$sAggregationFunction¶ms[aggregation_attribute]=$sAggregationAttr&id=$sId&filter=".rawurlencode($this->m_oFilter->ToOQL()).$sContextParam;
|
||||
}
|
||||
|
||||
switch ($sChartType) {
|
||||
@@ -1788,7 +1788,7 @@ JS
|
||||
|
||||
$oBlock->sCsvFile = strtolower($this->m_oFilter->GetClass()).'.csv';
|
||||
$oBlock->sDownloadLink = utils::GetAbsoluteUrlAppRoot().'webservices/export.php?expression='.urlencode($this->m_oFilter->ToOQL(true)).'&format=csv&filename='.urlencode($oBlock->sCsvFile);
|
||||
$oBlock->sLinkToToggle = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&'.$oAppContext->GetForLink().'&filter='.rawurlencode($this->m_oFilter->serialize()).'&format=csv';
|
||||
$oBlock->sLinkToToggle = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search'.$oAppContext->GetForLink(true).'&filter='.rawurlencode($this->m_oFilter->serialize()).'&format=csv';
|
||||
// Pass the parameters via POST, since expression may be very long
|
||||
$aParamsToPost = array(
|
||||
'expression' => $this->m_oFilter->ToOQL(true),
|
||||
@@ -1888,10 +1888,7 @@ class MenuBlock extends DisplayBlock
|
||||
&& (!isset($aExtraParams['menu']) || $aExtraParams['menu'] === "1" || $aExtraParams['menu'] === true)
|
||||
) {
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sContext = $oAppContext->GetForLink();
|
||||
if (utils::IsNotNullOrEmptyString($sContext)) {
|
||||
$sContext = '&'.$sContext;
|
||||
}
|
||||
$sContext = $oAppContext->GetForLink(true);
|
||||
|
||||
|
||||
$sFilter = $this->GetFilter()->serialize();
|
||||
@@ -2027,8 +2024,8 @@ class MenuBlock extends DisplayBlock
|
||||
$sSelectedClassName = MetaModel::GetName($sSelectedClass);
|
||||
|
||||
// Check rights on class
|
||||
$bIsBulkModifyAllowed = (!MetaModel::IsAbstract($sSelectedClass)) && UserRights::IsActionAllowed($sSelectedClass, UR_ACTION_BULK_MODIFY, $oSet) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject'));
|
||||
$bIsBulkDeleteAllowed = (bool) UserRights::IsActionAllowed($sSelectedClass, UR_ACTION_BULK_DELETE, $sSelectedClass);
|
||||
$bIsBulkModifyAllowed = (!MetaModel::IsAbstract($sSelectedClass)) && UserRights::IsActionAllowed($sSelectedClass, UR_ACTION_BULK_MODIFY) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject'));
|
||||
$bIsBulkDeleteAllowed = (bool) UserRights::IsActionAllowed($sSelectedClass, UR_ACTION_BULK_DELETE);
|
||||
|
||||
// Refine filter on selected class so bullk actions occur on the right class
|
||||
$oSelectedClassFilter = $this->GetFilter()->DeepClone();
|
||||
@@ -2581,11 +2578,8 @@ class MenuBlock extends DisplayBlock
|
||||
$sUrl = "{$sRootUrl}pages/{$sUIPage}?{$sUrlParams}";
|
||||
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sContext = $oAppContext->GetForLink();
|
||||
if (utils::IsNotNullOrEmptyString($sContext)) {
|
||||
$sUrl .= '&'.$sContext;
|
||||
}
|
||||
$sContext = $oAppContext->GetForLink(true);
|
||||
|
||||
return $sUrl;
|
||||
return $sUrl . $sContext;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,12 +40,16 @@ class CoreCannotSaveObjectException extends CoreException
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @since 3.2.3 add param $bWithHeader
|
||||
*/
|
||||
public function getHtmlMessage()
|
||||
public function getHtmlMessage($bWithHeader = false)
|
||||
{
|
||||
$sTitle = Dict::S('UI:Error:SaveFailed');
|
||||
$sContent = "<span><strong>".utils::HtmlEntities($sTitle)."</strong></span>";
|
||||
|
||||
if ($bWithHeader) {
|
||||
$oObject = MetaModel::GetObject($this->sObjectClass, $this->iObjectId, true, true);
|
||||
$sContent .= " <span>".$oObject->Get('friendlyname')."</span>";
|
||||
}
|
||||
if (count($this->aIssues) == 1) {
|
||||
$sIssue = reset($this->aIssues);
|
||||
$sContent .= " <span>".utils::HtmlEntities($sIssue)."</span>";
|
||||
@@ -60,6 +64,24 @@ class CoreCannotSaveObjectException extends CoreException
|
||||
return $sContent;
|
||||
}
|
||||
|
||||
public function getTextMessage()
|
||||
{
|
||||
$sTitle = Dict::S('UI:Error:SaveFailed');
|
||||
$sContent = $sTitle;
|
||||
|
||||
if (count($this->aIssues) == 1) {
|
||||
$sIssue = reset($this->aIssues);
|
||||
$sContent .= $sIssue;
|
||||
} else {
|
||||
foreach ($this->aIssues as $sError) {
|
||||
$sContent .= " " . $sError . ", ";
|
||||
}
|
||||
}
|
||||
|
||||
return $sContent;
|
||||
}
|
||||
|
||||
|
||||
public function getIssues()
|
||||
{
|
||||
return $this->aIssues;
|
||||
|
||||
@@ -67,7 +67,7 @@ class CoreException extends Exception
|
||||
|
||||
public function getHtmlDesc($sHighlightHtmlBegin = '<b>', $sHighlightHtmlEnd = '</b>')
|
||||
{
|
||||
return $this->getMessage();
|
||||
return utils::EscapeHtml($this->getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -209,7 +209,7 @@ class DesignerForm
|
||||
public function RenderAsPropertySheet($oP, $bReturnHTML = false, $sNotifyParentSelector = null)
|
||||
{
|
||||
$sReturn = '';
|
||||
$sActionUrl = addslashes($this->sSubmitTo);
|
||||
$sActionUrl = addslashes($this->sSubmitTo ?? '');
|
||||
$sJSSubmitParams = json_encode($this->aSubmitParams);
|
||||
$sFormId = $this->GetFormId();
|
||||
if ($this->oParentForm == null) {
|
||||
@@ -1330,7 +1330,8 @@ class DesignerComboField extends DesignerFormField
|
||||
{
|
||||
if ($this->bMultipleSelection)
|
||||
{
|
||||
$sHtml = "<span><select $sCSSClasses multiple size=\"8\"id=\"$sId\" name=\"$sName\">";
|
||||
$iSize = max(1, min(8, count($this->aAllowedValues)));
|
||||
$sHtml = "<span><select $sCSSClasses multiple size=\"$iSize\" id=\"$sId\" name=\"$sName\">";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -53,6 +53,12 @@ class LoginDefaultBefore extends AbstractLoginFSMExtension
|
||||
{
|
||||
// Force login mode
|
||||
LoginWebPage::SetLoginModeAndReload($sProposedLoginMode);
|
||||
} else {
|
||||
$sRawLoginMode = utils::ReadParam('login_mode', '', false, utils::ENUM_SANITIZATION_FILTER_RAW_DATA);
|
||||
if ($sProposedLoginMode !== $sRawLoginMode) {
|
||||
IssueLog::Error("Authentication issue due to login_mode parameter sanitization. Please avoid special characters", null, ['sRawLoginMode' => $sRawLoginMode]);
|
||||
//IssueLog::Error("Authentication issue due to login_mode parameter sanitization. Please avoid special characters", null, ['sRawLoginMode' => utils::HtmlEntities($sRawLoginMode)]);
|
||||
}
|
||||
}
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
}
|
||||
|
||||
@@ -52,12 +52,13 @@ class LoginTwigContext
|
||||
/**
|
||||
* Set the absolute path on disk of the folder containing the twig templates
|
||||
*
|
||||
* @param string $sPath absolute path of twig templates directory
|
||||
* @api
|
||||
*
|
||||
*@param string $sAbsPath Absolute path of twig templates directory
|
||||
*/
|
||||
public function SetLoaderPath($sPath)
|
||||
public function SetLoaderPath($sAbsPath)
|
||||
{
|
||||
$this->sTwigLoaderPath = $sPath;
|
||||
$this->sTwigLoaderPath = $sAbsPath;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,24 +85,27 @@ class LoginTwigContext
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the URL of a CSS file to link to the login screen
|
||||
* Add the absolute URL of a CSS file to link to the login screen
|
||||
*
|
||||
* @param string $sFile URL of the CSS file to link
|
||||
* @api
|
||||
*
|
||||
* @param string $sFileAbsURL Absolute URL of the CSS file to link
|
||||
*/
|
||||
public function AddCSSFile($sFile)
|
||||
public function AddCSSFile($sFileAbsURL)
|
||||
{
|
||||
$this->aCSSFiles[] = $sFile;
|
||||
$this->aCSSFiles[] = $sFileAbsURL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the URL of a javascript file to link to the login screen
|
||||
* @param string $sFile URL of the javascript file to link
|
||||
* Add the absolute URL of a javascript file to link to the login screen
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @param string $sFileAbsURL Absolute URL of the javascript file to link
|
||||
*/
|
||||
public function AddJsFile($sFile)
|
||||
public function AddJsFile($sFileAbsURL)
|
||||
{
|
||||
$this->aJsFiles[] = $sFile;
|
||||
$this->aJsFiles[] = $sFileAbsURL;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -133,7 +137,7 @@ class LoginTwigContext
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @return array Absolute URLs of the CSS files
|
||||
*/
|
||||
public function GetCSSFiles()
|
||||
{
|
||||
@@ -141,7 +145,7 @@ class LoginTwigContext
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @return array Absolute URLs of the JS files
|
||||
*/
|
||||
public function GetJsFiles()
|
||||
{
|
||||
@@ -274,12 +278,12 @@ class LoginTwigRenderer
|
||||
$aCSSFiles = $oFormData->GetCSSFiles();
|
||||
foreach ($aCSSFiles as $sCSSFile)
|
||||
{
|
||||
$oPage->add_linked_stylesheet($sCSSFile);
|
||||
$oPage->LinkStylesheetFromURI($sCSSFile);
|
||||
}
|
||||
$aJsFiles = $oFormData->GetJsFiles();
|
||||
foreach ($aJsFiles as $sJsFile)
|
||||
{
|
||||
$oPage->add_linked_script($sJsFile);
|
||||
$oPage->LinkScriptFromURI($sJsFile);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1159,11 +1159,11 @@ class OQLMenuNode extends MenuNode
|
||||
{
|
||||
$sUsageId = utils::GetSafeId($sUsageId);
|
||||
$oSearch = DBObjectSearch::FromOQL($sOql);
|
||||
$sClass= $oSearch->GetClass();
|
||||
$sClass= $oSearch->GetClass();
|
||||
$sIcon = MetaModel::GetClassIcon($sClass, false);
|
||||
if ($bSearchPane) {
|
||||
$aParams = array_merge(['open' => $bSearchOpen, 'table_id' => $sUsageId, 'submit_on_load' => false], $aExtraParams);
|
||||
$oBlock = new DisplayBlock($oSearch, 'search', false /* Asynchronous */, $aParams);
|
||||
$oBlock = new DisplayBlock($oSearch, DisplayBlock::ENUM_STYLE_SEARCH, false /* Asynchronous */, $aParams);
|
||||
$oBlock->Display($oPage, 0);
|
||||
$oPage->add("<div class='sf_results_area ibo-add-margin-top-250' data-target='search_results'>");
|
||||
}
|
||||
|
||||
@@ -250,7 +250,7 @@ class UIExtKeyWidget
|
||||
foreach ($aAdditionalField as $sAdditionalField) {
|
||||
array_push($aArguments, $oObj->Get($sAdditionalField));
|
||||
}
|
||||
$aOption['additional_field'] = utils::HtmlEntities(vsprintf($sFormatAdditionalField, $aArguments));
|
||||
$aOption['additional_field'] = utils::HtmlEntities(utils::VSprintf($sFormatAdditionalField, $aArguments));
|
||||
}
|
||||
if (!empty($sObjectImageAttCode)) {
|
||||
// Try to retrieve image for contact
|
||||
|
||||
@@ -17,6 +17,7 @@ require_once(APPROOT.'application/displayblock.class.inc.php');
|
||||
class UILinksWidget
|
||||
{
|
||||
protected $m_sClass;
|
||||
protected $m_sClassLabel;
|
||||
protected $m_sAttCode;
|
||||
protected $m_sNameSuffix;
|
||||
protected $m_sInputId;
|
||||
@@ -24,6 +25,7 @@ class UILinksWidget
|
||||
protected $m_sExtKeyToRemote;
|
||||
protected $m_sExtKeyToMe;
|
||||
protected $m_sLinkedClass;
|
||||
protected $m_sLinkedClassLabel;
|
||||
protected $m_sRemoteClass;
|
||||
protected $m_bDuplicatesAllowed;
|
||||
/** @var string[] list of editables attcodes */
|
||||
@@ -46,6 +48,7 @@ class UILinksWidget
|
||||
public function __construct($sClass, $sAttCode, $sInputId, $sNameSuffix = '', $bDuplicatesAllowed = false)
|
||||
{
|
||||
$this->m_sClass = $sClass;
|
||||
$this->m_sClassLabel = MetaModel::GetName($this->m_sClass);
|
||||
$this->m_sAttCode = $sAttCode;
|
||||
$this->m_sInputId = $sInputId;
|
||||
$this->m_sNameSuffix = $sNameSuffix;
|
||||
@@ -56,6 +59,7 @@ class UILinksWidget
|
||||
/** @var AttributeLinkedSetIndirect $oAttDef */
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sAttCode);
|
||||
$this->m_sLinkedClass = $oAttDef->GetLinkedClass();
|
||||
$this->m_sLinkedClassLabel = MetaModel::GetName($this->m_sLinkedClass);
|
||||
$this->m_sExtKeyToRemote = $oAttDef->GetExtKeyToRemote();
|
||||
$this->m_sExtKeyToMe = $oAttDef->GetExtKeyToMe();
|
||||
|
||||
@@ -365,11 +369,21 @@ class UILinksWidget
|
||||
return $this->m_sClass;
|
||||
}
|
||||
|
||||
public function GetClassLabel(): string
|
||||
{
|
||||
return $this->m_sClassLabel;
|
||||
}
|
||||
|
||||
public function GetLinkedClass(): string
|
||||
{
|
||||
return $this->m_sLinkedClass;
|
||||
}
|
||||
|
||||
public function GetLinkedClassLabel(): string
|
||||
{
|
||||
return $this->m_sLinkedClassLabel;
|
||||
}
|
||||
|
||||
public function GetAttCode(): string
|
||||
{
|
||||
return $this->m_sAttCode;
|
||||
|
||||
@@ -249,9 +249,9 @@ class appUserPreferences extends DBObject
|
||||
(
|
||||
"category" => "gui",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "userid",
|
||||
"name_attcode" => "login",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"reconc_keys" => array("userid","login"),
|
||||
"db_table" => "priv_app_preferences",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
@@ -260,6 +260,10 @@ class appUserPreferences extends DBObject
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("userid", array("targetclass"=>"User", "allowed_values"=>null, "sql"=>"userid", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributePropertySet("preferences", array("allowed_values"=>null, "sql"=>"preferences", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("org_id", array("allowed_values" => null, "extkey_attcode" => 'userid', "target_attcode" => "org_id")));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("login", array("allowed_values" => null, "extkey_attcode" => 'userid', "target_attcode" => "login")));
|
||||
MetaModel::Init_SetZListItems('list', array('org_id','preferences'));
|
||||
MetaModel::Init_SetZListItems('default_search', array('userid','login','org_id'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,10 +22,12 @@ use Combodo\iTop\Application\UI\Base\iUIBlock;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock;
|
||||
use Combodo\iTop\Application\UI\Hook\iKeyboardShortcut;
|
||||
use Combodo\iTop\Application\WebPage\WebPage;
|
||||
use Combodo\iTop\Service\InterfaceDiscovery\InterfaceDiscovery;
|
||||
use Combodo\iTop\Service\Module\ModuleService;
|
||||
use ScssPhp\ScssPhp\Compiler;
|
||||
use ScssPhp\ScssPhp\OutputStyle;
|
||||
use ScssPhp\ScssPhp\ValueConverter;
|
||||
use Soundasleep\Html2Text;
|
||||
|
||||
|
||||
/**
|
||||
@@ -111,6 +113,11 @@ class utils
|
||||
* @since 2.7.10 3.0.0
|
||||
*/
|
||||
public const ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER = 'element_identifier';
|
||||
/**
|
||||
* @var string For XML / HTML node id/class selector
|
||||
* @since 3.1.2 3.2.1
|
||||
*/
|
||||
public const ENUM_SANITIZATION_FILTER_ELEMENT_SELECTOR = 'element_selector';
|
||||
/**
|
||||
* @var string For variables names
|
||||
* @since 3.0.0
|
||||
@@ -173,6 +180,7 @@ class utils
|
||||
*/
|
||||
private static $sAbsoluteUrlAppRootCache = null;
|
||||
|
||||
|
||||
protected static function LoadParamFile($sParamFile)
|
||||
{
|
||||
if (!file_exists($sParamFile)) {
|
||||
@@ -411,11 +419,26 @@ class utils
|
||||
* @since 2.7.7, 3.0.2, 3.1.0 N°4899 - new 'url' filter
|
||||
* @since 2.7.10 N°6606 use the utils::ENUM_SANITIZATION_* const
|
||||
* @since 2.7.10 N°6606 new case for ENUM_SANITIZATION_FILTER_PHP_CLASS
|
||||
* @since 3.2.1-1 N°8242 Allow value to be an array for every filter
|
||||
*
|
||||
* @link https://www.php.net/manual/en/filter.filters.sanitize.php PHP sanitization filters
|
||||
*/
|
||||
protected static function Sanitize_Internal($value, $sSanitizationFilter)
|
||||
{
|
||||
if (is_array($value))
|
||||
{
|
||||
$retValue = array();
|
||||
foreach ($value as $key => $val)
|
||||
{
|
||||
$retValue[$key] = self::Sanitize_Internal($val, $sSanitizationFilter); // recursively check arrays
|
||||
if ($retValue[$key] === false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return $retValue;
|
||||
}
|
||||
|
||||
switch ($sSanitizationFilter)
|
||||
{
|
||||
case static::ENUM_SANITIZATION_FILTER_INTEGER:
|
||||
@@ -446,57 +469,50 @@ class utils
|
||||
case static::ENUM_SANITIZATION_FILTER_PARAMETER:
|
||||
case static::ENUM_SANITIZATION_FILTER_FIELD_NAME:
|
||||
case static::ENUM_SANITIZATION_FILTER_TRANSACTION_ID:
|
||||
if (is_array($value))
|
||||
switch ($sSanitizationFilter)
|
||||
{
|
||||
$retValue = array();
|
||||
foreach ($value as $key => $val)
|
||||
{
|
||||
$retValue[$key] = self::Sanitize_Internal($val, $sSanitizationFilter); // recursively check arrays
|
||||
if ($retValue[$key] === false)
|
||||
{
|
||||
$retValue = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch ($sSanitizationFilter)
|
||||
{
|
||||
case static::ENUM_SANITIZATION_FILTER_TRANSACTION_ID:
|
||||
// Same as parameter type but keep the dot character
|
||||
// transaction_id, the dot is mostly for Windows servers when using file storage as the tokens are named *.tmp
|
||||
// - See N°1835
|
||||
// - Note: It must be included at the regexp beginning otherwise you'll get an invalid character error
|
||||
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP, array("options" => array("regexp" => '/^[\. A-Za-z0-9_=-]*$/')));
|
||||
break;
|
||||
case static::ENUM_SANITIZATION_FILTER_TRANSACTION_ID:
|
||||
// Same as parameter type but keep the dot character
|
||||
// transaction_id, the dot is mostly for Windows servers when using file storage as the tokens are named *.tmp
|
||||
// - See N°1835
|
||||
// - Note: It must be included at the regexp beginning otherwise you'll get an invalid character error
|
||||
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP, array("options" => array("regexp" => '/^[\. A-Za-z0-9_=-]*$/')));
|
||||
break;
|
||||
|
||||
case static::ENUM_SANITIZATION_FILTER_ROUTE:
|
||||
case static::ENUM_SANITIZATION_FILTER_OPERATION:
|
||||
// - Routes should be of the "controller_namespace_code.controller_method_name" form
|
||||
// - Operations should be allowed to be namespaced as well even though then don't have dedicated controller yet
|
||||
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP, array("options" => array("regexp" => '/^[\.A-Za-z0-9_-]*$/')));
|
||||
break;
|
||||
case static::ENUM_SANITIZATION_FILTER_ROUTE:
|
||||
case static::ENUM_SANITIZATION_FILTER_OPERATION:
|
||||
// - Routes should be of the "controller_namespace_code.controller_method_name" form
|
||||
// - Operations should be allowed to be namespaced as well even though then don't have dedicated controller yet
|
||||
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP, array("options" => array("regexp" => '/^[\.A-Za-z0-9_-]*$/')));
|
||||
break;
|
||||
|
||||
case static::ENUM_SANITIZATION_FILTER_PARAMETER:
|
||||
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP, array("options" => array("regexp" => '/^[ A-Za-z0-9_=-]*$/'))); // the '=', '%3D, '%2B', '%2F'
|
||||
// Characters are used in serialized filters (starting 2.5, only the url encoded versions are presents, but the "=" is kept for BC)
|
||||
break;
|
||||
case static::ENUM_SANITIZATION_FILTER_PARAMETER:
|
||||
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP, array("options" => array("regexp" => '/^[ A-Za-z0-9_=-]*$/'))); // the '=', '%3D, '%2B', '%2F'
|
||||
// Characters are used in serialized filters (starting 2.5, only the url encoded versions are presents, but the "=" is kept for BC)
|
||||
break;
|
||||
|
||||
case static::ENUM_SANITIZATION_FILTER_FIELD_NAME:
|
||||
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP, array("options" => array("regexp" => '/^[A-Za-z0-9_]+(->[A-Za-z0-9_]+)*$/'))); // att_code or att_code->name or AttCode->Name or AttCode->Key2->Name
|
||||
break;
|
||||
case static::ENUM_SANITIZATION_FILTER_FIELD_NAME:
|
||||
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP, array("options" => array("regexp" => '/^[A-Za-z0-9_]+(->[A-Za-z0-9_]+)*$/'))); // att_code or att_code->name or AttCode->Name or AttCode->Key2->Name
|
||||
break;
|
||||
|
||||
case static::ENUM_SANITIZATION_FILTER_CONTEXT_PARAM:
|
||||
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP, array("options" => array("regexp" => '/^[ A-Za-z0-9_=%:+-]*$/')));
|
||||
break;
|
||||
case static::ENUM_SANITIZATION_FILTER_CONTEXT_PARAM:
|
||||
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP, array("options" => array("regexp" => '/^[ A-Za-z0-9_=%:+-]*$/')));
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// For XML / HTML node identifiers
|
||||
case static::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER:
|
||||
$retValue = preg_replace('/[^a-zA-Z0-9_-]/', '', $value);
|
||||
$retValue = filter_var($retValue, FILTER_VALIDATE_REGEXP,
|
||||
['options' => ['regexp' => '/^[A-Za-z0-9][A-Za-z0-9_-]*$/']]);
|
||||
break;
|
||||
|
||||
// For XML / HTML node id selector
|
||||
case static::ENUM_SANITIZATION_FILTER_ELEMENT_SELECTOR:
|
||||
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP,
|
||||
['options' => ['regexp' => '/^[#\.][A-Za-z0-9][A-Za-z0-9_-]*$/']]);
|
||||
break;
|
||||
|
||||
case static::ENUM_SANITIZATION_FILTER_VARIABLE_NAME:
|
||||
@@ -505,8 +521,8 @@ class utils
|
||||
|
||||
// For URL
|
||||
case static::ENUM_SANITIZATION_FILTER_URL:
|
||||
// N°6350 - returns only valid URLs
|
||||
$retValue = filter_var($value, FILTER_VALIDATE_URL);
|
||||
$retValue = filter_var($value, FILTER_SANITIZE_URL);
|
||||
$retValue = filter_var($retValue, FILTER_VALIDATE_URL);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -1435,9 +1451,12 @@ class utils
|
||||
* @return string A path to a folder into which any module can store cache data
|
||||
* The corresponding folder is created or cleaned upon code compilation
|
||||
*/
|
||||
public static function GetCachePath()
|
||||
public static function GetCachePath(string $sEnvironment = null): string
|
||||
{
|
||||
return static::GetDataPath().'cache-'.MetaModel::GetEnvironment().'/';
|
||||
if (is_null($sEnvironment)) {
|
||||
$sEnvironment = MetaModel::GetEnvironment();
|
||||
}
|
||||
return static::GetDataPath()."cache-$sEnvironment/";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1497,12 +1516,12 @@ class utils
|
||||
case iPopupMenuExtension::MENU_OBJLIST_TOOLKIT:
|
||||
/** @var \DBObjectSet $param */
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sContext = $oAppContext->GetForLink();
|
||||
$sContext = $oAppContext->GetForLink(true);
|
||||
$sDataTableId = is_null($sDataTableId) ? '' : $sDataTableId;
|
||||
$sUIPage = cmdbAbstractObject::ComputeStandardUIPage($param->GetFilter()->GetClass());
|
||||
$sOQL = addslashes($param->GetFilter()->ToOQL(true));
|
||||
$sFilter = urlencode($param->GetFilter()->serialize());
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/$sUIPage?operation=search&filter=".$sFilter."&{$sContext}";
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/$sUIPage?operation=search&filter=".$sFilter.$sContext;
|
||||
$oContainerBlock->AddJsFileRelPath('js/tabularfieldsselector.js');
|
||||
$oContainerBlock->AddJsFileRelPath('js/jquery.dragtable.js');
|
||||
$oContainerBlock->AddCssFileRelPath('css/dragtable.css');
|
||||
@@ -1536,6 +1555,10 @@ class utils
|
||||
}
|
||||
$aResult[] = new JSPopupMenuItem('UI:Menu:AddToDashboard', Dict::S('UI:Menu:AddToDashboard'), "DashletCreationDlg('$sOQL', '$sContext')");
|
||||
$aResult[] = new JSPopupMenuItem('UI:Menu:ShortcutList', Dict::S('UI:Menu:ShortcutList'), "ShortcutListDlg('$sOQL', '$sDataTableId', '$sContext')");
|
||||
if (ApplicationMenu::IsMenuIdEnabled('RunQueriesMenu')) {
|
||||
$oMenuItemPlay = new JSPopupMenuItem('UI:Menu:OpenOQL', Dict::S('UI:Menu:OpenOQL'), "OpenOql('$sOQL')");
|
||||
$aResult[] = $oMenuItemPlay;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@@ -1672,8 +1695,8 @@ class utils
|
||||
$oAppContext = new ApplicationContext();
|
||||
|
||||
$sUrl = $sAppRootUrl
|
||||
.'pages/UI.php?operation=search&'
|
||||
.$oAppContext->GetForLink()
|
||||
.'pages/UI.php?operation=search'
|
||||
.$oAppContext->GetForLink(true)
|
||||
.'&filter='.rawurlencode($oDataTableSearchFilter->serialize());
|
||||
$sUrl .= '&aParams='.rawurlencode($sParams); // Not working... yet, cause not handled by UI.php
|
||||
|
||||
@@ -2056,6 +2079,127 @@ SQL;
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a string using vsprintf with safety checks to avoid ValueError
|
||||
*
|
||||
* This method fills missing arguments with their original format specifiers,
|
||||
* then calls vsprintf with the complete array.
|
||||
*
|
||||
* @param string $sFormat The format string
|
||||
* @param array $aArgs The arguments to format
|
||||
* @param bool $bLogErrors Whether to log errors (defaults to true)
|
||||
*
|
||||
* @return string The formatted string
|
||||
* @since 3.2.2
|
||||
*/
|
||||
public static function VSprintf(string $sFormat, array $aArgs, bool $bLogErrors = true): string
|
||||
{
|
||||
// Extract all format specifiers
|
||||
$sPattern = '/%(?:(?:[1-9][0-9]*)\$)?[-+\'0# ]*(?:[0-9]*|\*)?(?:\.(?:[0-9]*|\*))?(?:[hlL])?[diouxXeEfFgGcrs%]/';
|
||||
preg_match_all($sPattern, $sFormat, $aMatches, PREG_OFFSET_CAPTURE);
|
||||
|
||||
// Process matches, keeping track of their positions and excluding escaped percent signs (%%)
|
||||
$aSpecifierMatches = [];
|
||||
foreach ($aMatches[0] as $sMatch) {
|
||||
if ($sMatch[0] !== '%%') {
|
||||
$aSpecifierMatches[] = $sMatch;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for positional specifiers and build position map
|
||||
$bHasPositional = false;
|
||||
$iMaxPosition = 0;
|
||||
$aPositions = [];
|
||||
$aUniquePositions = [];
|
||||
|
||||
foreach ($aSpecifierMatches as $index => $match) {
|
||||
$sSpec = $match[0];
|
||||
if (preg_match('/^%([1-9][0-9]*)\$/', $sSpec, $posMatch)) {
|
||||
$bHasPositional = true;
|
||||
$iPosition = (int)$posMatch[1] - 1; // Convert to 0-based
|
||||
$aPositions[$index] = $iPosition;
|
||||
$aUniquePositions[$iPosition] = true;
|
||||
$iMaxPosition = max($iMaxPosition, $iPosition + 1);
|
||||
} else {
|
||||
$aPositions[$index] = $index;
|
||||
$aUniquePositions[$index] = true;
|
||||
$iMaxPosition = max($iMaxPosition, $index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Count unique positions, this tells us how many arguments we actually need
|
||||
$iExpectedCount = count($aUniquePositions);
|
||||
$iActualCount = count($aArgs);
|
||||
|
||||
// If we have enough arguments, just use vsprintf
|
||||
if ($iActualCount >= $iExpectedCount) {
|
||||
return vsprintf($sFormat, $aArgs);
|
||||
}
|
||||
// else log the error if needed
|
||||
if ($bLogErrors) {
|
||||
IssueLog::Warning("Format string requires $iExpectedCount arguments, but only $iActualCount provided. Format: '$sFormat'" );
|
||||
}
|
||||
|
||||
// Create a replacement map
|
||||
if ($bHasPositional) {
|
||||
// For positional, we need to handle the exact positions
|
||||
$aReplacements = array_fill(0, $iMaxPosition, null);
|
||||
|
||||
// Fill in the real arguments first
|
||||
foreach ($aArgs as $index => $sValue) {
|
||||
if ($index < $iMaxPosition) {
|
||||
$aReplacements[$index] = $sValue;
|
||||
}
|
||||
}
|
||||
|
||||
// For null values in the replacement map, use the original specifier
|
||||
foreach ($aSpecifierMatches as $index => $sMatch) {
|
||||
$iPosition = $aPositions[$index];
|
||||
if ($aReplacements[$iPosition] === null) {
|
||||
// Use the original format specifier when we don't have an argument
|
||||
$aReplacements[$iPosition] = $sMatch[0];
|
||||
}
|
||||
}
|
||||
|
||||
// Remove any remaining nulls (for positions that weren't referenced)
|
||||
$aReplacements = array_filter($aReplacements, static function($val) { return $val !== null; });
|
||||
} else {
|
||||
// For non-positional, we need to map each position
|
||||
$aReplacements = [];
|
||||
$iUsed = 0;
|
||||
|
||||
// Create a map of what values to use for each position
|
||||
$aPositionValues = [];
|
||||
for ($i = 0; $i < $iMaxPosition; $i++) {
|
||||
if (isset($aUniquePositions[$i])) {
|
||||
if ($iUsed < $iActualCount) {
|
||||
// We have an actual argument for this position
|
||||
$aPositionValues[$i] = $aArgs[$iUsed++];
|
||||
} else {
|
||||
// Mark this position to use the original specifier
|
||||
$aPositionValues[$i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build the replacements array preserving the original order
|
||||
foreach ($aSpecifierMatches as $index => $sMatch) {
|
||||
$iPosition = $aPositions[$index];
|
||||
if (isset($aPositionValues[$iPosition])) {
|
||||
$aReplacements[] = $aPositionValues[$iPosition];
|
||||
} else {
|
||||
// Use the original format specifier when we don't have an argument
|
||||
$aReplacements[] = $sMatch[0];
|
||||
// Mark this position as used, so if it appears again, it gets the same replacement
|
||||
$aPositionValues[$iPosition] = $sMatch[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process the format string with our filled-in arguments
|
||||
return vsprintf($sFormat, $aReplacements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string containing some (valid) HTML markup to plain text
|
||||
*
|
||||
@@ -2067,7 +2211,7 @@ SQL;
|
||||
{
|
||||
try {
|
||||
//return '<?xml encoding="UTF-8">'.$sHtml;
|
||||
return \Html2Text\Html2Text::convert('<?xml encoding="UTF-8">'.$sHtml);
|
||||
return Html2Text::convert('<?xml encoding="UTF-8">'.$sHtml, ['ignore_errors' => true]);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
return $e->getMessage();
|
||||
@@ -2374,53 +2518,75 @@ SQL;
|
||||
return $bRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sPath
|
||||
*
|
||||
* @return false|\ormDocument
|
||||
* @throws \Exception
|
||||
*
|
||||
* @deprecated 3.2.1 use utils::GetDocumentFromSelfURL instead
|
||||
*/
|
||||
public static function IsSelfURL($sPath)
|
||||
{
|
||||
return self::GetDocumentFromSelfURL($sPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given URL is a link to download a document/image on the CURRENT iTop
|
||||
* In such a case we can read the content of the file directly in the database (if the users rights allow) and return the ormDocument
|
||||
*
|
||||
* @Since 3.2.1 a local URL is transformed into a local file to read
|
||||
*
|
||||
* @param string $sPath
|
||||
* @return false|ormDocument
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function IsSelfURL($sPath)
|
||||
public static function GetDocumentFromSelfURL(string $sPath)
|
||||
{
|
||||
$result = false;
|
||||
$sPageUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.document.php';
|
||||
if (substr($sPath, 0, strlen($sPageUrl)) == $sPageUrl)
|
||||
{
|
||||
if (utils::StartsWith($sPath, $sPageUrl)) {
|
||||
// If the URL is an URL pointing to this instance of iTop, then
|
||||
// extract the "query" part of the URL and analyze it
|
||||
$sQuery = parse_url($sPath, PHP_URL_QUERY);
|
||||
if ($sQuery !== null)
|
||||
{
|
||||
if ($sQuery !== null) {
|
||||
$aParams = array();
|
||||
foreach(explode('&', $sQuery) as $sChunk)
|
||||
{
|
||||
foreach (explode('&', $sQuery) as $sChunk) {
|
||||
$aParts = explode('=', $sChunk ?? '');
|
||||
if (count($aParts) != 2) continue;
|
||||
if (count($aParts) != 2) {
|
||||
continue;
|
||||
}
|
||||
$aParams[$aParts[0]] = urldecode($aParts[1]);
|
||||
}
|
||||
$result = array_key_exists('operation', $aParams) && array_key_exists('class', $aParams) && array_key_exists('id', $aParams) && array_key_exists('field', $aParams) && ($aParams['operation'] == 'download_document');
|
||||
if ($result)
|
||||
{
|
||||
if ($result) {
|
||||
// This is a 'download_document' operation, let's retrieve the document directly from the database
|
||||
$sClass = $aParams['class'];
|
||||
$iKey = $aParams['id'];
|
||||
$sAttCode = $aParams['field'];
|
||||
|
||||
$oObj = MetaModel::GetObject($sClass, $iKey, false /* must exist */); // Users rights apply here !!
|
||||
if ($oObj)
|
||||
{
|
||||
if ($oObj) {
|
||||
/**
|
||||
* @var ormDocument $result
|
||||
*/
|
||||
$result = clone $oObj->Get($sAttCode);
|
||||
return $result;
|
||||
return clone $oObj->Get($sAttCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new Exception('Invalid URL. This iTop URL is not pointing to a valid Document/Image.');
|
||||
}
|
||||
return $result;
|
||||
|
||||
if (utils::StartsWith($sPath, utils::GetAbsoluteUrlAppRoot())) {
|
||||
$sFilePath = utils::LocalPath(APPROOT.substr($sPath, strlen(utils::GetAbsoluteUrlAppRoot())));
|
||||
if (false === $sFilePath) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$sFilePath = APPROOT.$sFilePath;
|
||||
return ormDocument::FromFile($sFilePath);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2428,115 +2594,70 @@ SQL;
|
||||
* - an URL pointing to a blob (image/document) on the current iTop server
|
||||
* - an http(s) URL
|
||||
* - the local file system (but only if you are an administrator)
|
||||
* @param string $sPath
|
||||
*
|
||||
* @param string|null $sPath
|
||||
* @return ormDocument|null
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function FileGetContentsAndMIMEType($sPath)
|
||||
{
|
||||
$oUploadedDoc = null;
|
||||
$aKnownExtensions = array(
|
||||
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
|
||||
'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
|
||||
'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
|
||||
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||
'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
|
||||
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
|
||||
'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
|
||||
'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
|
||||
'jpg' => 'image/jpeg',
|
||||
'jpeg' => 'image/jpeg',
|
||||
'gif' => 'image/gif',
|
||||
'png' => 'image/png',
|
||||
'pdf' => 'application/pdf',
|
||||
'doc' => 'application/msword',
|
||||
'dot' => 'application/msword',
|
||||
'xls' => 'application/vnd.ms-excel',
|
||||
'ppt' => 'application/vnd.ms-powerpoint',
|
||||
'vsd' => 'application/x-visio',
|
||||
'vdx' => 'application/visio.drawing',
|
||||
'odt' => 'application/vnd.oasis.opendocument.text',
|
||||
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
|
||||
'odp' => 'application/vnd.oasis.opendocument.presentation',
|
||||
'zip' => 'application/zip',
|
||||
'txt' => 'text/plain',
|
||||
'htm' => 'text/html',
|
||||
'html' => 'text/html',
|
||||
'exe' => 'application/octet-stream',
|
||||
);
|
||||
|
||||
$sData = null;
|
||||
$sMimeType = 'text/plain'; // Default MIME Type: treat the file as a bunch a characters...
|
||||
$sFileName = 'uploaded-file'; // Default name for downloaded-files
|
||||
$sExtension = '.txt'; // Default file extension in case we don't know the MIME Type
|
||||
|
||||
if(empty($sPath))
|
||||
{
|
||||
if (utils::IsNullOrEmptyString($sPath)) {
|
||||
// Empty path (NULL or '') means that there is no input, making an empty document.
|
||||
$oUploadedDoc = new ormDocument('', '', '');
|
||||
return new ormDocument('', '', '');
|
||||
}
|
||||
elseif (static::IsURL($sPath))
|
||||
{
|
||||
if ($oUploadedDoc = static::IsSelfURL($sPath))
|
||||
{
|
||||
// Nothing more to do, we've got it !!
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remote file, let's use the HTTP headers to find the MIME Type
|
||||
$sData = @file_get_contents($sPath);
|
||||
if ($sData === false)
|
||||
{
|
||||
throw new Exception("Failed to load the file from the URL '$sPath'.");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isset($http_response_header))
|
||||
{
|
||||
$aHeaders = static::ParseHeaders($http_response_header);
|
||||
$sMimeType = array_key_exists('Content-Type', $aHeaders) ? strtolower($aHeaders['Content-Type']) : 'application/x-octet-stream';
|
||||
// Compute the file extension from the MIME Type
|
||||
foreach ($aKnownExtensions as $sExtValue => $sMime) {
|
||||
if ($sMime === $sMimeType) {
|
||||
$sExtension = '.'.$sExtValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
$sPathName = pathinfo($sPath, PATHINFO_FILENAME);
|
||||
if (utils::IsNotNullOrEmptyString($sPathName)) {
|
||||
$sFileName = $sPathName;
|
||||
}
|
||||
$sFileName .= $sExtension;
|
||||
}
|
||||
$oUploadedDoc = new ormDocument($sData, $sMimeType, $sFileName);
|
||||
}
|
||||
}
|
||||
else if (UserRights::IsAdministrator())
|
||||
{
|
||||
// Only administrators are allowed to read local files
|
||||
$sData = @file_get_contents($sPath);
|
||||
if ($sData === false)
|
||||
{
|
||||
throw new Exception("Failed to load the file '$sPath'. The file does not exist or the current process is not allowed to access it.");
|
||||
}
|
||||
$sExtension = strtolower(pathinfo($sPath, PATHINFO_EXTENSION));
|
||||
$sFileName = basename($sPath);
|
||||
|
||||
if (array_key_exists($sExtension, $aKnownExtensions))
|
||||
{
|
||||
$sMimeType = $aKnownExtensions[$sExtension];
|
||||
if (static::IsURL($sPath)) {
|
||||
$oUploadedDoc = static::GetDocumentFromSelfURL($sPath);
|
||||
if ($oUploadedDoc) {
|
||||
return $oUploadedDoc;
|
||||
}
|
||||
else if (extension_loaded('fileinfo'))
|
||||
{
|
||||
$finfo = new finfo(FILEINFO_MIME);
|
||||
$sMimeType = $finfo->file($sPath);
|
||||
|
||||
// Remote file, let's use the HTTP headers to find the MIME Type
|
||||
$sData = @file_get_contents($sPath);
|
||||
if ($sData === false) {
|
||||
IssueLog::Error(<<<TXT
|
||||
Failed to load the file from URL. This can happen for multiple reasons:
|
||||
- Invalid URL
|
||||
- URL using HTTPS with an untrusted certificate on the remote server
|
||||
- ...
|
||||
TXT
|
||||
, LogChannels::CORE, [
|
||||
'URL' => $sPath,
|
||||
]);
|
||||
throw new Exception("Failed to load the file from the URL '$sPath'.");
|
||||
}
|
||||
$oUploadedDoc = new ormDocument($sData, $sMimeType, $sFileName);
|
||||
|
||||
$sMimeType = 'text/plain'; // Default MIME Type: treat the file as a bunch a characters...
|
||||
$sFileName = 'uploaded-file'; // Default name for downloaded-files
|
||||
$sExtension = '.txt'; // Default file extension in case we don't know the MIME Type
|
||||
|
||||
if (isset($http_response_header)) {
|
||||
$aHeaders = static::ParseHeaders($http_response_header);
|
||||
$sMimeType = array_key_exists('Content-Type', $aHeaders) ? strtolower($aHeaders['Content-Type']) : 'application/x-octet-stream';
|
||||
// Compute the file extension from the MIME Type
|
||||
foreach (ormDocument::GetKnownExtensions() as $sExtValue => $sMime) {
|
||||
if ($sMime === $sMimeType) {
|
||||
$sExtension = '.'.$sExtValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
$sPathName = pathinfo($sPath, PATHINFO_FILENAME);
|
||||
if (utils::IsNotNullOrEmptyString($sPathName)) {
|
||||
$sFileName = $sPathName;
|
||||
}
|
||||
$sFileName .= $sExtension;
|
||||
|
||||
return new ormDocument($sData, $sMimeType, $sFileName);
|
||||
}
|
||||
return $oUploadedDoc;
|
||||
|
||||
// Local file
|
||||
if (UserRights::IsAdministrator()) {
|
||||
// Only administrators are allowed to read local files
|
||||
return ormDocument::FromFile($sPath);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected static function ParseHeaders($aHeaders)
|
||||
@@ -2782,95 +2903,12 @@ SQL;
|
||||
*
|
||||
* @return array classes are returned in the same order as the module dependency tree, so core classes on top
|
||||
* @since 3.0.0
|
||||
* @deprecated 3.2.0 Use {@see InterfaceDiscovery::FindItopClasses()} instead
|
||||
*/
|
||||
public static function GetClassesForInterface(string $sInterface, string $sClassNameFilter = '', $aExcludedPath = []): array
|
||||
{
|
||||
$aMatchingClasses = [];
|
||||
|
||||
if (!utils::IsDevelopmentEnvironment()) {
|
||||
// Try to read from cache
|
||||
$aFilePath = explode("\\", $sInterface);
|
||||
$sInterfaceName = end($aFilePath);
|
||||
$sCacheFileName = utils::GetCachePath()."ImplementingInterfaces/$sInterfaceName.php";
|
||||
if (is_file($sCacheFileName)) {
|
||||
$aMatchingClasses = include $sCacheFileName;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($aMatchingClasses)) {
|
||||
$aAutoloadClassMaps = [APPROOT.'lib/composer/autoload_classmap.php'];
|
||||
// guess all the autoload class maps from the extensions
|
||||
$aAutoloadClassMaps = array_merge($aAutoloadClassMaps, glob(APPROOT.'env-'.utils::GetCurrentEnvironment().'/*/vendor/composer/autoload_classmap.php'));
|
||||
|
||||
$aClassMap = [];
|
||||
$aAutoloaderErrors = [];
|
||||
foreach ($aAutoloadClassMaps as $sAutoloadFile) {
|
||||
if (false === static::RealPath($sAutoloadFile, APPROOT)) {
|
||||
// can happen when we still have the autoloader symlink in env-*, but it points to a file that no longer exists
|
||||
$aAutoloaderErrors[] = $sAutoloadFile;
|
||||
continue;
|
||||
}
|
||||
$aTmpClassMap = include $sAutoloadFile;
|
||||
/** @noinspection SlowArrayOperationsInLoopInspection we are getting an associative array so the documented workarounds cannot be used */
|
||||
$aClassMap = array_merge($aClassMap, $aTmpClassMap);
|
||||
}
|
||||
if (count($aAutoloaderErrors) > 0) {
|
||||
IssueLog::Debug(
|
||||
"\utils::GetClassesForInterface cannot load some of the autoloader files",
|
||||
LogChannels::CORE,
|
||||
['autoloader_errors' => $aAutoloaderErrors]
|
||||
);
|
||||
}
|
||||
|
||||
// Add already loaded classes
|
||||
$aCurrentClasses = array_fill_keys(get_declared_classes(), '');
|
||||
$aClassMap = array_merge($aCurrentClasses, $aClassMap);
|
||||
|
||||
foreach ($aClassMap as $sPHPClass => $sPHPFile) {
|
||||
$bSkipped = false;
|
||||
|
||||
// Check if our class matches name filter, or is in an excluded path
|
||||
if ($sClassNameFilter !== '' && strpos($sPHPClass, $sClassNameFilter) === false) {
|
||||
$bSkipped = true;
|
||||
}
|
||||
// For some PHP classes we don't have their file path as they are already in memory, so we never filter on their paths
|
||||
elseif (utils::IsNotNullOrEmptyString($sPHPFile)) {
|
||||
$sPHPFile = self::LocalPath($sPHPFile);
|
||||
if ($sPHPFile !== false) {
|
||||
$sPHPFile = '/'.$sPHPFile; // for regex
|
||||
foreach ($aExcludedPath as $sExcludedPath) {
|
||||
// Note: We use '#' as delimiters as usual '/' is often used in paths.
|
||||
if ($sExcludedPath !== '' && preg_match('#'.$sExcludedPath.'#', $sPHPFile) === 1) {
|
||||
$bSkipped = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$bSkipped = true; // file not found
|
||||
}
|
||||
}
|
||||
|
||||
if(!$bSkipped){
|
||||
try {
|
||||
$oRefClass = new ReflectionClass($sPHPClass);
|
||||
if ($oRefClass->implementsInterface($sInterface) &&
|
||||
!$oRefClass->isInterface() && !$oRefClass->isAbstract() && !$oRefClass->isTrait()) {
|
||||
$aMatchingClasses[] = $sPHPClass;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!utils::IsDevelopmentEnvironment()) {
|
||||
// Save to cache
|
||||
$sCacheContent = "<?php\n\nreturn ".var_export($aMatchingClasses, true).";";
|
||||
SetupUtils::builddir(dirname($sCacheFileName));
|
||||
file_put_contents($sCacheFileName, $sCacheContent);
|
||||
}
|
||||
}
|
||||
|
||||
return $aMatchingClasses;
|
||||
$oInterfaceDiscoveryService = InterfaceDiscovery::GetInstance();
|
||||
return $oInterfaceDiscoveryService->FindItopClasses($sInterface);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2884,9 +2922,9 @@ SQL;
|
||||
{
|
||||
$aResultPref = [];
|
||||
$aShortcutPrefs = appUserPreferences::GetPref('keyboard_shortcuts', []);
|
||||
// Note: Mind the 4 blackslashes, see utils::GetClassesForInterface()
|
||||
$aShortcutClasses = utils::GetClassesForInterface(iKeyboardShortcut::class, '', array('[\\\\/]lib[\\\\/]', '[\\\\/]node_modules[\\\\/]', '[\\\\/]test[\\\\/]', '[\\\\/]tests[\\\\/]'));
|
||||
|
||||
/** @var iKeyboardShortcut[] $aShortcutClasses */
|
||||
$aShortcutClasses = InterfaceDiscovery::GetInstance()->FindItopClasses(iKeyboardShortcut::class);
|
||||
foreach ($aShortcutClasses as $cShortcutPlugin) {
|
||||
$sTriggeredElement = $cShortcutPlugin::GetShortcutTriggeredElementSelector();
|
||||
foreach ($cShortcutPlugin::GetShortcutKeys() as $aShortcutKey) {
|
||||
@@ -3190,30 +3228,13 @@ SQL;
|
||||
* @throws \Exception
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static function GetMentionedObjectsFromText(string $sText, string $sFormat = self::ENUM_TEXT_FORMAT_HTML): array
|
||||
public static function GetMentionedObjectsFromText(string $sText): 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);
|
||||
$aMentionedObjects = [];
|
||||
$aMentionMatches = [];
|
||||
$sText = html_entity_decode($sText);
|
||||
|
||||
preg_match_all('/<a\s*([^>]*)data-object-class="([^"]*)"\s.*data-object-key="([^"]*)"/Ui', $sText, $aMentionMatches);
|
||||
foreach ($aMentionMatches[0] as $iMatchIdx => $sCompleteMatch) {
|
||||
$sMatchedClass = $aMentionMatches[2][$iMatchIdx];
|
||||
$sMatchedId = $aMentionMatches[3][$iMatchIdx];
|
||||
|
||||
@@ -23,7 +23,7 @@ define('ITOP_DESIGN_LATEST_VERSION', '3.2');
|
||||
* @used-by utils::GetItopVersionWikiSyntax()
|
||||
* @used-by iTopModulesPhpVersionIntegrationTest
|
||||
*/
|
||||
define('ITOP_CORE_VERSION', '3.2.0');
|
||||
define('ITOP_CORE_VERSION', '3.2.1');
|
||||
|
||||
/**
|
||||
* @var string
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
"apereo/phpcas": "~1.6.0",
|
||||
"firebase/php-jwt": "^6.4.0",
|
||||
"guzzlehttp/guzzle": "^7.5.1",
|
||||
"laminas/laminas-mail": "^2.11",
|
||||
"laminas/laminas-servicemanager": "^3.5",
|
||||
"league/oauth2-google": "^4.0.1",
|
||||
"nikic/php-parser": "^4.14.0",
|
||||
"pear/archive_tar": "~1.4.14",
|
||||
@@ -32,9 +30,10 @@
|
||||
"symfony/twig-bundle": "~6.4.0",
|
||||
"symfony/var-dumper": "~6.4.0",
|
||||
"symfony/yaml": "~6.4.0",
|
||||
"symfony/mailer": "~6.4.0",
|
||||
"tecnickcom/tcpdf": "^6.6.0",
|
||||
"thenetworg/oauth2-azure": "^2.0",
|
||||
"masterminds/html5": "^2.8.0"
|
||||
"soundasleep/html2text": "~2.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/debug-bundle": "~6.4.0",
|
||||
|
||||
1264
composer.lock
generated
1264
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -181,7 +181,7 @@ abstract class Action extends cmdbAbstractObject
|
||||
{
|
||||
parent::DisplayBareRelations($oPage, false);
|
||||
|
||||
if ($oPage instanceof iTopWebPage) {
|
||||
if ($oPage instanceof iTopWebPage && !$this->IsNew()) {
|
||||
$this->GenerateLastExecutionsTab($oPage, $bEditMode);
|
||||
}
|
||||
}
|
||||
@@ -628,7 +628,7 @@ class ActionEmail extends ActionNotification
|
||||
*/
|
||||
protected function _DoExecute($oTrigger, $aContextArgs, &$oLog)
|
||||
{
|
||||
$sStyles = file_get_contents(APPROOT.'css/email.css');
|
||||
$sStyles = file_get_contents(APPROOT . utils::GetCSSFromSASS("css/email.scss"));
|
||||
$sStyles .= MetaModel::GetConfig()->Get('email_css');
|
||||
|
||||
$oEmail = new EMail();
|
||||
@@ -867,7 +867,13 @@ class ActionEmail extends ActionNotification
|
||||
*/
|
||||
protected function BuildMessageBody(bool $bHighlightPlaceholders = false): string
|
||||
{
|
||||
$sBody = $this->Get('body');
|
||||
// Wrap content with a specific class in order to apply styles of HTML fields through the emogrifier (see `css/email.scss`)
|
||||
$sBody = <<<HTML
|
||||
<div class="email-is-html-content">
|
||||
{$this->Get('body')}
|
||||
</div>
|
||||
HTML;
|
||||
|
||||
/** @var ormDocument $oHtmlTemplate */
|
||||
$oHtmlTemplate = $this->Get('html_template');
|
||||
if ($oHtmlTemplate && !$oHtmlTemplate->IsEmpty()) {
|
||||
|
||||
@@ -89,7 +89,7 @@ abstract class AsyncTask extends DBObject
|
||||
// The value is set from null to planned in the setup program
|
||||
MetaModel::Init_AddAttribute(new AttributeEnum("status", array("allowed_values"=>new ValueSetEnum('planned,running,idle,error'), "sql"=>"status", "default_value"=>"planned", "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeDateTime("created", array("allowed_values"=>null, "sql"=>"created", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeDateTime("created", array("allowed_values"=>null, "sql"=>"created", "default_value"=>"NOW()", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeDateTime("started", array("allowed_values"=>null, "sql"=>"started", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeDateTime("planned", array("allowed_values"=>null, "sql"=>"planned", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("event_id", array("targetclass"=>"Event", "jointype"=> "", "allowed_values"=>null, "sql"=>"event_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_SILENT, "depends_on"=>array())));
|
||||
@@ -489,7 +489,7 @@ class AsyncSendNewsroom extends AsyncTask {
|
||||
MetaModel::Init_AddAttribute(new AttributeInteger("object_id", array("allowed_values"=>null, "sql"=>"object_id", "default_value"=>null, "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("object_class", array("allowed_values"=>null, "sql"=>"object_class", "default_value"=>null, "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeText("url", array("allowed_values"=>null, "sql"=>"url", "default_value"=>null, "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeDateTime("date", array("allowed_values"=>null, "sql"=>"date", "default_value"=>null, "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeDateTime("date", array("allowed_values"=>null, "sql"=>"date", "default_value"=>'NOW()', "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -142,7 +142,7 @@ abstract class AttributeDefinition
|
||||
|
||||
protected $aCSSClasses;
|
||||
|
||||
public function GetType()
|
||||
public function GetType()
|
||||
{
|
||||
return Dict::S('Core:'.get_class($this));
|
||||
}
|
||||
@@ -1715,8 +1715,8 @@ class AttributeLinkedSet extends AttributeDefinition
|
||||
public function GetEditMode()
|
||||
{
|
||||
return $this->GetOptional('edit_mode', LINKSET_EDITMODE_ACTIONS);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int see LINKSET_EDITWHEN_* constants
|
||||
* @since 3.1.1 3.2.0 N°6385
|
||||
@@ -1758,7 +1758,7 @@ class AttributeLinkedSet extends AttributeDefinition
|
||||
{
|
||||
return $this->GetOptional('with_php_computation', false);
|
||||
}
|
||||
|
||||
|
||||
public function GetLinkedClass()
|
||||
{
|
||||
return $this->Get('linked_class');
|
||||
@@ -2069,7 +2069,7 @@ class AttributeLinkedSet extends AttributeDefinition
|
||||
public function GetImportColumns()
|
||||
{
|
||||
$aColumns = array();
|
||||
$aColumns[$this->GetCode()] = 'TEXT'.CMDBSource::GetSqlStringColumnDefinition();
|
||||
$aColumns[$this->GetCode()] = 'MEDIUMTEXT'.CMDBSource::GetSqlStringColumnDefinition();
|
||||
|
||||
return $aColumns;
|
||||
}
|
||||
@@ -4193,7 +4193,7 @@ class AttributeFinalClass extends AttributeString
|
||||
*/
|
||||
class AttributePassword extends AttributeString implements iAttributeNoGroupBy
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
|
||||
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
@@ -4270,7 +4270,7 @@ class AttributePassword extends AttributeString implements iAttributeNoGroupBy
|
||||
*/
|
||||
class AttributeEncryptedString extends AttributeString implements iAttributeNoGroupBy
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
|
||||
|
||||
protected function GetSQLCol($bFullSpec = false)
|
||||
{
|
||||
@@ -4995,7 +4995,7 @@ class AttributeCaseLog extends AttributeLongText
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strlen($proposedValue) > 0)
|
||||
if (utils::StrLen($proposedValue) > 0)
|
||||
{
|
||||
//N°5135 - add impersonation information in caselog
|
||||
if (UserRights::IsImpersonated()){
|
||||
@@ -6346,10 +6346,15 @@ class AttributeDateTime extends AttributeDBField
|
||||
|
||||
$oFormField = parent::MakeFormField($oObject, $oFormField);
|
||||
|
||||
// After call to the parent as it sets the current value
|
||||
$oFormField->SetCurrentValue($this->GetFormat()->Format($oObject->Get($this->GetCode())));
|
||||
// After call to the parent as it sets the current value
|
||||
$oValue = $oObject->Get($this->GetCode());
|
||||
if ($oValue === $this->GetNullValue()) {
|
||||
$oValue = $this->GetDefaultValue($oObject);
|
||||
}
|
||||
$oFormField->SetCurrentValue($this->GetFormat()->Format($oValue));
|
||||
|
||||
return $oFormField;
|
||||
|
||||
return $oFormField;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -6433,8 +6438,26 @@ class AttributeDateTime extends AttributeDBField
|
||||
|
||||
public function GetDefaultValue(DBObject $oHostObject = null)
|
||||
{
|
||||
if (!$this->IsNullAllowed()) {
|
||||
return date($this->GetInternalFormat());
|
||||
$sDefaultValue = $this->Get('default_value');
|
||||
if (utils::IsNotNullOrEmptyString($sDefaultValue)) {
|
||||
try {
|
||||
$sDefaultDate = Expression::FromOQL($sDefaultValue)->Evaluate([]);
|
||||
} catch (Exception $e) {
|
||||
try {
|
||||
$sDefaultDate = Expression::FromOQL('"'.$sDefaultValue.'"')->Evaluate([]);
|
||||
} catch (Exception $e) {
|
||||
IssueLog::Error("Invalid default value '$sDefaultValue' for field '{$this->GetCode()}' on class '{$this->GetHostClass()}', defaulting to null");
|
||||
|
||||
return $this->GetNullValue();
|
||||
}
|
||||
}
|
||||
try {
|
||||
$oDate = new DateTimeImmutable($sDefaultDate);
|
||||
} catch (Exception $e) {
|
||||
IssueLog::Error("Invalid default value '$sDefaultValue' for field '{$this->GetCode()}' on class '{$this->GetHostClass()}', defaulting to null");
|
||||
return $this->GetNullValue();
|
||||
}
|
||||
return $oDate->format($this->GetInternalFormat());
|
||||
}
|
||||
return $this->GetNullValue();
|
||||
}
|
||||
@@ -8142,7 +8165,7 @@ class AttributeURL extends AttributeString
|
||||
* @since 3.0.3 moved from Config to AttributeURL constant
|
||||
*/
|
||||
public const DEFAULT_VALIDATION_PATTERN = /** @lang RegExp */
|
||||
'(https?|ftp)\://([a-zA-Z0-9+!*(),;?&=\$_.-]+(\:[a-zA-Z0-9+!*(),;?&=\$_.-]+)?@)?([a-zA-Z0-9-.]{3,})(\:[0-9]{2,5})?(/([a-zA-Z0-9:%+\$_-]\.?)+)*/?(\?[a-zA-Z+&\$_.-][a-zA-Z0-9;:[\]@&%=+/\$_.,-]*)?(#[a-zA-Z0-9_.-][a-zA-Z0-9+\$_.-]*)?';
|
||||
'(https?|ftp)\://([a-zA-Z0-9+!*(),;?&=\$_.-]+(\:[a-zA-Z0-9+!*(),;?&=\$_.-]+)?@)?([a-zA-Z0-9-.]{3,})(\:[0-9]{2,5})?(/([a-zA-Z0-9:%@+\$_-]\.?)+)*/?(\?[a-zA-Z+&\$_.-][a-zA-Z0-9;:[\]@&%=+/\$_.,-]*)?(#[a-zA-Z0-9_.-][a-zA-Z0-9+\$_.-]*)?';
|
||||
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
@@ -9433,8 +9456,13 @@ class AttributeStopWatch extends AttributeDefinition
|
||||
case 'deadline':
|
||||
if ($value)
|
||||
{
|
||||
$sDate = date(AttributeDateTime::GetInternalFormat(), $value);
|
||||
$sRet = AttributeDeadline::FormatDeadline($sDate);
|
||||
if (is_int($value))
|
||||
{
|
||||
$sDate = date(AttributeDateTime::GetInternalFormat(), $value);
|
||||
$sRet = AttributeDeadline::FormatDeadline($sDate);
|
||||
} else {
|
||||
$sRet = $value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -9984,7 +10012,7 @@ class AttributeSubItem extends AttributeDefinition
|
||||
*/
|
||||
class AttributeOneWayPassword extends AttributeDefinition implements iAttributeNoGroupBy
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
|
||||
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
@@ -10926,12 +10954,12 @@ abstract class AttributeSet extends AttributeDBFieldVoid
|
||||
$sDescription = utils::EscapeHtml($this->GetValueDescription($sValue));
|
||||
$oFilter = DBSearch::FromOQL("SELECT $sClass WHERE $sAttCode MATCHES '$sValue'");
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sContext = $oAppContext->GetForLink();
|
||||
$sContext = $oAppContext->GetForLink(true);
|
||||
$sUIPage = cmdbAbstractObject::ComputeStandardUIPage($oFilter->GetClass());
|
||||
$sFilter = rawurlencode($oFilter->serialize());
|
||||
$sLink = '';
|
||||
if ($bWithLink && $this->bDisplayLink) {
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/$sUIPage?operation=search&filter=".$sFilter."&{$sContext}";
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/$sUIPage?operation=search&filter=".$sFilter.$sContext;
|
||||
$sLink = ' href="'.$sUrl.'"';
|
||||
}
|
||||
|
||||
@@ -12248,13 +12276,13 @@ class AttributeTagSet extends AttributeSet
|
||||
$sTagDescription = $oTag->Get('description');
|
||||
$oFilter = DBSearch::FromOQL("SELECT $sClass WHERE $sAttCode MATCHES '$sTagCode'");
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sContext = $oAppContext->GetForLink();
|
||||
$sContext = $oAppContext->GetForLink(true);
|
||||
$sUIPage = cmdbAbstractObject::ComputeStandardUIPage($oFilter->GetClass());
|
||||
$sFilter = rawurlencode($oFilter->serialize());
|
||||
|
||||
$sLink = '';
|
||||
if ($bWithLink && $this->bDisplayLink) {
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/$sUIPage?operation=search&filter=".$sFilter."&{$sContext}";
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/$sUIPage?operation=search&filter=".$sFilter.$sContext;
|
||||
$sLink = ' href="'.$sUrl.'"';
|
||||
}
|
||||
|
||||
|
||||
@@ -201,6 +201,7 @@ abstract class AbstractWeeklyScheduledProcess implements iScheduledProcess
|
||||
static::MODULE_SETTING_TIME,
|
||||
static::GetDefaultModuleSettingTime()
|
||||
);
|
||||
$sProcessTime = trim($sProcessTime);
|
||||
if (!preg_match('/[0-2]\d:[0-5]\d/', $sProcessTime))
|
||||
{
|
||||
throw new ProcessInvalidConfigException($this->GetModuleName().": wrong format for setting '".static::MODULE_SETTING_TIME."' (found '$sProcessTime')");
|
||||
@@ -230,7 +231,7 @@ abstract class AbstractWeeklyScheduledProcess implements iScheduledProcess
|
||||
$iFirstDayOfWeek = $aDays[0];
|
||||
$iDayMove = $oNow->format('N') - $iFirstDayOfWeek;
|
||||
$oRet = clone $oNow;
|
||||
$oRet->modify('-'.$iDayMove.' days');
|
||||
$oRet->modify(-$iDayMove.' days');
|
||||
$oRet->modify('+1 weeks');
|
||||
}
|
||||
else
|
||||
@@ -238,7 +239,7 @@ abstract class AbstractWeeklyScheduledProcess implements iScheduledProcess
|
||||
$iNextDayOfWeek = $aDays[$iNextPos];
|
||||
$iMove = $iNextDayOfWeek - $oNow->format('N');
|
||||
$oRet = clone $oNow;
|
||||
$oRet->modify('+'.$iMove.' days');
|
||||
$oRet->modify($iMove.' days');
|
||||
}
|
||||
list($sHours, $sMinutes) = explode(':', $sProcessTime);
|
||||
$oRet->setTime((int)$sHours, (int)$sMinutes);
|
||||
|
||||
@@ -1406,7 +1406,7 @@ class BulkChange
|
||||
$aDetails = array();
|
||||
while ($oChange = $oBulkChanges->Fetch())
|
||||
{
|
||||
$sDate = '<a href="csvimport.php?step=10&changeid='.$oChange->GetKey().'&'.$oAppContext->GetForLink().'">'.$oChange->Get('date').'</a>';
|
||||
$sDate = '<a href="csvimport.php?step=10&changeid='.$oChange->GetKey().$oAppContext->GetForLink(true).'">'.$oChange->Get('date').'</a>';
|
||||
$sUser = $oChange->GetUserName();
|
||||
if (preg_match('/^(.*)\\(CSV\\)$/i', $oChange->Get('userinfo'), $aMatches))
|
||||
{
|
||||
@@ -1488,7 +1488,7 @@ EOF
|
||||
function OnTruncatedHistoryToggle(bShowAll)
|
||||
{
|
||||
$('#csv_history_reload').html('<img src="' + GetAbsoluteUrlAppRoot() + 'images/indicator.gif"/>');
|
||||
$.get(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php?{$sAppContext}', {operation: 'displayCSVHistory', showall: bShowAll}, function(data)
|
||||
$.get(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php?$sAppContext', {operation: 'displayCSVHistory', showall: bShowAll}, function(data)
|
||||
{
|
||||
$('#$sAjaxDivId').html(data);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ define('EXPORTER_DEFAULT_CHUNK_SIZE', 1000);
|
||||
class BulkExportException extends Exception
|
||||
{
|
||||
protected $sLocalizedMessage;
|
||||
public function __construct($message, $sLocalizedMessage, $code = null, $previous = null)
|
||||
public function __construct($message, $sLocalizedMessage, $code = 0, $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
$this->sLocalizedMessage = $sLocalizedMessage;
|
||||
@@ -70,7 +70,7 @@ class BulkExportResult extends DBObject
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeDateTime("created", array("allowed_values"=>null, "sql"=>"created", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeDateTime("created", array("allowed_values"=>null, "sql"=>"created", "default_value"=>"NOW()", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeInteger("user_id", array("allowed_values"=>null, "sql"=>"user_id", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeInteger("chunk_size", array("allowed_values"=>null, "sql"=>"chunk_size", "default_value"=>0, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("format", array("allowed_values"=>null, "sql"=>"format", "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
|
||||
@@ -33,7 +33,7 @@ class CMDBChange extends DBObject
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
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 AttributeDateTime("date", array("allowed_values"=>null, "sql"=>"date", "default_value"=>"NOW()", "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(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())));
|
||||
|
||||
@@ -1169,8 +1169,8 @@ class CMDBSource
|
||||
*/
|
||||
public static function IsSameFieldTypes($sItopGeneratedFieldType, $sDbFieldType)
|
||||
{
|
||||
list($sItopFieldDataType, $sItopFieldTypeOptions, $sItopFieldOtherOptions) = static::GetFieldDataTypeAndOptions($sItopGeneratedFieldType);
|
||||
list($sDbFieldDataType, $sDbFieldTypeOptions, $sDbFieldOtherOptions) = static::GetFieldDataTypeAndOptions($sDbFieldType);
|
||||
[$sItopFieldDataType, $sItopFieldTypeOptions, $sItopFieldOtherOptions] = static::GetFieldDataTypeAndOptions($sItopGeneratedFieldType);
|
||||
[$sDbFieldDataType, $sDbFieldTypeOptions, $sDbFieldOtherOptions] = static::GetFieldDataTypeAndOptions($sDbFieldType);
|
||||
|
||||
if (strcasecmp($sItopFieldDataType, $sDbFieldDataType) !== 0)
|
||||
{
|
||||
@@ -1603,7 +1603,19 @@ class CMDBSource
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
public static function GetClusterNb()
|
||||
{
|
||||
$result = 0;
|
||||
$sSql = "SHOW STATUS LIKE 'wsrep_cluster_size';";
|
||||
$aRows = self::QueryToArray($sSql);
|
||||
if (count($aRows) > 0)
|
||||
{
|
||||
$result = $aRows[0]['Value'];
|
||||
}
|
||||
return intval($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://dev.mysql.com/doc/refman/5.7/en/charset-database.html
|
||||
* @return string query to upgrade database charset and collation if needed, null if not
|
||||
* @throws \MySQLException
|
||||
|
||||
@@ -108,7 +108,7 @@ class DefaultWorkingTimeComputer implements iWorkingTimeComputer
|
||||
// Default implementation: 24x7, no holidays: to compute the deadline, just add
|
||||
// the specified duration to the given date/time
|
||||
$oResult = clone $oStartDate;
|
||||
$oResult->modify('+'.$iDuration.' seconds');
|
||||
$oResult->modify($iDuration.' seconds');
|
||||
if (class_exists('WorkingTimeRecorder'))
|
||||
{
|
||||
WorkingTimeRecorder::SetValues($oStartDate->format('U'), $oResult->format('U'), $iDuration, WorkingTimeRecorder::COMPUTED_END);
|
||||
|
||||
@@ -71,7 +71,7 @@ define('DEFAULT_MAX_DISPLAY_LIMIT', 30);
|
||||
define('DEFAULT_STANDARD_RELOAD_INTERVAL', 5 * 60);
|
||||
define('DEFAULT_FAST_RELOAD_INTERVAL', 1 * 60);
|
||||
define('DEFAULT_SECURE_CONNECTION_REQUIRED', false);
|
||||
define('DEFAULT_ALLOWED_LOGIN_TYPES', 'form|external|basic');
|
||||
define('DEFAULT_ALLOWED_LOGIN_TYPES', 'form|external|basic|token');
|
||||
define('DEFAULT_EXT_AUTH_VARIABLE', '$_SERVER[\'REMOTE_USER\']');
|
||||
define('DEFAULT_ENCRYPTION_KEY', '@iT0pEncr1pti0n!'); // We'll use a random generated key later (if possible)
|
||||
define('DEFAULT_ENCRYPTION_LIB', 'Mcrypt'); // We'll define the best encryption available later
|
||||
@@ -1233,6 +1233,14 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'sessions_tracking.session_handler_extension' => [
|
||||
'type' => 'string',
|
||||
'description' => 'to store more data in itop session files, set your own iSessionHandlerExtension implementation class in this variable',
|
||||
'default' => '',
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'sessions_tracking.gc_threshold' => [
|
||||
'type' => 'integer',
|
||||
'description' => 'fallback in case cron is not active: probability in percent that session files are cleanup during any itop request (100 means always)',
|
||||
@@ -1747,6 +1755,14 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'developer_mode.interface_cache.enabled' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'If true then InterfaceDiscovery uses dynamic cache (in developer_mode)',
|
||||
'default' => false,
|
||||
'value' => false,
|
||||
'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.)',
|
||||
|
||||
@@ -16,6 +16,13 @@
|
||||
<attribute id="contact_id"/>
|
||||
</attributes>
|
||||
</naming>
|
||||
<reconciliation>
|
||||
<attributes>
|
||||
<attribute id="action_id"/>
|
||||
<attribute id="contact_id"/>
|
||||
<attribute id="trigger_id"/>
|
||||
</attributes>
|
||||
</reconciliation>
|
||||
<uniqueness_rules>
|
||||
<rule>
|
||||
<attributes>
|
||||
@@ -116,6 +123,11 @@
|
||||
<style>
|
||||
<icon>../../images/icons/icons8-notification.svg</icon>
|
||||
</style>
|
||||
<reconciliation>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
</reconciliation>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="title" xsi:type="AttributeString">
|
||||
@@ -185,18 +197,21 @@
|
||||
<item id="name">
|
||||
<rank>10</rank>
|
||||
</item>
|
||||
<item id="status">
|
||||
<item id="description">
|
||||
<rank>20</rank>
|
||||
</item>
|
||||
<item id="language">
|
||||
<item id="status">
|
||||
<rank>30</rank>
|
||||
</item>
|
||||
<item id="title">
|
||||
<item id="language">
|
||||
<rank>40</rank>
|
||||
</item>
|
||||
<item id="message">
|
||||
<item id="title">
|
||||
<rank>50</rank>
|
||||
</item>
|
||||
<item id="message">
|
||||
<rank>60</rank>
|
||||
</item>
|
||||
</items>
|
||||
</item>
|
||||
<item id="fieldset:ActionNewsroom:trigger">
|
||||
@@ -241,12 +256,15 @@
|
||||
<item id="title">
|
||||
<rank>10</rank>
|
||||
</item>
|
||||
<item id="status">
|
||||
<item id="description">
|
||||
<rank>20</rank>
|
||||
</item>
|
||||
<item id="language">
|
||||
<item id="status">
|
||||
<rank>30</rank>
|
||||
</item>
|
||||
<item id="language">
|
||||
<rank>40</rank>
|
||||
</item>
|
||||
</items>
|
||||
</list>
|
||||
</presentation>
|
||||
@@ -380,6 +398,11 @@
|
||||
<attribute id="title"/>
|
||||
</attributes>
|
||||
</naming>
|
||||
<reconciliation>
|
||||
<attributes>
|
||||
<attribute id="title"/>
|
||||
</attributes>
|
||||
</reconciliation>
|
||||
<order>
|
||||
<columns>
|
||||
<column id="date" ascending="false" />
|
||||
@@ -827,6 +850,18 @@
|
||||
<field id="language" xsi:type="AttributeApplicationLanguage"/>
|
||||
</fields>
|
||||
</class>
|
||||
<class id="Event" _delta="define">
|
||||
<!-- Generated by toolkit/export-class-to-meta.php -->
|
||||
<parent>DBObject</parent>
|
||||
<properties>
|
||||
<category>core/cmdb,view_in_gui</category>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="message" xsi:type="AttributeText"/>
|
||||
<field id="date" xsi:type="AttributeDateTime"/>
|
||||
<field id="userinfo" xsi:type="AttributeString"/>
|
||||
</fields>
|
||||
</class>
|
||||
<class id="EventNotification" _delta="define">
|
||||
<!-- Generated by toolkit/export-class-to-meta.php -->
|
||||
<parent>Event</parent>
|
||||
|
||||
@@ -212,6 +212,8 @@ abstract class DBObject implements iDisplay
|
||||
private $aEventListeners = [];
|
||||
private array $aAllowedTransitions = [];
|
||||
|
||||
private ?string $sStimulusBeingApplied = null;
|
||||
|
||||
/**
|
||||
* DBObject constructor.
|
||||
*
|
||||
@@ -768,6 +770,42 @@ abstract class DBObject implements iDisplay
|
||||
$this->Set($sAttCode, $sValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \MySQLException
|
||||
* @throws \OQLException
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
protected function PreDeleteActions(): void
|
||||
{
|
||||
$this->SetReadOnly('No modification allowed before delete');
|
||||
$this->FireEventAboutToDelete();
|
||||
$oKPI = new ExecutionKPI();
|
||||
$this->OnDelete();
|
||||
$oKPI->ComputeStatsForExtension($this, 'OnDelete');
|
||||
|
||||
// Activate any existing trigger
|
||||
$sClass = get_class($this);
|
||||
$aParams = array('class_list' => MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL));
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL('SELECT TriggerOnObjectDelete AS t WHERE t.target_class IN (:class_list)'), array(),
|
||||
$aParams);
|
||||
while ($oTrigger = $oSet->Fetch()) {
|
||||
/** @var \TriggerOnObjectDelete $oTrigger */
|
||||
try {
|
||||
$oKPI = new ExecutionKPI();
|
||||
$oTrigger->DoActivate($this->ToArgs('this'));
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$oTrigger->LogException($e, $this);
|
||||
utils::EnrichRaisedException($oTrigger, $e);
|
||||
}
|
||||
finally {
|
||||
$oKPI->ComputeStatsForExtension($this, 'TriggerOnObjectDelete');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @throws \ReflectionException
|
||||
@@ -1170,7 +1208,7 @@ abstract class DBObject implements iDisplay
|
||||
if ($aCallInfo["function"] != "ComputeValues") continue;
|
||||
return; //skip!
|
||||
}
|
||||
$this->FireEventComputeValues();
|
||||
$this->FireEventComputeValues($this->sStimulusBeingApplied);
|
||||
$oKPI = new ExecutionKPI();
|
||||
$this->ComputeValues();
|
||||
$oKPI->ComputeStatsForExtension($this, 'ComputeValues');
|
||||
@@ -2094,7 +2132,7 @@ abstract class DBObject implements iDisplay
|
||||
|
||||
return "Bad type";
|
||||
}
|
||||
elseif (($oAtt instanceof AttributeClassAttCodeSet) || ($oAtt instanceof AttributeEnumSet))
|
||||
elseif ($oAtt instanceof AttributeSet)
|
||||
{
|
||||
if (is_string($toCheck))
|
||||
{
|
||||
@@ -2633,7 +2671,7 @@ abstract class DBObject implements iDisplay
|
||||
|
||||
// Ultimate check - ensure DB integrity
|
||||
$this->SetReadOnly('No modification allowed during CheckToCreate');
|
||||
$this->FireEventCheckToWrite();
|
||||
$this->FireEventCheckToWrite($this->sStimulusBeingApplied);
|
||||
$this->SetReadWrite();
|
||||
|
||||
$oKPI = new ExecutionKPI();
|
||||
@@ -2829,6 +2867,14 @@ abstract class DBObject implements iDisplay
|
||||
protected function ListChangedValues(array $aProposal)
|
||||
{
|
||||
$aDelta = array();
|
||||
$sClass = get_class($this);
|
||||
if (MetaModel::HasLifecycle($sClass) && utils::IsNotNullOrEmptyString($this->sStimulusBeingApplied)) {
|
||||
$sStateAttCode = MetaModel::GetStateAttributeCode($sClass);
|
||||
if (!in_array($sStateAttCode, $aProposal)) {
|
||||
// Same state but the transition was asked, act as if the state was changed
|
||||
$aDelta[$sStateAttCode] = $this->m_aCurrValues[$sStateAttCode];
|
||||
}
|
||||
}
|
||||
foreach ($aProposal as $sAtt => $proposedValue)
|
||||
{
|
||||
if (!array_key_exists($sAtt, $this->m_aOrigValues))
|
||||
@@ -3362,7 +3408,7 @@ abstract class DBObject implements iDisplay
|
||||
$this->OnInsert();
|
||||
$oKPI->ComputeStatsForExtension($this, 'OnInsert');
|
||||
|
||||
$this->FireEventBeforeWrite();
|
||||
$this->FireEventBeforeWrite(null);
|
||||
|
||||
// If not automatically computed, then check that the key is given by the caller
|
||||
if (!MetaModel::IsAutoIncrementKey($sRootClass)) {
|
||||
@@ -3458,6 +3504,7 @@ abstract class DBObject implements iDisplay
|
||||
|
||||
$this->m_bIsInDB = true;
|
||||
$this->m_bDirty = false;
|
||||
$this->m_bFullyLoaded = true;
|
||||
foreach ($this->m_aCurrValues as $sAttCode => $value) {
|
||||
if (is_object($value)) {
|
||||
$value = clone $value;
|
||||
@@ -3496,7 +3543,7 @@ abstract class DBObject implements iDisplay
|
||||
*/
|
||||
protected function PostInsertActions(): void
|
||||
{
|
||||
$this->FireEventAfterWrite([], true);
|
||||
$this->FireEventAfterWrite([], true, null);
|
||||
$oKPI = new ExecutionKPI();
|
||||
$this->AfterInsert();
|
||||
$oKPI->ComputeStatsForExtension($this, 'AfterInsert');
|
||||
@@ -3577,13 +3624,13 @@ abstract class DBObject implements iDisplay
|
||||
*/
|
||||
public function DBUpdate()
|
||||
{
|
||||
$this->LogCRUDEnter(__METHOD__);
|
||||
if (!MetaModel::StartReentranceProtection($this)) {
|
||||
$this->LogCRUDExit(__METHOD__, 'Rejected (reentrance)');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->LogCRUDEnter(__METHOD__);
|
||||
if (!$this->m_bIsInDB)
|
||||
{
|
||||
throw new CoreException("DBUpdate: could not update a newly created object, please call DBInsert instead");
|
||||
@@ -3604,7 +3651,7 @@ abstract class DBObject implements iDisplay
|
||||
$this->OnUpdate();
|
||||
$oKPI->ComputeStatsForExtension($this, 'OnUpdate');
|
||||
|
||||
$this->FireEventBeforeWrite();
|
||||
$this->FireEventBeforeWrite($this->sStimulusBeingApplied);
|
||||
|
||||
// Freeze the changes at this point
|
||||
$this->InitPreviousValuesForUpdatedAttributes();
|
||||
@@ -3772,7 +3819,7 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
|
||||
try {
|
||||
$this->PostUpdateActions($aChanges, $sClass);
|
||||
$this->PostUpdateActions($this->m_aPreviousValuesForUpdatedAttributes, $sClass);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$this->LogCRUDExit(__METHOD__, 'Error: '.$e->getMessage());
|
||||
@@ -3815,7 +3862,9 @@ abstract class DBObject implements iDisplay
|
||||
*/
|
||||
protected function PostUpdateActions(array $aChanges): void
|
||||
{
|
||||
$this->FireEventAfterWrite($aChanges, false);
|
||||
$sStimulusBeingApplied = $this->sStimulusBeingApplied;
|
||||
$this->sStimulusBeingApplied = null;
|
||||
$this->FireEventAfterWrite($aChanges, false, $sStimulusBeingApplied);
|
||||
$oKPI = new ExecutionKPI();
|
||||
$this->AfterUpdate();
|
||||
$oKPI->ComputeStatsForExtension($this, 'AfterUpdate');
|
||||
@@ -3827,39 +3876,37 @@ abstract class DBObject implements iDisplay
|
||||
$this->ActivateOnObjectUpdateTriggersForTargetObjects();
|
||||
|
||||
$sClass = get_class($this);
|
||||
if (MetaModel::HasLifecycle($sClass))
|
||||
if (utils::IsNotNullOrEmptyString($sStimulusBeingApplied))
|
||||
{
|
||||
$sStateAttCode = MetaModel::GetStateAttributeCode($sClass);
|
||||
if (isset($this->m_aPreviousValuesForUpdatedAttributes[$sStateAttCode])) {
|
||||
$sPreviousState = $this->m_aPreviousValuesForUpdatedAttributes[$sStateAttCode];
|
||||
// Change state triggers...
|
||||
$aParams = array(
|
||||
'class_list' => MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL),
|
||||
'previous_state' => $sPreviousState,
|
||||
'new_state' => $this->Get($sStateAttCode),
|
||||
);
|
||||
$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 \TriggerOnStateLeave $oTrigger */
|
||||
try {
|
||||
$oTrigger->DoActivate($this->ToArgs('this'));
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$oTrigger->LogException($e, $this);
|
||||
utils::EnrichRaisedException($oTrigger, $e);
|
||||
}
|
||||
$sPreviousState = $this->m_aPreviousValuesForUpdatedAttributes[$sStateAttCode];
|
||||
// Change state triggers...
|
||||
$aParams = array(
|
||||
'class_list' => MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL),
|
||||
'previous_state' => $sPreviousState,
|
||||
'new_state' => $this->Get($sStateAttCode),
|
||||
);
|
||||
$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 \TriggerOnStateLeave $oTrigger */
|
||||
try {
|
||||
$oTrigger->DoActivate($this->ToArgs('this'));
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$oTrigger->LogException($e, $this);
|
||||
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 \TriggerOnStateEnter $oTrigger */
|
||||
try {
|
||||
$oTrigger->DoActivate($this->ToArgs('this'));
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$oTrigger->LogException($e, $this);
|
||||
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 \TriggerOnStateEnter $oTrigger */
|
||||
try {
|
||||
$oTrigger->DoActivate($this->ToArgs('this'));
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$oTrigger->LogException($e, $this);
|
||||
utils::EnrichRaisedException($oTrigger, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3987,7 +4034,7 @@ abstract class DBObject implements iDisplay
|
||||
foreach ($aUpdatedLogAttCodes as $sAttCode) {
|
||||
/** @var \ormCaseLog $oUpdatedCaseLog */
|
||||
$oUpdatedCaseLog = $this->Get($sAttCode);
|
||||
$aMentionedObjects = array_merge_recursive($aMentionedObjects, utils::GetMentionedObjectsFromText($oUpdatedCaseLog->GetModifiedEntry()));
|
||||
$aMentionedObjects = array_merge_recursive($aMentionedObjects, utils::GetMentionedObjectsFromText($oUpdatedCaseLog->GetModifiedEntry(ormCaseLog::ENUM_FORMAT_HTML)));
|
||||
}
|
||||
|
||||
// 3 - Trigger for those objects
|
||||
@@ -4092,16 +4139,17 @@ abstract class DBObject implements iDisplay
|
||||
CMDBSource::DeleteFrom($sDeleteSQL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @throws ArchivedObjectException
|
||||
* @throws CoreException
|
||||
* @throws CoreUnexpectedValue
|
||||
* @throws MySQLException
|
||||
* @throws MySQLHasGoneAwayException
|
||||
* @throws OQLException
|
||||
*/
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
* @throws \OQLException
|
||||
* @throws \Random\RandomException
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
protected function DBDeleteSingleObject()
|
||||
{
|
||||
$this->LogCRUDEnter(__METHOD__);
|
||||
@@ -4112,29 +4160,7 @@ abstract class DBObject implements iDisplay
|
||||
return;
|
||||
}
|
||||
|
||||
$this->SetReadOnly("No modification allowed before delete");
|
||||
$this->FireEventAboutToDelete();
|
||||
$oKPI = new ExecutionKPI();
|
||||
$this->OnDelete();
|
||||
$oKPI->ComputeStatsForExtension($this, 'OnDelete');
|
||||
|
||||
// Activate any existing trigger
|
||||
$sClass = get_class($this);
|
||||
$aParams = array('class_list' => MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL));
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnObjectDelete AS t WHERE t.target_class IN (:class_list)"), array(),
|
||||
$aParams);
|
||||
while ($oTrigger = $oSet->Fetch())
|
||||
{
|
||||
/** @var \TriggerOnObjectDelete $oTrigger */
|
||||
try
|
||||
{
|
||||
$oTrigger->DoActivate($this->ToArgs('this'));
|
||||
}
|
||||
catch(Exception $e) {
|
||||
$oTrigger->LogException($e, $this);
|
||||
utils::EnrichRaisedException($oTrigger, $e);
|
||||
}
|
||||
}
|
||||
$this->PreDeleteActions();
|
||||
|
||||
$this->RecordObjDeletion($this->m_iKey); // May cause a reload for storing history information
|
||||
|
||||
@@ -4485,6 +4511,8 @@ abstract class DBObject implements iDisplay
|
||||
*/
|
||||
public function ApplyStimulus($sStimulusCode, $bDoNotWrite = false)
|
||||
{
|
||||
$this->LogCRUDEnter(__METHOD__, "Code: $sStimulusCode");
|
||||
|
||||
$sClass = get_class($this);
|
||||
if (!MetaModel::HasLifecycle($sClass))
|
||||
{
|
||||
@@ -4511,6 +4539,8 @@ abstract class DBObject implements iDisplay
|
||||
} else {
|
||||
$aBackupValues[$sAttCode] = $value;
|
||||
}
|
||||
} else {
|
||||
$aBackupValues[$sAttCode] = $oAttDef->GetNullValue();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4518,7 +4548,6 @@ abstract class DBObject implements iDisplay
|
||||
|
||||
// Change the state before proceeding to the actions, this is necessary because an action might
|
||||
// trigger another stimuli (alternative: push the stimuli into a queue)
|
||||
$sPreviousState = $this->Get($sStateAttCode);
|
||||
$sNewState = $aTransitionDef['target_state'];
|
||||
$this->Set($sStateAttCode, $sNewState);
|
||||
|
||||
@@ -4526,67 +4555,71 @@ abstract class DBObject implements iDisplay
|
||||
// array('target_state'=>..., 'actions'=>array of handlers procs, 'user_restriction'=>TBD
|
||||
|
||||
$bSuccess = true;
|
||||
$sActionDesc = '';
|
||||
foreach ($aTransitionDef['actions'] as $actionHandler)
|
||||
{
|
||||
if (is_string($actionHandler))
|
||||
{
|
||||
// Old (pre-2.1.0 modules) action definition without any parameter
|
||||
$aActionCallSpec = array($this, $actionHandler);
|
||||
$sActionDesc = $sClass.'::'.$actionHandler;
|
||||
// Prevent current object from being updated by the actions
|
||||
$this->AddCurrentObjectInCrudStack('APPLY_STIMULUS');
|
||||
$bIsNewlyProtected = MetaModel::StartReentranceProtection($this);
|
||||
try {
|
||||
foreach ($aTransitionDef['actions'] as $actionHandler) {
|
||||
if (is_string($actionHandler)) {
|
||||
// Old (pre-2.1.0 modules) action definition without any parameter
|
||||
$aActionCallSpec = array($this, $actionHandler);
|
||||
$sActionDesc = $sClass.'::'.$actionHandler;
|
||||
|
||||
if (!is_callable($aActionCallSpec))
|
||||
{
|
||||
throw new CoreException("Unable to call action: $sClass::$actionHandler");
|
||||
}
|
||||
$bRet = call_user_func($aActionCallSpec, $sStimulusCode);
|
||||
}
|
||||
else // if (is_array($actionHandler))
|
||||
{
|
||||
// New syntax: 'verb' and typed parameters
|
||||
$sAction = $actionHandler['verb'];
|
||||
$sActionDesc = "$sClass::$sAction";
|
||||
$aParams = array();
|
||||
foreach($actionHandler['params'] as $aDefinition)
|
||||
{
|
||||
$sParamType = array_key_exists('type', $aDefinition) ? $aDefinition['type'] : 'string';
|
||||
switch($sParamType)
|
||||
{
|
||||
case 'int':
|
||||
$value = (int)$aDefinition['value'];
|
||||
break;
|
||||
|
||||
case 'float':
|
||||
$value = (float)$aDefinition['value'];
|
||||
break;
|
||||
|
||||
case 'bool':
|
||||
$value = (bool)$aDefinition['value'];
|
||||
break;
|
||||
|
||||
case 'reference':
|
||||
$value = ${$aDefinition['value']};
|
||||
break;
|
||||
|
||||
case 'string':
|
||||
default:
|
||||
$value = (string)$aDefinition['value'];
|
||||
if (!is_callable($aActionCallSpec)) {
|
||||
throw new CoreException("Unable to call action: $sClass::$actionHandler");
|
||||
}
|
||||
$aParams[] = $value;
|
||||
$bRet = call_user_func($aActionCallSpec, $sStimulusCode);
|
||||
} else // if (is_array($actionHandler))
|
||||
{
|
||||
// New syntax: 'verb' and typed parameters
|
||||
$sAction = $actionHandler['verb'];
|
||||
$sActionDesc = "$sClass::$sAction";
|
||||
$aParams = array();
|
||||
foreach ($actionHandler['params'] as $aDefinition) {
|
||||
$sParamType = array_key_exists('type', $aDefinition) ? $aDefinition['type'] : 'string';
|
||||
switch ($sParamType) {
|
||||
case 'int':
|
||||
$value = (int)$aDefinition['value'];
|
||||
break;
|
||||
|
||||
case 'float':
|
||||
$value = (float)$aDefinition['value'];
|
||||
break;
|
||||
|
||||
case 'bool':
|
||||
$value = (bool)$aDefinition['value'];
|
||||
break;
|
||||
|
||||
case 'reference':
|
||||
$value = ${$aDefinition['value']};
|
||||
break;
|
||||
|
||||
case 'string':
|
||||
default:
|
||||
$value = (string)$aDefinition['value'];
|
||||
}
|
||||
$aParams[] = $value;
|
||||
}
|
||||
$aCallSpec = array($this, $sAction);
|
||||
$bRet = call_user_func_array($aCallSpec, $aParams);
|
||||
}
|
||||
// if one call fails, the whole is considered as failed
|
||||
// (in case there is no returned value, null is obtained and means "ok")
|
||||
if ($bRet === false) {
|
||||
IssueLog::Info("Lifecycle action $sActionDesc returned false on object #$sClass:".$this->GetKey());
|
||||
$bSuccess = false;
|
||||
}
|
||||
$aCallSpec = array($this, $sAction);
|
||||
$bRet = call_user_func_array($aCallSpec, $aParams);
|
||||
}
|
||||
// if one call fails, the whole is considered as failed
|
||||
// (in case there is no returned value, null is obtained and means "ok")
|
||||
if ($bRet === false)
|
||||
{
|
||||
IssueLog::Info("Lifecycle action $sActionDesc returned false on object #$sClass:".$this->GetKey());
|
||||
$bSuccess = false;
|
||||
} finally {
|
||||
if ($bIsNewlyProtected) {
|
||||
// Stops protection only if the object was not already protected
|
||||
MetaModel::StopReentranceProtection($this);
|
||||
}
|
||||
$this->RemoveCurrentObjectInCrudStack();
|
||||
}
|
||||
if ($bSuccess)
|
||||
{
|
||||
$this->sStimulusBeingApplied = $sStimulusCode;
|
||||
// Stop watches
|
||||
foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
|
||||
{
|
||||
@@ -4615,6 +4648,7 @@ abstract class DBObject implements iDisplay
|
||||
$this->m_aCurrValues[$sAttCode] = $aBackupValues[$sAttCode];
|
||||
}
|
||||
}
|
||||
$this->LogCRUDExit(__METHOD__, 'Current State: '.$this->Get($sStateAttCode));
|
||||
return $bSuccess;
|
||||
}
|
||||
|
||||
@@ -6601,7 +6635,7 @@ abstract class DBObject implements iDisplay
|
||||
* @return void
|
||||
* @since 3.1.0
|
||||
*/
|
||||
protected function FireEventCheckToWrite(): void
|
||||
protected function FireEventCheckToWrite(?string $sStimulusBeingApplied): void
|
||||
{
|
||||
}
|
||||
|
||||
@@ -6609,7 +6643,7 @@ abstract class DBObject implements iDisplay
|
||||
* @return void
|
||||
* @since 3.1.0
|
||||
*/
|
||||
protected function FireEventBeforeWrite()
|
||||
protected function FireEventBeforeWrite(?string $sStimulusBeingApplied)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -6619,7 +6653,7 @@ abstract class DBObject implements iDisplay
|
||||
* @return void
|
||||
* @since 3.1.0
|
||||
*/
|
||||
protected function FireEventAfterWrite(array $aChanges, bool $bIsNew): void
|
||||
protected function FireEventAfterWrite(array $aChanges, bool $bIsNew, ?string $sStimulusBeingApplied): void
|
||||
{
|
||||
}
|
||||
|
||||
@@ -6657,7 +6691,7 @@ abstract class DBObject implements iDisplay
|
||||
* @return void
|
||||
* @since 3.1.0
|
||||
*/
|
||||
protected function FireEventComputeValues(): void
|
||||
protected function FireEventComputeValues(?string $sStimulusBeingApplied): void
|
||||
{
|
||||
}
|
||||
|
||||
@@ -6699,12 +6733,13 @@ abstract class DBObject implements iDisplay
|
||||
$oRootClass = MetaModel::GetRootClass($sClass);
|
||||
|
||||
foreach (self::$m_aCrudStack as $aCrudStackEntry) {
|
||||
if (($oRootClass === $aCrudStackEntry['class'])
|
||||
&& ($sConvertedId === $aCrudStackEntry['id'])) {
|
||||
if (($oRootClass === $aCrudStackEntry['class']) && ($sConvertedId === $aCrudStackEntry['id'])) {
|
||||
IssueLog::Trace('CRUD '.__METHOD__." $sClass:$sId IS in CRUD Stack", LogChannels::DM_CRUD);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
IssueLog::Trace('CRUD '.__METHOD__." $sClass:$sId NOT in CRUD Stack", LogChannels::DM_CRUD);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -6722,10 +6757,12 @@ abstract class DBObject implements iDisplay
|
||||
$sRootClass = MetaModel::GetRootClass($sClass);
|
||||
foreach (self::$m_aCrudStack as $aCrudStackEntry) {
|
||||
if ($sRootClass === $aCrudStackEntry['class']) {
|
||||
IssueLog::Trace("CRUD ".__METHOD__." $sClass IS in CRUD Stack", LogChannels::DM_CRUD);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
IssueLog::Trace('CRUD '.__METHOD__." $sClass NOT in CRUD Stack", LogChannels::DM_CRUD);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -6735,17 +6772,20 @@ abstract class DBObject implements iDisplay
|
||||
* @param string $sCrudType
|
||||
*
|
||||
* @return void
|
||||
* @throws \CoreException
|
||||
* @since 3.1.0 N°5609
|
||||
*/
|
||||
private function AddCurrentObjectInCrudStack(string $sCrudType): void
|
||||
{
|
||||
$this->LogCRUDDebug(__METHOD__);
|
||||
$sRootClass = MetaModel::GetRootClass(get_class($this));
|
||||
$sKey = (string)$this->GetKey();
|
||||
self::$m_aCrudStack[] = [
|
||||
'type' => $sCrudType,
|
||||
'class' => $sRootClass,
|
||||
'id' => (string)$this->GetKey(), // GetKey() doesn't have type hinting, so forcing type to avoid getting an int
|
||||
'id' => $sKey, // GetKey() doesn't have type hinting, so forcing type to avoid getting an int
|
||||
];
|
||||
$iCount = count(self::$m_aCrudStack);
|
||||
$this->LogCRUDDebug(__METHOD__, "$sCrudType $sRootClass:$sKey count $iCount");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -6757,10 +6797,15 @@ abstract class DBObject implements iDisplay
|
||||
*/
|
||||
private function UpdateCurrentObjectInCrudStack(): void
|
||||
{
|
||||
$this->LogCRUDDebug(__METHOD__);
|
||||
$aCurrentCrudStack = array_pop(self::$m_aCrudStack);
|
||||
$aCurrentCrudStack['id'] = (string)$this->GetKey();
|
||||
$sOldId = $aCurrentCrudStack['id'];
|
||||
$sNewId = (string)$this->GetKey();
|
||||
$aCurrentCrudStack['id'] = $sNewId;
|
||||
self::$m_aCrudStack[] = $aCurrentCrudStack;
|
||||
$sClass = $aCurrentCrudStack['class'];
|
||||
$sType = $aCurrentCrudStack['type'];
|
||||
$iCount = count(self::$m_aCrudStack);
|
||||
$this->LogCRUDDebug(__METHOD__, "$sType $sClass:$sOldId => $sClass:$sNewId count $iCount");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -6772,7 +6817,11 @@ abstract class DBObject implements iDisplay
|
||||
private function RemoveCurrentObjectInCrudStack(): void
|
||||
{
|
||||
$aRemoved = array_pop(self::$m_aCrudStack);
|
||||
$this->LogCRUDDebug(__METHOD__, $aRemoved['class'].':'.$aRemoved['id']);
|
||||
$sType = $aRemoved['type'];
|
||||
$sClass = $aRemoved['class'];
|
||||
$sId = $aRemoved['id'];
|
||||
$iCount = count(self::$m_aCrudStack);
|
||||
$this->LogCRUDDebug(__METHOD__, "$sType $sClass:$sId count $iCount");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -6789,37 +6838,53 @@ abstract class DBObject implements iDisplay
|
||||
protected function LogCRUDEnter($sFunction, $sComment = '')
|
||||
{
|
||||
$sClass = get_class($this);
|
||||
if (utils::StartsWith($sClass, 'CMDBChange')) {
|
||||
return;
|
||||
}
|
||||
$sKey = $this->GetKey();
|
||||
$sUUID = $this->m_sObjectUniqId;
|
||||
$sPadding = str_pad('', count(self::$m_aCrudStack), '-');
|
||||
IssueLog::Debug("CRUD +$sPadding> $sFunction $sClass:$sKey $sComment", LogChannels::DM_CRUD);
|
||||
IssueLog::Debug("CRUD +$sPadding> $sFunction $sClass:$sKey ($sUUID) $sComment", LogChannels::DM_CRUD);
|
||||
}
|
||||
|
||||
protected function LogCRUDExit($sFunction, $sComment = '')
|
||||
{
|
||||
$sClass = get_class($this);
|
||||
if (utils::StartsWith($sClass, 'CMDBChange')) {
|
||||
return;
|
||||
}
|
||||
$sKey = $this->GetKey();
|
||||
$sUUID = $this->m_sObjectUniqId;
|
||||
$sPadding = str_pad('', count(self::$m_aCrudStack), '-');
|
||||
if (strlen($sComment) === 0) {
|
||||
IssueLog::Trace("CRUD <$sPadding+ $sFunction $sClass:$sKey", LogChannels::DM_CRUD);
|
||||
} else {
|
||||
IssueLog::Debug("CRUD <$sPadding+ $sFunction $sClass:$sKey $sComment", LogChannels::DM_CRUD);
|
||||
IssueLog::Debug("CRUD <$sPadding+ $sFunction $sClass:$sKey ($sUUID) $sComment", LogChannels::DM_CRUD);
|
||||
}
|
||||
}
|
||||
|
||||
protected function LogCRUDDebug($sFunction, $sComment = '')
|
||||
{
|
||||
$sClass = get_class($this);
|
||||
if (utils::StartsWith($sClass, 'CMDBChange')) {
|
||||
return;
|
||||
}
|
||||
$sKey = $this->GetKey();
|
||||
$sUUID = $this->m_sObjectUniqId;
|
||||
$sPadding = str_pad('', count(self::$m_aCrudStack), '-');
|
||||
IssueLog::Debug("CRUD --$sPadding $sFunction $sClass:$sKey $sComment", LogChannels::DM_CRUD);
|
||||
IssueLog::Debug("CRUD --$sPadding $sFunction $sClass:$sKey ($sUUID) $sComment", LogChannels::DM_CRUD);
|
||||
}
|
||||
|
||||
protected function LogCRUDError($sFunction, $sComment = '')
|
||||
{
|
||||
$sClass = get_class($this);
|
||||
if (utils::StartsWith($sClass, 'CMDBChange')) {
|
||||
return;
|
||||
}
|
||||
$sKey = $this->GetKey();
|
||||
$sUUID = $this->m_sObjectUniqId;
|
||||
$sPadding = str_pad('', count(self::$m_aCrudStack), '!');
|
||||
IssueLog::Error("CRUD !!$sPadding Error $sFunction $sClass:$sKey $sComment", LogChannels::DM_CRUD);
|
||||
IssueLog::Error("CRUD !!$sPadding Error $sFunction $sClass:$sKey ($sUUID) $sComment", LogChannels::DM_CRUD);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -51,7 +51,7 @@ class DBProperty extends DBObject
|
||||
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("value", array("allowed_values"=>null, "sql"=>"value", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeDateTime("change_date", array("allowed_values"=>null, "sql"=>"change_date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeDateTime("change_date", array("allowed_values"=>null, "sql"=>"change_date", "default_value"=>"NOW()", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("change_comment", array("allowed_values"=>null, "sql"=>"change_comment", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ class Dict
|
||||
|
||||
public static function GetUserLanguage()
|
||||
{
|
||||
if (self::$m_sCurrentLanguage == null) // May happen when no user is logged in (i.e login screen, non authentifed page)
|
||||
if (self::$m_sCurrentLanguage == null) // May happen when no user is logged in (i.e. login screen, non-authenticated page)
|
||||
{
|
||||
// In which case let's use the default language
|
||||
return self::$m_sDefaultLanguage;
|
||||
@@ -122,7 +122,7 @@ class Dict
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a localised string from the dictonary with its associated lang code
|
||||
* Returns a localised string from the dictionary with its associated lang code
|
||||
*
|
||||
* @param string $sStringCode The code identifying the dictionary entry
|
||||
* @param string $sDefault Default value if there is no match in the dictionary
|
||||
@@ -201,12 +201,12 @@ class Dict
|
||||
|
||||
if ($sLocalizedFormat == $sFormatCode)
|
||||
{
|
||||
// Make sure the information will be displayed (ex: an error occuring before the dictionary gets loaded)
|
||||
// Make sure the information will be displayed (ex: an error occurring before the dictionary gets loaded)
|
||||
return $sFormatCode.' - '.implode(', ', $aArguments);
|
||||
}
|
||||
|
||||
try{
|
||||
return vsprintf($sLocalizedFormat, $aArguments);
|
||||
return utils::VSprintf($sLocalizedFormat, $aArguments);
|
||||
} catch(\Throwable $e){
|
||||
\IssueLog::Error("Cannot format dict key", null, ["sFormatCode" => $sFormatCode, "sLangCode" => $sLangCode, 'exception_msg' => $e->getMessage() ]);
|
||||
return $sFormatCode.' - '.implode(', ', $aArguments);
|
||||
@@ -214,9 +214,9 @@ class Dict
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a the entries for a given language (replaces the former Add() method)
|
||||
* Initialize the entries for a given language (replaces the former Add() method)
|
||||
* @param string $sLanguageCode Code identifying the language i.e. 'FR-FR', 'EN-US'
|
||||
* @param array $aEntries Hash array of dictionnary entries
|
||||
* @param array $aEntries Hash array of dictionary entries
|
||||
*/
|
||||
public static function SetEntries($sLanguageCode, $aEntries)
|
||||
{
|
||||
@@ -271,9 +271,9 @@ class Dict
|
||||
if (self::$m_aData[$sLangCode] === false) {
|
||||
unset(self::$m_aData[$sLangCode]);
|
||||
} else if (! is_array(self::$m_aData[$sLangCode])) {
|
||||
// N°4125: we dont fix dictionnary corrupted cache (on iTop side).
|
||||
// N°4125: we don't fix dictionary corrupted cache (on iTop side).
|
||||
// but we log an error in a dedicated channel to let itop administrator be aware of a potential APCu issue to fix.
|
||||
IssueLog::Error("APCu corrupted data (with $sLangCode dictionnary). APCu configuration and running version should be troubleshooted...", LogChannels::APC);
|
||||
IssueLog::Error("APCu corrupted data (with $sLangCode dictionary). APCu configuration and running version should be troubleshooted...", LogChannels::APC);
|
||||
$bResult = true;
|
||||
} else {
|
||||
$bResult = true;
|
||||
@@ -296,7 +296,7 @@ class Dict
|
||||
|
||||
/**
|
||||
* Enable caching (cached using APC)
|
||||
* @param string $sApplicationPrefix The prefix for uniquely identiying this iTop instance
|
||||
* @param string $sApplicationPrefix The prefix for uniquely identifying this iTop instance
|
||||
*/
|
||||
public static function EnableCache($sApplicationPrefix)
|
||||
{
|
||||
@@ -305,7 +305,7 @@ class Dict
|
||||
|
||||
/**
|
||||
* Reset the cached entries (cached using APC)
|
||||
* @param string $sApplicationPrefix The prefix for uniquely identiying this iTop instance
|
||||
* @param string $sApplicationPrefix The prefix for uniquely identifying this iTop instance
|
||||
*/
|
||||
public static function ResetCache($sApplicationPrefix)
|
||||
{
|
||||
@@ -385,7 +385,7 @@ class Dict
|
||||
// sLanguageCode: Code identifying the language i.e. FR-FR
|
||||
// sEnglishLanguageDesc: Description of the language code, in English. i.e. French (France)
|
||||
// sLocalizedLanguageDesc: Description of the language code, in its own language. i.e. Français (France)
|
||||
// aEntries: Hash array of dictionnary entries
|
||||
// aEntries: Hash array of dictionary entries
|
||||
// ~~ or ~* can be used to indicate entries still to be translated.
|
||||
public static function Add($sLanguageCode, $sEnglishLanguageDesc, $sLocalizedLanguageDesc, $aEntries)
|
||||
{
|
||||
|
||||
@@ -1470,8 +1470,8 @@ class DisplayableGraph extends SimpleGraph
|
||||
try {
|
||||
$this->InitFromGraphviz();
|
||||
$sExportAsPdfURL = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=relation_pdf&relation='.$sRelation.'&direction='.($this->bDirectionDown ? 'down' : 'up');
|
||||
$sContext = $oAppContext->GetForLink();
|
||||
$sDrillDownURL = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=details&class=%1$s&id=%2$s&'.$sContext;
|
||||
$sContext = $oAppContext->GetForLink(true);
|
||||
$sDrillDownURL = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=details&class=%1$s&id=%2$s'.$sContext;
|
||||
$sExportAsDocumentURL = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=relation_attachment&relation='.$sRelation.'&direction='.($this->bDirectionDown ? 'down' : 'up');
|
||||
$sLoadFromURL = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=relation_json&relation='.$sRelation.'&direction='.($this->bDirectionDown ? 'down' : 'up');
|
||||
$sAttachmentExportTitle = '';
|
||||
@@ -1496,6 +1496,7 @@ class DisplayableGraph extends SimpleGraph
|
||||
'excluded' => $aExcludedByClass,
|
||||
'grouping_threshold' => $iGroupingThreshold,
|
||||
'export_as_pdf' => array('url' => $sExportAsPdfURL, 'label' => Dict::S('UI:Relation:ExportAsPDF')),
|
||||
'transaction_id' => utils::GetNewTransactionId(),
|
||||
'export_as_attachment' => array('url' => $sExportAsDocumentURL, 'label' => Dict::S('UI:Relation:ExportAsAttachment'), 'obj_class' => $sObjClass, 'obj_key' => $iObjKey),
|
||||
'drill_down' => array('url' => $sDrillDownURL, 'label' => Dict::S('UI:Relation:DrillDown')),
|
||||
'labels' => array(
|
||||
|
||||
@@ -114,9 +114,6 @@ class EMail implements iEMail
|
||||
* @param string $sSerializedMessage The serialized representation of the message
|
||||
*
|
||||
* @return \Email
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \Symfony\Component\CssSelector\Exception\SyntaxErrorException
|
||||
*/
|
||||
public static function UnSerializeV2($sSerializedMessage)
|
||||
{
|
||||
@@ -154,7 +151,7 @@ class EMail implements iEMail
|
||||
*/
|
||||
public function SetInReplyTo(string $sMessageId)
|
||||
{
|
||||
$this->AddToHeader('In-Reply-To', $sMessageId);
|
||||
$this->oMailer->SetInReplyTo($sMessageId);
|
||||
}
|
||||
|
||||
public function SetBody($sBody, $sMimeType = 'text/html', $sCustomStyles = null)
|
||||
|
||||
@@ -39,7 +39,7 @@ class Event extends DBObject implements iDisplay
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeText("message", array("allowed_values"=>null, "sql"=>"message", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
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 AttributeDateTime("date", array("allowed_values"=>null, "sql"=>"date", "default_value"=>"NOW()", "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 AttributeString("userinfo", array("allowed_values"=>null, "sql"=>"userinfo", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
|
||||
@@ -231,7 +231,7 @@ class EventIssue extends Event
|
||||
|
||||
if (array_key_exists('_GET', $GLOBALS) && is_array($GLOBALS['_GET']))
|
||||
{
|
||||
$this->Set('arguments_get', $GLOBALS['_GET']);
|
||||
$this->Set('arguments_get', $this->SanitizeRequestParams($GLOBALS['_GET']));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -240,22 +240,7 @@ class EventIssue extends Event
|
||||
|
||||
if (array_key_exists('_POST', $GLOBALS) && is_array($GLOBALS['_POST']))
|
||||
{
|
||||
$aPost = array();
|
||||
foreach($GLOBALS['_POST'] as $sKey => $sValue)
|
||||
{
|
||||
if (is_string($sValue))
|
||||
{
|
||||
if (mb_strlen($sValue) < 256) {
|
||||
$aPost[$sKey] = $sValue;
|
||||
} else {
|
||||
$aPost[$sKey] = "!long string: ".mb_strlen($sValue)." chars";
|
||||
}
|
||||
} else {
|
||||
// Not a string (avoid warnings in case the value cannot be easily casted into a string)
|
||||
$aPost[$sKey] = @(string)$sValue;
|
||||
}
|
||||
}
|
||||
$this->Set('arguments_post', $aPost);
|
||||
$this->Set('arguments_post', $this->SanitizeRequestParams($GLOBALS['_POST']));
|
||||
} else {
|
||||
$this->Set('arguments_post', array());
|
||||
}
|
||||
@@ -274,6 +259,29 @@ class EventIssue extends Event
|
||||
$this->Set('page', mb_substr($this->Get('page'), 0, 210)." -truncated ($sLength chars)");
|
||||
}
|
||||
}
|
||||
|
||||
protected function SanitizeRequestParams(array $aParams): array
|
||||
{
|
||||
$aSanitizedParams = [];
|
||||
|
||||
foreach ($aParams as $sKey => $sValue) {
|
||||
if (is_string($sValue)) {
|
||||
if (stristr($sKey, 'pwd') !== false || stristr($sKey, 'passwd') !== false || stristr($sKey, 'password') !== false) {
|
||||
$aSanitizedParams[$sKey] = '****';
|
||||
} elseif (mb_strlen($sValue) < 256) {
|
||||
$aSanitizedParams[$sKey] = $sValue;
|
||||
} else {
|
||||
$aSanitizedParams[$sKey] = '!long string: '.mb_strlen($sValue).' chars';
|
||||
}
|
||||
} else {
|
||||
// Not a string (avoid warnings in case the value cannot be easily cast into a string)
|
||||
$aSanitizedParams[$sKey] = @(string)$sValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return $aSanitizedParams;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -278,7 +278,7 @@ class HTMLDOMSanitizer extends DOMSanitizer
|
||||
protected static $aTagsWhiteList = array(
|
||||
'html' => array(),
|
||||
'body' => array(),
|
||||
'a' => array('href', 'name', 'style', 'class', 'target', 'title', 'data-role', 'data-object-class', 'data-object-id'),
|
||||
'a' => array('href', 'name', 'style', 'class', 'target', 'title', 'data-role', 'data-object-class', 'data-object-id', 'data-object-key'),
|
||||
'p' => array('style', 'class'),
|
||||
'blockquote' => array('style', 'class'),
|
||||
'br' => array(),
|
||||
@@ -301,6 +301,8 @@ class HTMLDOMSanitizer extends DOMSanitizer
|
||||
'section' => array('style', 'class'),
|
||||
'code' => array('style', 'class'),
|
||||
'table' => array('style', 'class', 'width', 'summary', 'align', 'border', 'cellpadding', 'cellspacing'),
|
||||
'colgroup' => array(),
|
||||
'col' => array('style'),
|
||||
'thead' => array('style', 'class'),
|
||||
'tbody' => array('style', 'class'),
|
||||
'tr' => array('style', 'class', 'colspan', 'rowspan'),
|
||||
@@ -352,6 +354,8 @@ class HTMLDOMSanitizer extends DOMSanitizer
|
||||
'font-style',
|
||||
'height',
|
||||
'margin',
|
||||
'margin-left',
|
||||
'margin-right',
|
||||
'padding',
|
||||
'text-align',
|
||||
'vertical-align',
|
||||
|
||||
@@ -54,7 +54,7 @@ class InlineImage extends DBObject
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeDateTime("expire", array("allowed_values"=>null, "sql"=>'expire', "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array(), "always_load_in_tables"=>false)));
|
||||
MetaModel::Init_AddAttribute(new AttributeDateTime("expire", array("allowed_values" => null, "sql" => 'expire', "default_value" => 'DATE_ADD(NOW(), INTERVAL 1 DAY)', "is_null_allowed" => false, "depends_on" => array(), "always_load_in_tables" => false)));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("temp_id", array("allowed_values"=>null, "sql"=>'temp_id', "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array(), "always_load_in_tables"=>false)));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("item_class", array("allowed_values"=>null, "sql"=>'item_class', "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array(), "always_load_in_tables"=>false)));
|
||||
MetaModel::Init_AddAttribute(new AttributeObjectKey("item_id", array("class_attcode"=>'item_class', "allowed_values"=>null, "sql"=>'item_id', "is_null_allowed"=>true, "depends_on"=>array(), "always_load_in_tables"=>false)));
|
||||
@@ -541,80 +541,6 @@ JS
|
||||
CombodoCKEditorHandler.EnableImageUpload('#' + $(this).attr('id'), '$sAbsoluteUrlAppRoot'+'pages/ajax.render.php?operation=cke_img_upload&temp_id=$sTempId&obj_class=$sObjClass&obj_key=$iObjKey');
|
||||
});
|
||||
JS;
|
||||
|
||||
return
|
||||
<<<JS
|
||||
// Hook the file upload of all CKEditor instances
|
||||
$('.htmlEditor').each(function() {
|
||||
var oEditor = $(this).ckeditorGet();
|
||||
oEditor.config.filebrowserBrowseUrl = '$sAbsoluteUrlAppRoot'+'pages/ajax.render.php?operation=cke_browse&temp_id=$sTempId&obj_class=$sObjClass&obj_key=$iObjKey';
|
||||
oEditor.on( 'fileUploadResponse', function( evt ) {
|
||||
var fileLoader = evt.data.fileLoader;
|
||||
var xhr = fileLoader.xhr;
|
||||
var data = evt.data;
|
||||
try {
|
||||
var response = JSON.parse( xhr.responseText );
|
||||
|
||||
// Error message does not need to mean that upload finished unsuccessfully.
|
||||
// It could mean that ex. file name was changes during upload due to naming collision.
|
||||
if ( response.error && response.error.message ) {
|
||||
data.message = response.error.message;
|
||||
}
|
||||
|
||||
// But !uploaded means error.
|
||||
if ( !response.uploaded ) {
|
||||
evt.cancel();
|
||||
} else {
|
||||
data.fileName = response.fileName;
|
||||
data.url = response.url;
|
||||
|
||||
// Do not call the default listener.
|
||||
evt.stop();
|
||||
}
|
||||
} catch ( err ) {
|
||||
// Response parsing error.
|
||||
data.message = fileLoader.lang.filetools.responseError;
|
||||
window.console && window.console.log( xhr.responseText );
|
||||
|
||||
evt.cancel();
|
||||
}
|
||||
} );
|
||||
|
||||
oEditor.on( 'fileUploadRequest', function( evt ) {
|
||||
evt.data.fileLoader.uploadUrl += '?operation=cke_img_upload&temp_id=$sTempId&obj_class=$sObjClass';
|
||||
}, null, null, 4 ); // Listener with priority 4 will be executed before priority 5.
|
||||
|
||||
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 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)
|
||||
{
|
||||
$('#'+oEditor.id+'_toolbar_collapser').trigger('click');
|
||||
}
|
||||
});
|
||||
}
|
||||
if (oEditor.widgets.registered.uploadimage)
|
||||
{
|
||||
oEditor.widgets.registered.uploadimage.onUploaded = function( upload ) {
|
||||
var oData = JSON.parse(upload.xhr.responseText);
|
||||
this.replaceWith( '<img src="' + upload.url + '" ' +
|
||||
'width="' + oData.width + '" ' +
|
||||
'height="' + oData.height + '">' );
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
JS
|
||||
;
|
||||
}
|
||||
public static function EnableCKEditor5ImageUpload(DBObject $oObject, $sTempId){
|
||||
return <<<JS
|
||||
// Hook the file upload of all CKEditor instances
|
||||
JS;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -469,7 +469,8 @@ class ExecutionKPI
|
||||
// Invoke extensions to log the KPI operation
|
||||
/** @var \iKPILoggerExtension $oExtensionInstance */
|
||||
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
|
||||
$sExtension = ModuleService::GetInstance()->GetModuleNameFromCallStack(1);
|
||||
//$sExtension = ModuleService::GetInstance()->GetModuleNameFromCallStack(1);
|
||||
$sExtension = '';
|
||||
$oKPILogData = new KpiLogData(
|
||||
KpiLogData::TYPE_STATS,
|
||||
$sOperation,
|
||||
|
||||
@@ -542,32 +542,32 @@ class FileLog
|
||||
*/
|
||||
class LogChannels
|
||||
{
|
||||
public const APC = 'apc';
|
||||
public const APC = 'Apc';
|
||||
|
||||
/**
|
||||
* @var string Everything related to the backup / restore
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public const BACKUP = 'backup';
|
||||
public const BACKUP = 'Backup';
|
||||
|
||||
/**
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public const CLI = 'CLI';
|
||||
public const CLI = 'Cli';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @since 2.7.7 N°4558 use this new channel when logging DB transactions
|
||||
* @since 3.0.0 logs info in CMDBSource (see commit a117906f)
|
||||
*/
|
||||
public const CMDB_SOURCE = 'cmdbsource';
|
||||
public const CMDB_SOURCE = 'CmdbSource';
|
||||
|
||||
/**
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public const CONSOLE = 'console';
|
||||
public const CONSOLE = 'Console';
|
||||
|
||||
public const CORE = 'core';
|
||||
public const CORE = 'Core';
|
||||
|
||||
/**
|
||||
* @var string Everything related to the datatable component
|
||||
@@ -592,10 +592,10 @@ class LogChannels
|
||||
* @var string Everything related to the datamodel CRUD
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public const DM_CRUD = 'DMCRUD';
|
||||
public const DM_CRUD = 'DMCrud';
|
||||
|
||||
/**
|
||||
* @var string Everything related to the datamodel CRUD
|
||||
* @var string Everything related to webrequests
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public const WEB_REQUEST = 'WebRequest';
|
||||
@@ -610,7 +610,7 @@ class LogChannels
|
||||
* @var string
|
||||
* @since 2.7.9 3.0.3 3.1.0 N°5588
|
||||
*/
|
||||
public const EXPORT = 'export';
|
||||
public const EXPORT = 'Export';
|
||||
|
||||
public const INLINE_IMAGE = 'InlineImage';
|
||||
|
||||
@@ -619,9 +619,9 @@ class LogChannels
|
||||
* @since 3.0.1 N°4849
|
||||
* @since 2.7.7 N°4635
|
||||
*/
|
||||
public const NOTIFICATIONS = 'notifications';
|
||||
public const NOTIFICATIONS = 'Notifications';
|
||||
|
||||
public const PORTAL = 'portal';
|
||||
public const PORTAL = 'Portal';
|
||||
|
||||
public const TEMPORARY_OBJECTS = 'TemporaryObjects';
|
||||
|
||||
@@ -629,7 +629,13 @@ class LogChannels
|
||||
* @var string
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public const ROUTER = 'router';
|
||||
public const ROUTER = 'Router';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public const SECURITY = 'Security';
|
||||
}
|
||||
|
||||
|
||||
@@ -1167,7 +1173,7 @@ class DeprecatedCallsLog extends LogAPI
|
||||
|
||||
/**
|
||||
* This will catch a message for all E_DEPRECATED and E_USER_DEPRECATED errors.
|
||||
* This handler is set in DeprecatedCallsLog::Enable
|
||||
* This handler is set in {@see DeprecatedCallsLog::Enable}
|
||||
*
|
||||
* @param int $errno
|
||||
* @param string $errstr
|
||||
@@ -1187,52 +1193,22 @@ class DeprecatedCallsLog extends LogAPI
|
||||
return false;
|
||||
}
|
||||
|
||||
$aStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 4);
|
||||
if (isset($aStack[2]['function']) && ($aStack[2]['function'] == 'ForwardToTriggerError')) {
|
||||
// Let the notice bubble up
|
||||
return false;
|
||||
}
|
||||
|
||||
if (false === static::IsLogLevelEnabledSafe(self::LEVEL_WARNING, self::ENUM_CHANNEL_PHP_LIBMETHOD)) {
|
||||
// returns true so that nothing is throwned !
|
||||
// returns true so that nothing is thrown!
|
||||
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 (can be either 'trigger_deprecation' or the faulty method), level 3 = In some cases, method containing the 'trigger_deprecation' call
|
||||
// In case current level is actually a 'trigger_deprecation' call, try to go one level further to get the real deprecated method
|
||||
if (array_key_exists($iStackDeprecatedMethodLevel, $aStack) && ($aStack[$iStackDeprecatedMethodLevel]['function'] === 'trigger_deprecation') && array_key_exists($iStackDeprecatedMethodLevel + 1, $aStack)) {
|
||||
$iStackDeprecatedMethodLevel++;
|
||||
}
|
||||
|
||||
$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;
|
||||
}
|
||||
$aStack = static::StripCallStack($aStack);
|
||||
$sMessage = "$errstr, called from ".static::SummarizeCallStack($aStack);
|
||||
|
||||
static::Warning($sMessage, self::ENUM_CHANNEL_PHP_LIBMETHOD);
|
||||
static::ForwardToTriggerError($sMessage);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1258,6 +1234,8 @@ class DeprecatedCallsLog extends LogAPI
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this helper at the beginning of a deprecated file (in its global scope)
|
||||
*
|
||||
* @since 3.0.1 3.1.0 N°4725 silently handles ConfigException
|
||||
* @since 3.0.4 3.1.0 N°4725 remove forgotten throw PHPDoc annotation
|
||||
*
|
||||
@@ -1292,9 +1270,12 @@ class DeprecatedCallsLog extends LogAPI
|
||||
}
|
||||
|
||||
static::Warning($sMessage, static::ENUM_CHANNEL_FILE);
|
||||
static::ForwardToTriggerError($sMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this helper when calling a deprecated extension method
|
||||
*
|
||||
* @param string $sImplementationClass Class implementing the deprecated API
|
||||
* @param string $sDeprecatedApi Class name of the deprecated API
|
||||
* @param string $sDeprecatedMethod Method name of the deprecated API
|
||||
@@ -1321,9 +1302,12 @@ class DeprecatedCallsLog extends LogAPI
|
||||
}
|
||||
|
||||
static::Warning($sMessage, self::ENUM_CHANNEL_PHP_API);
|
||||
static::ForwardToTriggerError($sMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this helper within deprecated methods
|
||||
*
|
||||
* @param string|null $sAdditionalMessage
|
||||
*
|
||||
* @link https://www.php.net/debug_backtrace
|
||||
@@ -1341,52 +1325,24 @@ class DeprecatedCallsLog extends LogAPI
|
||||
}
|
||||
|
||||
$aStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
|
||||
$sMessage = self::GetMessageFromStack($aStack);
|
||||
|
||||
if (!is_null($sAdditionalMessage)) {
|
||||
$sMessage .= ' : '.$sAdditionalMessage;
|
||||
if (isset($aStack[1]['class'])) {
|
||||
$sFunctionDesc = $aStack[1]['class'].$aStack[1]['type'].$aStack[1]['function'];
|
||||
}
|
||||
else {
|
||||
$sFunctionDesc = $aStack[1]['function'];
|
||||
}
|
||||
|
||||
$sMessage = "Function $sFunctionDesc() is deprecated";
|
||||
if (!is_null($sAdditionalMessage)) {
|
||||
$sMessage .= ': '.$sAdditionalMessage;
|
||||
}
|
||||
|
||||
$sMessage .= '. Caller: '.self::SummarizeCallStack(array_slice($aStack, 1));
|
||||
|
||||
|
||||
static::Warning($sMessage, self::ENUM_CHANNEL_PHP_METHOD);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $aDebugBacktrace data from {@see debug_backtrace()}
|
||||
*
|
||||
* @return string message to print to the log
|
||||
*/
|
||||
private static function GetMessageFromStack(array $aDebugBacktrace): string
|
||||
{
|
||||
// level 0 = current method
|
||||
// level 1 = deprecated method, containing the `NotifyDeprecatedPhpMethod` call
|
||||
$sMessage = 'Call'.self::GetMessageForCurrentStackLevel($aDebugBacktrace[1], " to ");
|
||||
|
||||
// level 2 = caller of the deprecated method
|
||||
if (array_key_exists(2, $aDebugBacktrace)) {
|
||||
$sMessage .= ' (from ';
|
||||
$sMessage .= self::GetMessageForCurrentStackLevel($aDebugBacktrace[2]);
|
||||
$sMessage .= ')';
|
||||
}
|
||||
|
||||
return $sMessage;
|
||||
}
|
||||
|
||||
private static function GetMessageForCurrentStackLevel(array $aCurrentLevelDebugTrace, ?string $sPrefix = ""): string
|
||||
{
|
||||
$sMessage = "";
|
||||
if (array_key_exists('class', $aCurrentLevelDebugTrace)) {
|
||||
$sDeprecatedObject = $aCurrentLevelDebugTrace['class'];
|
||||
$sDeprecatedMethod = $aCurrentLevelDebugTrace['function'] ?? "";
|
||||
$sMessage = "{$sPrefix}{$sDeprecatedObject}::{$sDeprecatedMethod} in ";
|
||||
}
|
||||
|
||||
if (array_key_exists('file', $aCurrentLevelDebugTrace)) {
|
||||
$sCallerFile = $aCurrentLevelDebugTrace['file'];
|
||||
$sCallerLine = $aCurrentLevelDebugTrace['line'] ?? "";
|
||||
$sMessage .= "{$sCallerFile}#L{$sCallerLine}";
|
||||
}
|
||||
|
||||
return $sMessage;
|
||||
static::ForwardToTriggerError($sMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1416,14 +1372,11 @@ class DeprecatedCallsLog extends LogAPI
|
||||
}
|
||||
|
||||
static::Warning($sMessage, self::ENUM_CHANNEL_PHP_ENDPOINT);
|
||||
static::ForwardToTriggerError($sMessage);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -1431,6 +1384,61 @@ class DeprecatedCallsLog extends LogAPI
|
||||
// nothing much we can do... and we don't want to crash the caller !
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Strips some elements from the top of the call stack to skip calls that are not relevant to report the deprecated call
|
||||
* @param array $aCallStack Call stack as returned by {@see debug_backtrace()}
|
||||
*/
|
||||
protected static function StripCallStack($aCallStack): array
|
||||
{
|
||||
if (!isset($aCallStack[0]['line'])) {
|
||||
$aCallStack = array_slice($aCallStack, 1);
|
||||
}
|
||||
if (isset($aCallStack[1]['function']) && $aCallStack[1]['function'] === 'trigger_deprecation') {
|
||||
$aCallStack = array_slice($aCallStack, 1);
|
||||
}
|
||||
|
||||
return $aCallStack;
|
||||
}
|
||||
|
||||
protected static function SummarizeCallStack($aCallStack, $bRecurse = true)
|
||||
{
|
||||
if (count($aCallStack) == 0) {
|
||||
return null;
|
||||
}
|
||||
$sFileLine = $aCallStack[0]['file'].'#'.$aCallStack[0]['line'];
|
||||
$sSummary = $sFileLine;
|
||||
|
||||
// If possible and meaningful, add the class and method
|
||||
if (isset($aCallStack[1]['class'])) {
|
||||
$sSummary = $aCallStack[1]['class'].$aCallStack[1]['type'].$aCallStack[1]['function']." ($sFileLine)";
|
||||
}
|
||||
elseif (isset($aCallStack[1]['function'])) {
|
||||
if (in_array($aCallStack[1]['function'], ['include', 'require', 'include_once', 'require_once'])) {
|
||||
// No need to show the generic mechanism of inclusion
|
||||
$bRecurse = false;
|
||||
}
|
||||
else {
|
||||
$sSummary = $aCallStack[1]['function']." ($sFileLine)";
|
||||
}
|
||||
}
|
||||
|
||||
if ($bRecurse) {
|
||||
$sUpperSummary = static::SummarizeCallStack(array_slice($aCallStack, 1), false);
|
||||
if (!is_null($sUpperSummary)) {
|
||||
$sSummary .= ', itself called from '.$sUpperSummary;
|
||||
}
|
||||
}
|
||||
|
||||
return $sSummary;
|
||||
}
|
||||
|
||||
private static function ForwardToTriggerError(string $sMessage): void
|
||||
{
|
||||
if (true === utils::IsDevelopmentEnvironment()) {
|
||||
trigger_error($sMessage, E_USER_DEPRECATED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -6873,6 +6873,9 @@ abstract class MetaModel
|
||||
/**
|
||||
* Instantiate an object already persisted to the Database.
|
||||
*
|
||||
* Note that LinkedSet attributes are not loaded.
|
||||
* DBObject::Reload() will be called when getting a LinkedSet attribute
|
||||
*
|
||||
* @api
|
||||
* @see MetaModel::GetObjectWithArchive to get object even if it's archived
|
||||
* @see utils::PushArchiveMode() to enable search on archived objects
|
||||
@@ -7078,7 +7081,7 @@ abstract class MetaModel
|
||||
* @param array $aParams
|
||||
* @param bool $bAllowAllData
|
||||
*
|
||||
* @return \DBObject
|
||||
* @return \DBObject|null
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public static function GetObjectFromOQL($sQuery, $aParams = null, $bAllowAllData = false)
|
||||
@@ -7474,6 +7477,8 @@ abstract class MetaModel
|
||||
* @param string|null $sFilterInstanceOf [optional] if given, only instance of this string will be returned
|
||||
*
|
||||
* @return array classes=>instance implementing the given interface
|
||||
*
|
||||
* @see \Combodo\iTop\Service\InterfaceDiscovery\InterfaceDiscovery::FindItopClasses() to add extensibility to modules
|
||||
*/
|
||||
public static function EnumPlugins($sInterface, $sFilterInstanceOf = null)
|
||||
{
|
||||
@@ -7527,8 +7532,41 @@ abstract class MetaModel
|
||||
return $aEntries;
|
||||
}
|
||||
|
||||
public static function ResetAllCaches($sEnvironment = null)
|
||||
{
|
||||
if (is_null($sEnvironment)) {
|
||||
$sEnvironment = MetaModel::GetEnvironment();
|
||||
}
|
||||
|
||||
$sEnvironmentId = md5(APPROOT).'-'.$sEnvironment;
|
||||
$sAppIdentity = 'itop-'.$sEnvironmentId;
|
||||
require_once(APPROOT.'/core/dict.class.inc.php');
|
||||
Dict::ResetCache($sAppIdentity);
|
||||
|
||||
if (function_exists('apc_delete')) {
|
||||
foreach (self::GetCacheEntries($sEnvironmentId) as $sKey => $aAPCInfo) {
|
||||
$sAPCKey = $aAPCInfo['info'];
|
||||
apc_delete($sAPCKey);
|
||||
}
|
||||
}
|
||||
|
||||
require_once(APPROOT.'core/userrights.class.inc.php');
|
||||
UserRights::FlushPrivileges();
|
||||
|
||||
// Reset the opcache since otherwise the PHP "model" files may still be cached !!
|
||||
if (function_exists('opcache_reset')) {
|
||||
// Zend opcode cache
|
||||
opcache_reset();
|
||||
}
|
||||
|
||||
require_once(APPROOT.'setup/setuputils.class.inc.php');
|
||||
SetupUtils::rrmdir(utils::GetCachePath($sEnvironment));
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @param string $sEnvironmentId
|
||||
* @deprecated 3.2.1
|
||||
*/
|
||||
public static function ResetCache($sEnvironmentId = null)
|
||||
{
|
||||
@@ -7552,6 +7590,13 @@ abstract class MetaModel
|
||||
|
||||
require_once(APPROOT.'core/userrights.class.inc.php');
|
||||
UserRights::FlushPrivileges();
|
||||
|
||||
// Reset the opcache since otherwise the PHP "model" files may still be cached !!
|
||||
if (function_exists('opcache_reset'))
|
||||
{
|
||||
// Zend opcode cache
|
||||
opcache_reset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -77,7 +77,7 @@ abstract class ModelReflection
|
||||
return $sFormatCode.' - '.implode(', ', $aArguments);
|
||||
}
|
||||
|
||||
return vsprintf($sLocalizedFormat, $aArguments);
|
||||
return utils::VSprintf($sLocalizedFormat, $aArguments);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,17 +33,19 @@ class ParseyyToken implements ArrayAccess
|
||||
return $this->string;
|
||||
}
|
||||
|
||||
function offsetExists($offset)
|
||||
function offsetExists($offset): bool
|
||||
{
|
||||
return isset($this->metadata[$offset]);
|
||||
}
|
||||
|
||||
function offsetGet($offset)
|
||||
// Return type mixed is not supported by PHP 7.4, we can remove the following PHP attribute and add the return type once iTop min PHP version is PHP 8.0+
|
||||
#[\ReturnTypeWillChange]
|
||||
function offsetGet($offset)
|
||||
{
|
||||
return $this->metadata[$offset];
|
||||
}
|
||||
|
||||
function offsetSet($offset, $value)
|
||||
function offsetSet($offset, $value): void
|
||||
{
|
||||
if ($offset === null) {
|
||||
if (isset($value[0])) {
|
||||
@@ -66,7 +68,7 @@ class ParseyyToken implements ArrayAccess
|
||||
}
|
||||
}
|
||||
|
||||
function offsetUnset($offset)
|
||||
function offsetUnset($offset): void
|
||||
{
|
||||
unset($this->metadata[$offset]);
|
||||
}
|
||||
|
||||
@@ -182,14 +182,14 @@ class PHP_LexerGenerator_Lexer
|
||||
$this->token = self::COMMENTEND;
|
||||
return true;
|
||||
}
|
||||
if (preg_match('/\G%([a-z]+)/', $this->data, $token, null, $this->N)) {
|
||||
if (preg_match('/\G%([a-z]+)/', $this->data, $token, 0, $this->N)) {
|
||||
$this->value = $token[1];
|
||||
$this->N += strlen($token[1]) + 1;
|
||||
$this->state = 'DeclarePI';
|
||||
$this->token = self::PI;
|
||||
return true;
|
||||
}
|
||||
if (preg_match('/\G[a-zA-Z_][a-zA-Z0-9_]*/', $this->data, $token, null, $this->N)) {
|
||||
if (preg_match('/\G[a-zA-Z_][a-zA-Z0-9_]*/', $this->data, $token, 0, $this->N)) {
|
||||
$this->value = $token[0];
|
||||
$this->token = self::PATTERN;
|
||||
$this->N += strlen($token[0]);
|
||||
@@ -216,7 +216,7 @@ class PHP_LexerGenerator_Lexer
|
||||
if ($this->data[$this->N] == '{') {
|
||||
return $this->lexCode();
|
||||
}
|
||||
if (!preg_match("/\G[^\n]+/", $this->data, $token, null, $this->N)) {
|
||||
if (!preg_match("/\G[^\n]+/", $this->data, $token, 0, $this->N)) {
|
||||
$this->error('Unexpected end of file');
|
||||
return false;
|
||||
}
|
||||
@@ -242,7 +242,7 @@ class PHP_LexerGenerator_Lexer
|
||||
if ($this->data[$this->N] == '{') {
|
||||
return $this->lexCode();
|
||||
}
|
||||
if (!preg_match("/\G[^\n]+/", $this->data, $token, null, $this->N)) {
|
||||
if (!preg_match("/\G[^\n]+/", $this->data, $token, 0, $this->N)) {
|
||||
$this->error('Unexpected end of file');
|
||||
return false;
|
||||
}
|
||||
@@ -406,7 +406,7 @@ class PHP_LexerGenerator_Lexer
|
||||
if ($this->data[$this->N] == '\'') {
|
||||
return $this->lexQuote('\'');
|
||||
}
|
||||
if (preg_match('/\G%([a-zA-Z_]+)/', $this->data, $token, null, $this->N)) {
|
||||
if (preg_match('/\G%([a-zA-Z_]+)/', $this->data, $token, 0, $this->N)) {
|
||||
$this->value = $token[1];
|
||||
$this->N += strlen($token[1]) + 1;
|
||||
$this->state = 'DeclarePIRule';
|
||||
@@ -419,7 +419,7 @@ class PHP_LexerGenerator_Lexer
|
||||
if ($this->data[$this->N] == '"') {
|
||||
return $this->lexQuote();
|
||||
}
|
||||
if (preg_match('/\G[a-zA-Z_][a-zA-Z0-9_]*/', $this->data, $token, null, $this->N)) {
|
||||
if (preg_match('/\G[a-zA-Z_][a-zA-Z0-9_]*/', $this->data, $token, 0, $this->N)) {
|
||||
$this->value = $token[0];
|
||||
$this->N += strlen($token[0]);
|
||||
$this->token = self::SUBPATTERN;
|
||||
|
||||
@@ -33,17 +33,19 @@ class PHP_LexerGenerator_ParseryyToken implements ArrayAccess
|
||||
return $this->_string;
|
||||
}
|
||||
|
||||
function offsetExists($offset)
|
||||
function offsetExists($offset): bool
|
||||
{
|
||||
return isset($this->metadata[$offset]);
|
||||
}
|
||||
|
||||
// Return type mixed is not supported by PHP 7.4, we can remove the following PHP attribute and add the return type once iTop min PHP version is PHP 8.0+
|
||||
#[\ReturnTypeWillChange]
|
||||
function offsetGet($offset)
|
||||
{
|
||||
return $this->metadata[$offset];
|
||||
}
|
||||
|
||||
function offsetSet($offset, $value)
|
||||
function offsetSet($offset, $value): void
|
||||
{
|
||||
if ($offset === null) {
|
||||
if (isset($value[0])) {
|
||||
@@ -66,7 +68,7 @@ class PHP_LexerGenerator_ParseryyToken implements ArrayAccess
|
||||
}
|
||||
}
|
||||
|
||||
function offsetUnset($offset)
|
||||
function offsetUnset($offset): void
|
||||
{
|
||||
unset($this->metadata[$offset]);
|
||||
}
|
||||
@@ -278,7 +280,7 @@ class PHP_LexerGenerator_Parser#line 171 "Parser.php"
|
||||
$match = false;
|
||||
foreach ($yy_yymore_patterns[' . $this->token . '] as $index => $rule) {
|
||||
if (preg_match(\'/\' . $rule . \'/' . $this->patternFlags . '\',
|
||||
' . $this->input . ', $yymatches, null, ' . $this->counter . ')) {
|
||||
' . $this->input . ', $yymatches, 0, ' . $this->counter . ')) {
|
||||
$yymatches = array_filter($yymatches, \'strlen\'); // remove empty sub-patterns
|
||||
if ($match) {
|
||||
if (strlen($yymatches[0]) > strlen($match[0][0])) {
|
||||
@@ -350,7 +352,7 @@ class PHP_LexerGenerator_Parser#line 171 "Parser.php"
|
||||
$pattern . '\';' . "\n");
|
||||
fwrite($this->out, '
|
||||
do {
|
||||
if (preg_match($yy_global_pattern,' . $this->input . ', $yymatches, null, ' .
|
||||
if (preg_match($yy_global_pattern,' . $this->input . ', $yymatches, 0, ' .
|
||||
$this->counter .
|
||||
')) {
|
||||
$yysubmatches = $yymatches;
|
||||
@@ -408,7 +410,7 @@ class PHP_LexerGenerator_Parser#line 171 "Parser.php"
|
||||
}
|
||||
$yysubmatches = array();
|
||||
if (preg_match(\'/\' . $yy_yymore_patterns[' . $this->token . '][1] . \'/' . $this->patternFlags . '\',
|
||||
' . $this->input . ', $yymatches, null, ' . $this->counter .')) {
|
||||
' . $this->input . ', $yymatches, 0, ' . $this->counter .')) {
|
||||
$yysubmatches = $yymatches;
|
||||
$yymatches = array_filter($yymatches, \'strlen\'); // remove empty sub-patterns
|
||||
next($yymatches); // skip global match
|
||||
|
||||
@@ -187,7 +187,7 @@ require_once 'PHP/LexerGenerator/Exception.php';
|
||||
$match = false;
|
||||
foreach ($yy_yymore_patterns[' . $this->token . '] as $index => $rule) {
|
||||
if (preg_match(\'/\' . $rule . \'/' . $this->patternFlags . '\',
|
||||
' . $this->input . ', $yymatches, null, ' . $this->counter . ')) {
|
||||
' . $this->input . ', $yymatches, 0, ' . $this->counter . ')) {
|
||||
$yymatches = array_filter($yymatches, \'strlen\'); // remove empty sub-patterns
|
||||
if ($match) {
|
||||
if (strlen($yymatches[0]) > strlen($match[0][0])) {
|
||||
@@ -259,7 +259,7 @@ require_once 'PHP/LexerGenerator/Exception.php';
|
||||
$pattern . '\';' . "\n");
|
||||
fwrite($this->out, '
|
||||
do {
|
||||
if (preg_match($yy_global_pattern,' . $this->input . ', $yymatches, null, ' .
|
||||
if (preg_match($yy_global_pattern,' . $this->input . ', $yymatches, 0, ' .
|
||||
$this->counter .
|
||||
')) {
|
||||
$yysubmatches = $yymatches;
|
||||
@@ -317,7 +317,7 @@ require_once 'PHP/LexerGenerator/Exception.php';
|
||||
}
|
||||
$yysubmatches = array();
|
||||
if (preg_match(\'/\' . $yy_yymore_patterns[' . $this->token . '][1] . \'/' . $this->patternFlags . '\',
|
||||
' . $this->input . ', $yymatches, null, ' . $this->counter .')) {
|
||||
' . $this->input . ', $yymatches, 0, ' . $this->counter .')) {
|
||||
$yysubmatches = $yymatches;
|
||||
$yymatches = array_filter($yymatches, \'strlen\'); // remove empty sub-patterns
|
||||
next($yymatches); // skip global match
|
||||
|
||||
@@ -110,7 +110,7 @@ class PHP_LexerGenerator_Regex_Lexer
|
||||
$yy_global_pattern = '/\G(\\\\\\\\)|\G([^[\\\\^$.|()?*+{}]+)|\G(\\\\[][{}*.^$|?()+])|\G(\\[)|\G(\\|)|\G(\\\\[frnt]|\\\\x[0-9a-fA-F][0-9a-fA-F]?|\\\\[0-7][0-7][0-7]|\\\\x\\{[0-9a-fA-F]+\\})|\G(\\\\[0-9][0-9])|\G(\\\\[abBGcedDsSwW0C]|\\\\c\\\\)|\G(\\^)|\G(\\\\A)|\G(\\))|\G(\\$)|\G(\\*\\?|\\+\\?|[*?+]|\\{[0-9]+\\}|\\{[0-9]+,\\}|\\{[0-9]+,[0-9]+\\})|\G(\\\\[zZ])|\G(\\(\\?)|\G(\\()|\G(\\.)|\G(\\\\[1-9])|\G(\\\\p\\{\\^?..?\\}|\\\\P\\{..?\\}|\\\\X)|\G(\\\\p\\{C[cfnos]?|L[lmotu]?|M[cen]?|N[dlo]?|P[cdefios]?|S[ckmo]?|Z[lps]?\\})|\G(\\\\p\\{\\^C[cfnos]?|L[lmotu]?|M[cen]?|N[dlo]?|P[cdefios]?|S[ckmo]?|Z[lps]?\\})|\G(\\\\p[CLMNPSZ])|\G(\\\\)/';
|
||||
|
||||
do {
|
||||
if (preg_match($yy_global_pattern,$this->input, $yymatches, null, $this->N)) {
|
||||
if (preg_match($yy_global_pattern,$this->input, $yymatches, 0, $this->N)) {
|
||||
$yysubmatches = $yymatches;
|
||||
$yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
|
||||
if (!count($yymatches)) {
|
||||
@@ -180,7 +180,7 @@ class PHP_LexerGenerator_Regex_Lexer
|
||||
}
|
||||
$yysubmatches = array();
|
||||
if (preg_match('/' . $yy_yymore_patterns[$this->token][1] . '/',
|
||||
$this->input, $yymatches, null, $this->N)) {
|
||||
$this->input, $yymatches, 0, $this->N)) {
|
||||
$yysubmatches = $yymatches;
|
||||
$yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
|
||||
next($yymatches); // skip global match
|
||||
@@ -360,7 +360,7 @@ class PHP_LexerGenerator_Regex_Lexer
|
||||
$yy_global_pattern = '/\G(\\^)|\G(\\])|\G(.)/';
|
||||
|
||||
do {
|
||||
if (preg_match($yy_global_pattern,$this->input, $yymatches, null, $this->N)) {
|
||||
if (preg_match($yy_global_pattern,$this->input, $yymatches, 0, $this->N)) {
|
||||
$yysubmatches = $yymatches;
|
||||
$yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
|
||||
if (!count($yymatches)) {
|
||||
@@ -410,7 +410,7 @@ class PHP_LexerGenerator_Regex_Lexer
|
||||
}
|
||||
$yysubmatches = array();
|
||||
if (preg_match('/' . $yy_yymore_patterns[$this->token][1] . '/',
|
||||
$this->input, $yymatches, null, $this->N)) {
|
||||
$this->input, $yymatches, 0, $this->N)) {
|
||||
$yysubmatches = $yymatches;
|
||||
$yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
|
||||
next($yymatches); // skip global match
|
||||
@@ -497,7 +497,7 @@ class PHP_LexerGenerator_Regex_Lexer
|
||||
$yy_global_pattern = '/\G(\\\\\\\\)|\G(\\])|\G(\\\\[frnt]|\\\\x[0-9a-fA-F][0-9a-fA-F]?|\\\\[0-7][0-7][0-7]|\\\\x\\{[0-9a-fA-F]+\\})|\G(\\\\[bacedDsSwW0C]|\\\\c\\\\|\\\\x\\{[0-9a-fA-F]+\\}|\\\\[0-7][0-7][0-7]|\\\\x[0-9a-fA-F][0-9a-fA-F]?)|\G(\\\\[0-9][0-9])|\G(\\\\[1-9])|\G(\\\\[]\.\-\^])|\G(-(?!]))|\G([^\-\\\\])|\G(\\\\)|\G(.)/';
|
||||
|
||||
do {
|
||||
if (preg_match($yy_global_pattern,$this->input, $yymatches, null, $this->N)) {
|
||||
if (preg_match($yy_global_pattern,$this->input, $yymatches, 0, $this->N)) {
|
||||
$yysubmatches = $yymatches;
|
||||
$yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
|
||||
if (!count($yymatches)) {
|
||||
@@ -555,7 +555,7 @@ class PHP_LexerGenerator_Regex_Lexer
|
||||
}
|
||||
$yysubmatches = array();
|
||||
if (preg_match('/' . $yy_yymore_patterns[$this->token][1] . '/',
|
||||
$this->input, $yymatches, null, $this->N)) {
|
||||
$this->input, $yymatches, 0, $this->N)) {
|
||||
$yysubmatches = $yymatches;
|
||||
$yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
|
||||
next($yymatches); // skip global match
|
||||
@@ -678,7 +678,7 @@ class PHP_LexerGenerator_Regex_Lexer
|
||||
$yy_global_pattern = '/\G(\\\\\\\\)|\G(\\\\\\])|\G(\\\\[bacedDsSwW0C]|\\\\c\\\\|\\\\x\\{[0-9a-fA-F]+\\}|\\\\[0-7][0-7][0-7]|\\\\x[0-9a-fA-F][0-9a-fA-F]?)|\G(\\\\[0-9][0-9])|\G(\\\\[1-9])|\G([^\-\\\\])|\G(\\\\)/';
|
||||
|
||||
do {
|
||||
if (preg_match($yy_global_pattern,$this->input, $yymatches, null, $this->N)) {
|
||||
if (preg_match($yy_global_pattern,$this->input, $yymatches, 0, $this->N)) {
|
||||
$yysubmatches = $yymatches;
|
||||
$yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
|
||||
if (!count($yymatches)) {
|
||||
@@ -732,7 +732,7 @@ class PHP_LexerGenerator_Regex_Lexer
|
||||
}
|
||||
$yysubmatches = array();
|
||||
if (preg_match('/' . $yy_yymore_patterns[$this->token][1] . '/',
|
||||
$this->input, $yymatches, null, $this->N)) {
|
||||
$this->input, $yymatches, 0, $this->N)) {
|
||||
$yysubmatches = $yymatches;
|
||||
$yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
|
||||
next($yymatches); // skip global match
|
||||
@@ -842,7 +842,7 @@ class PHP_LexerGenerator_Regex_Lexer
|
||||
$yy_global_pattern = '/\G([imsxUX]+-[imsxUX]+|[imsxUX]+|-[imsxUX]+)|\G(:)|\G(\\))|\G(P<[^>]+>)|\G(<=)|\G(<!)|\G(=)|\G(!)|\G(>)|\G(\\(\\?)|\G(#[^)]+)|\G(R)|\G(.)/';
|
||||
|
||||
do {
|
||||
if (preg_match($yy_global_pattern,$this->input, $yymatches, null, $this->N)) {
|
||||
if (preg_match($yy_global_pattern,$this->input, $yymatches, 0, $this->N)) {
|
||||
$yysubmatches = $yymatches;
|
||||
$yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
|
||||
if (!count($yymatches)) {
|
||||
@@ -902,7 +902,7 @@ class PHP_LexerGenerator_Regex_Lexer
|
||||
}
|
||||
$yysubmatches = array();
|
||||
if (preg_match('/' . $yy_yymore_patterns[$this->token][1] . '/',
|
||||
$this->input, $yymatches, null, $this->N)) {
|
||||
$this->input, $yymatches, 0, $this->N)) {
|
||||
$yysubmatches = $yymatches;
|
||||
$yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
|
||||
next($yymatches); // skip global match
|
||||
|
||||
@@ -33,17 +33,19 @@ class PHP_LexerGenerator_Regex_yyToken implements ArrayAccess
|
||||
return $this->_string;
|
||||
}
|
||||
|
||||
function offsetExists($offset)
|
||||
function offsetExists($offset): bool
|
||||
{
|
||||
return isset($this->metadata[$offset]);
|
||||
}
|
||||
|
||||
// Return type mixed is not supported by PHP 7.4, we can remove the following PHP attribute and add the return type once iTop min PHP version is PHP 8.0+
|
||||
#[\ReturnTypeWillChange]
|
||||
function offsetGet($offset)
|
||||
{
|
||||
return $this->metadata[$offset];
|
||||
}
|
||||
|
||||
function offsetSet($offset, $value)
|
||||
function offsetSet($offset, $value): void
|
||||
{
|
||||
if ($offset === null) {
|
||||
if (isset($value[0])) {
|
||||
@@ -66,7 +68,7 @@ class PHP_LexerGenerator_Regex_yyToken implements ArrayAccess
|
||||
}
|
||||
}
|
||||
|
||||
function offsetUnset($offset)
|
||||
function offsetUnset($offset): void
|
||||
{
|
||||
unset($this->metadata[$offset]);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#
|
||||
# Rebuild the iTop Lexer / Parser
|
||||
# PEAR is required to build (really?)
|
||||
# PHP 8.0+ is not supported by the parser generator
|
||||
# Launch this batch from the core/oql/build directory
|
||||
# with ./build.bash
|
||||
#
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
rem must be run with current directory = the directory of the batch
|
||||
rem PEAR is required to build
|
||||
rem PHP 8.0+ is not supported by the parser generator
|
||||
php -d include_path=".;C:\iTop\PHP\PEAR" ".\PHP\LexerGenerator\cli.php" ..\oql-lexer.plex
|
||||
php ".\PHP\ParserGenerator\cli.php" ..\oql-parser.y
|
||||
php -r "echo date('Y-m-d');" > ..\version.txt
|
||||
|
||||
@@ -575,6 +575,15 @@ class BinaryExpression extends Expression
|
||||
case 'LIKE':
|
||||
$sType = 'like';
|
||||
break;
|
||||
case 'NOT LIKE':
|
||||
$sType = 'notlike';
|
||||
break;
|
||||
case 'IN':
|
||||
$sType = 'in';
|
||||
break;
|
||||
case 'NOT IN':
|
||||
$sType = 'notin';
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Operator '$sOperator' not yet supported");
|
||||
}
|
||||
@@ -639,7 +648,26 @@ class BinaryExpression extends Expression
|
||||
case 'like':
|
||||
$sEscaped = preg_quote($mRight, '/');
|
||||
$sEscaped = str_replace(array('%', '_', '\\\\.*', '\\\\.'), array('.*', '.', '%', '_'), $sEscaped);
|
||||
$result = (int) preg_match("/$sEscaped/i", $mLeft);
|
||||
$pregRes = preg_match("/$sEscaped/i", $mLeft);
|
||||
if ($pregRes === false) {
|
||||
throw new Exception("Error in regular expression '$sEscaped'");
|
||||
}
|
||||
$result = ($pregRes === 1);
|
||||
break;
|
||||
case 'notlike':
|
||||
$sEscaped = preg_quote($mRight, '/');
|
||||
$sEscaped = str_replace(array('%', '_', '\\\\.*', '\\\\.'), array('.*', '.', '%', '_'), $sEscaped);
|
||||
$pregRes = preg_match("/$sEscaped/i", $mLeft);
|
||||
if ($pregRes === false) {
|
||||
throw new Exception("Error in regular expression '$sEscaped'");
|
||||
}
|
||||
$result = ($pregRes !== 1);
|
||||
break;
|
||||
case 'in':
|
||||
$result = in_array($mLeft, $mRight);
|
||||
break;
|
||||
case 'notin':
|
||||
$result = !in_array($mLeft, $mRight);
|
||||
break;
|
||||
}
|
||||
return $result;
|
||||
@@ -2250,7 +2278,12 @@ class ListExpression extends Expression
|
||||
*/
|
||||
public function Evaluate(array $aArgs)
|
||||
{
|
||||
throw new Exception('list expression not yet supported');
|
||||
//throw new Exception('list expression not yet supported');
|
||||
$aResult = [];
|
||||
foreach ($this->m_aExpressions as $oExpressions) {
|
||||
$aResult[] = $oExpressions->Evaluate($aArgs);
|
||||
}
|
||||
return $aResult;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -235,7 +235,7 @@ class OQLLexerRaw
|
||||
$match = false;
|
||||
foreach ($yy_yymore_patterns[$this->token] as $index => $rule) {
|
||||
if (preg_match('/' . $rule . '/',
|
||||
$this->data, $yymatches, null, $this->count)) {
|
||||
$this->data, $yymatches, 0, $this->count)) {
|
||||
$yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
|
||||
if ($match) {
|
||||
if (strlen($yymatches[0]) > strlen($match[0][0])) {
|
||||
|
||||
@@ -40,7 +40,7 @@ class OQLParser_yyToken implements ArrayAccess
|
||||
|
||||
// Return type mixed is not supported by PHP 7.4, we can remove the following PHP attribute and add the return type once iTop min PHP version is PHP 8.0+
|
||||
#[\ReturnTypeWillChange]
|
||||
function offsetGet($offset)
|
||||
function offsetGet($offset)
|
||||
{
|
||||
return $this->metadata[$offset];
|
||||
}
|
||||
@@ -99,7 +99,7 @@ class OQLParser_yyStackEntry
|
||||
|
||||
// declare_class is output here
|
||||
#line 24 "..\oql-parser.y"
|
||||
class OQLParserRaw#line 102 "..\oql-parser.php"
|
||||
class OQLParserRaw#line 104 "..\oql-parser.php"
|
||||
{
|
||||
/* First off, code is included which follows the "include_class" declaration
|
||||
** in the input file. */
|
||||
@@ -1180,7 +1180,7 @@ static public $yy_action = array(
|
||||
#line 30 "..\oql-parser.y"
|
||||
|
||||
throw new OQLParserStackOverFlowException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol);
|
||||
#line 1186 "..\oql-parser.php"
|
||||
#line 1188 "..\oql-parser.php"
|
||||
return;
|
||||
}
|
||||
$yytos = new OQLParser_yyStackEntry;
|
||||
@@ -1482,49 +1482,49 @@ throw new OQLParserStackOverFlowException($this->m_sSourceQuery, $this->m_iLine,
|
||||
*/
|
||||
#line 37 "..\oql-parser.y"
|
||||
function yy_r0(){ $this->my_result = $this->yystack[$this->yyidx + 0]->minor; }
|
||||
#line 1488 "..\oql-parser.php"
|
||||
#line 1490 "..\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 1493 "..\oql-parser.php"
|
||||
#line 1495 "..\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 1498 "..\oql-parser.php"
|
||||
#line 1500 "..\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 1503 "..\oql-parser.php"
|
||||
#line 1505 "..\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 1508 "..\oql-parser.php"
|
||||
#line 1510 "..\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 1513 "..\oql-parser.php"
|
||||
#line 1515 "..\oql-parser.php"
|
||||
#line 63 "..\oql-parser.y"
|
||||
function yy_r9(){
|
||||
$this->_retvalue = array($this->yystack[$this->yyidx + 0]->minor);
|
||||
}
|
||||
#line 1518 "..\oql-parser.php"
|
||||
#line 1520 "..\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 1524 "..\oql-parser.php"
|
||||
#line 1526 "..\oql-parser.php"
|
||||
#line 71 "..\oql-parser.y"
|
||||
function yy_r11(){ $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; }
|
||||
#line 1527 "..\oql-parser.php"
|
||||
#line 1529 "..\oql-parser.php"
|
||||
#line 72 "..\oql-parser.y"
|
||||
function yy_r12(){ $this->_retvalue = null; }
|
||||
#line 1530 "..\oql-parser.php"
|
||||
#line 1532 "..\oql-parser.php"
|
||||
#line 74 "..\oql-parser.y"
|
||||
function yy_r13(){
|
||||
// insert the join statement on top of the existing list
|
||||
@@ -1532,63 +1532,63 @@ throw new OQLParserStackOverFlowException($this->m_sSourceQuery, $this->m_iLine,
|
||||
// and return the updated array
|
||||
$this->_retvalue = $this->yystack[$this->yyidx + 0]->minor;
|
||||
}
|
||||
#line 1538 "..\oql-parser.php"
|
||||
#line 1540 "..\oql-parser.php"
|
||||
#line 80 "..\oql-parser.y"
|
||||
function yy_r14(){
|
||||
$this->_retvalue = Array($this->yystack[$this->yyidx + 0]->minor);
|
||||
}
|
||||
#line 1543 "..\oql-parser.php"
|
||||
#line 1545 "..\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 1549 "..\oql-parser.php"
|
||||
#line 1551 "..\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 1555 "..\oql-parser.php"
|
||||
#line 1557 "..\oql-parser.php"
|
||||
#line 96 "..\oql-parser.y"
|
||||
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 1560 "..\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 1563 "..\oql-parser.php"
|
||||
#line 98 "..\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 1564 "..\oql-parser.php"
|
||||
#line 1566 "..\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 1569 "..\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 1572 "..\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 1575 "..\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 1578 "..\oql-parser.php"
|
||||
#line 103 "..\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 1579 "..\oql-parser.php"
|
||||
#line 1581 "..\oql-parser.php"
|
||||
#line 104 "..\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 1582 "..\oql-parser.php"
|
||||
#line 1584 "..\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 1587 "..\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 1590 "..\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 1593 "..\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 1596 "..\oql-parser.php"
|
||||
#line 119 "..\oql-parser.y"
|
||||
function yy_r37(){
|
||||
if ($this->yystack[$this->yyidx + -1]->minor == 'MATCHES')
|
||||
@@ -1600,43 +1600,43 @@ throw new OQLParserStackOverFlowException($this->m_sSourceQuery, $this->m_iLine,
|
||||
$this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor);
|
||||
}
|
||||
}
|
||||
#line 1606 "..\oql-parser.php"
|
||||
#line 1608 "..\oql-parser.php"
|
||||
#line 136 "..\oql-parser.y"
|
||||
function yy_r42(){
|
||||
$this->_retvalue = new ListOqlExpression($this->yystack[$this->yyidx + -1]->minor);
|
||||
}
|
||||
#line 1611 "..\oql-parser.php"
|
||||
#line 1613 "..\oql-parser.php"
|
||||
#line 139 "..\oql-parser.y"
|
||||
function yy_r43(){
|
||||
$this->_retvalue = new NestedQueryOqlExpression($this->yystack[$this->yyidx + -1]->minor);
|
||||
}
|
||||
#line 1616 "..\oql-parser.php"
|
||||
#line 1618 "..\oql-parser.php"
|
||||
#line 154 "..\oql-parser.y"
|
||||
function yy_r47(){
|
||||
$this->_retvalue = array();
|
||||
}
|
||||
#line 1621 "..\oql-parser.php"
|
||||
#line 1623 "..\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 1624 "..\oql-parser.php"
|
||||
#line 1626 "..\oql-parser.php"
|
||||
#line 178 "..\oql-parser.y"
|
||||
function yy_r61(){ $this->_retvalue = new ScalarOqlExpression($this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1627 "..\oql-parser.php"
|
||||
#line 1629 "..\oql-parser.php"
|
||||
#line 180 "..\oql-parser.y"
|
||||
function yy_r63(){ $this->_retvalue = new ScalarOqlExpression(null); }
|
||||
#line 1630 "..\oql-parser.php"
|
||||
#line 1632 "..\oql-parser.php"
|
||||
#line 182 "..\oql-parser.y"
|
||||
function yy_r64(){ $this->_retvalue = new FieldOqlExpression($this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1633 "..\oql-parser.php"
|
||||
#line 1635 "..\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 1636 "..\oql-parser.php"
|
||||
#line 1638 "..\oql-parser.php"
|
||||
#line 184 "..\oql-parser.y"
|
||||
function yy_r66(){ $this->_retvalue=$this->yystack[$this->yyidx + 0]->minor; }
|
||||
#line 1639 "..\oql-parser.php"
|
||||
#line 1641 "..\oql-parser.php"
|
||||
#line 187 "..\oql-parser.y"
|
||||
function yy_r67(){ $this->_retvalue = new VariableOqlExpression(substr($this->yystack[$this->yyidx + 0]->minor, 1)); }
|
||||
#line 1642 "..\oql-parser.php"
|
||||
#line 1644 "..\oql-parser.php"
|
||||
#line 189 "..\oql-parser.y"
|
||||
function yy_r68(){
|
||||
if ($this->yystack[$this->yyidx + 0]->minor[0] == '`')
|
||||
@@ -1649,22 +1649,22 @@ throw new OQLParserStackOverFlowException($this->m_sSourceQuery, $this->m_iLine,
|
||||
}
|
||||
$this->_retvalue = new OqlName($name, $this->m_iColPrev);
|
||||
}
|
||||
#line 1655 "..\oql-parser.php"
|
||||
#line 1657 "..\oql-parser.php"
|
||||
#line 200 "..\oql-parser.y"
|
||||
function yy_r69(){$this->_retvalue=(int)$this->yystack[$this->yyidx + 0]->minor; }
|
||||
#line 1658 "..\oql-parser.php"
|
||||
#line 1660 "..\oql-parser.php"
|
||||
#line 201 "..\oql-parser.y"
|
||||
function yy_r70(){$this->_retvalue=(int)-$this->yystack[$this->yyidx + 0]->minor; }
|
||||
#line 1661 "..\oql-parser.php"
|
||||
#line 1663 "..\oql-parser.php"
|
||||
#line 202 "..\oql-parser.y"
|
||||
function yy_r71(){$this->_retvalue=new OqlHexValue($this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1664 "..\oql-parser.php"
|
||||
#line 1666 "..\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 1667 "..\oql-parser.php"
|
||||
#line 1669 "..\oql-parser.php"
|
||||
#line 206 "..\oql-parser.y"
|
||||
function yy_r73(){$this->_retvalue=$this->yystack[$this->yyidx + 0]->minor; }
|
||||
#line 1670 "..\oql-parser.php"
|
||||
#line 1672 "..\oql-parser.php"
|
||||
|
||||
/**
|
||||
* placeholder for the left hand side in a reduce operation.
|
||||
@@ -1768,7 +1768,7 @@ throw new OQLParserStackOverFlowException($this->m_sSourceQuery, $this->m_iLine,
|
||||
#line 33 "..\oql-parser.y"
|
||||
|
||||
throw new OQLParserParseFailureException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol);
|
||||
#line 1775 "..\oql-parser.php"
|
||||
#line 1777 "..\oql-parser.php"
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1783,7 +1783,7 @@ throw new OQLParserParseFailureException($this->m_sSourceQuery, $this->m_iLine,
|
||||
#line 25 "..\oql-parser.y"
|
||||
|
||||
throw new OQLParserSyntaxErrorException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol, $this->tokenName($yymajor), $TOKEN);
|
||||
#line 1791 "..\oql-parser.php"
|
||||
#line 1793 "..\oql-parser.php"
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2043,4 +2043,4 @@ class OQLParser extends OQLParserRaw
|
||||
}
|
||||
}
|
||||
|
||||
#line 2052 "..\oql-parser.php"
|
||||
#line 2054 "..\oql-parser.php"
|
||||
|
||||
@@ -36,7 +36,13 @@ class UnknownClassOqlException extends OqlNormalizeException
|
||||
{
|
||||
public function __construct($sInput, OqlName $oName, $aExpecting = null)
|
||||
{
|
||||
parent::__construct('Unknown class', $sInput, $oName, $aExpecting);
|
||||
$aAllowedClasses = [];
|
||||
foreach ($aExpecting as $sClass) {
|
||||
if (UserRights::IsActionAllowed($sClass, UR_ACTION_READ)) {
|
||||
$aAllowedClasses[] = $sClass;
|
||||
}
|
||||
}
|
||||
parent::__construct('Unknown class', $sInput, $oName, $aAllowedClasses);
|
||||
}
|
||||
|
||||
public function GetUserFriendlyDescription()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user