mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-20 02:44:11 +01:00
Compare commits
667 Commits
support/3.
...
3.1.0-alph
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6c4289a491 | ||
|
|
8940051c3d | ||
|
|
72dad5dd07 | ||
|
|
dc38db1c3d | ||
|
|
45df1002ec | ||
|
|
737388053f | ||
|
|
d0b000d125 | ||
|
|
1d0ce1862d | ||
|
|
c0589715c1 | ||
|
|
8b18fd7cc0 | ||
|
|
33717b9610 | ||
|
|
b97a464bb1 | ||
|
|
58c0da7e87 | ||
|
|
4203382920 | ||
|
|
825e402dd6 | ||
|
|
fa75c8964a | ||
|
|
fc58c6f4ce | ||
|
|
ef4e38f6b9 | ||
|
|
1086eaaffe | ||
|
|
f7ee21f1d7 | ||
|
|
35b0b16e20 | ||
|
|
ca72ec736e | ||
|
|
e63d66aa5d | ||
|
|
2803f0be49 | ||
|
|
278b0d643f | ||
|
|
569045f675 | ||
|
|
25989bc00f | ||
|
|
fc60759db5 | ||
|
|
3a931ff79e | ||
|
|
f6bd380ad0 | ||
|
|
97c4ecf8bc | ||
|
|
5f4ef74d34 | ||
|
|
6184dbafff | ||
|
|
630c1dcdc8 | ||
|
|
cb84cbf7a8 | ||
|
|
b025f86b21 | ||
|
|
5a76175ec1 | ||
|
|
ce162f05da | ||
|
|
d358c9e8fa | ||
|
|
86a208a86d | ||
|
|
372888c7a7 | ||
|
|
547a520ab1 | ||
|
|
8bf788dcb9 | ||
|
|
a157e754a9 | ||
|
|
eae84ab399 | ||
|
|
4d4d3bd9a2 | ||
|
|
9f9630d011 | ||
|
|
de1c58a84c | ||
|
|
1fe9520b7e | ||
|
|
3fbc5e1ce0 | ||
|
|
786e7d647c | ||
|
|
1768274aaf | ||
|
|
7cc1e33950 | ||
|
|
72a586e490 | ||
|
|
0cce58e0cf | ||
|
|
53b68f88b8 | ||
|
|
7fb10da013 | ||
|
|
0b1352fe37 | ||
|
|
e0cc00e772 | ||
|
|
002e52cc3e | ||
|
|
bbea978259 | ||
|
|
bddfa4cd7a | ||
|
|
cd1b441025 | ||
|
|
0f97b93617 | ||
|
|
b61c8ea4ca | ||
|
|
f48df74933 | ||
|
|
6fb00cbbf3 | ||
|
|
af2421a2cb | ||
|
|
0374e303e4 | ||
|
|
27bd78d76b | ||
|
|
bc3b43958c | ||
|
|
fd4a2f17d2 | ||
|
|
d741656028 | ||
|
|
f7e8bd31f5 | ||
|
|
1e37370789 | ||
|
|
5844717980 | ||
|
|
9437968d0e | ||
|
|
e72ed33a40 | ||
|
|
2a825c6ba0 | ||
|
|
6b8d9ea08d | ||
|
|
52d57293c9 | ||
|
|
40cebf1eb7 | ||
|
|
f1d6f3e5c2 | ||
|
|
834c4a2654 | ||
|
|
ed1a076ebb | ||
|
|
1dcf38f2ea | ||
|
|
2f034253e7 | ||
|
|
a10ac7fdcb | ||
|
|
3775ccea74 | ||
|
|
f743971c93 | ||
|
|
dd213df9c4 | ||
|
|
96825c646e | ||
|
|
1152b2f401 | ||
|
|
8f7003c694 | ||
|
|
fb1ceebaa4 | ||
|
|
9482139b5a | ||
|
|
7ea2315323 | ||
|
|
9487d63b9c | ||
|
|
3d9a85d577 | ||
|
|
b2397fc570 | ||
|
|
0d15b01b3f | ||
|
|
f7e87c1ea1 | ||
|
|
f21f6aec30 | ||
|
|
c76d351379 | ||
|
|
b4813de0a5 | ||
|
|
8e3bad11ae | ||
|
|
13550fd643 | ||
|
|
44977d69b6 | ||
|
|
15f108152c | ||
|
|
9be167cf5e | ||
|
|
34688c2bf6 | ||
|
|
c1922e2b3f | ||
|
|
e1ffa65d8b | ||
|
|
cc2881a7b0 | ||
|
|
0d8a21f6ef | ||
|
|
01d9d34118 | ||
|
|
6549c95d4f | ||
|
|
da07fadfb3 | ||
|
|
b2c256e51e | ||
|
|
7305a30b50 | ||
|
|
59792b2a3d | ||
|
|
19dda61d4f | ||
|
|
06a5d645da | ||
|
|
5d1852fe45 | ||
|
|
ced4f82585 | ||
|
|
de0110abc6 | ||
|
|
a53bea5eb9 | ||
|
|
47435869b3 | ||
|
|
e04ea2d178 | ||
|
|
42ce397e13 | ||
|
|
22162aa55f | ||
|
|
c693d03a77 | ||
|
|
29e0df9bbc | ||
|
|
ec90c0b6cd | ||
|
|
7b79da3f32 | ||
|
|
4d7bac89f3 | ||
|
|
57ab3a0336 | ||
|
|
4a54b2f8ff | ||
|
|
9b47ee12e7 | ||
|
|
b354058eb5 | ||
|
|
43dd0b7df8 | ||
|
|
43d86ad8e2 | ||
|
|
d3d89b1ee2 | ||
|
|
15d3201a40 | ||
|
|
cf4616d9a1 | ||
|
|
083d19c688 | ||
|
|
4e9a34d8d8 | ||
|
|
25048ba0cc | ||
|
|
4f7a1ea9da | ||
|
|
a62a373f64 | ||
|
|
6b23171d65 | ||
|
|
09b69728a0 | ||
|
|
bc0dbc7860 | ||
|
|
a693c343e8 | ||
|
|
cd17eb484a | ||
|
|
259eb60f97 | ||
|
|
1afa3e2003 | ||
|
|
4c4d7c12e8 | ||
|
|
97c3cacfaa | ||
|
|
98ec568788 | ||
|
|
626ba7ee66 | ||
|
|
3920f84f45 | ||
|
|
30d2557342 | ||
|
|
f3cea730af | ||
|
|
0fad249124 | ||
|
|
94d7d94bff | ||
|
|
48645dd519 | ||
|
|
9df9077595 | ||
|
|
756241918e | ||
|
|
f819fdae98 | ||
|
|
7127b192a2 | ||
|
|
1cac189077 | ||
|
|
0383fb124b | ||
|
|
f378fd3d72 | ||
|
|
44fac686f9 | ||
|
|
eff2afbc69 | ||
|
|
86b3129c08 | ||
|
|
bdd5741290 | ||
|
|
53091fc5df | ||
|
|
06db3561dc | ||
|
|
5cb2f19ea0 | ||
|
|
a668b92051 | ||
|
|
eda11e97e3 | ||
|
|
64146b6e1b | ||
|
|
7b85cb92b0 | ||
|
|
a28fcb7c8c | ||
|
|
f8ec180a0f | ||
|
|
0810a113c1 | ||
|
|
1b4a21fafa | ||
|
|
fae857175b | ||
|
|
9c21ca2ee6 | ||
|
|
ccdb5ec79f | ||
|
|
207d312b12 | ||
|
|
08d83478da | ||
|
|
8665f4ff06 | ||
|
|
6a5beb89cc | ||
|
|
3d09e3b57b | ||
|
|
27cf8159d0 | ||
|
|
314dd7d7ad | ||
|
|
6412f1ab23 | ||
|
|
32ee1a8226 | ||
|
|
fe997d1254 | ||
|
|
e38951ebae | ||
|
|
fde6cac7a2 | ||
|
|
93209265f2 | ||
|
|
b50675fc29 | ||
|
|
deb6d1fd9a | ||
|
|
432579657c | ||
|
|
f09bd5fdff | ||
|
|
4f37a9e447 | ||
|
|
32297d93dd | ||
|
|
7083e1263d | ||
|
|
329d1a7c41 | ||
|
|
22749caeb4 | ||
|
|
baaa4474f9 | ||
|
|
af804a9dc1 | ||
|
|
899f4fdc0a | ||
|
|
8cced3a79e | ||
|
|
4eb45f1b34 | ||
|
|
c2f503e2ac | ||
|
|
e4e654100e | ||
|
|
3270c3f775 | ||
|
|
7f88a6aa1a | ||
|
|
c02f84969a | ||
|
|
bdebea62b6 | ||
|
|
4a22361f39 | ||
|
|
025f8b9dc3 | ||
|
|
9defedb862 | ||
|
|
e4d8489292 | ||
|
|
0579f1b1dc | ||
|
|
a551bf0499 | ||
|
|
4f36819ab2 | ||
|
|
26359cf1ee | ||
|
|
00a25f3e8b | ||
|
|
2d122448c8 | ||
|
|
e5d67d2219 | ||
|
|
4ac0de5fff | ||
|
|
dbe7fae82e | ||
|
|
98a53a46f0 | ||
|
|
e6946d33e6 | ||
|
|
3548251997 | ||
|
|
93e0dde1a1 | ||
|
|
f02e95e8bf | ||
|
|
98bcfeb2ba | ||
|
|
669c69ff39 | ||
|
|
c5e00194b7 | ||
|
|
ded2f8af9a | ||
|
|
084f3ec52b | ||
|
|
bedc8bbf46 | ||
|
|
4ec3f22173 | ||
|
|
ebf3f48584 | ||
|
|
8db89c3d5f | ||
|
|
649674c786 | ||
|
|
038e6d7f3f | ||
|
|
bdb29fd99a | ||
|
|
a0a5037554 | ||
|
|
6af62c4a50 | ||
|
|
0ef5e9d3ff | ||
|
|
1c705bdb94 | ||
|
|
28a1324d3a | ||
|
|
af51f05c56 | ||
|
|
1a7b3f0631 | ||
|
|
4ff82f5c12 | ||
|
|
e2e08351b0 | ||
|
|
d83c45812d | ||
|
|
6258b97b92 | ||
|
|
3aab49c372 | ||
|
|
ecfae75e10 | ||
|
|
f60331e0f9 | ||
|
|
8f6065d4f4 | ||
|
|
52c984d5e7 | ||
|
|
6585f717c5 | ||
|
|
363a1507cf | ||
|
|
0bba9b0fad | ||
|
|
75c8738538 | ||
|
|
a553616ea2 | ||
|
|
8c14d36dfe | ||
|
|
4486842169 | ||
|
|
f5b216fd9a | ||
|
|
dcdce52608 | ||
|
|
73139e82cb | ||
|
|
36ec455e6d | ||
|
|
df3fc8be7f | ||
|
|
3191caa700 | ||
|
|
9a3324de07 | ||
|
|
07fc8c1e5b | ||
|
|
e14cf23ecb | ||
|
|
974dddbe19 | ||
|
|
5e0698b7f3 | ||
|
|
2dccedf8d7 | ||
|
|
3d083fca05 | ||
|
|
e496513275 | ||
|
|
e70e9de1c8 | ||
|
|
fcdfd54066 | ||
|
|
efdf780c79 | ||
|
|
e90335e943 | ||
|
|
f0bce86e28 | ||
|
|
fac689ecf7 | ||
|
|
6e8949a880 | ||
|
|
7028b2f76c | ||
|
|
a547358be4 | ||
|
|
ddcff1f8ba | ||
|
|
1d7c527ea1 | ||
|
|
4baff1b3d1 | ||
|
|
9c4f1419b1 | ||
|
|
848c4ca204 | ||
|
|
e9aa9cc8df | ||
|
|
2eadb632df | ||
|
|
a57056a035 | ||
|
|
c1dde51404 | ||
|
|
1fcb3af4c0 | ||
|
|
9ccff4f8de | ||
|
|
1e0fc4714d | ||
|
|
8fc8342358 | ||
|
|
14673ff1f5 | ||
|
|
ae95cf37f5 | ||
|
|
567f333017 | ||
|
|
acd8944aec | ||
|
|
5b1b5b0cc1 | ||
|
|
30664bc5ec | ||
|
|
f32f36fc74 | ||
|
|
5157f511fc | ||
|
|
3196e105a1 | ||
|
|
b9d865f881 | ||
|
|
2e39a650eb | ||
|
|
e4f6a02de6 | ||
|
|
fac455da48 | ||
|
|
b01627f39d | ||
|
|
766c9f0e7e | ||
|
|
d1414a3f34 | ||
|
|
6386a302b2 | ||
|
|
31454b2946 | ||
|
|
4aad555649 | ||
|
|
93ee565d29 | ||
|
|
6c097a128b | ||
|
|
eea3f78cec | ||
|
|
6c4caf64c8 | ||
|
|
88f0013330 | ||
|
|
aa31da34e5 | ||
|
|
71464f6d0e | ||
|
|
e5a9648206 | ||
|
|
abf2120097 | ||
|
|
2f5eaa2dfd | ||
|
|
b161a863df | ||
|
|
cea75e07f2 | ||
|
|
7d1893a2b8 | ||
|
|
b32d82f11e | ||
|
|
9f963686b5 | ||
|
|
56f8dd2942 | ||
|
|
e4e4365cce | ||
|
|
b061c29a5b | ||
|
|
5d9e58f194 | ||
|
|
e6821f1d16 | ||
|
|
320eb83c10 | ||
|
|
7621a0fd31 | ||
|
|
2b84c0384d | ||
|
|
146ed0f6a6 | ||
|
|
9826b40ec6 | ||
|
|
b154952869 | ||
|
|
ef07f8366d | ||
|
|
9a9707e15c | ||
|
|
bbb5b86864 | ||
|
|
7b60c9c71a | ||
|
|
2f90b8e6a6 | ||
|
|
428827ed91 | ||
|
|
a31451d5af | ||
|
|
99f75111ce | ||
|
|
a2c6b08cf4 | ||
|
|
25af155113 | ||
|
|
d5917299fc | ||
|
|
25103b9f0b | ||
|
|
5442768229 | ||
|
|
8f0a62c1c9 | ||
|
|
02d5f8560f | ||
|
|
c94e24c0ff | ||
|
|
9438d063cd | ||
|
|
bb948fdd1b | ||
|
|
6a384b0405 | ||
|
|
df6d459bb0 | ||
|
|
37cb882507 | ||
|
|
ebab664c1a | ||
|
|
a577799fee | ||
|
|
26b779cd0e | ||
|
|
7a9e16766f | ||
|
|
706b81b929 | ||
|
|
8fab14f072 | ||
|
|
08387e2814 | ||
|
|
1f3e91efae | ||
|
|
2aacb58e7f | ||
|
|
cc7dceed1a | ||
|
|
8d8cefe948 | ||
|
|
83b4f2bf34 | ||
|
|
11d9435f08 | ||
|
|
952bceb3c3 | ||
|
|
4b0c94d609 | ||
|
|
02afa2f19b | ||
|
|
8cdc251378 | ||
|
|
57c36d0e51 | ||
|
|
30021d9236 | ||
|
|
95c43adc4e | ||
|
|
8357e5b8d0 | ||
|
|
02af3cf197 | ||
|
|
1655a377f0 | ||
|
|
aad4a5e20c | ||
|
|
78a29ab3ca | ||
|
|
79021fba1f | ||
|
|
cf21025cd7 | ||
|
|
b718d40eb9 | ||
|
|
ef81bdb1e1 | ||
|
|
784f568575 | ||
|
|
b30bc64319 | ||
|
|
ca5bbc596e | ||
|
|
8188d76f63 | ||
|
|
57a3da1cca | ||
|
|
eb9b9cfce7 | ||
|
|
8b8e6bab33 | ||
|
|
549bfcafd9 | ||
|
|
435dce91d7 | ||
|
|
8c7fa53696 | ||
|
|
d60109d3c9 | ||
|
|
12c9aad445 | ||
|
|
0c90b2f1aa | ||
|
|
8c27476d2a | ||
|
|
dfd0406160 | ||
|
|
af36177b03 | ||
|
|
7e15c0874d | ||
|
|
86e1f165cd | ||
|
|
ee783257d9 | ||
|
|
d4a0c141b3 | ||
|
|
192dd86469 | ||
|
|
cd861263fb | ||
|
|
dbd5c32535 | ||
|
|
76062ca8b2 | ||
|
|
ce7f251064 | ||
|
|
5e497aff90 | ||
|
|
70d1504cd4 | ||
|
|
742ef2b23b | ||
|
|
f6c50733fc | ||
|
|
25a612da04 | ||
|
|
31cc294cc3 | ||
|
|
d93a0b698b | ||
|
|
9cf329c475 | ||
|
|
f978cc4e22 | ||
|
|
494b70950c | ||
|
|
f71fadda17 | ||
|
|
91c73b562a | ||
|
|
3ae862d690 | ||
|
|
51d5c00bcd | ||
|
|
99b4fceaf3 | ||
|
|
e91e53c0ec | ||
|
|
2c265aab44 | ||
|
|
fe28319d22 | ||
|
|
c56cb516ed | ||
|
|
db9b239d71 | ||
|
|
d793a4e3ab | ||
|
|
dbf4389307 | ||
|
|
e62c1c629f | ||
|
|
797184f452 | ||
|
|
9f0ac99abb | ||
|
|
6d025479c6 | ||
|
|
616381a16c | ||
|
|
e1bf3d9ee0 | ||
|
|
5ff4db5a4d | ||
|
|
01023801b8 | ||
|
|
00bdcf14dd | ||
|
|
b98e1889d5 | ||
|
|
d1eef0853b | ||
|
|
29f23fa992 | ||
|
|
7ec12f1e12 | ||
|
|
139be3a9b7 | ||
|
|
7f6f5c0c3b | ||
|
|
f5389a2d2d | ||
|
|
31dffcf5e0 | ||
|
|
4afadc39aa | ||
|
|
f13ed9494b | ||
|
|
acd5547618 | ||
|
|
8733ac416c | ||
|
|
ab40b1b556 | ||
|
|
682ab44dea | ||
|
|
0ff25728a4 | ||
|
|
56fdf8dd63 | ||
|
|
0b2510f6cc | ||
|
|
eb408e2ea4 | ||
|
|
958a86bbb5 | ||
|
|
b45da1eaa5 | ||
|
|
6facaec353 | ||
|
|
7dc4a2776b | ||
|
|
09b0ac38ef | ||
|
|
9d62e87de4 | ||
|
|
04dbaef257 | ||
|
|
ff12dd99b5 | ||
|
|
7ee5184afc | ||
|
|
212565b01e | ||
|
|
130b4b176f | ||
|
|
0c14423213 | ||
|
|
bf01be6f19 | ||
|
|
b47d3cda12 | ||
|
|
01c76f19ed | ||
|
|
eb8ea0fbd4 | ||
|
|
784e553d23 | ||
|
|
de040a0922 | ||
|
|
4af54f26b6 | ||
|
|
02dab4eb72 | ||
|
|
306e16147a | ||
|
|
aae1e12b2e | ||
|
|
bd3724be8f | ||
|
|
683ab25e83 | ||
|
|
977a8add67 | ||
|
|
79da71ecf8 | ||
|
|
abb13b70b9 | ||
|
|
023ead39ec | ||
|
|
8890940b06 | ||
|
|
44a8bfd764 | ||
|
|
7176218a5f | ||
|
|
6aef59e42d | ||
|
|
2b885beb82 | ||
|
|
86024107af | ||
|
|
754f87fd0c | ||
|
|
972e894bc5 | ||
|
|
86a2db7e7f | ||
|
|
4c31081de2 | ||
|
|
23c95ebbf3 | ||
|
|
e77f21a0b5 | ||
|
|
35e1f080b8 | ||
|
|
812c1f6bb4 | ||
|
|
9adb7f20ce | ||
|
|
c7e54c66c8 | ||
|
|
f6855b0d2b | ||
|
|
1ceef602f0 | ||
|
|
93cc29f4d9 | ||
|
|
c9317542c8 | ||
|
|
aed8337c51 | ||
|
|
af4a5e1b8d | ||
|
|
1fd792fed9 | ||
|
|
e7c09c83f0 | ||
|
|
56103d1952 | ||
|
|
301c308fec | ||
|
|
b827c68187 | ||
|
|
3d541b2a2d | ||
|
|
44ca8acf37 | ||
|
|
b371072a61 | ||
|
|
d874b749d0 | ||
|
|
cff26563cc | ||
|
|
fd1064b044 | ||
|
|
6086131d3c | ||
|
|
2b0cc4d0e7 | ||
|
|
4eb43cd02d | ||
|
|
6a332ca60b | ||
|
|
82f4736ad2 | ||
|
|
020937d4f6 | ||
|
|
438b84d4ee | ||
|
|
afc8354523 | ||
|
|
23fab1bab0 | ||
|
|
8b26b34f96 | ||
|
|
c20cedf266 | ||
|
|
cfd0b80225 | ||
|
|
865aaf0191 | ||
|
|
bb2c9dedeb | ||
|
|
891dd31290 | ||
|
|
6a4217e87b | ||
|
|
34cac2ac1b | ||
|
|
1dd25be6ef | ||
|
|
7bfa7fa9f4 | ||
|
|
0a5411d411 | ||
|
|
7598b9e29a | ||
|
|
edd96d46f9 | ||
|
|
00581b82f5 | ||
|
|
8c8ab9988c | ||
|
|
fe4aa9dcd7 | ||
|
|
a71a5f53f7 | ||
|
|
197bad19ab | ||
|
|
32ed40e733 | ||
|
|
fa5644064a | ||
|
|
dd3f77a397 | ||
|
|
909fb4c75b | ||
|
|
c0f0e354dd | ||
|
|
d6f58d04f1 | ||
|
|
cc88ee1de0 | ||
|
|
8008e4d840 | ||
|
|
a4390fbb57 | ||
|
|
f4170ade8a | ||
|
|
1d9a9bcb28 | ||
|
|
178ba60973 | ||
|
|
c47f224566 | ||
|
|
8fcd454445 | ||
|
|
ba9d5f0c4b | ||
|
|
0efc978004 | ||
|
|
b026e5ab87 | ||
|
|
1e75154cc7 | ||
|
|
e4a04de9f4 | ||
|
|
645e612e8b | ||
|
|
26a78a10bc | ||
|
|
8ba28adf68 | ||
|
|
1feb5e6ad6 | ||
|
|
34a26d33a1 | ||
|
|
b0a55e057b | ||
|
|
5ac9b05b2d | ||
|
|
63e582a07f | ||
|
|
470076daa2 | ||
|
|
f6d92a189b | ||
|
|
42599eae64 | ||
|
|
c788c93542 | ||
|
|
b75f29b8d7 | ||
|
|
81d285a143 | ||
|
|
ad4442ae78 | ||
|
|
1db4b9a12e | ||
|
|
a773f0d8a2 | ||
|
|
29c6b73d93 | ||
|
|
5b52ca4776 | ||
|
|
8ddaf1b731 | ||
|
|
964ce44577 | ||
|
|
cea6c557ce | ||
|
|
f4ecdf116a | ||
|
|
8d4545f008 | ||
|
|
5854c199d0 | ||
|
|
a937d08655 | ||
|
|
4bdf84bf6c | ||
|
|
9ce88699ca | ||
|
|
f9e8bf88c8 | ||
|
|
bddf7a11c8 | ||
|
|
ee7fb15fc2 | ||
|
|
8473df0f0c | ||
|
|
7eab003f3c | ||
|
|
d437e2d662 | ||
|
|
99819527db | ||
|
|
965273009c | ||
|
|
7bee616b1b | ||
|
|
f5302133d9 | ||
|
|
bf2aba1b06 | ||
|
|
60c372f535 | ||
|
|
22f9d16743 | ||
|
|
3d3d94e837 | ||
|
|
3bec96abe1 | ||
|
|
44c18395b3 | ||
|
|
09ff052c1f | ||
|
|
9ef5fd70c0 | ||
|
|
3b599a40f3 | ||
|
|
1495ab992f | ||
|
|
d89dc1aa4d | ||
|
|
1fac2ae787 | ||
|
|
ff7d1f2d6a | ||
|
|
91d4da85e1 | ||
|
|
dcd52d6919 | ||
|
|
9a75ca2c21 | ||
|
|
812606db78 | ||
|
|
c17ceb1702 | ||
|
|
ce01dad875 | ||
|
|
c04beea38c | ||
|
|
9ca106d889 | ||
|
|
5f575d524a | ||
|
|
93d88cca37 | ||
|
|
0997750816 | ||
|
|
427c8b0794 | ||
|
|
06008ed8eb | ||
|
|
374b71c017 | ||
|
|
fba78e7d9b | ||
|
|
551abc861e | ||
|
|
16142bd979 | ||
|
|
c1e2ba8abc | ||
|
|
cc2efe432e | ||
|
|
e879ae2f11 | ||
|
|
8bcd2ce571 | ||
|
|
d301562ffe | ||
|
|
caed22ea8d | ||
|
|
a90a483756 | ||
|
|
78f51d40f6 | ||
|
|
8bfc54e6b4 |
Binary file not shown.
|
After Width: | Height: | Size: 983 KiB |
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 1.4 MiB |
65
.doc/itop-version-history.md
Normal file
65
.doc/itop-version-history.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# iTop version history
|
||||
|
||||
```mermaid
|
||||
%%{init: { 'logLevel': 'debug', 'theme': 'base', '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"
|
||||
```
|
||||
|
||||
To learn more, check the [iTop community versions history on the official wiki](https://www.itophub.io/wiki/page?id=latest:release:start).
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -146,3 +146,10 @@ local.properties
|
||||
.cache-main
|
||||
.scala_dependencies
|
||||
.worksheet
|
||||
|
||||
# Mac
|
||||
.DS_Store
|
||||
|
||||
# Windows
|
||||
Thumbs.db
|
||||
|
||||
|
||||
@@ -19,16 +19,23 @@
|
||||
*
|
||||
*/
|
||||
|
||||
$iTopFolder = __DIR__ . "/../../" ;
|
||||
/**
|
||||
* Alias for `composer show -loD`
|
||||
* You can also use `composer outdated -D`
|
||||
*
|
||||
* @link https://getcomposer.org/doc/03-cli.md#show
|
||||
*/
|
||||
|
||||
require_once ("$iTopFolder/approot.inc.php");
|
||||
$iTopFolder = __DIR__."/../../";
|
||||
|
||||
require_once("$iTopFolder/approot.inc.php");
|
||||
$sApproot = APPROOT;
|
||||
$aTrace = array();
|
||||
|
||||
$aParamsConfig = array(
|
||||
'composer-path' => array(
|
||||
'default' => 'composer.phar',
|
||||
)
|
||||
'default' => 'composer',
|
||||
),
|
||||
);
|
||||
$aParamsConfigNotFound = array_flip(array_keys($aParamsConfig));
|
||||
$aGivenArgs = $argv;
|
||||
|
||||
@@ -6,11 +6,19 @@
|
||||
* Will update version in the following files :
|
||||
*
|
||||
* datamodels/2.x/.../datamodel.*.xml
|
||||
* application/*.xml
|
||||
* core/*.xml
|
||||
*
|
||||
* Usage :
|
||||
* `php .make\release\update-xml.php "1.7"`
|
||||
* `php .make\release\update-xml.php`
|
||||
*
|
||||
* @since 2.7.0
|
||||
* If no parameter provided then the current XML version will be used as target version
|
||||
*
|
||||
* @since 2.7.0 simple version change using regexp (not doing conversion)
|
||||
* @since 3.1.0 N°5405 now does a real conversion
|
||||
* @since 3.1.0 N°5633 allow to use without parameter
|
||||
* @since 3.1.0 N°5633 add /application and /core XML files
|
||||
******************************************************************************/
|
||||
|
||||
|
||||
@@ -22,10 +30,12 @@ require_once (__DIR__.DIRECTORY_SEPARATOR.'update.classes.inc.php');
|
||||
|
||||
if (count($argv) === 1)
|
||||
{
|
||||
echo '/!\ You must pass the new version as parameter';
|
||||
exit(1);
|
||||
echo '/!\ No version passed: assuming target XML version is current XML version ('.ITOP_DESIGN_LATEST_VERSION.")\n";
|
||||
$sVersionLabel = ITOP_DESIGN_LATEST_VERSION;
|
||||
} else {
|
||||
$sVersionLabel = $argv[1];
|
||||
}
|
||||
$sVersionLabel = $argv[1];
|
||||
|
||||
if (empty($sVersionLabel))
|
||||
{
|
||||
echo 'Version passed as parameter is empty !';
|
||||
|
||||
@@ -125,16 +125,31 @@ class iTopVersionFileUpdater extends AbstractSingleFileVersionUpdater
|
||||
|
||||
abstract class AbstractGlobFileVersionUpdater extends FileVersionUpdater
|
||||
{
|
||||
protected $sGlobPattern;
|
||||
/** @var array|string glob patterns to seek for files to modify */
|
||||
protected $globPattern;
|
||||
|
||||
public function __construct($sGlobPattern)
|
||||
public function __construct($globPattern)
|
||||
{
|
||||
$this->sGlobPattern = $sGlobPattern;
|
||||
$this->globPattern = $globPattern;
|
||||
}
|
||||
|
||||
public function GetFiles()
|
||||
{
|
||||
return glob($this->sGlobPattern);
|
||||
$aGlobPatterns = (is_array($this->globPattern))
|
||||
? $this->globPattern
|
||||
: [$this->globPattern];
|
||||
|
||||
$aFiles = [];
|
||||
foreach ($aGlobPatterns as $sGlobPattern) {
|
||||
$result = glob($sGlobPattern);
|
||||
if (false === $result) {
|
||||
continue;
|
||||
}
|
||||
/** @noinspection SlowArrayOperationsInLoopInspection */
|
||||
$aFiles = array_merge($aFiles, $result);
|
||||
}
|
||||
|
||||
return $aFiles;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,7 +181,11 @@ class DatamodelsXmlFiles extends AbstractGlobFileVersionUpdater
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(APPROOT.'datamodels/2.x/*/datamodel.*.xml');
|
||||
parent::__construct([
|
||||
APPROOT.'datamodels/2.x/*/datamodel.*.xml',
|
||||
APPROOT.'application/*.xml',
|
||||
APPROOT.'core/*.xml',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -174,10 +193,40 @@ class DatamodelsXmlFiles extends AbstractGlobFileVersionUpdater
|
||||
*/
|
||||
public function UpdateFileContent($sVersionLabel, $sFileContent, $sFileFullPath)
|
||||
{
|
||||
return preg_replace(
|
||||
'/(<itop_design .* version=")[^"]+(">)/',
|
||||
'${1}'.$sVersionLabel.'${2}',
|
||||
$sFileContent
|
||||
);
|
||||
require_once APPROOT.'setup/itopdesignformat.class.inc.php';
|
||||
$oFileXml = new DOMDocument();
|
||||
/** @noinspection PhpComposerExtensionStubsInspection */
|
||||
libxml_clear_errors();
|
||||
$oFileXml->formatOutput = true;
|
||||
$oFileXml->preserveWhiteSpace = false;
|
||||
$oFileXml->loadXML($sFileContent);
|
||||
|
||||
$oFileItopFormat = new iTopDesignFormat($oFileXml);
|
||||
|
||||
$sDesignVersionToSet = static::GetDesignVersionToSet($oFileItopFormat->GetVersion());
|
||||
if (false === is_null($sDesignVersionToSet)) {
|
||||
// N°5779 if same as target version, we will try to convert from version below
|
||||
$oFileItopFormat->GetITopDesignNode()->setAttribute('version', $sDesignVersionToSet);
|
||||
}
|
||||
|
||||
$bConversionResult = $oFileItopFormat->Convert($sVersionLabel);
|
||||
|
||||
if (false === $bConversionResult) {
|
||||
throw new Exception("Error when converting $sFileFullPath");
|
||||
}
|
||||
|
||||
return $oFileItopFormat->GetXmlAsString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ?string version to use : if file version is same as current version then return previous version, else return null
|
||||
* @since 3.1.0 N°5779
|
||||
*/
|
||||
protected static function GetDesignVersionToSet($sFileDesignVersion):?string {
|
||||
if ($sFileDesignVersion !== ITOP_DESIGN_LATEST_VERSION) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return iTopDesignFormat::GetPreviousDesignVersion(ITOP_DESIGN_LATEST_VERSION);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,11 +27,14 @@ If you have an idea you're sure would benefit to all of iTop users, you may
|
||||
[create a corresponding ticket](https://sourceforge.net/p/itop/tickets/new/) to submit it, but be warned that there are lots of good
|
||||
reasons to refuse such changes.
|
||||
|
||||
### 📄 License
|
||||
iTop is distributed under the AGPL-3.0 license (see the [license.txt] file),
|
||||
your code must comply with this license.
|
||||
### 📄 License and copyright
|
||||
iTop is distributed under the AGPL-3.0 license (see the [license.txt] file).
|
||||
|
||||
If you want to use another license, you may [create an extension][wiki new ext].
|
||||
The iTop repository is divided in three parts: iTop (mainly PHP/JS/XML sources and dictionaries), images, and third-party libraries.
|
||||
Combodo has the copyright on most of the source files in the iTop part of the repository: please do not modify the existing file copyrights.
|
||||
Anyhow, you are encouraged to signal your contribution by the mean of `@author` annotations.
|
||||
|
||||
If you want to use another license or keep the code ownership (copyright), you may [create an extension][wiki new ext].
|
||||
|
||||
[license.txt]: https://github.com/Combodo/iTop/blob/develop/license.txt
|
||||
[wiki new ext]: https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Astart#by_writing_your_own_extension
|
||||
@@ -52,26 +55,26 @@ Here are the branches we use and their meaning :
|
||||
|
||||
For example, if no version is currently prepared for shipping we could have:
|
||||
|
||||
- `develop` containing future 3.0.0 version
|
||||
- `develop` containing future 3.1.0 version
|
||||
- `support/3.0`: 3.0.x maintenance version
|
||||
- `support/2.7`: 2.7.x maintenance version
|
||||
- `support/2.6`: 2.6.x maintenance version
|
||||
- `support/2.5`: 2.5.x maintenance version
|
||||
|
||||
In this example, when 3.0.0-beta is shipped that will become:
|
||||
In this example, when 3.1.0-beta is shipped that will become:
|
||||
|
||||
- `develop`: future 3.1.0 version
|
||||
- `release/3.0.0`: 3.0.0-beta
|
||||
- `develop`: future 3.2.0 version
|
||||
- `release/3.1.0`: 3.1.0-beta
|
||||
- `support/3.0`: 3.0.x maintenance version
|
||||
- `support/2.7`: 2.7.x maintenance version
|
||||
- `support/2.6`: 2.6.x maintenance version
|
||||
- `support/2.5`: 2.5.x maintenance version
|
||||
|
||||
And when 3.0.0 final will be out:
|
||||
And when 3.1.0 final will be out:
|
||||
|
||||
- `develop`: future 3.1.0 version
|
||||
- `support/3.0`: 3.0.x maintenance version (will host developments for 3.0.1)
|
||||
- `develop`: future 3.2.0 version
|
||||
- `support/3.1`: 3.1.x maintenance version (will host developments for 3.1.1)
|
||||
- `support/3.0`: 3.0.x maintenance version
|
||||
- `support/2.7`: 2.7.x maintenance version
|
||||
- `support/2.6`: 2.6.x maintenance version
|
||||
- `support/2.5`: 2.5.x maintenance version
|
||||
|
||||
Also note that we have a "micro-version" concept : each of those versions have a very small amount of modifications. They are made from
|
||||
`support/*` branches as well. For example 2.6.2-1 and 2.6.2-2 were made from the `support/2.6.2` branch.
|
||||
@@ -131,21 +134,20 @@ Our tests are located in the `test/` directory, containing a PHPUnit config file
|
||||
|
||||
When your code is working, please:
|
||||
|
||||
* squash as much as possible your commits,
|
||||
* rebase your branch on our repo last commit,
|
||||
* create a pull request.
|
||||
* Squash as much as possible your commits,
|
||||
* Rebase your branch on our repo last commit,
|
||||
* Create a pull request. _Detailed procedure to work on fork and create PR is available [in GitHub help pages](https://help.github.com/articles/creating-a-pull-request-from-a-fork/)_.
|
||||
* Pull request description: mind to add all the information useful to understand why you're suggesting this modification and anything necessary to dive into your work. Especially:
|
||||
- Bugfixes: exact steps to reproduce the bug (given/when/then), description of the bug cause and what solution is implemented
|
||||
- Enhancements: use cases, implementation details if needed
|
||||
* Mind to check the "[Allow edits from maintainers](https://docs.github.com/en/github-ae@latest/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork)" option !
|
||||
|
||||
Detailed procedure to work on fork and create PR is available [in GitHub help pages](https://help.github.com/articles/creating-a-pull-request-from-a-fork/).
|
||||
|
||||
You might check the ["Allow edits from maintainers" PR checkbox][allow_edits_checkbox] to ease review.
|
||||
## 🙏 We are thankful
|
||||
|
||||
[allow_edits_checkbox]: https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork#enabling-repository-maintainer-permissions-on-existing-pull-requests
|
||||
We are thankful for all your contributions to the iTop universe! As a thank you gift, we will send stickers to every iTop (& extensions) contributors!
|
||||
|
||||
### 🙏 We are thankful
|
||||
|
||||
We are thankful for all your contributions to the iTop universe! As a thank you gift, we will send stickers to every iTop (& extensions) contributors!
|
||||
|
||||
Stickers' design might change from one year to another. For the first year we wanted to try a "craft beer label" look, see examples below:
|
||||
We have one sticker per contribution type. You might get multiple stickers with one contribution though :)
|
||||
|
||||
* Bug hunter: Fix a bug
|
||||
* Translator: Add/update translations
|
||||
@@ -157,4 +159,6 @@ Stickers' design might change from one year to another. For the first year we wa
|
||||
* Beta tester: Test and give feedback on beta releases
|
||||
* Extension developer: Develop and publish an extension
|
||||
|
||||

|
||||
Here is the design of each stickers for year 2022:
|
||||
|
||||

|
||||
|
||||
11
README.md
11
README.md
@@ -3,9 +3,9 @@
|
||||
</a></p>
|
||||
|
||||
|
||||
iTop stands for IT Operations Portal. It is a complete open source and web based IT service management platform including a fully customizable CMDB, a helpdesk system and a document management tool. It is ITIL compliant and easily customizable and extensible thanks to a high number of adds-on and web services to integrate with your IT.
|
||||
iTop stands for IT Operations Portal. It is a complete open source and web-based IT service management platform, including a fully customizable CMDB, a helpdesk system, and a document management tool. It is ITIL compliant and easily customizable and extensible thanks to a high number of add-ons and web services to integrate with your IT.
|
||||
|
||||
iTop also offers mass import tools to help you being even more efficient.
|
||||
iTop also offers mass import tools to help you become even more efficient.
|
||||
|
||||
## Features
|
||||
- Fully configurable [Configuration Management (CMDB)][10]
|
||||
@@ -40,6 +40,7 @@ iTop also offers mass import tools to help you being even more efficient.
|
||||
- [Software requirements][4]
|
||||
- [Documentation][5] covering both iTop and its official extensions
|
||||
- [iTop Hub][6] : discover and install extensions !
|
||||
- [iTop versions history][7]
|
||||
|
||||
|
||||
[1]: https://sourceforge.net/p/itop/discussion/
|
||||
@@ -48,6 +49,7 @@ iTop also offers mass import tools to help you being even more efficient.
|
||||
[4]: https://www.itophub.io/wiki/page?id=latest:install:upgrading_itop
|
||||
[5]: https://www.itophub.io/wiki
|
||||
[6]: https://store.itophub.io/en_US/
|
||||
[7]: .doc/itop-version-history.md
|
||||
|
||||
[10]: https://www.itophub.io/wiki/page?id=latest%3Adatamodel%3Astart#configuration_management_cmdb
|
||||
[11]: https://www.itophub.io/wiki/page?id=latest%3Adatamodel%3Astart#ticketing
|
||||
@@ -63,7 +65,7 @@ iTop also offers mass import tools to help you being even more efficient.
|
||||
|
||||
## About Us
|
||||
|
||||
iTop development is sponsored, led and supported by [Combodo][0].
|
||||
iTop development is sponsored, led, and supported by [Combodo][0].
|
||||
|
||||
[0]: https://www.combodo.com
|
||||
|
||||
@@ -98,8 +100,11 @@ We would like to give a special thank you 🤗 to the people from the community
|
||||
- Lucas, Jonathan
|
||||
- Malik, Remie
|
||||
- Mindêllo de Andrade, Lucas (a.k.a [@rokam](https://www.github.com/rokam))
|
||||
- Mozart de Oliveira, Eduardo (a.k.a [@eduardomozart](https://github.com/eduardomozart))
|
||||
- Raenker, Martin
|
||||
- Roháč, Richard (a.k.a [@RohacRichard](https://github.com/RohacRichard))
|
||||
- Rosenke, Stephan
|
||||
- Rudner, Björn (a.k.a [@rudnerbjoern](https://github.com/rudnerbjoern))
|
||||
- Seki, Shoji
|
||||
- Shilov, Vladimir
|
||||
- Stukalov, Ilya (a.k.a [@ilya](https://www.github.com/ilya)-stukalov)
|
||||
|
||||
12
SECURITY.md
12
SECURITY.md
@@ -18,8 +18,7 @@ to [itop-security@combodo.com](mailto:itop-security@combodo.com).
|
||||
|
||||
|
||||
|
||||
## 📆 Disclosure Policy
|
||||
|
||||
## 🔍 Combodo acknowledgment and investigation
|
||||
Report sent to us will be acknowledged within the week.
|
||||
|
||||
Then, a Combodo developer will be assigned to the reported issue and will:
|
||||
@@ -34,3 +33,12 @@ Then, a Combodo developer will be assigned to the reported issue and will:
|
||||
Security issues always take precedence over bug fixes and feature work.
|
||||
|
||||
The assignee will keep you informed of the resolution progress, and may ask you for additional information or guidance.
|
||||
|
||||
|
||||
## 📆 Disclosure Policy
|
||||
Once the fix is done and acknowledged by every stakeholder, it will be included in the next iTop version.
|
||||
Mind we have at least 2 active branches (LTS and STS, see [iTop Community Releases [iTop Documentation]](https://www.itophub.io/wiki/page?id=latest:release:start))
|
||||
|
||||
The release communications will include the information of the vulnerability fix.
|
||||
|
||||
Corresponding GitHub advisories and CVE will be published 3 months after the iTop version release date so that iTop instances can be updated.
|
||||
|
||||
@@ -121,7 +121,6 @@ class UserRightsMatrix extends UserRightsAddOnAPI
|
||||
public function CreateAdministrator($sAdminUser, $sAdminPwd, $sLanguage = 'EN US')
|
||||
{
|
||||
// Maybe we should check that no other user with userid == 0 exists
|
||||
CMDBObject::SetTrackInfo('Initialization');
|
||||
$oUser = new UserLocal();
|
||||
$oUser->Set('login', $sAdminUser);
|
||||
$oUser->Set('password', $sAdminPwd);
|
||||
|
||||
@@ -124,7 +124,7 @@ class URP_Profiles extends UserRightsBaseClassGUI
|
||||
$bGrant = $oUserRights->GetClassStimulusGrant($this->GetKey(), $sClass, $sStimulusCode);
|
||||
if ($bGrant === true)
|
||||
{
|
||||
$aStimuli[] = '<span title="'.$sStimulusCode.': '.htmlentities($oStimulus->GetDescription(), ENT_QUOTES, 'UTF-8').'">'.htmlentities($oStimulus->GetLabel(), ENT_QUOTES, 'UTF-8').'</span>';
|
||||
$aStimuli[] = '<span title="'.$sStimulusCode.': '.utils::EscapeHtml($oStimulus->GetDescription()).'">'.utils::EscapeHtml($oStimulus->GetLabel()).'</span>';
|
||||
}
|
||||
}
|
||||
$sStimuli = implode(', ', $aStimuli);
|
||||
@@ -435,20 +435,18 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
||||
// Installation: create the very first user
|
||||
public function CreateAdministrator($sAdminUser, $sAdminPwd, $sLanguage = 'EN US')
|
||||
{
|
||||
CMDBObject::SetTrackInfo('Initialization');
|
||||
CMDBObject::SetCurrentChangeFromParams('Initialization create administrator');
|
||||
|
||||
$iContactId = 0;
|
||||
// Support drastic data model changes: no organization class (or not writable)!
|
||||
if (MetaModel::IsValidClass('Organization') && !MetaModel::IsAbstract('Organization'))
|
||||
{
|
||||
if (MetaModel::IsValidClass('Organization') && !MetaModel::IsAbstract('Organization')) {
|
||||
$oOrg = MetaModel::NewObject('Organization');
|
||||
$oOrg->Set('name', 'My Company/Department');
|
||||
$oOrg->Set('code', 'SOMECODE');
|
||||
$iOrgId = $oOrg->DBInsertNoReload();
|
||||
|
||||
// Support drastic data model changes: no Person class (or not writable)!
|
||||
if (MetaModel::IsValidClass('Person') && !MetaModel::IsAbstract('Person'))
|
||||
{
|
||||
if (MetaModel::IsValidClass('Person') && !MetaModel::IsAbstract('Person')) {
|
||||
$oContact = MetaModel::NewObject('Person');
|
||||
$oContact->Set('name', 'My last name');
|
||||
$oContact->Set('first_name', 'My first name');
|
||||
|
||||
@@ -278,8 +278,8 @@ class URP_Profiles extends UserRightsBaseClassGUI
|
||||
{
|
||||
$oGrant = $oUserRights->GetClassStimulusGrant($this->GetKey(), $sClass, $sStimulusCode);
|
||||
if (is_object($oGrant) && ($oGrant->Get('permission') == 'yes'))
|
||||
{
|
||||
$aStimuli[] = '<span title="'.$sStimulusCode.': '.htmlentities($oStimulus->GetDescription(), ENT_QUOTES, 'UTF-8').'">'.htmlentities($oStimulus->GetLabel(), ENT_QUOTES, 'UTF-8').'</span>';
|
||||
{
|
||||
$aStimuli[] = '<span title="'.$sStimulusCode.': '.utils::EscapeHtml($oStimulus->GetDescription()).'">'.utils::EscapeHtml($oStimulus->GetLabel()).'</span>';
|
||||
}
|
||||
}
|
||||
$sStimuli = implode(', ', $aStimuli);
|
||||
@@ -508,24 +508,18 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
||||
public function CreateAdministrator($sAdminUser, $sAdminPwd, $sLanguage = 'EN US')
|
||||
{
|
||||
// Create a change to record the history of the User object
|
||||
/** @var \CMDBChange $oChange */
|
||||
$oChange = MetaModel::NewObject("CMDBChange");
|
||||
$oChange->Set("date", time());
|
||||
$oChange->Set("userinfo", "Initialization");
|
||||
CMDBObject::SetCurrentChangeFromParams('Initialization : create first user admin profile');
|
||||
|
||||
$iContactId = 0;
|
||||
// Support drastic data model changes: no organization class (or not writable)!
|
||||
if (MetaModel::IsValidClass('Organization') && !MetaModel::IsAbstract('Organization'))
|
||||
{
|
||||
if (MetaModel::IsValidClass('Organization') && !MetaModel::IsAbstract('Organization')) {
|
||||
$oOrg = MetaModel::NewObject('Organization');
|
||||
$oOrg->Set('name', 'My Company/Department');
|
||||
$oOrg->Set('code', 'SOMECODE');
|
||||
$oOrg::SetCurrentChange($oChange);
|
||||
$iOrgId = $oOrg->DBInsertNoReload();
|
||||
|
||||
// Support drastic data model changes: no Person class (or not writable)!
|
||||
if (MetaModel::IsValidClass('Person') && !MetaModel::IsAbstract('Person'))
|
||||
{
|
||||
if (MetaModel::IsValidClass('Person') && !MetaModel::IsAbstract('Person')) {
|
||||
$oContact = MetaModel::NewObject('Person');
|
||||
$oContact->Set('name', 'My last name');
|
||||
$oContact->Set('first_name', 'My first name');
|
||||
@@ -534,7 +528,6 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
||||
$oContact->Set('org_id', $iOrgId);
|
||||
}
|
||||
$oContact->Set('email', 'my.email@foo.org');
|
||||
$oContact::SetCurrentChange($oChange);
|
||||
$iContactId = $oContact->DBInsertNoReload();
|
||||
}
|
||||
}
|
||||
@@ -543,24 +536,22 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
||||
$oUser = new UserLocal();
|
||||
$oUser->Set('login', $sAdminUser);
|
||||
$oUser->Set('password', $sAdminPwd);
|
||||
if (MetaModel::IsValidAttCode('UserLocal', 'contactid') && ($iContactId != 0))
|
||||
{
|
||||
if (MetaModel::IsValidAttCode('UserLocal', 'contactid') && ($iContactId != 0)) {
|
||||
$oUser->Set('contactid', $iContactId);
|
||||
}
|
||||
$oUser->Set('language', $sLanguage); // Language was chosen during the installation
|
||||
|
||||
// Add this user to the very specific 'admin' profile
|
||||
$oAdminProfile = MetaModel::GetObjectFromOQL("SELECT URP_Profiles WHERE name = :name", array('name' => ADMIN_PROFILE_NAME), true /*all data*/);
|
||||
if (is_object($oAdminProfile))
|
||||
{
|
||||
if (is_object($oAdminProfile)) {
|
||||
$oUserProfile = new URP_UserProfile();
|
||||
$oUserProfile->Set('profileid', $oAdminProfile->GetKey());
|
||||
$oUserProfile->Set('reason', 'By definition, the administrator must have the administrator profile');
|
||||
$oSet = DBObjectSet::FromObject($oUserProfile);
|
||||
$oUser->Set('profile_list', $oSet);
|
||||
}
|
||||
$oUser::SetCurrentChange($oChange);
|
||||
$iUserId = $oUser->DBInsertNoReload();
|
||||
$oUser->DBInsertNoReload();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -110,8 +110,8 @@ class URP_Profiles extends UserRightsBaseClass
|
||||
{
|
||||
$oGrant = $oUserRights->GetClassStimulusGrant($this->GetKey(), $sClass, $sStimulusCode);
|
||||
if (is_object($oGrant) && ($oGrant->Get('permission') == 'yes'))
|
||||
{
|
||||
$aStimuli[] = '<span title="'.$sStimulusCode.': '.htmlentities($oStimulus->GetDescription(), ENT_QUOTES, 'UTF-8').'">'.htmlentities($oStimulus->GetLabel(), ENT_QUOTES, 'UTF-8').'</span>';
|
||||
{
|
||||
$aStimuli[] = '<span title="'.$sStimulusCode.': '.utils::EscapeHtml($oStimulus->GetDescription()).'">'.utils::EscapeHtml($oStimulus->GetLabel()).'</span>';
|
||||
}
|
||||
}
|
||||
$sStimuli = implode(', ', $aStimuli);
|
||||
@@ -568,14 +568,11 @@ class UserRightsProjection extends UserRightsAddOnAPI
|
||||
public function CreateAdministrator($sAdminUser, $sAdminPwd, $sLanguage = 'EN US')
|
||||
{
|
||||
// Create a change to record the history of the User object
|
||||
$oChange = MetaModel::NewObject("CMDBChange");
|
||||
$oChange->Set("date", time());
|
||||
$oChange->Set("userinfo", "Initialization");
|
||||
CMDBObject::SetCurrentChangeFromParams('Initialization : create first user admin');
|
||||
|
||||
$oOrg = new Organization();
|
||||
$oOrg->Set('name', 'My Company/Department');
|
||||
$oOrg->Set('code', 'SOMECODE');
|
||||
$oOrg::SetCurrentChange($oChange);
|
||||
$iOrgId = $oOrg->DBInsertNoReload();
|
||||
|
||||
$oContact = new Person();
|
||||
@@ -584,7 +581,6 @@ class UserRightsProjection extends UserRightsAddOnAPI
|
||||
//$oContact->Set('status', 'available');
|
||||
$oContact->Set('org_id', $iOrgId);
|
||||
$oContact->Set('email', 'my.email@foo.org');
|
||||
$oContact::SetCurrentChange($oChange);
|
||||
$iContactId = $oContact->DBInsertNoReload();
|
||||
|
||||
$oUser = new UserLocal();
|
||||
@@ -592,7 +588,6 @@ class UserRightsProjection extends UserRightsAddOnAPI
|
||||
$oUser->Set('password', $sAdminPwd);
|
||||
$oUser->Set('contactid', $iContactId);
|
||||
$oUser->Set('language', $sLanguage); // Language was chosen during the installation
|
||||
$oUser::SetCurrentChange($oChange);
|
||||
$iUserId = $oUser->DBInsertNoReload();
|
||||
|
||||
// Add this user to the very specific 'admin' profile
|
||||
@@ -600,7 +595,6 @@ class UserRightsProjection extends UserRightsAddOnAPI
|
||||
$oUserProfile->Set('userid', $iUserId);
|
||||
$oUserProfile->Set('profileid', ADMIN_PROFILE_ID);
|
||||
$oUserProfile->Set('reason', 'By definition, the administrator must have the administrator profile');
|
||||
$oUserProfile::SetCurrentChange($oChange);
|
||||
$oUserProfile->DBInsertNoReload();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/AjaxPage.php, now loadable using autoloader
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/AjaxPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
*/
|
||||
|
||||
// cannot notify depreciation for now as this is still MASSIVELY used in iTop core !
|
||||
//DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/AjaxPage.php, now loadable using autoloader');
|
||||
// cannot notify depreciation for now as this is still load in autoloader
|
||||
//DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/AjaxPage.php, now loadable using autoloader');
|
||||
|
||||
/**
|
||||
* Class ajax_page
|
||||
*
|
||||
* @deprecated will be removed in 3.1.0 - moved to AjaxPage
|
||||
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to AjaxPage
|
||||
*/
|
||||
class ajax_page extends AjaxPage
|
||||
{
|
||||
|
||||
function __construct($s_title)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('ajax_page is deprecated. Please use AjaxPage instead');
|
||||
parent::__construct($s_title);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ require_once(APPROOT.'/application/applicationcontext.class.inc.php');
|
||||
require_once(APPROOT.'/application/cmdbabstract.class.inc.php');
|
||||
require_once(APPROOT.'/application/displayblock.class.inc.php');
|
||||
require_once(APPROOT.'/application/audit.category.class.inc.php');
|
||||
require_once(APPROOT.'/application/audit.domain.class.inc.php');
|
||||
require_once(APPROOT.'/application/audit.rule.class.inc.php');
|
||||
require_once(APPROOT.'/application/query.class.inc.php');
|
||||
require_once(APPROOT.'/setup/moduleinstallation.class.inc.php');
|
||||
|
||||
@@ -224,7 +224,7 @@ class ApplicationContext
|
||||
{
|
||||
$sContext = "";
|
||||
foreach ($this->aValues as $sName => $sValue) {
|
||||
$sContext .= "<input type=\"hidden\" name=\"c[$sName]\" value=\"".htmlentities($sValue, ENT_QUOTES, 'UTF-8')."\" />\n";
|
||||
$sContext .= "<input type=\"hidden\" name=\"c[$sName]\" value=\"".utils::EscapeHtml($sValue)."\" />\n";
|
||||
}
|
||||
return $sContext;
|
||||
}
|
||||
@@ -238,7 +238,7 @@ class ApplicationContext
|
||||
{
|
||||
$aContextInputBlocks = [];
|
||||
foreach ($this->aValues as $sName => $sValue) {
|
||||
$aContextInputBlocks[] = InputUIBlockFactory::MakeForHidden("c[$sName]", htmlentities($sValue, ENT_QUOTES, 'UTF-8'));
|
||||
$aContextInputBlocks[] = InputUIBlockFactory::MakeForHidden("c[$sName]", utils::EscapeHtml($sValue));
|
||||
}
|
||||
return $aContextInputBlocks;
|
||||
}
|
||||
|
||||
@@ -29,19 +29,22 @@ require_once(APPROOT.'application/newsroomprovider.class.inc.php');
|
||||
* Definition of interfaces that can be implemented to customize iTop.
|
||||
* You may implement such interfaces in a module file (e.g. main.mymodule.php)
|
||||
*
|
||||
* @api
|
||||
* @package LoginExtensibilityAPI
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @since 2.7.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @api
|
||||
* @package LoginExtensibilityAPI
|
||||
* @since 2.7.0
|
||||
*/
|
||||
interface iLoginExtension
|
||||
{
|
||||
/**
|
||||
* Return the list of supported login modes for this plugin
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @return array of supported login modes
|
||||
*/
|
||||
public function ListSupportedLoginModes();
|
||||
@@ -71,8 +74,17 @@ interface iLoginFSMExtension extends iLoginExtension
|
||||
}
|
||||
|
||||
/**
|
||||
* Login finite state machine
|
||||
*
|
||||
* Execute the action corresponding to the current login state.
|
||||
*
|
||||
* * 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
|
||||
*
|
||||
* @api
|
||||
* @package LoginExtensibilityAPI
|
||||
* @package LoginExtensibilityAPI
|
||||
* @since 2.7.0
|
||||
*/
|
||||
abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
|
||||
@@ -148,7 +160,7 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
|
||||
* This step can be called multiple times by the FSM:
|
||||
* for example:
|
||||
* 1 - display login form
|
||||
* 2 - read the values posted by the user
|
||||
* 2 - read the values posted by the user (store that in session)
|
||||
*
|
||||
* @api
|
||||
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
|
||||
@@ -161,7 +173,7 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
|
||||
}
|
||||
|
||||
/**
|
||||
* Control the validity of the data provided by the user
|
||||
* Control the validity of the data from the session
|
||||
* Automatic user provisioning can be done here
|
||||
*
|
||||
* @api
|
||||
@@ -221,7 +233,7 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
|
||||
|
||||
/**
|
||||
* @api
|
||||
* @package LoginExtensibilityAPI
|
||||
* @package LoginExtensibilityAPI
|
||||
* @since 2.7.0
|
||||
*/
|
||||
interface iLogoutExtension extends iLoginExtension
|
||||
@@ -234,8 +246,10 @@ interface iLogoutExtension extends iLoginExtension
|
||||
}
|
||||
|
||||
/**
|
||||
* Login page extensibility
|
||||
*
|
||||
* @api
|
||||
* @package UIExtensibilityAPI
|
||||
* @package UIExtensibilityAPI
|
||||
* @since 2.7.0
|
||||
*/
|
||||
interface iLoginUIExtension extends iLoginExtension
|
||||
@@ -322,6 +336,7 @@ abstract class AbstractPreferencesExtension implements iPreferencesExtension
|
||||
*
|
||||
* @api
|
||||
* @package UIExtensibilityAPI
|
||||
* @deprecated
|
||||
*/
|
||||
interface iApplicationUIExtension
|
||||
{
|
||||
@@ -472,6 +487,7 @@ interface iApplicationUIExtension
|
||||
* @api
|
||||
* @package UIExtensibilityAPI
|
||||
* @since 2.7.0
|
||||
* @deprecated
|
||||
*/
|
||||
abstract class AbstractApplicationUIExtension implements iApplicationUIExtension
|
||||
{
|
||||
@@ -538,7 +554,7 @@ abstract class AbstractApplicationUIExtension implements iApplicationUIExtension
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this interface to perform specific things when objects are manipulated
|
||||
* Implement this interface to perform specific operations when objects are manipulated
|
||||
*
|
||||
* Note that those methods will be called when objects are manipulated, either in a programmatic way
|
||||
* or through the GUI.
|
||||
@@ -831,7 +847,6 @@ abstract class ApplicationPopupMenuItem
|
||||
* Constructor
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @param string $sUID The unique identifier of this menu in iTop... make sure you pass something unique enough
|
||||
* @param string $sLabel The display label of the menu (must be localized)
|
||||
*/
|
||||
@@ -901,6 +916,7 @@ abstract class ApplicationPopupMenuItem
|
||||
/**
|
||||
* @param $sTooltip
|
||||
*
|
||||
* @api
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function SetTooltip($sTooltip)
|
||||
@@ -911,6 +927,7 @@ abstract class ApplicationPopupMenuItem
|
||||
/**
|
||||
* @return string
|
||||
*
|
||||
* @api
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function GetTooltip()
|
||||
@@ -921,6 +938,7 @@ abstract class ApplicationPopupMenuItem
|
||||
/**
|
||||
* @param $sIconClass
|
||||
*
|
||||
* @api
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function SetIconClass($sIconClass)
|
||||
@@ -931,6 +949,7 @@ abstract class ApplicationPopupMenuItem
|
||||
/**
|
||||
* @return string
|
||||
*
|
||||
* @api
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function GetIconClass()
|
||||
@@ -1032,7 +1051,6 @@ class JSPopupMenuItem extends ApplicationPopupMenuItem
|
||||
* Class for adding an item that triggers some Javascript code
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @param string $sUID The unique identifier of this menu in iTop... make sure you pass something unique enough
|
||||
* @param string $sLabel The display label of the menu (must be localized)
|
||||
* @param string $sJSCode In case the menu consists in executing some havascript code inside the page, pass it here. If supplied $sURL
|
||||
@@ -1203,7 +1221,7 @@ interface iPageUIExtension
|
||||
* the specified place and can use the passed iTopWebPage object to add javascript or CSS definitions
|
||||
*
|
||||
* @api
|
||||
* @package UIBlockExtensibilityAPI
|
||||
* @package BackofficeUIExtensibilityAPI
|
||||
* @since 3.0.0
|
||||
*/
|
||||
interface iPageUIBlockExtension
|
||||
@@ -1211,6 +1229,7 @@ interface iPageUIBlockExtension
|
||||
/**
|
||||
* Add content to the "admin banner"
|
||||
*
|
||||
* @api
|
||||
* @return iUIBlock|null The Block to add into the page
|
||||
*/
|
||||
public function GetBannerBlock();
|
||||
@@ -1218,6 +1237,7 @@ interface iPageUIBlockExtension
|
||||
/**
|
||||
* Add content to the header of the page
|
||||
*
|
||||
* @api
|
||||
* @return iUIBlock|null The Block to add into the page
|
||||
*/
|
||||
public function GetHeaderBlock();
|
||||
@@ -1225,6 +1245,7 @@ interface iPageUIBlockExtension
|
||||
/**
|
||||
* Add content to the footer of the page
|
||||
*
|
||||
* @api
|
||||
* @return iUIBlock|null The Block to add into the page
|
||||
*/
|
||||
public function GetFooterBlock();
|
||||
@@ -1236,7 +1257,7 @@ interface iPageUIBlockExtension
|
||||
* @api
|
||||
* @package UIExtensibilityAPI
|
||||
* @since 2.7.0
|
||||
* @deprecated since 3.0.0 use AbstractPageUIBlockExtension instead
|
||||
* @deprecated 3.0.0 use AbstractPageUIBlockExtension instead
|
||||
*/
|
||||
abstract class AbstractPageUIExtension implements iPageUIExtension
|
||||
{
|
||||
@@ -1317,7 +1338,9 @@ abstract class AbstractPageUIBlockExtension implements iPageUIBlockExtension
|
||||
interface iBackofficeLinkedScriptsExtension
|
||||
{
|
||||
/**
|
||||
* @see \iTopWebPage::$a_linked_scripts Each script will be included using this property
|
||||
* Each script will be included using this property
|
||||
* @api
|
||||
* @see \iTopWebPage::$a_linked_scripts
|
||||
* @return array An array of absolute URLs to the files to include
|
||||
*/
|
||||
public function GetLinkedScriptsAbsUrls(): array;
|
||||
@@ -1335,6 +1358,7 @@ interface iBackofficeLinkedScriptsExtension
|
||||
interface iBackofficeEarlyScriptExtension
|
||||
{
|
||||
/**
|
||||
* @api
|
||||
* @see \iTopWebPage::$a_early_scripts
|
||||
* @return string
|
||||
*/
|
||||
@@ -1352,6 +1376,7 @@ interface iBackofficeEarlyScriptExtension
|
||||
interface iBackofficeScriptExtension
|
||||
{
|
||||
/**
|
||||
* @api
|
||||
* @see \iTopWebPage::$a_scripts
|
||||
* @return string
|
||||
*/
|
||||
@@ -1369,6 +1394,7 @@ interface iBackofficeScriptExtension
|
||||
interface iBackofficeInitScriptExtension
|
||||
{
|
||||
/**
|
||||
* @api
|
||||
* @see \iTopWebPage::$a_init_scripts
|
||||
* @return string
|
||||
*/
|
||||
@@ -1386,6 +1412,7 @@ interface iBackofficeInitScriptExtension
|
||||
interface iBackofficeReadyScriptExtension
|
||||
{
|
||||
/**
|
||||
* @api
|
||||
* @see \iTopWebPage::$a_ready_scripts
|
||||
* @return string
|
||||
*/
|
||||
@@ -1403,6 +1430,7 @@ interface iBackofficeReadyScriptExtension
|
||||
interface iBackofficeLinkedStylesheetsExtension
|
||||
{
|
||||
/**
|
||||
* @api
|
||||
* @see \iTopWebPage::$a_linked_stylesheets
|
||||
* @return array An array of absolute URLs to the files to include
|
||||
*/
|
||||
@@ -1420,6 +1448,7 @@ interface iBackofficeLinkedStylesheetsExtension
|
||||
interface iBackofficeStyleExtension
|
||||
{
|
||||
/**
|
||||
* @api
|
||||
* @see \iTopWebPage::$a_styles
|
||||
* @return string
|
||||
*/
|
||||
@@ -1437,6 +1466,7 @@ interface iBackofficeStyleExtension
|
||||
interface iBackofficeDictEntriesExtension
|
||||
{
|
||||
/**
|
||||
* @api
|
||||
* @see \iTopWebPage::a_dict_entries
|
||||
* @return array
|
||||
*/
|
||||
@@ -1454,6 +1484,7 @@ interface iBackofficeDictEntriesExtension
|
||||
interface iBackofficeDictEntriesPrefixesExtension
|
||||
{
|
||||
/**
|
||||
* @api
|
||||
* @see \iTopWebPage::a_dict_entries_prefixes
|
||||
* @return array
|
||||
*/
|
||||
@@ -1648,7 +1679,7 @@ interface iRestServiceProvider
|
||||
* Minimal REST response structure. Derive this structure to add response data and error codes.
|
||||
*
|
||||
* @api
|
||||
* @package RESTExtensibilityAPI
|
||||
* @package RESTAPI
|
||||
* @since 2.0.1
|
||||
*/
|
||||
class RestResult
|
||||
@@ -1724,11 +1755,13 @@ class RestResult
|
||||
}
|
||||
|
||||
/**
|
||||
* Result code
|
||||
* @var int
|
||||
* @api
|
||||
*/
|
||||
public $code;
|
||||
/**
|
||||
* Result message
|
||||
* @var string
|
||||
* @api
|
||||
*/
|
||||
@@ -1739,7 +1772,7 @@ class RestResult
|
||||
* Helpers for implementing REST services
|
||||
*
|
||||
* @api
|
||||
* @package RESTExtensibilityAPI
|
||||
* @package RESTAPI
|
||||
*/
|
||||
class RestUtils
|
||||
{
|
||||
@@ -1764,9 +1797,7 @@ class RestUtils
|
||||
* Read a mandatory parameter from from a Rest/Json structure.
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @param string $sParamName Name of the parameter to fetch from the input data
|
||||
*
|
||||
* @param StdClass $oData Structured input data. Must contain the entry defined by sParamName.
|
||||
*
|
||||
* @return mixed parameter value if present
|
||||
@@ -1786,10 +1817,9 @@ class RestUtils
|
||||
|
||||
|
||||
/**
|
||||
* Read an optional parameter from from a Rest/Json structure.
|
||||
* Read an optional parameter from a Rest/Json structure.
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @param string $sParamName Name of the parameter to fetch from the input data
|
||||
* @param mixed $default Default value if the parameter is not found in the input data
|
||||
*
|
||||
@@ -1812,12 +1842,10 @@ class RestUtils
|
||||
|
||||
|
||||
/**
|
||||
* Read a class from a Rest/Json structure.
|
||||
* Read a class from a Rest/Json structure.
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @param string $sParamName Name of the parameter to fetch from the input data
|
||||
*
|
||||
* @param StdClass $oData Structured input data. Must contain the entry defined by sParamName.
|
||||
*
|
||||
* @return string
|
||||
@@ -1839,7 +1867,6 @@ class RestUtils
|
||||
* Read a list of attribute codes from a Rest/Json structure.
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @param StdClass $oData Structured input data.
|
||||
* @param string $sParamName Name of the parameter to fetch from the input data
|
||||
*
|
||||
@@ -1942,7 +1969,6 @@ class RestUtils
|
||||
* Find an object from a polymorph search specification (Rest/Json)
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @param mixed $key Either search criteria (substructure), or an object or an OQL string.
|
||||
* @param bool $bAllowNullValue Allow the cases such as key = 0 or key = {null} and return null then
|
||||
* @param string $sClass Name of the class
|
||||
@@ -2050,7 +2076,6 @@ class RestUtils
|
||||
* Interpret the Rest/Json value and get a valid attribute value
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @param string $sAttCode Attribute code
|
||||
* @param mixed $value Depending on the type of attribute (a scalar, or search criteria, or list of related objects...)
|
||||
* @param string $sClass Name of the class
|
||||
@@ -2117,7 +2142,6 @@ class RestUtils
|
||||
* Interpret a Rest/Json structure that defines attribute values, and build an object
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @param array $aFields A hash of attribute code => value specification.
|
||||
* @param string $sClass Name of the class
|
||||
*
|
||||
@@ -2147,7 +2171,6 @@ class RestUtils
|
||||
* Interpret a Rest/Json structure that defines attribute values, and update the given object
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @param array $aFields A hash of attribute code => value specification.
|
||||
* @param DBObject $oObject The object being modified
|
||||
*
|
||||
@@ -2184,5 +2207,8 @@ class RestUtils
|
||||
*/
|
||||
interface iModuleExtension
|
||||
{
|
||||
/**
|
||||
* @api
|
||||
*/
|
||||
public function __construct();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* This class manages the audit "categories". Each category defines a set of objects
|
||||
* to check and is linked to a set of rules that determine the valid or invalid objects
|
||||
* inside the set
|
||||
* inside the set
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
@@ -48,13 +48,39 @@ class AuditCategory extends cmdbAbstractObject
|
||||
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeOQL("definition_set", array("allowed_values"=>null, "sql"=>"definition_set", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeLinkedSet("rules_list", array("linked_class"=>"AuditRule", "ext_key_to_me"=>"category_id", "allowed_values"=>null, "count_min"=>0, "count_max"=>0, "depends_on"=>array(), "edit_mode" => LINKSET_EDITMODE_INPLACE, "tracking_level" => LINKSET_TRACKING_ALL)));
|
||||
MetaModel::Init_AddAttribute(new AttributeInteger("ok_error_tolerance", array("allowed_values"=>null, "sql"=>"ok_error_tolerance", "default_value"=>5, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeInteger("warning_error_tolerance", array("allowed_values"=>null, "sql"=>"warning_error_tolerance", "default_value"=>25, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("domains_list", array("linked_class" => "lnkAuditCategoryToAuditDomain", "ext_key_to_me" => "category_id", "ext_key_to_remote" => "domain_id", "allowed_values" => null, "count_min" => 0, "count_max" => 0, "depends_on" => array())));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('name', 'description', 'definition_set', 'rules_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('details', array('name', 'description', 'definition_set', 'ok_error_tolerance', 'warning_error_tolerance', 'rules_list', 'domains_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('description', )); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
MetaModel::Init_SetZListItems('standard_search', array('description', 'definition_set')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('default_search', array('name', 'description')); // Criteria of the default search form
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $iTotal
|
||||
* @param int $iErrors
|
||||
*
|
||||
* @return string A semantic color name (eg. red, green, orange, success, failure, ... {@see css/backoffice/utils/variables/colors/_semantic-palette.scss}) to use for this category depending on its error count and tolerance
|
||||
* @throws \CoreException
|
||||
*
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public function GetReportColor($iTotal, $iErrors)
|
||||
{
|
||||
$sResult = 'red';
|
||||
if ( ($iTotal == 0) || ($iErrors / $iTotal) <= ($this->Get('ok_error_tolerance') / 100) )
|
||||
{
|
||||
$sResult = 'green';
|
||||
}
|
||||
else if ( ($iErrors / $iTotal) <= ($this->Get('warning_error_tolerance') / 100) )
|
||||
{
|
||||
$sResult = 'orange';
|
||||
}
|
||||
return $sResult;
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
99
application/audit.domain.class.inc.php
Normal file
99
application/audit.domain.class.inc.php
Normal file
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2021 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* This class manages the audit "categories". Each category defines a set of objects
|
||||
* to check and is linked to a set of rules that determine the valid or invalid objects
|
||||
* inside the set
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @since 3.1.0
|
||||
*/
|
||||
class AuditDomain extends cmdbAbstractObject
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "application, grant_by_profile",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array('name'),
|
||||
"db_table" => "priv_auditdomain",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_AddAttribute(new AttributeString("name", array("description"=>"Short name for this category", "allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeImage("icon", array("is_null_allowed"=>true, "depends_on"=>array(), "display_max_width"=>96, "display_max_height"=>96, "storage_max_width"=>256, "storage_max_height"=>256, "default_image"=>null, "always_load_in_tables"=>false)));
|
||||
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("categories_list", array("linked_class" => "lnkAuditCategoryToAuditDomain", "ext_key_to_me" => "domain_id", "ext_key_to_remote" => "category_id", "allowed_values" => null, "count_min" => 0, "count_max" => 0, "depends_on" => array())));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('name', 'description', 'icon', 'categories_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('description', )); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
MetaModel::Init_SetZListItems('standard_search', array('description')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('default_search', array('name', 'description')); // Criteria of the default search form
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.1.0
|
||||
*/
|
||||
class lnkAuditCategoryToAuditDomain extends cmdbAbstractObject
|
||||
{
|
||||
/**
|
||||
* @throws \CoreException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "application, grant_by_profile",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array('category_id', 'domain_id'),
|
||||
"db_table" => "priv_link_audit_category_domain",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"is_link" => true,
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("category_id", array("targetclass" => "AuditCategory", "jointype" => '', "allowed_values" => null, "sql" => "category_id", "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("category_name", array("allowed_values" => null, "extkey_attcode" => 'category_id', "target_attcode" => "name")));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("domain_id", array("targetclass" => "AuditDomain", "jointype" => '', "allowed_values" => null, "sql" => "domain_id", "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("domain_name", array("allowed_values" => null, "extkey_attcode" => 'domain_id', "target_attcode" => "name")));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('category_id', 'domain_id'));
|
||||
MetaModel::Init_SetZListItems('list', array('category_id', 'domain_id'));
|
||||
// Search criteria
|
||||
MetaModel::Init_SetZListItems('standard_search', array('category_id', 'domain_id'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/CaptureWebPage.php, now loadable using autoloader
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/CaptureWebPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
*/
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/CaptureWebPage.php, now loadable using autoloader');
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/CaptureWebPage.php, now loadable using autoloader');
|
||||
@@ -1,8 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/CLIPage.php, now loadable using autoloader
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/CLIPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
*/
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/CLIPage.php, now loadable using autoloader');
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/CLIPage.php, now loadable using autoloader');
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/CSVPage.php, now loadable using autoloader
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/CSVPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
*/
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/CSVPage.php, now loadable using autoloader');
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/CSVPage.php, now loadable using autoloader');
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.0">
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.1">
|
||||
<classes>
|
||||
<class id="AbstractResource" _delta="define">
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
@@ -55,9 +55,9 @@
|
||||
<menus>
|
||||
<menu id="WelcomeMenu" xsi:type="MenuGroup" _delta="define">
|
||||
<rank>10</rank>
|
||||
<style>
|
||||
<decoration_classes>fas fa-home</decoration_classes>
|
||||
</style>
|
||||
<style>
|
||||
<decoration_classes>fas fa-home</decoration_classes>
|
||||
</style>
|
||||
</menu>
|
||||
<menu id="WelcomeMenuPage" xsi:type="DashboardMenuNode" _delta="define">
|
||||
<rank>10</rank>
|
||||
@@ -151,9 +151,9 @@
|
||||
</menu>
|
||||
<menu id="ConfigurationTools" xsi:type="MenuGroup" _delta="define_if_not_exists">
|
||||
<rank>90</rank>
|
||||
<style>
|
||||
<decoration_classes>fas fa-cog</decoration_classes>
|
||||
</style>
|
||||
<style>
|
||||
<decoration_classes>fas fa-cog</decoration_classes>
|
||||
</style>
|
||||
</menu>
|
||||
<menu id="DataSources" xsi:type="OQLMenuNode" _delta="define">
|
||||
<rank>20</rank>
|
||||
@@ -172,19 +172,356 @@
|
||||
</menu>
|
||||
<menu id="AdminTools" xsi:type="MenuGroup" _delta="define">
|
||||
<rank>80</rank>
|
||||
<style>
|
||||
<decoration_classes>fas fa-tools</decoration_classes>
|
||||
</style>
|
||||
<style>
|
||||
<decoration_classes>fas fa-tools</decoration_classes>
|
||||
</style>
|
||||
</menu>
|
||||
<menu id="SystemTools" xsi:type="MenuGroup" _delta="define">
|
||||
<rank>100</rank>
|
||||
<enable_class>ResourceSystemMenu</enable_class>
|
||||
<enable_action>UR_ACTION_MODIFY</enable_action>
|
||||
<style>
|
||||
<decoration_classes>fas fa-terminal</decoration_classes>
|
||||
</style>
|
||||
<style>
|
||||
<decoration_classes>fas fa-terminal</decoration_classes>
|
||||
</style>
|
||||
</menu>
|
||||
</menus>
|
||||
<events>
|
||||
<event id="EVENT_DB_CHECK_TO_WRITE" _delta="define">
|
||||
<description>Check an object before it is written into the database (no change possible). Call DBObject::AddCheckIssue() to signal an issue</description>
|
||||
<sources>
|
||||
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
|
||||
</sources>
|
||||
<replaces>cmdbAbstractObject::DoCheckToWrite</replaces>
|
||||
<event_data>
|
||||
<event_datum id="object">
|
||||
<description>The object to check</description>
|
||||
<type>DBObject</type>
|
||||
</event_datum>
|
||||
<event_datum id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
</event_data>
|
||||
</event>
|
||||
<event id="EVENT_DB_CREATE_DONE" _delta="define">
|
||||
<description>An object has been created into the database. The modifications can be propagated to other objects.</description>
|
||||
<sources>
|
||||
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
|
||||
</sources>
|
||||
<replaces>DBObject::AfterInsert</replaces>
|
||||
<event_data>
|
||||
<event_datum id="object">
|
||||
<description>The object inserted</description>
|
||||
<type>DBObject</type>
|
||||
</event_datum>
|
||||
<event_datum id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
</event_data>
|
||||
</event>
|
||||
<event id="EVENT_DB_UPDATE_DONE" _delta="define">
|
||||
<description>An object has been updated into the database and reloaded.</description>
|
||||
<sources>
|
||||
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
|
||||
</sources>
|
||||
<replaces>DBObject::AfterUpdate</replaces>
|
||||
<event_data>
|
||||
<event_datum id="object">
|
||||
<description>The object updated</description>
|
||||
<type>DBObject</type>
|
||||
</event_datum>
|
||||
<event_datum id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
</event_data>
|
||||
</event>
|
||||
<event id="EVENT_DB_CHECK_TO_DELETE" _delta="define">
|
||||
<description>Check an object before it is deleted from the database. Call DBObject::AddDeleteIssue() to signal an issue</description>
|
||||
<sources>
|
||||
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
|
||||
</sources>
|
||||
<replaces>cmdbAbstractObject::DoCheckToDelete</replaces>
|
||||
<event_data>
|
||||
<event_datum id="object">
|
||||
<description>The object to check</description>
|
||||
<type>DBObject</type>
|
||||
</event_datum>
|
||||
<event_datum id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
</event_data>
|
||||
</event>
|
||||
<event id="EVENT_DB_DELETE_DONE" _delta="define">
|
||||
<description>An object has been deleted into the database</description>
|
||||
<sources>
|
||||
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
|
||||
</sources>
|
||||
<replaces>DBObject::AfterDelete</replaces>
|
||||
<event_data>
|
||||
<event_datum id="object">
|
||||
<description>The object deleted</description>
|
||||
<type>DBObject</type>
|
||||
</event_datum>
|
||||
<event_datum id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
</event_data>
|
||||
</event>
|
||||
<event id="EVENT_DB_BEFORE_APPLY_STIMULUS" _delta="define">
|
||||
<description>A stimulus is about to be applied to an object</description>
|
||||
<sources>
|
||||
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
|
||||
</sources>
|
||||
<event_data>
|
||||
<event_datum id="object">
|
||||
<description>The object where the stimulus is targeted</description>
|
||||
<type>DBObject</type>
|
||||
</event_datum>
|
||||
<event_datum id="stimulus">
|
||||
<description>Current stimulus applied</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
<event_datum id="previous_state">
|
||||
<description>Object previous state</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
<event_datum id="new_state">
|
||||
<description>Object new state</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
<event_datum id="save_object">
|
||||
<description>The object must be saved in the database</description>
|
||||
<type>boolean</type>
|
||||
</event_datum>
|
||||
<event_datum id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
</event_data>
|
||||
</event>
|
||||
<event id="EVENT_DB_AFTER_APPLY_STIMULUS" _delta="define">
|
||||
<description>A stimulus has been applied to an object</description>
|
||||
<sources>
|
||||
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
|
||||
</sources>
|
||||
<event_data>
|
||||
<event_datum id="object">
|
||||
<description>The object where the stimulus is targeted</description>
|
||||
<type>DBObject</type>
|
||||
</event_datum>
|
||||
<event_datum id="stimulus">
|
||||
<description>Current stimulus applied</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
<event_datum id="previous_state">
|
||||
<description>Object previous state</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
<event_datum id="new_state">
|
||||
<description>Object new state</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
<event_datum id="save_object">
|
||||
<description>The object is asked to be saved in the database</description>
|
||||
<type>boolean</type>
|
||||
</event_datum>
|
||||
<event_datum id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
</event_data>
|
||||
</event>
|
||||
<event id="EVENT_DB_APPLY_STIMULUS_FAILED" _delta="define">
|
||||
<description>A stimulus has failed</description>
|
||||
<sources>
|
||||
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
|
||||
</sources>
|
||||
<event_data>
|
||||
<event_datum id="action">
|
||||
<description>The action that failed to apply the stimulus</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
<event_datum id="object">
|
||||
<description>The object where the stimulus is targeted</description>
|
||||
<type>DBObject</type>
|
||||
</event_datum>
|
||||
<event_datum id="stimulus">
|
||||
<description>Current stimulus applied</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
<event_datum id="previous_state">
|
||||
<description>Object previous state</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
<event_datum id="new_state">
|
||||
<description>Object new state</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
<event_datum id="save_object">
|
||||
<description>The object must be saved in the database</description>
|
||||
<type>boolean</type>
|
||||
</event_datum>
|
||||
<event_datum id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
</event_data>
|
||||
</event>
|
||||
<event id="EVENT_DB_LINKS_CHANGED" _delta="define">
|
||||
<description>At least one link class was changed</description>
|
||||
<sources>
|
||||
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
|
||||
</sources>
|
||||
<event_data>
|
||||
<event_datum id="object">
|
||||
<description>The object where the link is or was pointing to</description>
|
||||
<type>DBObject</type>
|
||||
</event_datum>
|
||||
<event_datum id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
</event_data>
|
||||
</event>
|
||||
<event id="EVENT_DB_OBJECT_RELOAD" _delta="define">
|
||||
<description>An object has been re-loaded from the database</description>
|
||||
<sources>
|
||||
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
|
||||
</sources>
|
||||
<event_data>
|
||||
<event_datum id="object">
|
||||
<description>The object re-loaded</description>
|
||||
<type>DBObject</type>
|
||||
</event_datum>
|
||||
<event_datum id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
</event_data>
|
||||
</event>
|
||||
<event id="EVENT_DB_COMPUTE_VALUES" _delta="define">
|
||||
<description>An object needs to be recomputed after changes</description>
|
||||
<sources>
|
||||
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
|
||||
</sources>
|
||||
<replaces>DBObject::ComputeValues</replaces>
|
||||
<event_data>
|
||||
<event_datum id="object">
|
||||
<description>The object inserted</description>
|
||||
<type>DBObject</type>
|
||||
</event_datum>
|
||||
<event_datum id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
</event_data>
|
||||
</event>
|
||||
<event id="EVENT_DB_ARCHIVE" _delta="define">
|
||||
<description>An object has been archived</description>
|
||||
<sources>
|
||||
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
|
||||
</sources>
|
||||
<event_data>
|
||||
<event_datum id="object">
|
||||
<description>The object archived</description>
|
||||
<type>DBObject</type>
|
||||
</event_datum>
|
||||
<event_datum id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
</event_data>
|
||||
</event>
|
||||
<event id="EVENT_DB_UNARCHIVE" _delta="define">
|
||||
<description>An object has been unarchived</description>
|
||||
<sources>
|
||||
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
|
||||
</sources>
|
||||
<event_data>
|
||||
<event_datum id="object">
|
||||
<description>The object unarchived</description>
|
||||
<type>DBObject</type>
|
||||
</event_datum>
|
||||
<event_datum id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
</event_data>
|
||||
</event>
|
||||
<event id="EVENT_DB_SET_ATTRIBUTES_FLAGS" _delta="define">
|
||||
<description>Set object attributes flags. Call cmdbAbstractObject::AddAttributeFlags() for all the attributes to be set for this target state.</description>
|
||||
<sources>
|
||||
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
|
||||
</sources>
|
||||
<event_data>
|
||||
<event_datum id="object">
|
||||
<description>The current object</description>
|
||||
<type>DBObject</type>
|
||||
</event_datum>
|
||||
<event_datum id="target_state">
|
||||
<description>The target state in which to evaluate the flags</description>
|
||||
<type>array</type>
|
||||
</event_datum>
|
||||
<event_datum id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
</event_data>
|
||||
</event>
|
||||
<event id="EVENT_DB_SET_INITIAL_ATTRIBUTES_FLAGS" _delta="define">
|
||||
<description>Set object initial attributes flags. Call cmdbAbstractObject::AddInitialAttributeFlags() for all the initial attributes to be set initially.</description>
|
||||
<sources>
|
||||
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
|
||||
</sources>
|
||||
<event_data>
|
||||
<event_datum id="object">
|
||||
<description>The current object</description>
|
||||
<type>DBObject</type>
|
||||
</event_datum>
|
||||
<event_datum id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
</event_data>
|
||||
</event>
|
||||
<event id="EVENT_DOWNLOAD_DOCUMENT" _delta="define">
|
||||
<description>A document has been downloaded from the GUI</description>
|
||||
<sources>
|
||||
<source id="Document">Document</source>
|
||||
</sources>
|
||||
<event_data>
|
||||
<event_datum id="object">
|
||||
<description>The object containing the document</description>
|
||||
<type>DBObject</type>
|
||||
</event_datum>
|
||||
<event_datum id="document">
|
||||
<description>The document downloaded</description>
|
||||
<type>ormDocument</type>
|
||||
</event_datum>
|
||||
<event_datum id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
</event_data>
|
||||
</event>
|
||||
<event id="EVENT_LOGIN" _delta="define">
|
||||
<description>Inform the listeners about the connection states</description>
|
||||
<event_data>
|
||||
<event_datum id="code">
|
||||
<description>The login step result code (LoginWebPage::EXIT_CODE_...) </description>
|
||||
<type>integer</type>
|
||||
</event_datum>
|
||||
<event_datum id="state">
|
||||
<description>Current login state (LoginWebPage::LOGIN_STATE_CONNECTED...)</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
</event_data>
|
||||
</event>
|
||||
</events>
|
||||
<meta>
|
||||
<classes>
|
||||
<class id="cmdbAbstractObject" _delta="define">
|
||||
|
||||
@@ -19,7 +19,7 @@ use Combodo\iTop\Renderer\Console\ConsoleBlockRenderer;
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
*
|
||||
* @deprecated since 3.0.0 use Combodo\iTop\Application\UI\Base\Component\DataTable\Datatable
|
||||
* @deprecated 3.0.0 use Combodo\iTop\Application\UI\Base\Component\DataTable\Datatable
|
||||
*/
|
||||
|
||||
class DataTable
|
||||
|
||||
@@ -272,6 +272,8 @@ class DisplayBlock
|
||||
'panel_title',
|
||||
/** string true if panel title should be displayed as html */
|
||||
'panel_title_is_html',
|
||||
/** string Description of the panel content, displayed as a hint on the title */
|
||||
'panel_title_tooltip',
|
||||
/** string class for panel block style */
|
||||
'panel_class',
|
||||
/** string class for panel block style */
|
||||
@@ -476,7 +478,7 @@ class DisplayBlock
|
||||
$oExceptionAlert = AlertUIBlockFactory::MakeForFailure('Cannot display results', $sExceptionContent);
|
||||
$oHtml->AddSubBlock($oExceptionAlert);
|
||||
}
|
||||
IssueLog::Error('Exception during GetDisplay: '.$e->getMessage());
|
||||
ExceptionLog::LogException($e);
|
||||
}
|
||||
} else {
|
||||
// render it as an Ajax (asynchronous) call
|
||||
@@ -566,7 +568,7 @@ class DisplayBlock
|
||||
if (($this->m_sStyle != 'links') && ($this->m_sStyle != 'search') && ($this->m_sStyle != 'list_search')) {
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sClass = $this->m_oFilter->GetClass();
|
||||
$aFilterCodes = array_keys(MetaModel::GetClassFilterDefs($sClass));
|
||||
$aFilterCodes = MetaModel::GetFiltersList($sClass);
|
||||
$aCallSpec = array($sClass, 'MapContextParam');
|
||||
if (is_callable($aCallSpec)) {
|
||||
foreach ($oAppContext->GetNames() as $sContextParam) {
|
||||
@@ -1045,11 +1047,11 @@ JS
|
||||
$aCount = $aCounts[$sStateValue];
|
||||
$sHyperlink = $aCount['link'];
|
||||
$sCountLabel = $aCount['label'];
|
||||
|
||||
|
||||
$oPill = PillFactory::MakeForState($sClass, $sStateValue);
|
||||
// N°5849 - Unencode label for ExternalKey attribute because friendlyname is already html encoded thanks to DBObject::GetName() in AttributeExternalKey::GetAllowedValues(). (A fix in this function may have too much impact).
|
||||
if ($oAttDef instanceof AttributeExternalKey) {
|
||||
$sPillTooltip = utils::HtmlEntityDecode($sStateLabel);
|
||||
$sPillTooltip = htmlspecialchars_decode($sStateLabel, ENT_QUOTES | ENT_DISALLOWED | ENT_HTML5);
|
||||
$sPillLabel = $sStateLabel;
|
||||
} else {
|
||||
$sPillTooltip = $sStateLabel;
|
||||
@@ -1057,7 +1059,6 @@ JS
|
||||
}
|
||||
$oPill->SetTooltip($sPillTooltip)
|
||||
->AddHtml("<span class=\"ibo-dashlet-header-dynamic--count\">$sCountLabel</span><span class=\"ibo-dashlet-header-dynamic--label ibo-text-truncated-with-ellipsis\">".$sPillLabel."</span>");
|
||||
|
||||
if ($sHyperlink != '-') {
|
||||
$oPill->SetUrl($sHyperlink);
|
||||
}
|
||||
@@ -1314,17 +1315,12 @@ JS
|
||||
}
|
||||
}
|
||||
if (count($aAuthorizedClasses) > 0) {
|
||||
if ($this->m_oSet->CountWithLimit(1) > 0) {
|
||||
if (empty($aExtraParams['currentId'])) {
|
||||
$iListId = utils::GetUniqueId(); // Works only if not in an Ajax page !!
|
||||
} else {
|
||||
$iListId = $aExtraParams['currentId'];
|
||||
}
|
||||
$oBlock->AddSubBlock(DataTableUIBlockFactory::MakeForObject($oPage, $iListId, $this->m_oSet, $aExtraParams));
|
||||
if (empty($aExtraParams['currentId'])) {
|
||||
$iListId = utils::GetUniqueId(); // Works only if not in an Ajax page !!
|
||||
} else {
|
||||
// Empty set
|
||||
$oBlock->bEmptySet = true;
|
||||
$iListId = $aExtraParams['currentId'];
|
||||
}
|
||||
$oBlock->AddSubBlock(DataTableUIBlockFactory::MakeForObject($oPage, $iListId, $this->m_oSet, $aExtraParams));
|
||||
} else {
|
||||
// Not authorized
|
||||
$oBlock->bNotAuthorized = true;
|
||||
@@ -1347,43 +1343,13 @@ JS
|
||||
}
|
||||
|
||||
// The list is made of only 1 class of objects, actions on the list are possible
|
||||
if (($this->m_oSet->CountWithLimit(1) > 0) && (UserRights::IsActionAllowed($this->m_oSet->GetClass(), UR_ACTION_READ, $this->m_oSet) == UR_ALLOWED_YES)) {
|
||||
if (UserRights::IsActionAllowed($this->m_oSet->GetClass(), UR_ACTION_READ, $this->m_oSet) == UR_ALLOWED_YES) {
|
||||
$oBlock->AddSubBlock(cmdbAbstractObject::GetDisplaySetBlock($oPage, $this->m_oSet, $aExtraParams));
|
||||
} else {
|
||||
$oBlock->bEmptySet = true;
|
||||
$oBlock->sClass = $this->m_oFilter->GetClass();
|
||||
$oBlock->sClassLabel = MetaModel::GetName($oBlock->sClass);
|
||||
$bDisplayMenu = isset($aExtraParams['menu']) ? ($aExtraParams['menu'] == true) : true;
|
||||
if ($bDisplayMenu) {
|
||||
if ((UserRights::IsActionAllowed($oBlock->sClass, UR_ACTION_MODIFY) == UR_ALLOWED_YES)) {
|
||||
$oBlock->sLinkTarget = '';
|
||||
$oAppContext = new ApplicationContext();
|
||||
$oBlock->sParams = $oAppContext->GetForLink();
|
||||
// 1:n links, populate the target object as a default value when creating a new linked object
|
||||
if (isset($aExtraParams['target_attr'])) {
|
||||
$oBlock->sLinkTarget = ' target="_blank" ';
|
||||
$aExtraParams['default'][$aExtraParams['target_attr']] = $aExtraParams['object_id'];
|
||||
}
|
||||
if (!empty($aExtraParams['default'])) {
|
||||
foreach ($aExtraParams['default'] as $sKey => $sValue) {
|
||||
$oBlock->sDefault .= "&default[$sKey]=$sValue";
|
||||
}
|
||||
}
|
||||
$oBlock->bCreateNew = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($aExtraParams["surround_with_panel"]) && $aExtraParams["surround_with_panel"]) {
|
||||
$oPanel = PanelUIBlockFactory::MakeForClass($aExtraParams["panel_class"], $aExtraParams["panel_title"]);
|
||||
if(isset($aExtraParams["panel_icon"]) && strlen($aExtraParams["panel_icon"]) > 0){
|
||||
$oPanel->SetIcon($aExtraParams["panel_icon"]);
|
||||
}
|
||||
$oPanel->AddSubBlock($oBlock);
|
||||
|
||||
return $oPanel;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $oBlock;
|
||||
@@ -1481,6 +1447,7 @@ JS
|
||||
} else {
|
||||
$iListId = $aExtraParams['currentId'];
|
||||
}
|
||||
|
||||
$oBlock = DataTableUIBlockFactory::MakeForRendering($iListId, $oSet, $aExtraParams);
|
||||
$oHtml->AddHtml("<tr><td>");
|
||||
$oContentBlock->AddSubBlock($oBlock);
|
||||
@@ -1716,162 +1683,6 @@ JS
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class to manage 'blocks' of HTML pieces that are parts of a page and contain some list of cmdb objects
|
||||
*
|
||||
* Each block is actually rendered as a <div></div> tag that can be rendered synchronously
|
||||
* or as a piece of Javascript/JQuery/Ajax that will get its content from another page (ajax.render.php).
|
||||
* The list of cmdbObjects to be displayed into the block is defined by a filter
|
||||
* Right now the type of display is either: list, count or details
|
||||
* - list produces a table listing the objects
|
||||
* - count produces a paragraphs with a sentence saying 'cont' objects found
|
||||
* - details display (as table) the details of each object found (best if only one)
|
||||
*
|
||||
* @deprecated 3.0.0 will be removed in 3.1, see N°3824
|
||||
*/
|
||||
class HistoryBlock extends DisplayBlock
|
||||
{
|
||||
protected $iLimitCount;
|
||||
protected $iLimitStart;
|
||||
|
||||
public function __construct(DBSearch $oFilter, $sStyle = 'list', $bAsynchronous = false, $aParams = array(), $oSet = null)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod();
|
||||
parent::__construct($oFilter, $sStyle, $bAsynchronous, $aParams, $oSet);
|
||||
$this->iLimitStart = 0;
|
||||
$this->iLimitCount = 0;
|
||||
}
|
||||
|
||||
public function SetLimit($iCount, $iStart = 0)
|
||||
{
|
||||
$this->iLimitStart = $iStart;
|
||||
$this->iLimitCount = $iCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \WebPage $oPage
|
||||
* @param array $aExtraParams
|
||||
* @param string $sId
|
||||
*
|
||||
* @return string
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \DictExceptionMissingString
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
*
|
||||
* @since 2.7.7 3.0.1 3.1.0 N°3129 Remove default value for $aExtraParams and add type hinting for PHP 8.0 compatibility
|
||||
* (var is unused, and all calls were already made using a default value)
|
||||
*/
|
||||
public function GetRenderContent(WebPage $oPage, array $aExtraParams, string $sId)
|
||||
{
|
||||
$sHtml = '';
|
||||
$bTruncated = false;
|
||||
$oSet = new CMDBObjectSet($this->m_oFilter, array('date' => false));
|
||||
if (!$oPage->IsPrintableVersion()) {
|
||||
if (($this->iLimitStart > 0) || ($this->iLimitCount > 0)) {
|
||||
$oSet->SetLimit($this->iLimitCount, $this->iLimitStart);
|
||||
if (($this->iLimitCount - $this->iLimitStart) < $oSet->Count()) {
|
||||
$bTruncated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
$sHtml .= "<!-- filter: ".($this->m_oFilter->ToOQL())."-->\n";
|
||||
switch ($this->m_sStyle) {
|
||||
case 'toggle':
|
||||
// First the latest change that the user is allowed to see
|
||||
do {
|
||||
$oLatestChangeOp = $oSet->Fetch();
|
||||
} while (is_object($oLatestChangeOp) && ($oLatestChangeOp->GetDescription() == ''));
|
||||
|
||||
if (is_object($oLatestChangeOp)) {
|
||||
// There is one change in the list... only when the object has been created !
|
||||
$sDate = $oLatestChangeOp->GetAsHTML('date');
|
||||
$oChange = MetaModel::GetObject('CMDBChange', $oLatestChangeOp->Get('change'));
|
||||
$sUserInfo = $oChange->GetAsHTML('userinfo');
|
||||
$sHtml .= $oPage->GetStartCollapsibleSection(Dict::Format('UI:History:LastModified_On_By', $sDate, $sUserInfo));
|
||||
$sHtml .= $this->GetHistoryTable($oPage, $oSet);
|
||||
$sHtml .= $oPage->GetEndCollapsibleSection();
|
||||
}
|
||||
break;
|
||||
|
||||
case 'table':
|
||||
default:
|
||||
if ($bTruncated)
|
||||
{
|
||||
$sFilter = htmlentities($this->m_oFilter->serialize(), ENT_QUOTES, 'UTF-8');
|
||||
$sHtml .= '<div id="history_container"><p>';
|
||||
$sHtml .= Dict::Format('UI:TruncatedResults', $this->iLimitCount, $oSet->Count());
|
||||
$sHtml .= ' ';
|
||||
$sHtml .= '<a href="#" onclick="DisplayHistory(\'#history_container\', \''.$sFilter.'\', 0, 0); return false;">'.Dict::S('UI:DisplayAll').'</a>';
|
||||
$sHtml .= $this->GetHistoryTable($oPage, $oSet);
|
||||
$sHtml .= '</p></div>';
|
||||
$oPage->add_ready_script("$('#{$sId} table.listResults tr:last td').addClass('truncated');");
|
||||
}
|
||||
else
|
||||
{
|
||||
$sHtml .= $this->GetHistoryTable($oPage, $oSet);
|
||||
}
|
||||
$oPage->add_ready_script(InlineImage::FixImagesWidth());
|
||||
|
||||
$oPage->add_ready_script("$('.case-log-history-entry-toggle').on('click', function () { $(this).closest('.case-log-history-entry').toggleClass('expanded');});");
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
$('.history_entry').each(function() {
|
||||
var jMe = $(this);
|
||||
var oContent = $(this).find('.history_html_content');
|
||||
if (jMe.height() < oContent.height())
|
||||
{
|
||||
jMe.prepend('<span class="history_truncated_toggler"></span>');
|
||||
jMe.find('.history_truncated_toggler').on('click', function() {
|
||||
jMe.toggleClass('history_entry_truncated');
|
||||
});
|
||||
}
|
||||
});
|
||||
EOF
|
||||
);
|
||||
}
|
||||
return new Html($sHtml);
|
||||
}
|
||||
|
||||
protected function GetHistoryTable(WebPage $oPage, DBObjectSet $oSet)
|
||||
{
|
||||
$sHtml = '';
|
||||
// First the latest change that the user is allowed to see
|
||||
$oSet->Rewind(); // Reset the pointer to the beginning of the set
|
||||
$aChanges = array();
|
||||
while($oChangeOp = $oSet->Fetch())
|
||||
{
|
||||
$sChangeDescription = $oChangeOp->GetDescription();
|
||||
if ($sChangeDescription != '')
|
||||
{
|
||||
// The change is visible for the current user
|
||||
$changeId = $oChangeOp->Get('change');
|
||||
$aChanges[$changeId]['date'] = $oChangeOp->Get('date');
|
||||
$aChanges[$changeId]['userinfo'] = $oChangeOp->Get('userinfo');
|
||||
if (!isset($aChanges[$changeId]['log']))
|
||||
{
|
||||
$aChanges[$changeId]['log'] = array();
|
||||
}
|
||||
$aChanges[$changeId]['log'][] = $sChangeDescription;
|
||||
}
|
||||
}
|
||||
$aAttribs = array('date' => array('label' => Dict::S('UI:History:Date'), 'description' => Dict::S('UI:History:Date+')),
|
||||
'userinfo' => array('label' => Dict::S('UI:History:User'), 'description' => Dict::S('UI:History:User+')),
|
||||
'log' => array('label' => Dict::S('UI:History:Changes') , 'description' => Dict::S('UI:History:Changes+')),
|
||||
);
|
||||
$aValues = array();
|
||||
foreach($aChanges as $aChange)
|
||||
{
|
||||
$aValues[] = array('date' => AttributeDateTime::GetFormat()->Format($aChange['date']), 'userinfo' => htmlentities($aChange['userinfo'], ENT_QUOTES, 'UTF-8'), 'log' => "<ul><li>".implode('</li><li>', $aChange['log'])."</li></ul>");
|
||||
}
|
||||
$sHtml .= $oPage->GetTable($aAttribs, $aValues);
|
||||
return $sHtml;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the 'Actions' menu for a given (list of) object(s)
|
||||
* The 'style' of the list (see constructor of DisplayBlock) can be either 'list' or 'details'
|
||||
@@ -1918,9 +1729,14 @@ class MenuBlock extends DisplayBlock
|
||||
$this->m_sStyle = 'list';
|
||||
}
|
||||
|
||||
$sClass = $this->m_oFilter->GetClass();
|
||||
$oSet = new CMDBObjectSet($this->m_oFilter);
|
||||
$sClass = $this->GetFilter()->GetClass();
|
||||
$aSelectedClasses = $this->GetFilter()->GetSelectedClasses();
|
||||
$bIsForLinkset = isset($aExtraParams['target_attr']);
|
||||
$oSet = new CMDBObjectSet($this->GetFilter());
|
||||
$iSetCount = $oSet->Count();
|
||||
/** @var string $sRefreshAction JS snippet to run when clicking on the refresh button of the menu */
|
||||
$sRefreshAction = $aExtraParams['refresh_action'] ?? '';
|
||||
$bIsCreationInModalAllowed = isset($aExtraParams['creation_in_modal_is_allowed']) && $aExtraParams['creation_in_modal_is_allowed'] === true;
|
||||
|
||||
/** @var array $aRegularActions Any action other than a transition */
|
||||
$aRegularActions = [];
|
||||
@@ -1928,275 +1744,240 @@ class MenuBlock extends DisplayBlock
|
||||
$aTransitionActions = [];
|
||||
/** @var array $aToolkitActions Any "legacy" toolkit menu item, which are now displayed in the same menu as the $aRegularActions, after them */
|
||||
$aToolkitActions = [];
|
||||
if ((!isset($aExtraParams['selection_mode']) || $aExtraParams['selection_mode'] == "") && $this->m_sStyle != 'listInObject') {
|
||||
|
||||
if (!isset($aExtraParams['selection_mode']) || ($aExtraParams['selection_mode'] == "")) {
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sContext = $oAppContext->GetForLink();
|
||||
if (!empty($sContext)) {
|
||||
if (utils::IsNotNullOrEmptyString($sContext)) {
|
||||
$sContext = '&'.$sContext;
|
||||
}
|
||||
|
||||
$oReflectionClass = new ReflectionClass($sClass);
|
||||
$sFilter = $this->m_oFilter->serialize();
|
||||
$sFilter = $this->GetFilter()->serialize();
|
||||
$sUIPage = cmdbAbstractObject::ComputeStandardUIPage($sClass);
|
||||
$sRootUrl = utils::GetAbsoluteUrlAppRoot();
|
||||
|
||||
// Common params that will be applied to actions
|
||||
$aActionParams = array();
|
||||
if (isset($aExtraParams['menu_actions_target'])) {
|
||||
$aActionParams['target'] = $aExtraParams['menu_actions_target'];
|
||||
}
|
||||
$aActionParams = $this->GetDefaultParamsForMenuAction();
|
||||
|
||||
// 1:n links, populate the target object as a default value when creating a new linked object
|
||||
if (isset($aExtraParams['target_attr'])) {
|
||||
if ($bIsForLinkset) {
|
||||
$aExtraParams['default'][$aExtraParams['target_attr']] = $aExtraParams['object_id'];
|
||||
}
|
||||
$sDefault = '';
|
||||
/** @var string $sDefaultValuesAsUrlParams Default values for the object to create, already formatted as URL params (eg. "&default[org_id]=3&default[title]=Foo") */
|
||||
$sDefaultValuesAsUrlParams = '';
|
||||
if (!empty($aExtraParams['default'])) {
|
||||
foreach ($aExtraParams['default'] as $sKey => $sValue) {
|
||||
$sDefault .= "&default[$sKey]=$sValue";
|
||||
$sDefaultValuesAsUrlParams .= "&default[$sKey]=$sValue";
|
||||
}
|
||||
}
|
||||
$bIsCreationAllowed = (UserRights::IsActionAllowed($sClass,
|
||||
UR_ACTION_CREATE) == UR_ALLOWED_YES) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject'));
|
||||
switch ($oSet->Count()) {
|
||||
case 0:
|
||||
// No object in the set, the only possible action is "new"
|
||||
if ($bIsCreationAllowed) {
|
||||
$aRegularActions['UI:Menu:New'] = array(
|
||||
'label' => Dict::S('UI:Menu:New'),
|
||||
'url' => "{$sRootUrl}pages/$sUIPage?operation=new&class=$sClass{$sContext}{$sDefault}",
|
||||
) + $aActionParams;
|
||||
|
||||
// Check rights
|
||||
$bIsCreationAllowed = (UserRights::IsActionAllowed($sClass, UR_ACTION_CREATE) === UR_ALLOWED_YES) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject'));
|
||||
$bIsModifyAllowed = (UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY, $oSet) === UR_ALLOWED_YES) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject'));
|
||||
|
||||
// Create in new tab
|
||||
if ($bIsCreationAllowed && !$bIsCreationInModalAllowed) {
|
||||
$this->AddNewObjectMenuAction($aRegularActions, $sClass, $sDefaultValuesAsUrlParams);
|
||||
}
|
||||
|
||||
// Any style actions
|
||||
// - Bulk actions on objects set
|
||||
if ($iSetCount > 1) {
|
||||
// Bulk actions for each selected classes (eg. "link" and "remote" on n:n relations)
|
||||
foreach ($aSelectedClasses as $sSelectedAlias => $sSelectedClass) {
|
||||
$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);
|
||||
|
||||
// Refine filter on selected class so bullk actions occur on the right class
|
||||
$oSelectedClassFilter = $this->GetFilter()->DeepClone();
|
||||
$oSelectedClassFilter->SetSelectedClasses([$sSelectedAlias]);
|
||||
|
||||
// Action identifier is using the alias on purpose so they can be used as "shortcut actions" easily for "Link" or "Remote" aliases on linksets.
|
||||
// Action label dict code has a specific suffix for "Link" / "Remote" aliases to allow dedicated labels in linksets.
|
||||
$sActionLabelCodeSuffix = in_array($sSelectedAlias, ['Link', 'Remote']) ? $sSelectedAlias : 'Class';
|
||||
if ($bIsBulkModifyAllowed) {
|
||||
$this->AddBulkModifyObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:ModifyAll:'.$sSelectedAlias, Dict::Format('UI:Menu:ModifyAll_'.$sActionLabelCodeSuffix, $sSelectedClassName));
|
||||
}
|
||||
break;
|
||||
if ($bIsBulkDeleteAllowed) {
|
||||
$this->AddBulkDeleteObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:BulkDelete:'.$sSelectedAlias, Dict::Format('UI:Menu:BulkDelete_'.$sActionLabelCodeSuffix, $sSelectedClassName));
|
||||
}
|
||||
}
|
||||
|
||||
case 1:
|
||||
$oObj = $oSet->Fetch();
|
||||
if (is_null($oObj)) {
|
||||
if (!isset($aExtraParams['link_attr'])) {
|
||||
if ($bIsCreationAllowed) {
|
||||
$aRegularActions['UI:Menu:New'] = array(
|
||||
'label' => Dict::S('UI:Menu:New'),
|
||||
'url' => "{$sRootUrl}pages/$sUIPage?operation=new&class=$sClass{$sContext}{$sDefault}",
|
||||
) + $aActionParams;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$id = $oObj->GetKey();
|
||||
if (empty($sRefreshAction) && utils::ReadParam('operation') == 'details') {
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'GET') {
|
||||
$sRefreshAction = "window.location.reload();";
|
||||
} else {
|
||||
$sRefreshAction = "window.location.href='".ApplicationContext::MakeObjectUrl(get_class($oObj), $id)."';";
|
||||
}
|
||||
}
|
||||
// Stimuli
|
||||
$aStates = MetaModel::EnumStates($sClass);
|
||||
// Do not perform time-consuming computations if there are too many objects in the list
|
||||
$iLimit = MetaModel::GetConfig()->Get('complex_actions_limit');
|
||||
|
||||
$bLocked = false;
|
||||
if (MetaModel::GetConfig()->Get('concurrent_lock_enabled')) {
|
||||
$aLockInfo = iTopOwnershipLock::IsLocked(get_class($oObj), $id);
|
||||
if ($aLockInfo['locked']) {
|
||||
$bLocked = true;
|
||||
//$this->AddMenuSeparator($aActions);
|
||||
//$aActions['concurrent_lock_unlock'] = array ('label' => Dict::S('UI:Menu:ReleaseConcurrentLock'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=kill_lock&class=$sClass&id=$id{$sContext}");
|
||||
}
|
||||
}
|
||||
$bRawModifiedAllowed = (UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY, $oSet) == UR_ALLOWED_YES) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject'));
|
||||
$bIsModifyAllowed = !$bLocked && $bRawModifiedAllowed;
|
||||
$bIsDeleteAllowed = !$bLocked && UserRights::IsActionAllowed($sClass, UR_ACTION_DELETE, $oSet);
|
||||
// Just one object in the set, possible actions are "new / clone / modify and delete"
|
||||
if (!isset($aExtraParams['link_attr'])) {
|
||||
if ($bIsModifyAllowed) {
|
||||
$aRegularActions['UI:Menu:Modify'] = array(
|
||||
'label' => Dict::S('UI:Menu:Modify'),
|
||||
'url' => "{$sRootUrl}pages/$sUIPage?operation=modify&class=$sClass&id=$id{$sContext}#",
|
||||
) + $aActionParams;
|
||||
}
|
||||
if ($bIsCreationAllowed) {
|
||||
$aRegularActions['UI:Menu:New'] = array(
|
||||
'label' => Dict::S('UI:Menu:New'),
|
||||
'url' => "{$sRootUrl}pages/$sUIPage?operation=new&class=$sClass{$sContext}{$sDefault}",
|
||||
) + $aActionParams;
|
||||
}
|
||||
if ($bIsDeleteAllowed) {
|
||||
$aRegularActions['UI:Menu:Delete'] = array(
|
||||
'label' => Dict::S('UI:Menu:Delete'),
|
||||
'url' => "{$sRootUrl}pages/$sUIPage?operation=delete&class=$sClass&id=$id{$sContext}",
|
||||
) + $aActionParams;
|
||||
}
|
||||
if ((count($aStates) > 0) && (($iLimit == 0) || ($oSet->CountWithLimit($iLimit + 1) < $iLimit))) {
|
||||
// Life cycle actions may be available... if all objects are in the same state
|
||||
//
|
||||
// Group by <state>
|
||||
$oGroupByExp = new FieldExpression(MetaModel::GetStateAttributeCode($sClass), $this->m_oFilter->GetClassAlias());
|
||||
$aGroupBy = array('__state__' => $oGroupByExp);
|
||||
$aQueryParams = array();
|
||||
if (isset($aExtraParams['query_params'])) {
|
||||
$aQueryParams = $aExtraParams['query_params'];
|
||||
}
|
||||
|
||||
// Transitions / Stimuli
|
||||
if (!$bLocked) {
|
||||
$aTransitions = $oObj->EnumTransitions();
|
||||
if (count($aTransitions)) {
|
||||
$aStimuli = Metamodel::EnumStimuli(get_class($oObj));
|
||||
foreach ($aTransitions as $sStimulusCode => $aTransitionDef) {
|
||||
$iActionAllowed = (get_class($aStimuli[$sStimulusCode]) == 'StimulusUserAction') ? UserRights::IsStimulusAllowed($sClass,
|
||||
$sStimulusCode, $oSet) : UR_ALLOWED_NO;
|
||||
switch ($iActionAllowed) {
|
||||
case UR_ALLOWED_YES:
|
||||
$aTransitionActions[$sStimulusCode] = array(
|
||||
'label' => $aStimuli[$sStimulusCode]->GetLabel(),
|
||||
'url' => "{$sRootUrl}pages/UI.php?operation=stimulus&stimulus=$sStimulusCode&class=$sClass&id=$id{$sContext}",
|
||||
) + $aActionParams;
|
||||
break;
|
||||
|
||||
default:
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Relations...
|
||||
$aRelations = MetaModel::EnumRelationsEx($sClass);
|
||||
if (count($aRelations)) {
|
||||
$this->AddMenuSeparator($aRegularActions);
|
||||
foreach ($aRelations as $sRelationCode => $aRelationInfo) {
|
||||
if (array_key_exists('down', $aRelationInfo)) {
|
||||
$aRegularActions[$sRelationCode.'_down'] = array(
|
||||
'label' => $aRelationInfo['down'],
|
||||
'url' => "{$sRootUrl}pages/$sUIPage?operation=view_relations&relation=$sRelationCode&direction=down&class=$sClass&id=$id{$sContext}",
|
||||
$sSql = $this->m_oFilter->MakeGroupByQuery($aQueryParams, $aGroupBy);
|
||||
$aRes = CMDBSource::QueryToArray($sSql);
|
||||
if (count($aRes) == 1) {
|
||||
// All objects are in the same state...
|
||||
$sState = $aRes[0]['__state__'];
|
||||
$aTransitions = Metamodel::EnumTransitions($sClass, $sState);
|
||||
if (count($aTransitions)) {
|
||||
$aStimuli = Metamodel::EnumStimuli($sClass);
|
||||
foreach ($aTransitions as $sStimulusCode => $aTransitionDef) {
|
||||
$oSet->Rewind();
|
||||
// As soon as the user rights implementation will browse the object set,
|
||||
// then we might consider using OptimizeColumnLoad() here
|
||||
$iActionAllowed = UserRights::IsStimulusAllowed($sClass, $sStimulusCode, $oSet);
|
||||
$iActionAllowed = (get_class($aStimuli[$sStimulusCode]) == 'StimulusUserAction') ? $iActionAllowed : UR_ALLOWED_NO;
|
||||
switch ($iActionAllowed) {
|
||||
case UR_ALLOWED_YES:
|
||||
case UR_ALLOWED_DEPENDS:
|
||||
$aTransitionActions[$sStimulusCode] = array(
|
||||
'label' => $aStimuli[$sStimulusCode]->GetLabel(),
|
||||
'url' => "{$sRootUrl}pages/UI.php?operation=select_bulk_stimulus&stimulus=$sStimulusCode&state=$sState&class=$sClass&filter=".urlencode($sFilter)."{$sContext}",
|
||||
) + $aActionParams;
|
||||
}
|
||||
if (array_key_exists('up', $aRelationInfo)) {
|
||||
$aRegularActions[$sRelationCode.'_up'] = array(
|
||||
'label' => $aRelationInfo['up'],
|
||||
'url' => "{$sRootUrl}pages/$sUIPage?operation=view_relations&relation=$sRelationCode&direction=up&class=$sClass&id=$id{$sContext}",
|
||||
) + $aActionParams;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add a special menu to kill the lock, but only to allowed users who can also modify this object
|
||||
if ($bLocked && $bRawModifiedAllowed) {
|
||||
/** @var array $aAllowedProfiles */
|
||||
$aAllowedProfiles = MetaModel::GetConfig()->Get('concurrent_lock_override_profiles');
|
||||
$bCanKill = false;
|
||||
|
||||
$oUser = UserRights::GetUserObject();
|
||||
$aUserProfiles = array();
|
||||
if (!is_null($oUser)) {
|
||||
$oProfileSet = $oUser->Get('profile_list');
|
||||
while ($oProfile = $oProfileSet->Fetch()) {
|
||||
$aUserProfiles[$oProfile->Get('profile')] = true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($aAllowedProfiles as $sProfile) {
|
||||
if (array_key_exists($sProfile, $aUserProfiles)) {
|
||||
$bCanKill = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($bCanKill) {
|
||||
$this->AddMenuSeparator($aRegularActions);
|
||||
$aRegularActions['concurrent_lock_unlock'] = array(
|
||||
'label' => Dict::S('UI:Menu:KillConcurrentLock'),
|
||||
'url' => "{$sRootUrl}pages/$sUIPage?operation=kill_lock&class=$sClass&id=$id{$sContext}",
|
||||
);
|
||||
default:
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->AddMenuSeparator($aRegularActions);
|
||||
|
||||
$this->GetEnumAllowedActions($oSet, function ($sLabel, $data) use (&$aRegularActions, $aActionParams) {
|
||||
$aRegularActions[$sLabel] = array('label' => $sLabel, 'url' => $data) + $aActionParams;
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
// Check rights
|
||||
// New / Modify
|
||||
$bIsModifyAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY,
|
||||
$oSet) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject'));
|
||||
$bIsBulkModifyAllowed = (!MetaModel::IsAbstract($sClass)) && UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_MODIFY,
|
||||
$oSet) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject'));
|
||||
$bIsBulkDeleteAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_DELETE, $oSet);
|
||||
if (isset($aExtraParams['link_attr'])) {
|
||||
$id = $aExtraParams['object_id'];
|
||||
$sTargetAttr = $aExtraParams['target_attr'];
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sTargetAttr);
|
||||
$sTargetClass = $oAttDef->GetTargetClass();
|
||||
if ($bIsModifyAllowed) {
|
||||
$aRegularActions['UI:Menu:Add'] = array(
|
||||
'label' => Dict::S('UI:Menu:Add'),
|
||||
'url' => "{$sRootUrl}pages/$sUIPage?operation=modify_links&class=$sClass&link_attr=".$aExtraParams['link_attr']."&target_class=$sTargetClass&id=$id&addObjects=true{$sContext}",
|
||||
) + $aActionParams;
|
||||
}
|
||||
if ($bIsBulkModifyAllowed) {
|
||||
$aRegularActions['UI:Menu:Manage'] = array(
|
||||
'label' => Dict::S('UI:Menu:Manage'),
|
||||
'url' => "{$sRootUrl}pages/$sUIPage?operation=modify_links&class=$sClass&link_attr=".$aExtraParams['link_attr']."&target_class=$sTargetClass&id=$id{$sContext}",
|
||||
) + $aActionParams;
|
||||
}
|
||||
//if ($bIsBulkDeleteAllowed) { $aActions[] = array ('label' => 'Remove All...', 'url' => "#") + $aActionParams; }
|
||||
} else {
|
||||
// many objects in the set, possible actions are: new / modify all / delete all
|
||||
if ($bIsCreationAllowed) {
|
||||
$aRegularActions['UI:Menu:New'] = array(
|
||||
'label' => Dict::S('UI:Menu:New'),
|
||||
'url' => "{$sRootUrl}pages/$sUIPage?operation=new&class=$sClass{$sContext}{$sDefault}",
|
||||
) + $aActionParams;
|
||||
}
|
||||
if ($bIsBulkModifyAllowed) {
|
||||
$aRegularActions['UI:Menu:ModifyAll'] = array(
|
||||
'label' => Dict::S('UI:Menu:ModifyAll'),
|
||||
'url' => "{$sRootUrl}pages/$sUIPage?operation=select_for_modify_all&class=$sClass&filter=".urlencode($sFilter)."{$sContext}",
|
||||
) + $aActionParams;
|
||||
}
|
||||
if ($bIsBulkDeleteAllowed) {
|
||||
$aRegularActions['UI:Menu:BulkDelete'] = array(
|
||||
'label' => Dict::S('UI:Menu:BulkDelete'),
|
||||
'url' => "{$sRootUrl}pages/$sUIPage?operation=select_for_deletion&filter=".urlencode($sFilter)."{$sContext}",
|
||||
) + $aActionParams;
|
||||
}
|
||||
|
||||
// Stimuli
|
||||
$aStates = MetaModel::EnumStates($sClass);
|
||||
// Do not perform time consuming computations if there are too may objects in the list
|
||||
$iLimit = MetaModel::GetConfig()->Get('complex_actions_limit');
|
||||
|
||||
if ((count($aStates) > 0) && (($iLimit == 0) || ($oSet->CountWithLimit($iLimit + 1) < $iLimit))) {
|
||||
// Life cycle actions may be available... if all objects are in the same state
|
||||
//
|
||||
// Group by <state>
|
||||
$oGroupByExp = new FieldExpression(MetaModel::GetStateAttributeCode($sClass), $this->m_oFilter->GetClassAlias());
|
||||
$aGroupBy = array('__state__' => $oGroupByExp);
|
||||
$aQueryParams = array();
|
||||
if (isset($aExtraParams['query_params'])) {
|
||||
$aQueryParams = $aExtraParams['query_params'];
|
||||
// NOT "listInObject" style actions
|
||||
if ($this->m_sStyle !== 'listInObject') {
|
||||
switch ($iSetCount) {
|
||||
case 1:
|
||||
$oObj = $oSet->Fetch();
|
||||
if (false === is_null($oObj)) {
|
||||
$id = $oObj->GetKey();
|
||||
if (empty($sRefreshAction) && utils::ReadParam('operation') == 'details') {
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'GET') {
|
||||
$sRefreshAction = "window.location.reload();";
|
||||
} else {
|
||||
$sRefreshAction = "window.location.href='".ApplicationContext::MakeObjectUrl(get_class($oObj), $id)."';";
|
||||
}
|
||||
}
|
||||
|
||||
$sSql = $this->m_oFilter->MakeGroupByQuery($aQueryParams, $aGroupBy);
|
||||
$aRes = CMDBSource::QueryToArray($sSql);
|
||||
if (count($aRes) == 1) {
|
||||
// All objects are in the same state...
|
||||
$sState = $aRes[0]['__state__'];
|
||||
$aTransitions = Metamodel::EnumTransitions($sClass, $sState);
|
||||
if (count($aTransitions)) {
|
||||
$aStimuli = Metamodel::EnumStimuli($sClass);
|
||||
foreach ($aTransitions as $sStimulusCode => $aTransitionDef) {
|
||||
$oSet->Rewind();
|
||||
// As soon as the user rights implementation will browse the object set,
|
||||
// then we might consider using OptimizeColumnLoad() here
|
||||
$iActionAllowed = UserRights::IsStimulusAllowed($sClass, $sStimulusCode, $oSet);
|
||||
$iActionAllowed = (get_class($aStimuli[$sStimulusCode]) == 'StimulusUserAction') ? $iActionAllowed : UR_ALLOWED_NO;
|
||||
switch ($iActionAllowed) {
|
||||
case UR_ALLOWED_YES:
|
||||
case UR_ALLOWED_DEPENDS:
|
||||
$aTransitionActions[$sStimulusCode] = array(
|
||||
'label' => $aStimuli[$sStimulusCode]->GetLabel(),
|
||||
'url' => "{$sRootUrl}pages/UI.php?operation=select_bulk_stimulus&stimulus=$sStimulusCode&state=$sState&class=$sClass&filter=".urlencode($sFilter)."{$sContext}",
|
||||
) + $aActionParams;
|
||||
break;
|
||||
$bLocked = false;
|
||||
if (MetaModel::GetConfig()->Get('concurrent_lock_enabled')) {
|
||||
$aLockInfo = iTopOwnershipLock::IsLocked(get_class($oObj), $id);
|
||||
if ($aLockInfo['locked']) {
|
||||
$bLocked = true;
|
||||
}
|
||||
}
|
||||
$bRawModifiedAllowed = (UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY, $oSet) == UR_ALLOWED_YES) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject'));
|
||||
$bIsModifyAllowed = !$bLocked && $bRawModifiedAllowed;
|
||||
$bIsDeleteAllowed = !$bLocked && UserRights::IsActionAllowed($sClass, UR_ACTION_DELETE, $oSet);
|
||||
// Just one object in the set, possible actions are "new / clone / modify and delete"
|
||||
if (!isset($aExtraParams['link_attr'])) {
|
||||
if ($bIsModifyAllowed) {
|
||||
$aRegularActions['UI:Menu:Modify'] = array(
|
||||
'label' => Dict::S('UI:Menu:Modify'),
|
||||
'url' => "{$sRootUrl}pages/$sUIPage?route=object.modify&class=$sClass&id=$id{$sContext}#",
|
||||
) + $aActionParams;
|
||||
}
|
||||
if ($bIsDeleteAllowed) {
|
||||
$aRegularActions['UI:Menu:Delete'] = array(
|
||||
'label' => Dict::S('UI:Menu:Delete'),
|
||||
'url' => "{$sRootUrl}pages/$sUIPage?operation=delete&class=$sClass&id=$id{$sContext}",
|
||||
) + $aActionParams;
|
||||
}
|
||||
|
||||
default:
|
||||
// Do nothing
|
||||
// Transitions / Stimuli
|
||||
if (!$bLocked) {
|
||||
$aTransitions = $oObj->EnumTransitions();
|
||||
if (count($aTransitions)) {
|
||||
$aStimuli = Metamodel::EnumStimuli(get_class($oObj));
|
||||
foreach ($aTransitions as $sStimulusCode => $aTransitionDef) {
|
||||
$iActionAllowed = (get_class($aStimuli[$sStimulusCode]) == 'StimulusUserAction') ? UserRights::IsStimulusAllowed($sClass,
|
||||
$sStimulusCode, $oSet) : UR_ALLOWED_NO;
|
||||
switch ($iActionAllowed) {
|
||||
case UR_ALLOWED_YES:
|
||||
$aTransitionActions[$sStimulusCode] = array(
|
||||
'label' => $aStimuli[$sStimulusCode]->GetLabel(),
|
||||
'url' => "{$sRootUrl}pages/UI.php?operation=stimulus&stimulus=$sStimulusCode&class=$sClass&id=$id{$sContext}",
|
||||
) + $aActionParams;
|
||||
break;
|
||||
|
||||
default:
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Relations...
|
||||
$aRelations = MetaModel::EnumRelationsEx($sClass);
|
||||
if (count($aRelations)) {
|
||||
$this->AddMenuSeparator($aRegularActions);
|
||||
foreach ($aRelations as $sRelationCode => $aRelationInfo) {
|
||||
if (array_key_exists('down', $aRelationInfo)) {
|
||||
$aRegularActions[$sRelationCode.'_down'] = array(
|
||||
'label' => $aRelationInfo['down'],
|
||||
'url' => "{$sRootUrl}pages/$sUIPage?operation=view_relations&relation=$sRelationCode&direction=down&class=$sClass&id=$id{$sContext}",
|
||||
) + $aActionParams;
|
||||
}
|
||||
if (array_key_exists('up', $aRelationInfo)) {
|
||||
$aRegularActions[$sRelationCode.'_up'] = array(
|
||||
'label' => $aRelationInfo['up'],
|
||||
'url' => "{$sRootUrl}pages/$sUIPage?operation=view_relations&relation=$sRelationCode&direction=up&class=$sClass&id=$id{$sContext}",
|
||||
) + $aActionParams;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add a special menu to kill the lock, but only to allowed users who can also modify this object
|
||||
if ($bLocked && $bRawModifiedAllowed) {
|
||||
/** @var array $aAllowedProfiles */
|
||||
$aAllowedProfiles = MetaModel::GetConfig()->Get('concurrent_lock_override_profiles');
|
||||
$bCanKill = false;
|
||||
|
||||
$oUser = UserRights::GetUserObject();
|
||||
$aUserProfiles = array();
|
||||
if (!is_null($oUser)) {
|
||||
$oProfileSet = $oUser->Get('profile_list');
|
||||
while ($oProfile = $oProfileSet->Fetch()) {
|
||||
$aUserProfiles[$oProfile->Get('profile')] = true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($aAllowedProfiles as $sProfile) {
|
||||
if (array_key_exists($sProfile, $aUserProfiles)) {
|
||||
$bCanKill = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($bCanKill) {
|
||||
$this->AddMenuSeparator($aRegularActions);
|
||||
$aRegularActions['concurrent_lock_unlock'] = array(
|
||||
'label' => Dict::S('UI:Menu:KillConcurrentLock'),
|
||||
'url' => "{$sRootUrl}pages/$sUIPage?operation=kill_lock&class=$sClass&id=$id{$sContext}",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->AddMenuSeparator($aRegularActions);
|
||||
|
||||
$this->GetEnumAllowedActions($oSet, function ($sLabel, $data) use (&$aRegularActions, $aActionParams) {
|
||||
$aRegularActions[$sLabel] = array('label' => $sLabel, 'url' => $data) + $aActionParams;
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$this->AddMenuSeparator($aRegularActions);
|
||||
@@ -2221,8 +2002,9 @@ class MenuBlock extends DisplayBlock
|
||||
$sRefreshAction = "window.location.reload();";
|
||||
}
|
||||
} else {
|
||||
//it's easier just display configure this list and MENU_OBJLIST_TOOLKIT
|
||||
// It's easier just display configure this list and MENU_OBJLIST_TOOLKIT
|
||||
}
|
||||
|
||||
$param = null;
|
||||
if (is_null($sId)) {
|
||||
$sId = uniqid();
|
||||
@@ -2235,14 +2017,7 @@ class MenuBlock extends DisplayBlock
|
||||
case 'listInObject':
|
||||
$oSet->Rewind();
|
||||
$param = $oSet;
|
||||
$bToolkitMenu = true;
|
||||
if (isset($aExtraParams['toolkit_menu'])) {
|
||||
$bToolkitMenu = (bool)$aExtraParams['toolkit_menu'];
|
||||
}
|
||||
if ($bToolkitMenu) {
|
||||
$sLabel = Dict::S('UI:ConfigureThisList');
|
||||
$aRegularActions['iTop::ConfigureList'] = ['label' => $sLabel, 'url' => '#', 'onclick' => "$('#datatable_dlg_datatable_{$sId}').dialog('open'); return false;"];
|
||||
}
|
||||
|
||||
utils::GetPopupMenuItemsBlock($oPopupMenuItemsBlock, iPopupMenuExtension::MENU_OBJLIST_ACTIONS, $param, $aRegularActions, $sId);
|
||||
utils::GetPopupMenuItemsBlock($oPopupMenuItemsBlock, iPopupMenuExtension::MENU_OBJLIST_TOOLKIT, $param, $aToolkitActions, $sId);
|
||||
break;
|
||||
@@ -2254,6 +2029,7 @@ class MenuBlock extends DisplayBlock
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if ($oPopupMenuItemsBlock->HasSubBlocks()) {
|
||||
$oRenderBlock->AddSubBlock($oPopupMenuItemsBlock);
|
||||
} else {
|
||||
@@ -2264,6 +2040,7 @@ class MenuBlock extends DisplayBlock
|
||||
$oRenderBlock->AddCssFileRelPath($sCssPath);
|
||||
}
|
||||
}
|
||||
|
||||
// Extract favorite actions from their menus
|
||||
$aFavoriteRegularActions = [];
|
||||
$aFavoriteTransitionActions = [];
|
||||
@@ -2349,12 +2126,16 @@ class MenuBlock extends DisplayBlock
|
||||
break;
|
||||
|
||||
case 'UI:Menu:ModifyAll':
|
||||
case 'UI:Menu:ModifyAll:Link': // Link class on linkset
|
||||
case 'UI:Menu:ModifyAll:Remote': // Remote class on linkset
|
||||
case 'UI:Menu:Modify':
|
||||
$sIconClass = 'fas fa-pen';
|
||||
$sLabel = '';
|
||||
break;
|
||||
|
||||
case 'UI:Menu:BulkDelete':
|
||||
case 'UI:Menu:BulkDelete:Link': // Link class on linkset
|
||||
case 'UI:Menu:BulkDelete:Remote': // Remote class on linkset
|
||||
case 'UI:Menu:Delete':
|
||||
$sIconClass = 'fas fa-trash';
|
||||
$sLabel = '';
|
||||
@@ -2387,8 +2168,28 @@ class MenuBlock extends DisplayBlock
|
||||
$oActionsToolbar->AddSubBlock($oActionButton);
|
||||
}
|
||||
|
||||
// - Creation in modal
|
||||
if ($bIsCreationInModalAllowed === true) {
|
||||
$oAddLinkActionButton = ButtonUIBlockFactory::MakeIconAction(
|
||||
'fas fa-plus',
|
||||
Dict::Format('UI:ClickToCreateNew', MetaModel::GetName($sClass)),
|
||||
'UI:Links:New',
|
||||
'',
|
||||
false
|
||||
);
|
||||
|
||||
// - If we are used in a Datatable, 'datatable_' will be prefixed to our $sId, so we do the same here
|
||||
$sRealId = $sId;
|
||||
if(in_array($this->m_sStyle, ['list', 'links', 'listInObject'])){
|
||||
$sRealId = 'datatable_' . $sId;
|
||||
}
|
||||
$oAddLinkActionButton->AddCSSClasses(['ibo-action-button', 'ibo-regular-action-button'])
|
||||
->SetOnClickJsCode("$('#$sRealId').trigger('open_creation_modal.object.itop');");
|
||||
$oActionsToolbar->AddSubBlock($oAddLinkActionButton);
|
||||
}
|
||||
|
||||
// - Refresh
|
||||
if ($sRefreshAction != '') {
|
||||
if (utils::IsNotNullOrEmptyString($sRefreshAction)) {
|
||||
$oActionButton = ButtonUIBlockFactory::MakeAlternativeNeutral('', 'UI:Button:Refresh');
|
||||
$oActionButton->SetIconClass('fas fa-sync-alt')
|
||||
->SetOnClickJsCode($sRefreshAction)
|
||||
@@ -2398,7 +2199,7 @@ class MenuBlock extends DisplayBlock
|
||||
}
|
||||
|
||||
// - Search
|
||||
if ($this->m_sStyle == 'details') {
|
||||
if ($this->m_sStyle === 'details') {
|
||||
$oActionButton = ButtonUIBlockFactory::MakeIconLink('fas fa-search', Dict::Format('UI:SearchFor_Class', MetaModel::GetName($sClass)), "{$sRootUrl}pages/UI.php?operation=search_form&do_search=0&class=$sClass{$sContext}", '', 'UI:SearchFor_Class');
|
||||
$oActionButton->AddCSSClasses(['ibo-action-button', 'ibo-regular-action-button']);
|
||||
$oActionsToolbar->AddSubBlock($oActionButton);
|
||||
@@ -2423,6 +2224,12 @@ class MenuBlock extends DisplayBlock
|
||||
|
||||
// Toolkit actions
|
||||
if (!empty($aToolkitActions)) {
|
||||
// Add separator if necessary
|
||||
if (count($aRegularActions) > 0) {
|
||||
$oRegularActionsMenu->AddItem('separator-regular-actions-toolkit-actions', PopoverMenuItemFactory::MakeSeparator());
|
||||
}
|
||||
|
||||
// Add actions
|
||||
foreach ($aToolkitActions as $sActionId => $aActionData) {
|
||||
$oRegularActionsMenu->AddItem('toolkit-actions', PopoverMenuItemFactory::MakeFromApplicationPopupMenuItemData($sActionId, $aActionData));
|
||||
}
|
||||
@@ -2503,5 +2310,106 @@ class MenuBlock extends DisplayBlock
|
||||
$aActions['sep_'.(count($aActions)-1)] = array('label' => $sSeparator, 'url' => '');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a create action (to $aActions) for an $sClass object
|
||||
*
|
||||
* @param array &$aActions Pointer to the array in which the action will be added
|
||||
* @param string $sClass Datamodel class concerned by the action
|
||||
* @param string $sDefaultValuesAsUrlParams Default values for the new object form,
|
||||
*
|
||||
* @return void
|
||||
* @since 3.1.0
|
||||
* @internal
|
||||
*/
|
||||
protected function AddNewObjectMenuAction(array &$aActions, string $sClass, string $sDefaultValuesAsUrlParams = ''): void
|
||||
{
|
||||
$aActions['UI:Menu:New'] = [
|
||||
'label' => Dict::Format('UI:ClickToCreateNew', MetaModel::GetName($sClass)),
|
||||
'url' => $this->PrepareUrlForStandardMenuAction($sClass, "operation=new&class=$sClass{$sDefaultValuesAsUrlParams}"),
|
||||
] + $this->GetDefaultParamsForMenuAction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a bulk modify action (to $aActions) on objects defined by $sFilter
|
||||
*
|
||||
* @param array &$aActions Pointer to the array in which the action will be added
|
||||
* @param string $sClass Datamodel class concerned by the action
|
||||
* @param string $sFilter OQL of the objects to propose for bulk modify
|
||||
* @param string $sActionIdentifier Unique identifier for the action. Default if 'UI:Menu:ModifyAll'
|
||||
* @param string $sActionLabel Label for the action, can be either a dict code to translate or an hardcoded label. Default is 'UI:Menu:ModifyAll'.
|
||||
*
|
||||
* @return void
|
||||
* @since 3.1.0
|
||||
* @internal
|
||||
*/
|
||||
protected function AddBulkModifyObjectsMenuAction(array &$aActions, string $sClass, string $sFilter, string $sActionIdentifier = 'UI:Menu:ModifyAll', $sActionLabel = 'UI:Menu:ModifyAll'): void
|
||||
{
|
||||
$aActions[$sActionIdentifier] = [
|
||||
'label' => Dict::S($sActionLabel),
|
||||
'url' => $this->PrepareUrlForStandardMenuAction($sClass, "operation=select_for_modify_all&class=$sClass&filter=".urlencode($sFilter)),
|
||||
] + $this->GetDefaultParamsForMenuAction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a bulk delete action (to $aActions) on objects defined by $sFilter
|
||||
*
|
||||
* @param array &$aActions Pointer to the array in which the action will be added
|
||||
* @param string $sClass Datamodel class concerned by the action
|
||||
* @param string $sFilter OQL of the objects to propose for bulk deletion
|
||||
* @param string $sActionIdentifier Unique identifier for the action. Default if 'UI:Menu:BulkDelete'
|
||||
* @param string $sActionLabel Label for the action, can be either a dict code to translate or an hardcoded label. Default is 'UI:Menu:BulkDelete'.
|
||||
*
|
||||
* @return void
|
||||
* @since 3.1.0
|
||||
* @internal
|
||||
*/
|
||||
protected function AddBulkDeleteObjectsMenuAction(array &$aActions, string $sClass, string $sFilter, string $sActionIdentifier = 'UI:Menu:BulkDelete', $sActionLabel = 'UI:Menu:BulkDelete')
|
||||
{
|
||||
$aActions[$sActionIdentifier] = array(
|
||||
'label' => Dict::S($sActionLabel),
|
||||
'url' => $this->PrepareUrlForStandardMenuAction($sClass, "operation=select_for_deletion&filter=".urlencode($sFilter)),
|
||||
) + $this->GetDefaultParamsForMenuAction();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array Default parameters of a menu action
|
||||
* @since 3.1.0
|
||||
* @internal
|
||||
*/
|
||||
private function GetDefaultParamsForMenuAction(): array
|
||||
{
|
||||
$aDefaultParams = [];
|
||||
|
||||
if (isset($aExtraParams['menu_actions_target'])) {
|
||||
$aDefaultParams['target'] = $aExtraParams['menu_actions_target'];
|
||||
}
|
||||
|
||||
return $aDefaultParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sClass Datamodel class for which the URL is prepared
|
||||
* @param string $sUrlParams URL parameters to add to the URL, must already be concatenated as a string (eg. "foo=bar&some=thing&third=param")
|
||||
*
|
||||
* @return string An absolute URL for a menu action on the $sClass class with $sUrlParams
|
||||
* @throws \Exception
|
||||
* @internal
|
||||
*/
|
||||
private function PrepareUrlForStandardMenuAction(string $sClass, string $sUrlParams)
|
||||
{
|
||||
$sRootUrl = utils::GetAbsoluteUrlAppRoot();
|
||||
$sUIPage = cmdbAbstractObject::ComputeStandardUIPage($sClass);
|
||||
|
||||
$sUrl = "{$sRootUrl}pages/{$sUIPage}?{$sUrlParams}";
|
||||
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sContext = $oAppContext->GetForLink();
|
||||
if (utils::IsNotNullOrEmptyString($sContext)) {
|
||||
$sUrl .= '&'.$sContext;
|
||||
}
|
||||
|
||||
return $sUrl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/ErrorPage.php, now loadable using autoloader
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/ErrorPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
*/
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/ErrorPage.php, now loadable using autoloader');
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/ErrorPage.php, now loadable using autoloader');
|
||||
@@ -6,6 +6,10 @@
|
||||
|
||||
class CoreException extends Exception
|
||||
{
|
||||
protected $m_sIssue;
|
||||
protected $m_sImpact;
|
||||
protected $m_aContextData;
|
||||
|
||||
/**
|
||||
* CoreException constructor.
|
||||
*
|
||||
|
||||
10
application/exceptions/iTopXmlException.php
Normal file
10
application/exceptions/iTopXmlException.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2022 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
class iTopXmlException extends CoreException
|
||||
{
|
||||
|
||||
}
|
||||
@@ -838,7 +838,8 @@ class DesignerFormField
|
||||
{
|
||||
$sId = $this->oForm->GetFieldId($this->sCode);
|
||||
$sName = $this->oForm->GetFieldName($this->sCode);
|
||||
return array('label' => $this->sLabel, 'value' => "<input type=\"text\" id=\"$sId\" name=\"$sName\" value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\">");
|
||||
|
||||
return array('label' => $this->sLabel, 'value' => "<input type=\"text\" id=\"$sId\" name=\"$sName\" value=\"".utils::EscapeHtml($this->defaultValue)."\">");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1012,9 +1013,8 @@ class DesignerTextField extends DesignerFormField
|
||||
$sId = $this->oForm->GetFieldId($this->sCode);
|
||||
|
||||
$sName = $this->oForm->GetFieldName($this->sCode);
|
||||
if ($this->IsReadOnly())
|
||||
{
|
||||
$sHtmlValue = "<span>".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\"/></span>";
|
||||
if ($this->IsReadOnly()) {
|
||||
$sHtmlValue = "<span>".utils::EscapeHtml($this->defaultValue)."<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".utils::EscapeHtml($this->defaultValue)."\"/></span>";
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1038,11 +1038,10 @@ $('#$sId').on('change keyup validate', function() { ValidateWithPattern('$sId',
|
||||
EOF
|
||||
);
|
||||
$sCSSClasses = '';
|
||||
if (count($this->aCSSClasses) > 0)
|
||||
{
|
||||
if (count($this->aCSSClasses) > 0) {
|
||||
$sCSSClasses = 'class="'.implode(' ', $this->aCSSClasses).'"';
|
||||
}
|
||||
$sHtmlValue = "<input type=\"text\" $sCSSClasses id=\"$sId\" name=\"$sName\" value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\">";
|
||||
$sHtmlValue = "<input type=\"text\" $sCSSClasses id=\"$sId\" name=\"$sName\" value=\"".utils::EscapeHtml($this->defaultValue)."\">";
|
||||
}
|
||||
return array('label' => $this->sLabel, 'value' => $sHtmlValue);
|
||||
}
|
||||
@@ -1101,10 +1100,9 @@ class DesignerLongTextField extends DesignerTextField
|
||||
{
|
||||
$sCSSClasses = 'class="'.implode(' ', $this->aCSSClasses).'"';
|
||||
}
|
||||
if (!$this->IsReadOnly())
|
||||
{
|
||||
if (!$this->IsReadOnly()) {
|
||||
$oP->add_ready_script(
|
||||
<<<EOF
|
||||
<<<EOF
|
||||
$('#$sId').on('change keyup validate', function() { ValidateWithPattern('$sId', $sMandatory, '$sPattern', $(this).closest('form').attr('id'), $sForbiddenValues); } );
|
||||
{
|
||||
var myTimer = null;
|
||||
@@ -1112,11 +1110,10 @@ $('#$sId').on('change keyup validate', function() { ValidateWithPattern('$sId',
|
||||
}
|
||||
EOF
|
||||
);
|
||||
$sValue = "<textarea $sCSSClasses id=\"$sId\" name=\"$sName\">".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."</textarea>";
|
||||
$sValue = "<textarea $sCSSClasses id=\"$sId\" name=\"$sName\">".utils::EscapeHtml($this->defaultValue)."</textarea>";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sValue = "<div $sCSSClasses id=\"$sId\">".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."</div>";
|
||||
else {
|
||||
$sValue = "<div $sCSSClasses id=\"$sId\">".utils::EscapeHtml($this->defaultValue)."</div>";
|
||||
}
|
||||
return array('label' => $this->sLabel, 'value' => $sValue);
|
||||
}
|
||||
@@ -1145,9 +1142,8 @@ class DesignerIntegerField extends DesignerFormField
|
||||
$sId = $this->oForm->GetFieldId($this->sCode);
|
||||
|
||||
$sName = $this->oForm->GetFieldName($this->sCode);
|
||||
if ($this->IsReadOnly())
|
||||
{
|
||||
$sHtmlValue = "<span>".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\"/></span>";
|
||||
if ($this->IsReadOnly()) {
|
||||
$sHtmlValue = "<span>".utils::EscapeHtml($this->defaultValue)."<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".utils::EscapeHtml($this->defaultValue)."\"/></span>";
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1164,11 +1160,10 @@ $('#$sId').on('change keyup validate', function() { ValidateInteger('$sId', $sMa
|
||||
EOF
|
||||
);
|
||||
$sCSSClasses = '';
|
||||
if (count($this->aCSSClasses) > 0)
|
||||
{
|
||||
if (count($this->aCSSClasses) > 0) {
|
||||
$sCSSClasses = 'class="'.implode(' ', $this->aCSSClasses).'"';
|
||||
}
|
||||
$sHtmlValue = "<input type=\"text\" $sCSSClasses id=\"$sId\" name=\"$sName\" value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\">";
|
||||
$sHtmlValue = "<input type=\"text\" $sCSSClasses id=\"$sId\" name=\"$sName\" value=\"".utils::EscapeHtml($this->defaultValue)."\">";
|
||||
}
|
||||
return array('label' => $this->sLabel, 'value' => $sHtmlValue);
|
||||
}
|
||||
@@ -1289,22 +1284,18 @@ class DesignerComboField extends DesignerFormField
|
||||
{
|
||||
if ($this->bMultipleSelection)
|
||||
{
|
||||
if(in_array($sKey, $this->defaultValue))
|
||||
{
|
||||
if(in_array($sKey, $this->defaultValue)) {
|
||||
$aSelected[] = $sDisplayValue;
|
||||
$aHiddenValues[] = "<input type=\"hidden\" name=\"{$sName}[]\" value=\"".htmlentities($sKey, ENT_QUOTES, 'UTF-8')."\"/>";
|
||||
$aHiddenValues[] = "<input type=\"hidden\" name=\"{$sName}[]\" value=\"".utils::EscapeHtml($sKey)."\"/>";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($sKey == $this->defaultValue)
|
||||
{
|
||||
} else {
|
||||
if ($sKey == $this->defaultValue) {
|
||||
$aSelected[] = $sDisplayValue;
|
||||
$aHiddenValues[] = "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".htmlentities($sKey, ENT_QUOTES, 'UTF-8')."\"/>";
|
||||
$aHiddenValues[] = "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".utils::EscapeHtml($sKey)."\"/>";
|
||||
}
|
||||
}
|
||||
}
|
||||
$sHtml = "<span $sCSSClasses>".htmlentities(implode(', ', $aSelected), ENT_QUOTES, 'UTF-8').implode($aHiddenValues)."</span>";
|
||||
$sHtml = "<span $sCSSClasses>".utils::EscapeHtml(implode(', ', $aSelected)).implode($aHiddenValues)."</span>";
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1328,7 +1319,7 @@ class DesignerComboField extends DesignerFormField
|
||||
}
|
||||
// Quick and dirty: display the menu parents as a tree
|
||||
$sHtmlValue = str_replace(' ', ' ', $sDisplayValue);
|
||||
$sHtml .= "<option value=\"".htmlentities($sKey, ENT_QUOTES, 'UTF-8')."\" $sSelected>$sHtmlValue</option>";
|
||||
$sHtml .= "<option value=\"".utils::EscapeHtml($sKey)."\" $sSelected>$sHtmlValue</option>";
|
||||
}
|
||||
$sHtml .= "</select></span>";
|
||||
if ($this->bOtherChoices)
|
||||
@@ -1379,10 +1370,9 @@ class DesignerBooleanField extends DesignerFormField
|
||||
$sId = $this->oForm->GetFieldId($this->sCode);
|
||||
$sName = $this->oForm->GetFieldName($this->sCode);
|
||||
$sChecked = $this->defaultValue ? 'checked' : '';
|
||||
if ($this->IsReadOnly())
|
||||
{
|
||||
if ($this->IsReadOnly()) {
|
||||
$sLabel = $this->defaultValue ? Dict::S('UI:UserManagement:ActionAllowed:Yes') : Dict::S('UI:UserManagement:ActionAllowed:No'); //TODO use our own yes/no translations
|
||||
$sHtmlValue = "<span>".htmlentities($sLabel)."<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\"/></span>";
|
||||
$sHtmlValue = "<span>".utils::EscapeHtml($sLabel)."<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".utils::EscapeHtml($this->defaultValue)."\"/></span>";
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1450,8 +1440,8 @@ class DesignerHiddenField extends DesignerFormField
|
||||
{
|
||||
$sId = $this->oForm->GetFieldId($this->sCode);
|
||||
$sName = $this->oForm->GetFieldName($this->sCode);
|
||||
$sChecked = $this->defaultValue ? 'checked' : '';
|
||||
return array('label' =>'', 'value' => "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\">");
|
||||
|
||||
return array('label' => '', 'value' => "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".utils::EscapeHtml($this->defaultValue)."\">");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1518,7 +1508,7 @@ class DesignerIconSelectionField extends DesignerFormField
|
||||
EOF
|
||||
);
|
||||
} else {
|
||||
$sValue = '<span style="display:inline-block;line-height:48px;height:48px;"><span><img style="vertical-align:middle" src="'.$this->aAllowedValues[$idx]['icon'].'" /> '.htmlentities($this->aAllowedValues[$idx]['label'], ENT_QUOTES, 'UTF-8').'</span></span>';
|
||||
$sValue = '<span style="display:inline-block;line-height:48px;height:48px;"><span><img style="vertical-align:middle" src="'.$this->aAllowedValues[$idx]['icon'].'" /> '.utils::EscapeHtml($this->aAllowedValues[$idx]['label']).'</span></span>';
|
||||
}
|
||||
$sReadOnly = $this->IsReadOnly() ? 'disabled' : '';
|
||||
return array('label' => $this->sLabel, 'value' => $sValue);
|
||||
@@ -1665,14 +1655,14 @@ class DesignerSortableField extends DesignerFormField
|
||||
$sId = $this->oForm->GetFieldId($this->sCode);
|
||||
$sName = $this->oForm->GetFieldName($this->sCode);
|
||||
$sReadOnly = $this->IsReadOnly() ? 'readonly="readonly"' : '';
|
||||
$aResult = array('label' => $this->sLabel, 'value' => "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" $sReadOnly value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\">");
|
||||
|
||||
$aResult = array('label' => $this->sLabel, 'value' => "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" $sReadOnly value=\"".utils::EscapeHtml($this->defaultValue)."\">");
|
||||
|
||||
|
||||
$sJSFields = json_encode(array_keys($this->aAllowedValues));
|
||||
$oP->add_ready_script(
|
||||
"$('#$sId').sortable_field({aAvailableFields: $sJSFields});"
|
||||
);
|
||||
|
||||
|
||||
return $aResult;
|
||||
}
|
||||
}
|
||||
@@ -1761,8 +1751,8 @@ class DesignerFormSelectorField extends DesignerFormField
|
||||
foreach ($this->aSubForms as $iKey => $aFormData) {
|
||||
if ($iKey == $this->defaultValue) // Default value is actually the index
|
||||
{
|
||||
$sDisplayValue = htmlentities($aFormData['label'], ENT_QUOTES, 'UTF-8');
|
||||
$sHiddenValue = "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".htmlentities($iKey, ENT_QUOTES, 'UTF-8')."\"/>";
|
||||
$sDisplayValue = utils::EscapeHtml($aFormData['label']);
|
||||
$sHiddenValue = "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".utils::EscapeHtml($iKey)."\"/>";
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1770,8 +1760,8 @@ class DesignerFormSelectorField extends DesignerFormField
|
||||
} else {
|
||||
$sHtml = "<span class=\"ibo-input-select-wrapper\"><select $sCSSClasses id=\"$sId\" name=\"$sName\" $sReadOnly>";
|
||||
foreach ($this->aSubForms as $iKey => $aFormData) {
|
||||
$sDisplayValue = htmlentities($aFormData['label'], ENT_QUOTES, 'UTF-8');
|
||||
$sValue = htmlentities($aFormData['value'], ENT_QUOTES, 'UTF-8');
|
||||
$sDisplayValue = utils::EscapeHtml($aFormData['label']);
|
||||
$sValue = utils::EscapeHtml($aFormData['value']);
|
||||
$sSelected = ($iKey == $this->defaultValue) ? 'selected' : '';
|
||||
$sHtml .= "<option data-value=\"$sValue\" value=\"$iKey\" $sSelected>".$sDisplayValue."</option>";
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/iTopWebPage.php, now loadable using autoloader
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/iTopWebPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
*/
|
||||
|
||||
// cannot notify depreciation for now as this is still MASSIVELY used in iTop core !
|
||||
//DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/iTopWebPage.php, now loadable using autoloader');
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/iTopWebPage.php, now loadable using autoloader');
|
||||
@@ -1,8 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/iTopWizardWebPage.php, now loadable using autoloader
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/iTopWizardWebPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
*/
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/iTopWizardWebPage.php, now loadable using autoloader');
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/iTopWizardWebPage.php, now loadable using autoloader');
|
||||
@@ -88,4 +88,4 @@ class LoginExternal extends AbstractLoginFSMExtension
|
||||
/** @var string $sAuthUser */
|
||||
return $sAuthUser; // Retrieve the value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,10 @@
|
||||
|
||||
|
||||
use Combodo\iTop\Application\Branding;
|
||||
use Combodo\iTop\TwigExtension;
|
||||
use Combodo\iTop\Application\TwigBase\Twig\Extension;
|
||||
use Twig\Environment;
|
||||
use Twig\Loader\ChainLoader;
|
||||
use Twig\Loader\FilesystemLoader;
|
||||
|
||||
/**
|
||||
* Twig context for modules extending the login screen
|
||||
@@ -217,14 +220,14 @@ class LoginTwigRenderer
|
||||
$sTwigLoaderPath = $oLoginContext->GetTwigLoaderPath();
|
||||
if ($sTwigLoaderPath != null)
|
||||
{
|
||||
$oExtensionLoader = new Twig_Loader_Filesystem();
|
||||
$oExtensionLoader = new FilesystemLoader();
|
||||
$oExtensionLoader->setPaths($sTwigLoaderPath);
|
||||
$aTwigLoaders[] = $oExtensionLoader;
|
||||
}
|
||||
$this->aPostedVars = array_merge($this->aPostedVars, $oLoginContext->GetPostedVars());
|
||||
}
|
||||
|
||||
$oCoreLoader = new Twig_Loader_Filesystem(array(), APPROOT.'templates');
|
||||
$oCoreLoader = new FilesystemLoader(array(), APPROOT.'templates');
|
||||
$aCoreTemplatesPaths = array('pages/login', 'pages/login/password');
|
||||
// Having this path declared after the plugins let the plugins replace the core templates
|
||||
$oCoreLoader->setPaths($aCoreTemplatesPaths);
|
||||
@@ -232,9 +235,9 @@ class LoginTwigRenderer
|
||||
$oCoreLoader->setPaths($aCoreTemplatesPaths, 'ItopCore');
|
||||
$aTwigLoaders[] = $oCoreLoader;
|
||||
|
||||
$oLoader = new Twig_Loader_Chain($aTwigLoaders);
|
||||
$this->oTwig = new Twig_Environment($oLoader);
|
||||
TwigExtension::RegisterTwigExtensions($this->oTwig);
|
||||
$oLoader = new ChainLoader($aTwigLoaders);
|
||||
$this->oTwig = new Environment($oLoader);
|
||||
Extension::RegisterTwigExtensions($this->oTwig);
|
||||
}
|
||||
|
||||
public function GetDefaultVars()
|
||||
@@ -306,7 +309,7 @@ class LoginTwigRenderer
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Twig_Environment
|
||||
* @return \Twig\Environment
|
||||
*/
|
||||
public function GetTwig()
|
||||
{
|
||||
|
||||
@@ -92,4 +92,4 @@ class LoginURL extends AbstractLoginFSMExtension
|
||||
}
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
|
||||
use Combodo\iTop\Application\Branding;
|
||||
use Combodo\iTop\Application\Helper\Session;
|
||||
use Combodo\iTop\Service\Events\EventData;
|
||||
use Combodo\iTop\Service\Events\EventService;
|
||||
|
||||
/**
|
||||
* Web page used for displaying the login form
|
||||
@@ -131,10 +133,6 @@ class LoginWebPage extends NiceWebPage
|
||||
//add profiles not already linked with user
|
||||
foreach ($aProfiles as $iProfileId)
|
||||
{
|
||||
$oLink = new URP_UserProfile();
|
||||
$oLink->Set('profileid', $iProfileId);
|
||||
$oLink->Set('reason', $sOrigin);
|
||||
|
||||
$oProfilesSet->AddItem(MetaModel::NewObject('URP_UserProfile', array('profileid' => $iProfileId, 'reason' => $sOrigin)));
|
||||
}
|
||||
$oUser->Set('profile_list', $oProfilesSet);
|
||||
@@ -483,11 +481,13 @@ class LoginWebPage extends NiceWebPage
|
||||
$iResponse = $oLoginFSMExtensionInstance->LoginAction($sLoginState, $iErrorCode);
|
||||
if ($iResponse == self::LOGIN_FSM_RETURN)
|
||||
{
|
||||
EventService::FireEvent(new EventData(EVENT_LOGIN, null, ['code' => $iErrorCode, 'state' => $sLoginState]));
|
||||
Session::WriteClose();
|
||||
return $iErrorCode; // Asked to exit FSM, generally login OK
|
||||
}
|
||||
if ($iResponse == self::LOGIN_FSM_ERROR)
|
||||
{
|
||||
EventService::FireEvent(new EventData(EVENT_LOGIN, null, ['code' => $iErrorCode, 'state' => $sLoginState]));
|
||||
$sLoginState = self::LOGIN_STATE_SET_ERROR; // Next state will be error
|
||||
// An error was detected, skip the other plugins turn
|
||||
break;
|
||||
@@ -501,6 +501,7 @@ class LoginWebPage extends NiceWebPage
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
EventService::FireEvent(new EventData(EVENT_LOGIN, null, ['state' => $_SESSION['login_state']]));
|
||||
IssueLog::Error($e->getTraceAsString());
|
||||
static::ResetSession();
|
||||
die($e->getMessage());
|
||||
|
||||
@@ -291,7 +291,17 @@ class ApplicationMenu
|
||||
* @param string $sMenuGroupIdx
|
||||
* @param array $aExtraParams
|
||||
*
|
||||
* @return array
|
||||
* @return array{
|
||||
* array{
|
||||
* sId: string,
|
||||
* sTitle: string,
|
||||
* sLabel: string,
|
||||
* bHasCount: boolean,
|
||||
* sUrl: string,
|
||||
* bOpenInNewWindow: boolean,
|
||||
* aSubMenuNodes: array
|
||||
* }
|
||||
* } The aSubMenuNodes key contains the same structure recursively
|
||||
* @throws \DictExceptionMissingString
|
||||
* @throws \Exception
|
||||
* @since 3.0.0
|
||||
@@ -320,12 +330,13 @@ class ApplicationMenu
|
||||
}
|
||||
|
||||
$aSubMenuNodes[] = [
|
||||
'sId' => $oSubMenuNode->GetMenuId(),
|
||||
'sTitle' => $oSubMenuNode->GetTitle(),
|
||||
'bHasCount' => $oSubMenuNode->HasCount(),
|
||||
'sUrl' => $oSubMenuNode->GetHyperlink($aExtraParams),
|
||||
'sId' => $oSubMenuNode->GetMenuId(),
|
||||
'sTitle' => $oSubMenuNode->GetTitle(),
|
||||
'sLabel' => $oSubMenuNode->GetLabel(),
|
||||
'bHasCount' => $oSubMenuNode->HasCount(),
|
||||
'sUrl' => $oSubMenuNode->GetHyperlink($aExtraParams),
|
||||
'bOpenInNewWindow' => $oSubMenuNode->IsHyperLinkInNewWindow(),
|
||||
'aSubMenuNodes' => static::GetSubMenuNodes($sSubMenuItemIdx, $aExtraParams),
|
||||
'aSubMenuNodes' => static::GetSubMenuNodes($sSubMenuItemIdx, $aExtraParams),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -746,7 +757,7 @@ abstract class MenuNode
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @return string The "+" dictionary entry for this menu if exists, otherwise the Title (if we have a parent title, will output parentTitle / currentTitle)
|
||||
*/
|
||||
public function GetLabel()
|
||||
{
|
||||
@@ -758,7 +769,6 @@ abstract class MenuNode
|
||||
} else {
|
||||
$sRet = $this->GetTitle();
|
||||
}
|
||||
//$sRet = $this->GetTitle();
|
||||
}
|
||||
return $sRet;
|
||||
}
|
||||
@@ -1101,6 +1111,7 @@ class OQLMenuNode extends MenuNode
|
||||
*/
|
||||
public function RenderContent(WebPage $oPage, $aExtraParams = array())
|
||||
{
|
||||
ContextTag::AddContext(ContextTag::TAG_OBJECT_SEARCH);
|
||||
ApplicationMenu::CheckMenuIdEnabled($this->GetMenuId());
|
||||
OQLMenuNode::RenderOQLSearch
|
||||
(
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/NiceWebPage.php, now loadable using autoloader
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/NiceWebPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
*/
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/NiceWebPage.php, now loadable using autoloader');
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/NiceWebPage.php, now loadable using autoloader');
|
||||
@@ -1,8 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/PDFPage.php, now loadable using autoloader
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/PDFPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
*/
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/PDFPage.php, now loadable using autoloader');
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/PDFPage.php, now loadable using autoloader');
|
||||
@@ -18,8 +18,7 @@
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Application\UI\Base\Component\Alert\AlertUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Field\Field;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Field\FieldUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\FieldSet\FieldSetUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Input\TextArea;
|
||||
|
||||
@@ -51,6 +50,7 @@ abstract class Query extends cmdbAbstractObject
|
||||
"is_null_allowed" => false,
|
||||
"depends_on" => array(),
|
||||
)));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeText("description", array(
|
||||
"allowed_values" => null,
|
||||
"sql" => "description",
|
||||
@@ -68,6 +68,41 @@ abstract class Query extends cmdbAbstractObject
|
||||
'display_style' => 'radio_horizontal',
|
||||
)));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeInteger("export_count", array(
|
||||
"allowed_values" => null,
|
||||
"sql" => "export_count",
|
||||
"default_value" => 0,
|
||||
"is_null_allowed" => false,
|
||||
"depends_on" => array(),
|
||||
)));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeDateTime("export_last_date", array(
|
||||
"allowed_values" => null,
|
||||
"sql" => "export_last_date",
|
||||
"default_value" => null,
|
||||
"is_null_allowed" => true,
|
||||
"depends_on" => array(),
|
||||
)));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("export_last_user_id",
|
||||
array(
|
||||
"targetclass"=>'User',
|
||||
"allowed_values"=>null,
|
||||
"sql"=>'user_id',
|
||||
"is_null_allowed"=>true,
|
||||
"depends_on"=>array(),
|
||||
"display_style"=>'select',
|
||||
"always_load_in_tables"=>false,
|
||||
"on_target_delete"=>DEL_SILENT
|
||||
)));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("export_last_user_contact",
|
||||
array(
|
||||
"allowed_values"=>null,
|
||||
"extkey_attcode"=> "export_last_user_id",
|
||||
"target_attcode"=>"contactid"
|
||||
)));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details',
|
||||
array('name', 'is_template', 'description')); // Attributes to be displayed for the complete details
|
||||
@@ -78,6 +113,55 @@ abstract class Query extends cmdbAbstractObject
|
||||
array('name', 'description', 'is_template')); // Criteria of the default search form
|
||||
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public function GetAttributeFlags($sAttCode, &$aReasons = array(), $sTargetState = '')
|
||||
{
|
||||
// read only attribute
|
||||
if (in_array($sAttCode, ['export_count', 'export_last_date', 'export_last_user_id'])){
|
||||
return OPT_ATT_READONLY;
|
||||
}
|
||||
|
||||
return parent::GetAttributeFlags($sAttCode, $aReasons, $sTargetState);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return export url.
|
||||
*
|
||||
* @param array|null $aValues optional values for the query
|
||||
*
|
||||
* @return string|null
|
||||
* @since 3.1.0
|
||||
*/
|
||||
abstract public function GetExportUrl(array $aValues = null) : ?string;
|
||||
|
||||
/**
|
||||
* Update last export information.
|
||||
*
|
||||
* @return void
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \MySQLException
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public function UpdateLastExportInformation() : void
|
||||
{
|
||||
// last export information
|
||||
$this->Set('export_last_date', date(AttributeDateTime::GetSQLFormat()));
|
||||
$this->Set('export_last_user_id', UserRights::GetUserObject());
|
||||
$this->AllowWrite(true);
|
||||
$this->DBUpdate();
|
||||
|
||||
// increment usage counter
|
||||
$this->DBIncrement('export_count');
|
||||
}
|
||||
}
|
||||
|
||||
class QueryOQL extends Query
|
||||
@@ -116,13 +200,51 @@ class QueryOQL extends Query
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details',
|
||||
array('name', 'is_template', 'description', 'oql', 'fields')); // Attributes to be displayed for the complete details
|
||||
array(
|
||||
'col:col1' => array('fieldset:Query:baseinfo' => array('name', 'is_template', 'description', 'oql', 'fields')),
|
||||
'col:col2' => array('fieldset:Query:exportInfo' => array('export_count', 'export_last_date', 'export_last_user_id', 'export_last_user_contact'))
|
||||
)
|
||||
); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('description')); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
MetaModel::Init_SetZListItems('standard_search',
|
||||
array('name', 'description', 'is_template', 'fields', 'oql')); // Criteria of the std search form
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function GetExportUrl(array $aValues = null) : ?string
|
||||
{
|
||||
try{
|
||||
// retrieve attributes
|
||||
$sFields = trim($this->Get('fields'));
|
||||
$sOql = $this->Get('oql');
|
||||
|
||||
// construct base url depending on version
|
||||
$bExportV1Recommended = ($sFields == '');
|
||||
if ($bExportV1Recommended) {
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot().'webservices/export.php?format=spreadsheet&login_mode=basic&query='.$this->GetKey();
|
||||
}
|
||||
else{
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot().'webservices/export-v2.php?format=spreadsheet&login_mode=basic&date_format='.urlencode((string)AttributeDateTime::GetFormat()).'&query='.$this->GetKey();
|
||||
}
|
||||
|
||||
// search object from OQL
|
||||
$oSearch = DBObjectSearch::FromOQL($sOql);
|
||||
|
||||
// inject parameters
|
||||
$aParameters = $oSearch->GetQueryParams();
|
||||
foreach ($aParameters as $sParam => $val) {
|
||||
$paramValue = ($aValues === null || $aValues[$sParam] === null) ? $sParam : $aValues[$sParam];
|
||||
$sUrl .= '&arg_' . $sParam . '=' . $paramValue;
|
||||
}
|
||||
|
||||
return $sUrl;
|
||||
}
|
||||
catch(Exception $e){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array())
|
||||
{
|
||||
$aFieldsMap = parent::DisplayBareProperties($oPage, $bEditMode, $sPrefix, $aExtraParams);
|
||||
@@ -152,9 +274,11 @@ class QueryOQL extends Query
|
||||
$sUrl .= '&arg_'.$sParam.'=["'.$sParam.'"]';
|
||||
}
|
||||
|
||||
// add text area inside field set
|
||||
$oFieldSet = FieldSetUIBlockFactory::MakeStandard(Dict::S('UI:Query:UrlForExcel'));
|
||||
$oTextArea = new TextArea("", $sUrl, null, 80, 3);
|
||||
$oFieldUrl = FieldUIBlockFactory::MakeFromObject(Dict::S('UI:Query:UrlForExcel'), $oTextArea, Field::ENUM_FIELD_LAYOUT_LARGE);
|
||||
$oPage->AddSubBlock($oFieldUrl);
|
||||
$oFieldSet->AddSubBlock($oTextArea);
|
||||
$oPage->AddSubBlock($oFieldSet);
|
||||
|
||||
if (count($aParameters) == 0) {
|
||||
$oBlock = new DisplayBlock($oSearch, 'list');
|
||||
@@ -178,6 +302,7 @@ class QueryOQL extends Query
|
||||
return $aFieldsMap;
|
||||
}
|
||||
|
||||
|
||||
// Rolled back until 'fields' can be properly managed by AttributeQueryAttCodeSet
|
||||
//
|
||||
// public function ComputeValues()
|
||||
|
||||
@@ -15,12 +15,16 @@
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
use Combodo\iTop\Application\EventRegister\ApplicationEvents;
|
||||
use Combodo\iTop\Application\Helper\Session;
|
||||
use Combodo\iTop\Service\Events\EventData;
|
||||
use Combodo\iTop\Service\Events\EventService;
|
||||
|
||||
require_once(APPROOT.'/core/cmdbobject.class.inc.php');
|
||||
require_once(APPROOT.'/application/utils.inc.php');
|
||||
require_once(APPROOT.'/core/contexttag.class.inc.php');
|
||||
require_once(APPROOT.'/core/kpi.class.inc.php');
|
||||
require_once(APPROOT.'core/cmdbobject.class.inc.php');
|
||||
require_once(APPROOT.'application/utils.inc.php');
|
||||
require_once(APPROOT.'core/contexttag.class.inc.php');
|
||||
require_once(APPROOT.'core/kpi.class.inc.php');
|
||||
require_once(APPROOT.'setup/setuputils.class.inc.php');
|
||||
|
||||
|
||||
/**
|
||||
@@ -99,3 +103,6 @@ else
|
||||
}
|
||||
$sConfigFile = APPCONF.$sEnv.'/'.ITOP_CONFIG_FILE;
|
||||
MetaModel::Startup($sConfigFile, false /* $bModelOnly */, $bAllowCache, false /* $bTraceSourceFiles */, $sEnv);
|
||||
// Event service must be initialized after the MetaModel startup, otherwise it cannot discover classes implementing the iEventServiceSetup interface
|
||||
EventService::InitService();
|
||||
EventService::FireEvent(new EventData(ApplicationEvents::APPLICATION_EVENT_METAMODEL_STARTED));
|
||||
|
||||
@@ -229,12 +229,10 @@ class DisplayTemplate
|
||||
static public function UnitTest()
|
||||
{
|
||||
require_once(APPROOT.'/application/startup.inc.php');
|
||||
require_once(APPROOT."/application/itopwebpage.class.inc.php");
|
||||
|
||||
$sTemplate = '<div class="page_header">
|
||||
<div class="actions_details"><a href="#"><span>Actions</span></a></div>
|
||||
<h1>$class$: <span class="hilite">$name$</span></h1>
|
||||
<itopblock blockclass="HistoryBlock" type="toggle" encoding="text/oql">SELECT CMDBChangeOp WHERE objkey = $id$ AND objclass = \'$class$\'</itopblock>
|
||||
</div>
|
||||
<img src="../../images/connect_to_network.png" style="margin-top:-10px; margin-right:10px; float:right">
|
||||
<itoptabs>
|
||||
@@ -353,7 +351,7 @@ class ObjectDetailsTemplate extends DisplayTemplate
|
||||
$sTip = '';
|
||||
foreach($aReasons as $aRow)
|
||||
{
|
||||
$sDescription = htmlentities($aRow['description'], ENT_QUOTES, 'UTF-8');
|
||||
$sDescription = utils::EscapeHtml($aRow['description']);
|
||||
$sDescription = str_replace(array("\r\n", "\n"), "<br/>", $sDescription);
|
||||
$sTip .= "<div class='synchro-source'>";
|
||||
$sTip .= "<div class='synchro-source-title'>Synchronized with {$aRow['name']}</div>";
|
||||
@@ -361,10 +359,10 @@ class ObjectDetailsTemplate extends DisplayTemplate
|
||||
}
|
||||
$oPage->add_ready_script("$('#synchro_$iInputId').qtip( { content: '$sTip', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );");
|
||||
}
|
||||
|
||||
|
||||
// Attribute is read-only
|
||||
$sHTMLValue = "<span id=\"field_{$iInputId}\">".$this->m_oObj->GetAsHTML($sAttCode);
|
||||
$sHTMLValue .= '<input type="hidden" id="'.$iInputId.'" name="attr_'.$sAttCode.'" value="'.htmlentities($this->m_oObj->Get($sAttCode), ENT_QUOTES, 'UTF-8').'"/></span>';
|
||||
$sHTMLValue .= '<input type="hidden" id="'.$iInputId.'" name="attr_'.$sAttCode.'" value="'.utils::EscapeHtml($this->m_oObj->Get($sAttCode)).'"/></span>';
|
||||
$aFieldsMap[$sAttCode] = $iInputId;
|
||||
$aParams['this->comments('.$sAttCode.')'] = $sSynchroIcon;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<div class="page_header">
|
||||
<itopblock blockclass="MenuBlock" type="popup" encoding="text/oql" label="Actions">SELECT $class$ WHERE id = $id$</itopblock>
|
||||
<h1>$class$: <span class="hilite">$name$</span></h1>
|
||||
<itopblock blockclass="HistoryBlock" type="toggle" encoding="text/oql">SELECT CMDBChangeOp WHERE objkey = $id$ AND objclass = '$class$'</itopblock>
|
||||
</div>
|
||||
<img src="../../images/clean.png" style="margin-top:-20px; margin-right:10px; float:right">
|
||||
<itopblock blockclass="DisplayBlock" asynchronous="false" type="bare_details" encoding="text/oql">SELECT $class$ WHERE id = $id$</itopblock>
|
||||
|
||||
@@ -4,13 +4,17 @@ namespace Combodo\iTop;
|
||||
|
||||
use AttributeDate;
|
||||
use AttributeDateTime;
|
||||
use DeprecatedCallsLog;
|
||||
use Dict;
|
||||
use Exception;
|
||||
use Twig_Environment;
|
||||
use Twig_SimpleFilter;
|
||||
use Twig_SimpleFunction;
|
||||
use MetaModel;
|
||||
use Twig\Environment;
|
||||
use Twig\TwigFilter;
|
||||
use Twig\TwigFunction;
|
||||
use utils;
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('instead use sources/Application/TwigBase/Twig/Extension.php, which is loaded by the autoloader');
|
||||
|
||||
/**
|
||||
* Class TwigExtension
|
||||
*
|
||||
@@ -24,13 +28,13 @@ class TwigExtension
|
||||
* Registers Twig extensions such as filters or functions.
|
||||
* It allows us to access some stuff directly in twig.
|
||||
*
|
||||
* @param \Twig_Environment $oTwigEnv
|
||||
* @param Environment $oTwigEnv
|
||||
*/
|
||||
public static function RegisterTwigExtensions(Twig_Environment &$oTwigEnv)
|
||||
public static function RegisterTwigExtensions(Environment &$oTwigEnv)
|
||||
{
|
||||
// Filter to translate a string via the Dict::S function
|
||||
// Usage in twig: {{ 'String:ToTranslate'|dict_s }}
|
||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('dict_s',
|
||||
$oTwigEnv->addFilter(new TwigFilter('dict_s',
|
||||
function ($sStringCode, $sDefault = null, $bUserLanguageOnly = false) {
|
||||
return Dict::S($sStringCode, $sDefault, $bUserLanguageOnly);
|
||||
})
|
||||
@@ -38,7 +42,7 @@ class TwigExtension
|
||||
|
||||
// Filter to format a string via the Dict::Format function
|
||||
// Usage in twig: {{ 'String:ToTranslate'|dict_format() }}
|
||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('dict_format',
|
||||
$oTwigEnv->addFilter(new TwigFilter('dict_format',
|
||||
function ($sStringCode, $sParam01 = null, $sParam02 = null, $sParam03 = null, $sParam04 = null) {
|
||||
return Dict::Format($sStringCode, $sParam01, $sParam02, $sParam03, $sParam04);
|
||||
})
|
||||
@@ -47,16 +51,13 @@ class TwigExtension
|
||||
// Filter to format output
|
||||
// example a DateTime is converted to user format
|
||||
// Usage in twig: {{ 'String:ToFormat'|output_format }}
|
||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('date_format',
|
||||
$oTwigEnv->addFilter(new TwigFilter('date_format',
|
||||
function ($sDate) {
|
||||
try
|
||||
{
|
||||
if (preg_match('@^\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d$@', trim($sDate)))
|
||||
{
|
||||
try {
|
||||
if (preg_match('@^\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d$@', trim($sDate))) {
|
||||
return AttributeDateTime::GetFormat()->Format($sDate);
|
||||
}
|
||||
if (preg_match('@^\d\d\d\d-\d\d-\d\d$@', trim($sDate)))
|
||||
{
|
||||
if (preg_match('@^\d\d\d\d-\d\d-\d\d$@', trim($sDate))) {
|
||||
return AttributeDate::GetFormat()->Format($sDate);
|
||||
}
|
||||
}
|
||||
@@ -71,7 +72,7 @@ class TwigExtension
|
||||
// Filter to format output
|
||||
// example a DateTime is converted to user format
|
||||
// Usage in twig: {{ 'String:ToFormat'|output_format }}
|
||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('size_format',
|
||||
$oTwigEnv->addFilter(new TwigFilter('size_format',
|
||||
function ($sSize) {
|
||||
return utils::BytesToFriendlyFormat($sSize);
|
||||
})
|
||||
@@ -79,24 +80,25 @@ class TwigExtension
|
||||
|
||||
// Filter to enable base64 encode/decode
|
||||
// Usage in twig: {{ 'String to encode'|base64_encode }}
|
||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('base64_encode', 'base64_encode'));
|
||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('base64_decode', 'base64_decode'));
|
||||
$oTwigEnv->addFilter(new TwigFilter('base64_encode', 'base64_encode'));
|
||||
$oTwigEnv->addFilter(new TwigFilter('base64_decode', 'base64_decode'));
|
||||
|
||||
// Filter to enable json decode (encode already exists)
|
||||
// Usage in twig: {{ aSomeArray|json_decode }}
|
||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('json_decode', function ($sJsonString, $bAssoc = false) {
|
||||
$oTwigEnv->addFilter(new TwigFilter('json_decode', function ($sJsonString, $bAssoc = false) {
|
||||
return json_decode($sJsonString, $bAssoc);
|
||||
})
|
||||
);
|
||||
|
||||
// Filter to add itopversion to an url
|
||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('add_itop_version', function ($sUrl) {
|
||||
$oTwigEnv->addFilter(new TwigFilter('add_itop_version', function ($sUrl) {
|
||||
$sUrl = utils::AddParameterToUrl($sUrl, 'itopversion', ITOP_VERSION);
|
||||
|
||||
return $sUrl;
|
||||
}));
|
||||
|
||||
// Filter to add a module's version to an url
|
||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('add_module_version', function ($sUrl, $sModuleName) {
|
||||
$oTwigEnv->addFilter(new TwigFilter('add_module_version', function ($sUrl, $sModuleName) {
|
||||
$sModuleVersion = utils::GetCompiledModuleVersion($sModuleName);
|
||||
$sUrl = utils::AddParameterToUrl($sUrl, 'moduleversion', $sModuleVersion);
|
||||
|
||||
@@ -105,22 +107,19 @@ class TwigExtension
|
||||
|
||||
// Function to check our current environment
|
||||
// Usage in twig: {% if is_development_environment() %}
|
||||
$oTwigEnv->addFunction(new Twig_SimpleFunction('is_development_environment', function()
|
||||
{
|
||||
$oTwigEnv->addFunction(new TwigFunction('is_development_environment', function () {
|
||||
return utils::IsDevelopmentEnvironment();
|
||||
}));
|
||||
|
||||
// Function to get the URL of a static page in a module
|
||||
// Usage in twig: {{ get_static_page_module_url('itop-my-module', 'path-to-my-page') }}
|
||||
$oTwigEnv->addFunction(new Twig_SimpleFunction('get_static_page_module_url', function($sModuleName, $sPage)
|
||||
{
|
||||
$oTwigEnv->addFunction(new TwigFunction('get_static_page_module_url', function ($sModuleName, $sPage) {
|
||||
return utils::GetAbsoluteUrlModulesRoot().$sModuleName.'/'.$sPage;
|
||||
}));
|
||||
|
||||
// Function to get the URL of a php page in a module
|
||||
// Usage in twig: {{ get_page_module_url('itop-my-module', 'path-to-my-my-page.php') }}
|
||||
$oTwigEnv->addFunction(new Twig_SimpleFunction('get_page_module_url', function($sModuleName, $sPage)
|
||||
{
|
||||
$oTwigEnv->addFunction(new TwigFunction('get_page_module_url', function ($sModuleName, $sPage) {
|
||||
return utils::GetAbsoluteUrlModulePage($sModuleName, $sPage);
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -307,7 +307,7 @@ EOF
|
||||
$sHTMLValue .= "<input class=\"field_autocomplete ibo-input ibo-input-select ibo-input-select-autocomplete\" type=\"text\" id=\"label_$this->iId\" value=\"$sDisplayValue\" placeholder='...'/>";
|
||||
|
||||
// another hidden input to store & pass the object's Id
|
||||
$sHTMLValue .= "<input type=\"hidden\" id=\"$this->iId\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" value=\"".htmlentities($value, ENT_QUOTES, 'UTF-8')."\" />\n";
|
||||
$sHTMLValue .= "<input type=\"hidden\" id=\"$this->iId\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" value=\"".utils::HtmlEntities($value)."\" />\n";
|
||||
|
||||
$JSSearchMode = $this->bSearchMode ? 'true' : 'false';
|
||||
// Scripts to start the autocomplete and bind some events to it
|
||||
@@ -619,7 +619,7 @@ EOF
|
||||
$sHTMLValue .= "<div class=\"ibo-input-select--action-buttons\"><span class=\"field_input_btn\"><div class=\"mini_button ibo-input-select--action-button\" id=\"mini_search_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.Search();\"><i class=\"fas fa-search\"></i></div></span></div>";
|
||||
|
||||
// another hidden input to store & pass the object's Id
|
||||
$sHTMLValue .= "<input type=\"hidden\" id=\"$this->iId\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" value=\"".htmlentities($value, ENT_QUOTES, 'UTF-8')."\" />\n";
|
||||
$sHTMLValue .= "<input type=\"hidden\" id=\"$this->iId\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" value=\"".utils::EscapeHtml($value)."\" />\n";
|
||||
|
||||
$JSSearchMode = $this->bSearchMode ? 'true' : 'false';
|
||||
// Scripts to start the autocomplete and bind some events to it
|
||||
@@ -685,15 +685,15 @@ JS
|
||||
}
|
||||
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
|
||||
$oBlock = new DisplayBlock($oFilter, 'search', false, $aParams);
|
||||
$oPage->AddUiBlock($oBlock->GetDisplay($oPage, $this->iId,
|
||||
$oPage->AddUiBlock($oBlock->GetDisplay($oPage, 'dtc_'.$this->iId,
|
||||
array(
|
||||
'menu' => false,
|
||||
'currentId' => $this->iId,
|
||||
'table_id' => "dr_{$this->iId}",
|
||||
'menu' => false,
|
||||
'currentId' => $this->iId,
|
||||
'table_id' => "dr_{$this->iId}",
|
||||
'table_inner_id' => "{$this->iId}_results",
|
||||
'selection_mode' => true,
|
||||
'selection_type' => 'single',
|
||||
'cssCount' => '#count_'.$this->iId.'_results',
|
||||
'cssCount' => '#count_'.$this->iId.'_results',
|
||||
)
|
||||
));
|
||||
$sCancel = Dict::S('UI:Button:Cancel');
|
||||
@@ -971,7 +971,7 @@ HTML
|
||||
foreach (MetaModel::ListAttributeDefs($this->sTargetClass) as $sAttCode => $oAttDef) {
|
||||
if (($oAttDef instanceof AttributeBlob) || (false)) {
|
||||
$aFieldsFlags[$sAttCode] = OPT_ATT_READONLY;
|
||||
$aFieldsComments[$sAttCode] = ' <img src="../images/transp-lock.png" style="vertical-align:middle" title="'.htmlentities(Dict::S('UI:UploadNotSupportedInThisMode')).'"/>';
|
||||
$aFieldsComments[$sAttCode] = ' <img src="../images/transp-lock.png" style="vertical-align:middle" title="'.utils::EscapeHtml(Dict::S('UI:UploadNotSupportedInThisMode')).'"/>';
|
||||
}
|
||||
}
|
||||
cmdbAbstractObject::DisplayCreationForm($oPage, $this->sTargetClass, $oNewObj, array(), array('formPrefix' => $this->iId, 'noRelations' => true, 'fieldsFlags' => $aFieldsFlags, 'fieldsComments' => $aFieldsComments));
|
||||
@@ -984,7 +984,7 @@ HTML
|
||||
$oPage->add_ready_script(<<<JS
|
||||
$('#ac_create_{$this->iId}').dialog({ width: $(window).width() * 0.6, height: 'auto', maxHeight: $(window).height() - 50, autoOpen: false, modal: true});
|
||||
$('#dcr_{$this->iId} form').removeAttr('onsubmit');
|
||||
$('#dcr_{$this->iId} form').on('submit.uilinksWizard', oACWidget_{$this->iId}.DoCreateObject);
|
||||
$('#dcr_{$this->iId} form').find('button[type="submit"]').on('click', oACWidget_{$this->iId}.DoCreateObject);
|
||||
JS
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlockUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Links\Direct\BlockDirectLinksEditTable;
|
||||
use Combodo\iTop\Renderer\Console\ConsoleBlockRenderer;
|
||||
|
||||
/**
|
||||
* Class UILinksWidgetDirect
|
||||
@@ -19,10 +19,12 @@ class UILinksWidgetDirect
|
||||
protected $sAttCode;
|
||||
protected $sInputid;
|
||||
protected $sNameSuffix;
|
||||
protected $aZlist;
|
||||
protected $sLinkedClass;
|
||||
|
||||
/**
|
||||
* UILinksWidgetDirect constructor.
|
||||
*
|
||||
* @param string $sClass
|
||||
* @param string $sAttCode
|
||||
* @param string $sInputId
|
||||
@@ -80,97 +82,10 @@ class UILinksWidgetDirect
|
||||
*/
|
||||
public function Display(WebPage $oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj)
|
||||
{
|
||||
if (empty($aArgs)) {
|
||||
$aArgs = [];
|
||||
}
|
||||
$oBlock = new BlockDirectLinksEditTable($this, $this->sInputid);
|
||||
$oBlock->InitTable($oPage, $oValue, $sFormPrefix);
|
||||
|
||||
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
|
||||
switch($oLinksetDef->GetEditMode())
|
||||
{
|
||||
case LINKSET_EDITMODE_NONE: // The linkset is read-only
|
||||
$this->DisplayAsBlock($oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, false /* bDisplayMenu*/);
|
||||
break;
|
||||
|
||||
case LINKSET_EDITMODE_ADDONLY: // The only possible action is to open (in a new window) the form to create a new object
|
||||
if ($oCurrentObj && !$oCurrentObj->IsNew())
|
||||
{
|
||||
$sTargetClass = $oLinksetDef->GetLinkedClass();
|
||||
$sExtKeyToMe = $oLinksetDef->GetExtKeyToMe();
|
||||
$sDefault = "default[$sExtKeyToMe]=".$oCurrentObj->GetKey();
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sParams = $oAppContext->GetForLink();
|
||||
$oPage->p("<a target=\"_blank\" href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=new&class=$sTargetClass&$sParams&{$sDefault}\">".Dict::Format('UI:ClickToCreateNew', Metamodel::GetName($sTargetClass))."</a>\n");
|
||||
}
|
||||
$this->DisplayAsBlock($oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, false /* bDisplayMenu*/);
|
||||
break;
|
||||
|
||||
case LINKSET_EDITMODE_INPLACE: // The whole linkset can be edited 'in-place'
|
||||
$this->DisplayEditInPlace($oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj);
|
||||
break;
|
||||
|
||||
case LINKSET_EDITMODE_ADDREMOVE: // The whole linkset can be edited 'in-place'
|
||||
$sTargetClass = $oLinksetDef->GetLinkedClass();
|
||||
$sExtKeyToMe = $oLinksetDef->GetExtKeyToMe();
|
||||
$oExtKeyDef = MetaModel::GetAttributeDef($sTargetClass, $sExtKeyToMe);
|
||||
$aButtons = array('add');
|
||||
if ($oExtKeyDef->IsNullAllowed())
|
||||
{
|
||||
$aButtons = array('add', 'remove');
|
||||
}
|
||||
$this->DisplayEditInPlace($oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj, $aButtons);
|
||||
break;
|
||||
|
||||
case LINKSET_EDITMODE_ACTIONS:
|
||||
default:
|
||||
$this->DisplayAsBlock($oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, true /* bDisplayMenu*/);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param DBObjectSet $oValue
|
||||
* @param array $aArgs
|
||||
* @param string $sFormPrefix
|
||||
* @param DBObject $oCurrentObj
|
||||
* @param bool $bDisplayMenu
|
||||
*
|
||||
* @since 2.7.7 3.0.1 3.1.0 N°3129 Remove default value for $aArgs for PHP 8.0 compatibility (protected method, always called with default value)
|
||||
*/
|
||||
protected function DisplayAsBlock(WebPage $oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj, $bDisplayMenu)
|
||||
{
|
||||
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
|
||||
$sTargetClass = $oLinksetDef->GetLinkedClass();
|
||||
if ($oCurrentObj && $oCurrentObj->IsNew() && $bDisplayMenu)
|
||||
{
|
||||
$oPage->p(Dict::Format('UI:BeforeAdding_Class_ObjectsSaveThisObject', MetaModel::GetName($sTargetClass)));
|
||||
}
|
||||
else
|
||||
{
|
||||
$oFilter = new DBObjectSearch($sTargetClass);
|
||||
$oFilter->AddCondition($oLinksetDef->GetExtKeyToMe(), $oCurrentObj->GetKey(),'=');
|
||||
|
||||
$aDefaults = array($oLinksetDef->GetExtKeyToMe() => $oCurrentObj->GetKey());
|
||||
$oAppContext = new ApplicationContext();
|
||||
foreach($oAppContext->GetNames() as $sKey)
|
||||
{
|
||||
// The linked object inherits the parent's value for the context
|
||||
if (MetaModel::IsValidAttCode($this->sClass, $sKey) && $oCurrentObj)
|
||||
{
|
||||
$aDefaults[$sKey] = $oCurrentObj->Get($sKey);
|
||||
}
|
||||
}
|
||||
$aParams = array(
|
||||
'target_attr' => $oLinksetDef->GetExtKeyToMe(),
|
||||
'object_id' => $oCurrentObj ? $oCurrentObj->GetKey() : null,
|
||||
'menu' => $bDisplayMenu,
|
||||
'menu_actions_target' => '_blank',
|
||||
'default' => $aDefaults,
|
||||
'table_id' => $this->sClass.'_'.$this->sAttCode,
|
||||
);
|
||||
|
||||
$oBlock = new DisplayBlock($oFilter, 'list', false);
|
||||
$oBlock->Display($oPage, $this->sInputid, $aParams);
|
||||
}
|
||||
return ConsoleBlockRenderer::RenderBlockTemplateInPage($oPage, $oBlock);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -229,55 +144,6 @@ class UILinksWidgetDirect
|
||||
$oPage->add('</div></div>');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param DBObjectSet $oValue
|
||||
* @param array $aArgs
|
||||
* @param string $sFormPrefix
|
||||
* @param DBObject $oCurrentObj
|
||||
* @param array $aButtons
|
||||
*
|
||||
* @since 2.7.7 3.0.1 3.1.0 N°3129 Remove default value for $aArgs for PHP 8.0 compatibility (protected method, caller already handles it)
|
||||
*/
|
||||
protected function DisplayEditInPlace(WebPage $oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj, $aButtons = array('create', 'delete'))
|
||||
{
|
||||
$aAttribs = $this->GetTableConfig();
|
||||
$oValue->Rewind();
|
||||
$aData = array();
|
||||
while ($oLinkObj = $oValue->Fetch()) {
|
||||
$aRow = array();
|
||||
$aRow['form::select'] = '<input type="checkbox" class="selectList'.$this->sInputid.'" value="'.$oLinkObj->GetKey().'"/>';
|
||||
foreach ($this->aZlist as $sLinkedAttCode) {
|
||||
$aRow[$sLinkedAttCode] = $oLinkObj->GetAsHTML($sLinkedAttCode);
|
||||
}
|
||||
$aData[] = $aRow;
|
||||
}
|
||||
$oDiv = UIContentBlockUIBlockFactory::MakeStandard($this->sInputid, ['listContainer']);
|
||||
$oPage->AddSubBlock($oDiv);
|
||||
$oDatatable = DataTableUIBlockFactory::MakeForForm($this->sInputid, $aAttribs, $aData);
|
||||
$oDatatable->SetOptions(['select_mode' => 'custom']);
|
||||
$oDiv->AddSubBlock($oDatatable);
|
||||
$sInputName = $sFormPrefix.'attr_'.$this->sAttCode;
|
||||
$aLabels = array(
|
||||
'delete' => Dict::S('UI:Button:Delete'),
|
||||
// 'modify' => 'Modify...' ,
|
||||
'creation_title' => Dict::Format('UI:CreationTitle_Class', MetaModel::GetName($this->sLinkedClass)),
|
||||
'create' => Dict::Format('UI:ClickToCreateNew', MetaModel::GetName($this->sLinkedClass)),
|
||||
'remove' => Dict::S('UI:Button:Remove'),
|
||||
'add' => Dict::Format('UI:AddAnExisting_Class', MetaModel::GetName($this->sLinkedClass)),
|
||||
'selection_title' => Dict::Format('UI:SelectionOf_Class', MetaModel::GetName($this->sLinkedClass)),
|
||||
);
|
||||
$oContext = new ApplicationContext();
|
||||
$sSubmitUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?'.$oContext->GetForLink();
|
||||
$sJSONLabels = json_encode($aLabels);
|
||||
$sJSONButtons = json_encode($aButtons);
|
||||
$sWizHelper = 'oWizardHelper'.$sFormPrefix;
|
||||
// Don't automatically launch the search if the table is huge
|
||||
$bDoSearch = !utils::IsHighCardinality($this->sLinkedClass);
|
||||
$sJSDoSearch = $bDoSearch ? 'true' : 'false';
|
||||
$oPage->add_ready_script("$('#{$this->sInputid}').directlinks({class_name: '$this->sClass', att_code: '$this->sAttCode', input_name:'$sInputName', labels: $sJSONLabels, submit_to: '$sSubmitUrl', buttons: $sJSONButtons, oWizardHelper: $sWizHelper, do_search: $sJSDoSearch});");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param DBObject $oCurrentObj
|
||||
@@ -433,18 +299,21 @@ HTML
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected function GetTableConfig()
|
||||
|
||||
public function GetTableConfig()
|
||||
{
|
||||
$aAttribs = array();
|
||||
$aAttribs['form::select'] = array('label' => "<input type=\"checkbox\" onClick=\"CheckAll('.selectList{$this->sInputid}:not(:disabled)', this.checked);\" class=\"checkAll\"></input>", 'description' => Dict::S('UI:SelectAllToggle+'));
|
||||
$aAttribs['form::select'] = array(
|
||||
'label' => "<input type=\"checkbox\" onClick=\"CheckAll('.selectList{$this->sInputid}:not(:disabled)', this.checked);oWidget".$this->sInputid.".directlinks('instance')._onSelectChange();\" class=\"checkAll\"></input>",
|
||||
'description' => Dict::S('UI:SelectAllToggle+'),
|
||||
);
|
||||
|
||||
foreach($this->aZlist as $sLinkedAttCode)
|
||||
{
|
||||
foreach ($this->aZlist as $sLinkedAttCode) {
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->sLinkedClass, $sLinkedAttCode);
|
||||
$aAttribs[$sLinkedAttCode] = array('label' => MetaModel::GetLabel($this->sLinkedClass, $sLinkedAttCode), 'description' => $oAttDef->GetOrderByHint());
|
||||
}
|
||||
return $aAttribs;
|
||||
|
||||
return $aAttribs;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -543,12 +412,43 @@ HTML
|
||||
{
|
||||
$sAttCode = call_user_func($aCallSpec, $key); // Returns null when there is no mapping for this parameter
|
||||
}
|
||||
|
||||
if (MetaModel::IsValidAttCode($sDestClass, $sAttCode) && !empty($defaultValue))
|
||||
{
|
||||
|
||||
if (MetaModel::IsValidAttCode($sDestClass, $sAttCode) && !empty($defaultValue)) {
|
||||
$oSearch->AddCondition($sAttCode, $defaultValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function GetClass(): string
|
||||
{
|
||||
return $this->sClass;
|
||||
}
|
||||
|
||||
public function GetLinkedClass(): string
|
||||
{
|
||||
return $this->sLinkedClass;
|
||||
}
|
||||
|
||||
public function GetAttCode(): string
|
||||
{
|
||||
return $this->sAttCode;
|
||||
}
|
||||
|
||||
public function GetInputId(): string
|
||||
{
|
||||
return $this->sInputid;
|
||||
}
|
||||
|
||||
public function GetNameSuffix(): string
|
||||
{
|
||||
return $this->sNameSuffix;
|
||||
}
|
||||
|
||||
public function GetZList(): array
|
||||
{
|
||||
return $this->aZlist;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,18 +6,18 @@
|
||||
|
||||
use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\DataTable\StaticTable\FormTableRow\FormTableRow;
|
||||
use Combodo\iTop\Application\UI\Links\Indirect\BlockIndirectLinksEdit\BlockIndirectLinksEdit;
|
||||
use Combodo\iTop\Application\UI\Links\Indirect\BlockObjectPickerDialog\BlockObjectPickerDialog;
|
||||
use Combodo\iTop\Application\UI\Links\Indirect\BlockIndirectLinksEditTable;
|
||||
use Combodo\iTop\Application\UI\Links\Indirect\BlockObjectPickerDialog;
|
||||
use Combodo\iTop\Renderer\Console\ConsoleBlockRenderer;
|
||||
|
||||
require_once(APPROOT.'application/displayblock.class.inc.php');
|
||||
|
||||
class UILinksWidget
|
||||
class UILinksWidget
|
||||
{
|
||||
protected $m_sClass;
|
||||
protected $m_sAttCode;
|
||||
protected $m_sNameSuffix;
|
||||
protected $m_iInputId;
|
||||
protected $m_sInputId;
|
||||
protected $m_aAttributes;
|
||||
protected $m_sExtKeyToRemote;
|
||||
protected $m_sExtKeyToMe;
|
||||
@@ -33,7 +33,7 @@ class UILinksWidget
|
||||
*
|
||||
* @param string $sClass
|
||||
* @param string $sAttCode AttributeLinkedSetIndirect attcode
|
||||
* @param int $iInputId
|
||||
* @param string $sInputId
|
||||
* @param string $sNameSuffix
|
||||
* @param bool $bDuplicatesAllowed
|
||||
*
|
||||
@@ -41,13 +41,14 @@ class UILinksWidget
|
||||
* @throws \DictExceptionMissingString
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct($sClass, $sAttCode, $iInputId, $sNameSuffix = '', $bDuplicatesAllowed = false)
|
||||
public function __construct($sClass, $sAttCode, $sInputId, $sNameSuffix = '', $bDuplicatesAllowed = false)
|
||||
{
|
||||
$this->m_sClass = $sClass;
|
||||
$this->m_sAttCode = $sAttCode;
|
||||
$this->m_sInputId = $sInputId;
|
||||
$this->m_sNameSuffix = $sNameSuffix;
|
||||
$this->m_iInputId = $iInputId;
|
||||
$this->m_bDuplicatesAllowed = $bDuplicatesAllowed;
|
||||
|
||||
$this->m_aEditableFields = array();
|
||||
|
||||
/** @var AttributeLinkedSetIndirect $oAttDef */
|
||||
@@ -63,7 +64,7 @@ class UILinksWidget
|
||||
$this->m_aEditableFields = array();
|
||||
$this->m_aTableConfig = array();
|
||||
$this->m_aTableConfig['form::checkbox'] = array(
|
||||
'label' => "<input class=\"select_all\" type=\"checkbox\" value=\"1\" onClick=\"CheckAll('#linkedset_{$this->m_sAttCode}{$this->m_sNameSuffix} .selection', this.checked); oWidget".$this->m_iInputId.".OnSelectChange();\">",
|
||||
'label' => "<input class=\"select_all\" type=\"checkbox\" value=\"1\" onClick=\"CheckAll('#linkedset_{$this->m_sAttCode}{$this->m_sNameSuffix} .selection', this.checked); oWidget".$this->m_sInputId.".OnSelectChange();\">",
|
||||
'description' => Dict::S('UI:SelectAllToggle+'),
|
||||
);
|
||||
|
||||
@@ -91,234 +92,13 @@ class UILinksWidget
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A one-row form for editing a link record
|
||||
*
|
||||
* @param WebPage $oP Web page used for the ouput
|
||||
* @param DBObject $oLinkedObj Remote object
|
||||
* @param DBObject|int $linkObjOrId Either the lnk object or a unique number for new link records to add
|
||||
* @param array $aArgs Extra context arguments
|
||||
* @param DBObject $oCurrentObj The object to which all the elements of the linked set refer to
|
||||
* @param int $iUniqueId A unique identifier of new links
|
||||
* @param boolean $bReadOnly Display link as editable or read-only. Default is false (editable)
|
||||
*
|
||||
* @return array The HTML fragment of the one-row form
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function GetFormRow(WebPage $oP, DBObject $oLinkedObj, $linkObjOrId, $aArgs, $oCurrentObj, $iUniqueId, $bReadOnly = false)
|
||||
{
|
||||
$sPrefix = "$this->m_sAttCode{$this->m_sNameSuffix}";
|
||||
$aRow = array();
|
||||
$aFieldsMap = array();
|
||||
$iKey = 0;
|
||||
|
||||
if (is_object($linkObjOrId) && (!$linkObjOrId->IsNew()))
|
||||
{
|
||||
$iKey = $linkObjOrId->GetKey();
|
||||
$iRemoteObjKey = $linkObjOrId->Get($this->m_sExtKeyToRemote);
|
||||
$sPrefix .= "[$iKey][";
|
||||
$sNameSuffix = "]"; // To make a tabular form
|
||||
$aArgs['prefix'] = $sPrefix;
|
||||
$aArgs['wizHelper'] = "oWizardHelper{$this->m_iInputId}{$iKey}";
|
||||
$aArgs['this'] = $linkObjOrId;
|
||||
|
||||
if ($bReadOnly)
|
||||
{
|
||||
$aRow['form::checkbox'] = "";
|
||||
foreach ($this->m_aEditableFields as $sFieldCode)
|
||||
{
|
||||
$sDisplayValue = $linkObjOrId->GetEditValue($sFieldCode);
|
||||
$aRow[$sFieldCode] = $sDisplayValue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$aRow['form::checkbox'] = "<input class=\"selection\" data-remote-id=\"$iRemoteObjKey\" data-link-id=\"$iKey\" data-unique-id=\"$iUniqueId\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"$iKey\">";
|
||||
foreach ($this->m_aEditableFields as $sFieldCode)
|
||||
{
|
||||
$sSafeFieldId = $this->GetFieldId($linkObjOrId->GetKey(), $sFieldCode);
|
||||
$this->AddRowForFieldCode($aRow, $sFieldCode, $aArgs, $linkObjOrId, $oP, $sNameSuffix, $sSafeFieldId);
|
||||
$aFieldsMap[$sFieldCode] = $sSafeFieldId;
|
||||
}
|
||||
}
|
||||
|
||||
$sState = $linkObjOrId->GetState();
|
||||
$sRemoteKeySafeFieldId = $this->GetFieldId($aArgs['this']->GetKey(), $this->m_sExtKeyToRemote);;
|
||||
}
|
||||
else
|
||||
{
|
||||
// form for creating a new record
|
||||
if (is_object($linkObjOrId))
|
||||
{
|
||||
// New link existing only in memory
|
||||
$oNewLinkObj = $linkObjOrId;
|
||||
$iRemoteObjKey = $oNewLinkObj->Get($this->m_sExtKeyToRemote);
|
||||
$oNewLinkObj->Set($this->m_sExtKeyToMe,
|
||||
$oCurrentObj); // Setting the extkey with the object also fills the related external fields
|
||||
}
|
||||
else
|
||||
{
|
||||
$iRemoteObjKey = $linkObjOrId;
|
||||
$oNewLinkObj = MetaModel::NewObject($this->m_sLinkedClass);
|
||||
$oRemoteObj = MetaModel::GetObject($this->m_sRemoteClass, $iRemoteObjKey);
|
||||
$oNewLinkObj->Set($this->m_sExtKeyToRemote,
|
||||
$oRemoteObj); // Setting the extkey with the object alsoo fills the related external fields
|
||||
$oNewLinkObj->Set($this->m_sExtKeyToMe,
|
||||
$oCurrentObj); // Setting the extkey with the object also fills the related external fields
|
||||
}
|
||||
$sPrefix .= "[-$iUniqueId][";
|
||||
$sNameSuffix = "]"; // To make a tabular form
|
||||
$aArgs['prefix'] = $sPrefix;
|
||||
$aArgs['wizHelper'] = "oWizardHelper{$this->m_iInputId}_".($iUniqueId < 0 ? -$iUniqueId : $iUniqueId);
|
||||
$aArgs['this'] = $oNewLinkObj;
|
||||
$sInputValue = $iUniqueId > 0 ? "-$iUniqueId" : "$iUniqueId";
|
||||
$aRow['form::checkbox'] = "<input class=\"selection\" data-remote-id=\"$iRemoteObjKey\" data-link-id=\"0\" data-unique-id=\"$iUniqueId\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"$sInputValue\">";
|
||||
|
||||
if ($iUniqueId > 0)
|
||||
{
|
||||
// Rows created with ajax call need OnLinkAdded call.
|
||||
//
|
||||
$oP->add_ready_script(
|
||||
<<<EOF
|
||||
PrepareWidgets();
|
||||
oWidget{$this->m_iInputId}.OnLinkAdded($iUniqueId, $iRemoteObjKey);
|
||||
EOF
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Rows added before loading the form don't have to call OnLinkAdded.
|
||||
// Listeners are already present and DOM is not recreated
|
||||
$iPositiveUniqueId = -$iUniqueId;
|
||||
$oP->add_ready_script(<<<EOF
|
||||
oWidget{$this->m_iInputId}.AddLink($iPositiveUniqueId, $iRemoteObjKey);
|
||||
EOF
|
||||
);
|
||||
}
|
||||
|
||||
foreach($this->m_aEditableFields as $sFieldCode)
|
||||
{
|
||||
$sSafeFieldId = $this->GetFieldId($iUniqueId, $sFieldCode);
|
||||
$this->AddRowForFieldCode($aRow, $sFieldCode, $aArgs, $oNewLinkObj, $oP, $sNameSuffix, $sSafeFieldId);
|
||||
$aFieldsMap[$sFieldCode] = $sSafeFieldId;
|
||||
|
||||
$sValue = $oNewLinkObj->Get($sFieldCode);
|
||||
$oP->add_ready_script(
|
||||
<<<JS
|
||||
oWidget{$this->m_iInputId}.OnValueChange($iKey, $iUniqueId, '$sFieldCode', '$sValue');
|
||||
JS
|
||||
);
|
||||
}
|
||||
|
||||
$sState = '';
|
||||
$sRemoteKeySafeFieldId = $this->GetFieldId($iUniqueId, $this->m_sExtKeyToRemote);
|
||||
}
|
||||
|
||||
if (!$bReadOnly)
|
||||
{
|
||||
$sExtKeyToMeId = utils::GetSafeId($sPrefix.$this->m_sExtKeyToMe);
|
||||
$aFieldsMap[$this->m_sExtKeyToMe] = $sExtKeyToMeId;
|
||||
$aRow['form::checkbox'] .= "<input type=\"hidden\" id=\"$sExtKeyToMeId\" value=\"".$oCurrentObj->GetKey()."\">";
|
||||
|
||||
$sExtKeyToRemoteId = utils::GetSafeId($sPrefix.$this->m_sExtKeyToRemote);
|
||||
$aFieldsMap[$this->m_sExtKeyToRemote] = $sExtKeyToRemoteId;
|
||||
$aRow['form::checkbox'] .= "<input type=\"hidden\" id=\"$sExtKeyToRemoteId\" value=\"$iRemoteObjKey\">";
|
||||
}
|
||||
|
||||
// Adding fields from remote class
|
||||
// all fields are embedded in a span + added to $aFieldsMap array so that we can refresh them after extkey change
|
||||
$aRemoteFieldsMap = [];
|
||||
foreach (MetaModel::GetZListItems($this->m_sRemoteClass, 'list') as $sFieldCode)
|
||||
{
|
||||
$sSafeFieldId = $this->GetFieldId($aArgs['this']->GetKey(), $sFieldCode);
|
||||
$aRow['static::'.$sFieldCode] = "<span id='field_$sSafeFieldId'>".$oLinkedObj->GetAsHTML($sFieldCode).'</span>';
|
||||
$aRemoteFieldsMap[$sFieldCode] = $sSafeFieldId;
|
||||
}
|
||||
// id field is needed so that remote object could be load server side
|
||||
$aRemoteFieldsMap['id'] = $sRemoteKeySafeFieldId;
|
||||
|
||||
// Generate WizardHelper to update dependant fields
|
||||
$this->AddWizardHelperInit($oP, $aArgs['wizHelper'], $this->m_sLinkedClass, $sState, $aFieldsMap);
|
||||
//instantiate specific WizarHelper instance for remote class fields refresh
|
||||
$bHasExtKeyUpdatingRemoteClassFields = (
|
||||
array_key_exists('replaceDependenciesByRemoteClassFields', $aArgs)
|
||||
&& ($aArgs['replaceDependenciesByRemoteClassFields'])
|
||||
);
|
||||
if ($bHasExtKeyUpdatingRemoteClassFields)
|
||||
{
|
||||
$this->AddWizardHelperInit($oP, $aArgs['wizHelperRemote'], $this->m_sRemoteClass, $sState, $aRemoteFieldsMap);
|
||||
}
|
||||
|
||||
return $aRow;
|
||||
}
|
||||
|
||||
private function AddRowForFieldCode(&$aRow, $sFieldCode, &$aArgs, $oLnk, $oP, $sNameSuffix, $sSafeFieldId): void
|
||||
{
|
||||
if (($sFieldCode === $this->m_sExtKeyToRemote))
|
||||
{
|
||||
// current field is the lnk extkey to the remote class
|
||||
$aArgs['replaceDependenciesByRemoteClassFields'] = true;
|
||||
$sRowFieldCode = 'static::key';
|
||||
$aArgs['wizHelperRemote'] = $aArgs['wizHelper'].'_remote';
|
||||
$aRemoteAttDefs = MetaModel::GetZListAttDefsFilteredForIndirectRemoteClass($this->m_sRemoteClass);
|
||||
$aRemoteCodes = array_map(
|
||||
function ($value) {
|
||||
return $value->GetCode();
|
||||
},
|
||||
$aRemoteAttDefs
|
||||
);
|
||||
$aArgs['remoteCodes'] = $aRemoteCodes;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aArgs['replaceDependenciesByRemoteClassFields'] = false;
|
||||
$sRowFieldCode = $sFieldCode;
|
||||
}
|
||||
$sValue = $oLnk->Get($sFieldCode);
|
||||
$sDisplayValue = $oLnk->GetEditValue($sFieldCode);
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $sFieldCode);
|
||||
|
||||
$aRow[$sRowFieldCode] = '<div class="field_container" style="border:none;"><div class="field_data"><div class="field_value">'
|
||||
.cmdbAbstractObject::GetFormElementForField(
|
||||
$oP,
|
||||
$this->m_sLinkedClass,
|
||||
$sFieldCode,
|
||||
$oAttDef,
|
||||
$sValue,
|
||||
$sDisplayValue,
|
||||
$sSafeFieldId,
|
||||
$sNameSuffix,
|
||||
0,
|
||||
$aArgs
|
||||
)
|
||||
.'</div></div></div>';
|
||||
}
|
||||
|
||||
private function GetFieldId($iLnkId, $sFieldCode, $bSafe = true)
|
||||
{
|
||||
$sFieldId = $this->m_iInputId.'_'.$sFieldCode.'['.$iLnkId.']';
|
||||
$sFieldId = $this->m_sInputId.'_'.$sFieldCode.'['.$iLnkId.']';
|
||||
|
||||
return ($bSafe) ? utils::GetSafeId($sFieldId) : $sFieldId;
|
||||
}
|
||||
|
||||
private function AddWizardHelperInit($oP, $sWizardHelperVarName, $sWizardHelperClass, $sState, $aFieldsMap): void
|
||||
{
|
||||
$iFieldsCount = count($aFieldsMap);
|
||||
$sJsonFieldsMap = json_encode($aFieldsMap);
|
||||
|
||||
$oP->add_script(
|
||||
<<<JS
|
||||
var $sWizardHelperVarName = new WizardHelper('$sWizardHelperClass', '', '$sState');
|
||||
$sWizardHelperVarName.SetFieldsMap($sJsonFieldsMap);
|
||||
$sWizardHelperVarName.SetFieldsCount($iFieldsCount);
|
||||
$sWizardHelperVarName.SetReturnNotEditableFields(true);
|
||||
$sWizardHelperVarName.SetWizHelperJsVarName('$sWizardHelperVarName');
|
||||
JS
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the table with the form for editing all the links at once
|
||||
@@ -356,91 +136,12 @@ JS
|
||||
*/
|
||||
public function Display(WebPage $oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj): string
|
||||
{
|
||||
$sLinkedSetId = "{$this->m_sAttCode}{$this->m_sNameSuffix}";
|
||||
|
||||
$oBlock = new BlockIndirectLinksEdit("linkedset_{$sLinkedSetId}", ["ibo-block-indirect-links--edit"]);
|
||||
|
||||
$oBlock->sLinkedSetId = $sLinkedSetId;
|
||||
$oBlock->sClass = $this->m_sClass;
|
||||
$oBlock->sAttCode = $this->m_sAttCode;
|
||||
$oBlock->iInputId = $this->m_iInputId;
|
||||
$oBlock->sNameSuffix = $this->m_sNameSuffix;
|
||||
$oBlock->bDuplicates = ($this->m_bDuplicatesAllowed) ? 'true' : 'false';
|
||||
$oBlock->oWizHelper = 'oWizardHelper'.$sFormPrefix;
|
||||
$oBlock->sExtKeyToRemote = $this->m_sExtKeyToRemote;
|
||||
// Don't automatically launch the search if the table is huge
|
||||
$oBlock->bJSDoSearch = utils::IsHighCardinality($this->m_sRemoteClass) ? 'false' : 'true';
|
||||
$oBlock->sFormPrefix = $sFormPrefix;
|
||||
$oBlock->sRemoteClass = $this->m_sRemoteClass;
|
||||
|
||||
$oValue->Rewind();
|
||||
$aForm = array();
|
||||
$iMaxAddedId = 0;
|
||||
$iAddedId = -1; // Unique id for new links
|
||||
while ($oCurrentLink = $oValue->Fetch())
|
||||
{
|
||||
// We try to retrieve the remote object as usual
|
||||
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $oCurrentLink->Get($this->m_sExtKeyToRemote),
|
||||
false /* Must not be found */);
|
||||
// If successful, it means that we can edit its link
|
||||
if ($oLinkedObj !== null) {
|
||||
$bReadOnly = false;
|
||||
} // Else we retrieve it without restrictions (silos) and will display its link as readonly
|
||||
else {
|
||||
$bReadOnly = true;
|
||||
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $oCurrentLink->Get($this->m_sExtKeyToRemote), false /* Must not be found */, true);
|
||||
}
|
||||
|
||||
if ($oCurrentLink->IsNew()) {
|
||||
$key = $iAddedId--;
|
||||
} else {
|
||||
$key = $oCurrentLink->GetKey();
|
||||
}
|
||||
|
||||
$iMaxAddedId = max($iMaxAddedId, $key);
|
||||
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs, $oCurrentObj, $key, $bReadOnly);
|
||||
}
|
||||
$oBlock->iMaxAddedId = (int) $iMaxAddedId;
|
||||
|
||||
$oDataTable = DataTableUIBlockFactory::MakeForForm("{$this->m_sAttCode}{$this->m_sNameSuffix}", $this->m_aTableConfig, $aForm);
|
||||
$oDataTable->SetOptions(['select_mode' => 'custom']);
|
||||
$oBlock->AddSubBlock($oDataTable);
|
||||
|
||||
$oBlock->AddControls();
|
||||
$oBlock = new BlockIndirectLinksEditTable($this);
|
||||
$oBlock->InitTable($oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj, $this->m_aTableConfig);
|
||||
|
||||
return ConsoleBlockRenderer::RenderBlockTemplateInPage($oPage, $oBlock);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sClass
|
||||
* @param string $sAttCode
|
||||
*
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected static function GetTargetClass($sClass, $sAttCode)
|
||||
{
|
||||
/** @var AttributeLinkedSet $oAttDef */
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
$sLinkedClass = $oAttDef->GetLinkedClass();
|
||||
$sTargetClass = '';
|
||||
switch(get_class($oAttDef))
|
||||
{
|
||||
case 'AttributeLinkedSetIndirect':
|
||||
/** @var AttributeExternalKey $oLinkingAttDef */
|
||||
/** @var AttributeLinkedSetIndirect $oAttDef */
|
||||
$oLinkingAttDef = MetaModel::GetAttributeDef($sLinkedClass, $oAttDef->GetExtKeyToRemote());
|
||||
$sTargetClass = $oLinkingAttDef->GetTargetClass();
|
||||
break;
|
||||
|
||||
case 'AttributeLinkedSet':
|
||||
$sTargetClass = $sLinkedClass;
|
||||
break;
|
||||
}
|
||||
|
||||
return $sTargetClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param DBObject $oCurrentObj
|
||||
@@ -470,15 +171,10 @@ JS
|
||||
$oCurrentObj->PrefillForm('search', $aPrefillFormParam);
|
||||
}
|
||||
|
||||
$sLinkedSetId = "{$this->m_sAttCode}{$this->m_sNameSuffix}";
|
||||
|
||||
$oBlock = new BlockObjectPickerDialog();
|
||||
$oBlock = new BlockObjectPickerDialog($this);
|
||||
$oPage->AddUiBlock($oBlock);
|
||||
|
||||
$oBlock->sLinkedSetId = $sLinkedSetId;
|
||||
$oBlock->iInputId = $this->m_iInputId;
|
||||
$oBlock->sLinkedClassName = MetaModel::GetName($this->m_sLinkedClass);
|
||||
$oBlock->sClassName = MetaModel::GetName($this->m_sClass);
|
||||
$sLinkedSetId = $oBlock->oUILinksWidget->GetLinkedSetId();
|
||||
|
||||
$oDisplayBlock = new DisplayBlock($oFilter, 'search', false);
|
||||
$oBlock->AddSubBlock($oDisplayBlock->GetDisplay($oPage, "SearchFormToAdd_{$sLinkedSetId}",
|
||||
@@ -547,7 +243,8 @@ JS
|
||||
foreach ($aLinkedObjectIds as $iObjectId) {
|
||||
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $iObjectId, false);
|
||||
if (is_object($oLinkedObj)) {
|
||||
$aRow = $this->GetFormRow($oP, $oLinkedObj, $iObjectId, array(), $oCurrentObj, $iAdditionId); // Not yet created link get negative Ids
|
||||
$oBlock = new BlockIndirectLinksEditTable($this);
|
||||
$aRow = $oBlock->GetFormRow($oP, $oLinkedObj, $iObjectId, array(), $oCurrentObj, $iAdditionId); // Not yet created link get negative Ids
|
||||
$oRow = new FormTableRow("{$this->m_sAttCode}{$this->m_sNameSuffix}", $this->m_aTableConfig, $aRow, -$iAdditionId);
|
||||
$oP->AddUiBlock($oRow);
|
||||
$iAdditionId++;
|
||||
@@ -574,7 +271,8 @@ JS
|
||||
foreach ($aLinkedObjectIds as $iObjectId) {
|
||||
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $iObjectId, false);
|
||||
if (is_object($oLinkedObj)) {
|
||||
$aRow = $this->GetFormRow($oP, $oLinkedObj, $iObjectId, array(), $oCurrentObj, $iAdditionId); // Not yet created link get negative Ids
|
||||
$oBlock = new BlockIndirectLinksEditTable($this);
|
||||
$aRow = $oBlock->GetFormRow($oP, $oLinkedObj, $iObjectId, array(), $oCurrentObj, $iAdditionId); // Not yet created link get negative Ids
|
||||
$aData = [];
|
||||
foreach ($aRow as $item) {
|
||||
$aData[] = $item;
|
||||
@@ -635,24 +333,77 @@ JS
|
||||
/** @var AttributeExternalKey $oAttDef */
|
||||
$sTargetClass = $oAttDef->GetTargetClass();
|
||||
$sHierarchicalKeyCode = MetaModel::IsHierarchicalClass($sTargetClass);
|
||||
if ($sHierarchicalKeyCode !== false)
|
||||
{
|
||||
if ($sHierarchicalKeyCode !== false) {
|
||||
$oFilter = new DBObjectSearch($sTargetClass);
|
||||
$oFilter->AddCondition('id', $defaultValue);
|
||||
$oHKFilter = new DBObjectSearch($sTargetClass);
|
||||
$oHKFilter->AddCondition_PointingTo($oFilter, $sHierarchicalKeyCode, TREE_OPERATOR_BELOW);
|
||||
$oSearch->AddCondition_PointingTo($oHKFilter, $sAttCode);
|
||||
}
|
||||
} catch (Exception $e)
|
||||
{
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
catch (Exception $e) {
|
||||
}
|
||||
} else {
|
||||
$oSearch->AddCondition($sAttCode, $defaultValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function GetLinkedSetId(): string
|
||||
{
|
||||
return "{$this->m_sAttCode}{$this->m_sNameSuffix}";
|
||||
}
|
||||
|
||||
public function GetClass(): string
|
||||
{
|
||||
return $this->m_sClass;
|
||||
}
|
||||
|
||||
public function GetLinkedClass(): string
|
||||
{
|
||||
return $this->m_sLinkedClass;
|
||||
}
|
||||
|
||||
public function GetAttCode(): string
|
||||
{
|
||||
return $this->m_sAttCode;
|
||||
}
|
||||
|
||||
public function GetInputId(): string
|
||||
{
|
||||
return $this->m_sInputId;
|
||||
}
|
||||
|
||||
public function GetNameSuffix(): string
|
||||
{
|
||||
return $this->m_sNameSuffix;
|
||||
}
|
||||
|
||||
public function IsDuplicatesAllowed(): bool
|
||||
{
|
||||
return $this->m_bDuplicatesAllowed;
|
||||
}
|
||||
|
||||
public function GetExternalKeyToRemote(): string
|
||||
{
|
||||
return $this->m_sExtKeyToRemote;
|
||||
}
|
||||
|
||||
public function GetExternalKeyToMe(): string
|
||||
{
|
||||
return $this->m_sExtKeyToMe;
|
||||
}
|
||||
|
||||
public function GetRemoteClass(): string
|
||||
{
|
||||
return $this->m_sRemoteClass;
|
||||
}
|
||||
|
||||
public function GetEditableFields(): array
|
||||
{
|
||||
return $this->m_aEditableFields;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -60,8 +60,8 @@ class UIPasswordWidget
|
||||
$sChangedValue = (($sPasswordValue != '*****') || ($sConfirmPasswordValue != '*****')) ? 1 : 0;
|
||||
$sHtmlValue = '';
|
||||
$sHtmlValue .= '<div class="field_input_zone field_input_onewaypassword ibo-input-wrapper ibo-input-one-way-password-wrapper">';
|
||||
$sHtmlValue .= '<input class="ibo-input" type="password" maxlength="255" name="attr_'.$sCode.'[value]" id="'.$this->iId.'" value="'.htmlentities($sPasswordValue, ENT_QUOTES, 'UTF-8').'"/>';
|
||||
$sHtmlValue .= '<div class="ibo-input-wrapper ibo-input-wrapper--with-buttons"><input class="ibo-input" type="password" maxlength="255" id="'.$this->iId.'_confirm" value="'.htmlentities($sConfirmPasswordValue, ENT_QUOTES, 'UTF-8').'" name="attr_'.$sCode.'[confirm]"/>';
|
||||
$sHtmlValue .= '<input class="ibo-input" type="password" maxlength="255" name="attr_'.$sCode.'[value]" id="'.$this->iId.'" value="'.utils::EscapeHtml($sPasswordValue).'"/>';
|
||||
$sHtmlValue .= '<div class="ibo-input-wrapper ibo-input-wrapper--with-buttons"><input class="ibo-input" type="password" maxlength="255" id="'.$this->iId.'_confirm" value="'.utils::EscapeHtml($sConfirmPasswordValue).'" name="attr_'.$sCode.'[confirm]"/>';
|
||||
$sHtmlValue .= '<div class="ibo-input-select--action-buttons"><div class="ibo-input-select--action-button ibo-input-select--action-button--create" data-tooltip-content="'.Dict::S('UI:PasswordConfirm').'"><i class="fas fa-question-circle"></i></div></div></div>';
|
||||
$sHtmlValue .= '<button id="'.$this->iId.'_reset" class="ibo-button ibo-is-regular ibo-is-neutral" onClick="ResetPwd(\''.$this->iId.'\');">';
|
||||
$sHtmlValue .= '<span class="ibo-button--icon fas fa-undo"></span><span class="ibo-button--label">'.Dict::S('UI:Button:ResetPassword').'</span></button>';
|
||||
|
||||
@@ -69,6 +69,16 @@ class utils
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public const ENUM_SANITIZATION_FILTER_CONTEXT_PARAM = 'context_param';
|
||||
/**
|
||||
* @var string To filter routes passed to back-end router before being redirected to corresponding controller / method
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public const ENUM_SANITIZATION_FILTER_ROUTE = 'route';
|
||||
/**
|
||||
* @var string To filter operation codes passed to back-end router before being redirected to corresponding controller (/ business logic in case of legacy operations)
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public const ENUM_SANITIZATION_FILTER_OPERATION = 'operation';
|
||||
/**
|
||||
* @var string
|
||||
* @since 3.0.0
|
||||
@@ -406,6 +416,8 @@ class utils
|
||||
break;
|
||||
|
||||
case static::ENUM_SANITIZATION_FILTER_CONTEXT_PARAM:
|
||||
case static::ENUM_SANITIZATION_FILTER_ROUTE:
|
||||
case static::ENUM_SANITIZATION_FILTER_OPERATION:
|
||||
case static::ENUM_SANITIZATION_FILTER_PARAMETER:
|
||||
case static::ENUM_SANITIZATION_FILTER_FIELD_NAME:
|
||||
case static::ENUM_SANITIZATION_FILTER_TRANSACTION_ID:
|
||||
@@ -427,27 +439,31 @@ class utils
|
||||
switch ($sSanitizationFilter)
|
||||
{
|
||||
case static::ENUM_SANITIZATION_FILTER_TRANSACTION_ID:
|
||||
// same as parameter type but keep the dot character
|
||||
// see N°1835 : when using file transaction_id on Windows you get *.tmp tokens
|
||||
// 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_=-]*$/')));
|
||||
// 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_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)
|
||||
$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
|
||||
$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_=%:+-]*$/')));
|
||||
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP, array("options" => array("regexp" => '/^[ A-Za-z0-9_=%:+-]*$/')));
|
||||
break;
|
||||
|
||||
}
|
||||
@@ -813,21 +829,20 @@ class utils
|
||||
*/
|
||||
public static function StringToTime($sDate, $sFormat)
|
||||
{
|
||||
// Source: http://php.net/manual/fr/function.strftime.php
|
||||
// Source: http://php.net/manual/fr/function.strftime.php
|
||||
// (alternative: http://www.php.net/manual/fr/datetime.formats.date.php)
|
||||
static $aDateTokens = null;
|
||||
static $aDateRegexps = null;
|
||||
if (is_null($aDateTokens))
|
||||
{
|
||||
$aSpec = array(
|
||||
'%d' =>'(?<day>[0-9]{2})',
|
||||
if (is_null($aDateTokens)) {
|
||||
$aSpec = array(
|
||||
'%d' => '(?<day>[0-9]{2})',
|
||||
'%m' => '(?<month>[0-9]{2})',
|
||||
'%y' => '(?<year>[0-9]{2})',
|
||||
'%Y' => '(?<year>[0-9]{4})',
|
||||
'%H' => '(?<hour>[0-2][0-9])',
|
||||
'%i' => '(?<minute>[0-5][0-9])',
|
||||
'%s' => '(?<second>[0-5][0-9])',
|
||||
);
|
||||
);
|
||||
$aDateTokens = array_keys($aSpec);
|
||||
$aDateRegexps = array_values($aSpec);
|
||||
}
|
||||
@@ -859,11 +874,50 @@ class utils
|
||||
*/
|
||||
public static function DateTimeFormatToPHP($sOldDateTimeFormat)
|
||||
{
|
||||
$aSearch = array('%d', '%m', '%y', '%Y', '%H', '%i', '%s');
|
||||
$aReplacement = array('d', 'm', 'y', 'Y', 'H', 'i', 's');
|
||||
$aSearch = ['%d', '%m', '%y', '%Y', '%H', '%i', '%s'];
|
||||
$aReplacement = ['d', 'm', 'y', 'Y', 'H', 'i', 's'];
|
||||
return str_replace($aSearch, $aReplacement, $sOldDateTimeFormat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an old strtime() date/time format specification {@link https://www.php.net/manual/fr/function.strftime.php}
|
||||
* to a format compatible with \DateTime::format {@link https://www.php.net/manual/fr/datetime.format.php}
|
||||
*
|
||||
* Example: '%Y-%m-%d %H:%M:%S' => 'Y-m-d H:i:s'
|
||||
*
|
||||
* Note: Not all strftime() formats can be converted, in which case they will be present in the returned string (eg. '%U' or '%W')
|
||||
*
|
||||
* @param string $sOldStrftimeFormat
|
||||
*
|
||||
* @return string
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public static function StrftimeFormatToDateTimeFormat(string $sOldStrftimeFormat): string
|
||||
{
|
||||
$aSearch = [
|
||||
'%d', '%m', '%y', '%Y', '%H', '%M', '%S', // Most popular formats
|
||||
'%a', '%A', '%e', '%j', '%u', '%w', // Day formats
|
||||
'%U', '%V', '%W', // Week formats
|
||||
'%b', '%B', '%h', // Month formats
|
||||
'%C', '%g', '%G', // Year formats
|
||||
'%k', '%I', '%l', '%p', '%P', '%r', '%R', '%T', '%X', '%z', '%Z', // Time formats
|
||||
'%c', '%D', '%F', '%s', '%x', // Datetime formats
|
||||
'%n', '%t', '%%', // Misc. formats
|
||||
];
|
||||
$aReplacement = [
|
||||
'd', 'm', 'y', 'Y', 'H', 'i', 's',
|
||||
'D', 'l', 'j', 'z', 'N', 'w',
|
||||
'%U', 'W', '%W',
|
||||
'M', 'F', 'M',
|
||||
'%C', 'y', 'Y',
|
||||
'G', 'h', 'g', 'A', 'a', 'h:i:s A', 'H:i', 'H:i:s', '%X', 'O', 'T',
|
||||
'%c', 'm/d/y', 'Y-m-d', 'U', '%x',
|
||||
'%n', '%t', '%',
|
||||
];
|
||||
|
||||
return str_replace($aSearch, $aReplacement, $sOldStrftimeFormat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow to set cached config. Useful when running with {@link Parameters} for example.
|
||||
* @param \Config $oConfig
|
||||
@@ -1395,71 +1449,83 @@ class utils
|
||||
*/
|
||||
public static function GetPopupMenuItemsBlock(iUIBlock &$oContainerBlock, $iMenuId, $param, &$aActions, $sDataTableId = null)
|
||||
{
|
||||
$aResult = [];
|
||||
|
||||
// 1st - add standard built-in menu items
|
||||
//
|
||||
switch($iMenuId)
|
||||
{
|
||||
case iPopupMenuExtension::MENU_OBJLIST_TOOLKIT:
|
||||
// $param is a DBObjectSet
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sContext = $oAppContext->GetForLink();
|
||||
$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}";
|
||||
$oContainerBlock->AddJsFileRelPath('js/tabularfieldsselector.js');
|
||||
$oContainerBlock->AddJsFileRelPath('js/jquery.dragtable.js');
|
||||
$oContainerBlock->AddCssFileRelPath('css/dragtable.css');
|
||||
case iPopupMenuExtension::MENU_OBJLIST_ACTIONS:
|
||||
// No native action there yet
|
||||
break;
|
||||
|
||||
$aResult = array();
|
||||
if (strlen($sUrl) < SERVER_MAX_URL_LENGTH)
|
||||
{
|
||||
$aResult[] = new SeparatorPopupMenuItem();
|
||||
// Static menus: Email this page, CSV Export & Add to Dashboard
|
||||
$aResult[] = new URLPopupMenuItem('UI:Menu:EMail', Dict::S('UI:Menu:EMail'),
|
||||
"mailto:?body=".urlencode($sUrl).' ' // Add an extra space to make it work in Outlook
|
||||
);
|
||||
}
|
||||
|
||||
if (UserRights::IsActionAllowed($param->GetFilter()->GetClass(), UR_ACTION_BULK_READ, $param) != UR_ALLOWED_NO)
|
||||
{
|
||||
// Bulk export actions
|
||||
$aResult[] = new JSPopupMenuItem('UI:Menu:CSVExport', Dict::S('UI:Menu:CSVExport'), "ExportListDlg('$sOQL', '$sDataTableId', 'csv', ".json_encode(Dict::S('UI:Menu:CSVExport')).")");
|
||||
$aResult[] = new JSPopupMenuItem('UI:Menu:ExportXLSX', Dict::S('ExcelExporter:ExportMenu'), "ExportListDlg('$sOQL', '$sDataTableId', 'xlsx', ".json_encode(Dict::S('ExcelExporter:ExportMenu')).")");
|
||||
if (extension_loaded('gd'))
|
||||
{
|
||||
// PDF export requires GD
|
||||
$aResult[] = new JSPopupMenuItem('UI:Menu:ExportPDF', Dict::S('UI:Menu:ExportPDF'), "ExportListDlg('$sOQL', '$sDataTableId', 'pdf', ".json_encode(Dict::S('UI:Menu:ExportPDF')).")");
|
||||
case iPopupMenuExtension::MENU_OBJLIST_TOOLKIT:
|
||||
/** @var \DBObjectSet $param */
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sContext = $oAppContext->GetForLink();
|
||||
$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}";
|
||||
$oContainerBlock->AddJsFileRelPath('js/tabularfieldsselector.js');
|
||||
$oContainerBlock->AddJsFileRelPath('js/jquery.dragtable.js');
|
||||
$oContainerBlock->AddCssFileRelPath('css/dragtable.css');
|
||||
|
||||
// Configure this list on datatables
|
||||
if (utils::IsNotNullOrEmptyString($sDataTableId)) {
|
||||
$aResult[] = new JSPopupMenuItem(
|
||||
'iTop::ConfigureList',
|
||||
Dict::S('UI:ConfigureThisList'),
|
||||
"$('#datatable_dlg_datatable_{$sDataTableId}').dialog('open'); return false;"
|
||||
);
|
||||
$aResult[] = new SeparatorPopupMenuItem();
|
||||
}
|
||||
}
|
||||
$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')");
|
||||
|
||||
break;
|
||||
|
||||
if (strlen($sUrl) < SERVER_MAX_URL_LENGTH) {
|
||||
// Static menus: Email this page, CSV Export & Add to Dashboard
|
||||
$aResult[] = new URLPopupMenuItem('UI:Menu:EMail', Dict::S('UI:Menu:EMail'),
|
||||
"mailto:?body=".urlencode($sUrl).' ' // Add an extra space to make it work in Outlook
|
||||
);
|
||||
}
|
||||
|
||||
if (UserRights::IsActionAllowed($param->GetFilter()->GetClass(), UR_ACTION_BULK_READ, $param) != UR_ALLOWED_NO) {
|
||||
// Bulk export actions
|
||||
$aResult[] = new JSPopupMenuItem('UI:Menu:CSVExport', Dict::S('UI:Menu:CSVExport'), "ExportListDlg('$sOQL', '$sDataTableId', 'csv', ".json_encode(Dict::S('UI:Menu:CSVExport')).")");
|
||||
$aResult[] = new JSPopupMenuItem('UI:Menu:ExportXLSX', Dict::S('ExcelExporter:ExportMenu'), "ExportListDlg('$sOQL', '$sDataTableId', 'xlsx', ".json_encode(Dict::S('ExcelExporter:ExportMenu')).")");
|
||||
if (extension_loaded('gd'))
|
||||
{
|
||||
// PDF export requires GD
|
||||
$aResult[] = new JSPopupMenuItem('UI:Menu:ExportPDF', Dict::S('UI:Menu:ExportPDF'), "ExportListDlg('$sOQL', '$sDataTableId', 'pdf', ".json_encode(Dict::S('UI:Menu:ExportPDF')).")");
|
||||
}
|
||||
}
|
||||
$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')");
|
||||
|
||||
break;
|
||||
|
||||
case iPopupMenuExtension::MENU_OBJDETAILS_ACTIONS:
|
||||
// $param is a DBObject
|
||||
$oObj = $param;
|
||||
$sOQL = "SELECT ".get_class($oObj)." WHERE id=".$oObj->GetKey();
|
||||
$sUrl = ApplicationContext::MakeObjectUrl(get_class($oObj), $oObj->GetKey());
|
||||
$oContainerBlock->AddJsFileRelPath('js/tabularfieldsselector.js');
|
||||
$oContainerBlock->AddJsFileRelPath('js/jquery.dragtable.js');
|
||||
$oContainerBlock->AddCssFileRelPath('css/dragtable.css');
|
||||
$oContainerBlock->AddJsFileRelPath('js/tabularfieldsselector.js');
|
||||
$oContainerBlock->AddJsFileRelPath('js/jquery.dragtable.js');
|
||||
$oContainerBlock->AddCssFileRelPath('css/dragtable.css');
|
||||
|
||||
$aResult = array(
|
||||
new SeparatorPopupMenuItem(),
|
||||
// Static menus: Email this page & CSV Export
|
||||
new URLPopupMenuItem('UI:Menu:EMail', Dict::S('UI:Menu:EMail'), "mailto:?subject=".urlencode($oObj->GetRawName())."&body=".urlencode($sUrl).' '), // Add an extra space to make it work in Outlook
|
||||
new JSPopupMenuItem('UI:Menu:CSVExport', Dict::S('UI:Menu:CSVExport'), "ExportListDlg('$sOQL', '', 'csv', ".json_encode(Dict::S('UI:Menu:CSVExport')).")"),
|
||||
new JSPopupMenuItem('UI:Menu:ExportXLSX', Dict::S('ExcelExporter:ExportMenu'), "ExportListDlg('$sOQL', '', 'xlsx', ".json_encode(Dict::S('ExcelExporter:ExportMenu')).")"),
|
||||
new SeparatorPopupMenuItem(),
|
||||
new URLPopupMenuItem('UI:Menu:PrintableVersion', Dict::S('UI:Menu:PrintableVersion'), $sUrl.'&printable=1', '_blank'),
|
||||
);
|
||||
break;
|
||||
/** @var \DBObject $param */
|
||||
$oObj = $param;
|
||||
$sOQL = "SELECT ".get_class($oObj)." WHERE id=".$oObj->GetKey();
|
||||
$sUrl = ApplicationContext::MakeObjectUrl(get_class($oObj), $oObj->GetKey());
|
||||
$oContainerBlock->AddJsFileRelPath('js/tabularfieldsselector.js');
|
||||
$oContainerBlock->AddJsFileRelPath('js/jquery.dragtable.js');
|
||||
$oContainerBlock->AddCssFileRelPath('css/dragtable.css');
|
||||
$oContainerBlock->AddJsFileRelPath('js/tabularfieldsselector.js');
|
||||
$oContainerBlock->AddJsFileRelPath('js/jquery.dragtable.js');
|
||||
$oContainerBlock->AddCssFileRelPath('css/dragtable.css');
|
||||
|
||||
$aResult = array(
|
||||
new SeparatorPopupMenuItem(),
|
||||
// Static menus: Email this page & CSV Export
|
||||
new URLPopupMenuItem('UI:Menu:EMail', Dict::S('UI:Menu:EMail'), "mailto:?subject=".urlencode($oObj->GetRawName())."&body=".urlencode($sUrl).' '), // Add an extra space to make it work in Outlook
|
||||
new JSPopupMenuItem('UI:Menu:CSVExport', Dict::S('UI:Menu:CSVExport'), "ExportListDlg('$sOQL', '', 'csv', ".json_encode(Dict::S('UI:Menu:CSVExport')).")"),
|
||||
new JSPopupMenuItem('UI:Menu:ExportXLSX', Dict::S('ExcelExporter:ExportMenu'), "ExportListDlg('$sOQL', '', 'xlsx', ".json_encode(Dict::S('ExcelExporter:ExportMenu')).")"),
|
||||
new SeparatorPopupMenuItem(),
|
||||
new URLPopupMenuItem('UI:Menu:PrintableVersion', Dict::S('UI:Menu:PrintableVersion'), $sUrl.'&printable=1', '_blank'),
|
||||
);
|
||||
break;
|
||||
|
||||
case iPopupMenuExtension::MENU_DASHBOARD_ACTIONS:
|
||||
// $param is a Dashboard
|
||||
@@ -1488,34 +1554,25 @@ class utils
|
||||
|
||||
default:
|
||||
// Unknown type of menu, do nothing
|
||||
$aResult = array();
|
||||
}
|
||||
foreach ($aResult as $oMenuItem)
|
||||
{
|
||||
foreach ($aResult as $oMenuItem) {
|
||||
$aActions[$oMenuItem->GetUID()] = $oMenuItem->GetMenuItem();
|
||||
}
|
||||
|
||||
// Invoke the plugins
|
||||
//
|
||||
/** @var \iPopupMenuExtension $oExtensionInstance */
|
||||
foreach (MetaModel::EnumPlugins('iPopupMenuExtension') as $oExtensionInstance)
|
||||
{
|
||||
if (is_object($param) && !($param instanceof DBObject))
|
||||
{
|
||||
foreach (MetaModel::EnumPlugins('iPopupMenuExtension') as $oExtensionInstance) {
|
||||
if (is_object($param) && !($param instanceof DBObject)) {
|
||||
$tmpParam = clone $param; // In case the parameter is an DBObjectSet, clone it to prevent alterations
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$tmpParam = $param;
|
||||
}
|
||||
foreach($oExtensionInstance->EnumItems($iMenuId, $tmpParam) as $oMenuItem)
|
||||
{
|
||||
if (is_object($oMenuItem))
|
||||
{
|
||||
foreach ($oExtensionInstance->EnumItems($iMenuId, $tmpParam) as $oMenuItem) {
|
||||
if (is_object($oMenuItem)) {
|
||||
$aActions[$oMenuItem->GetUID()] = $oMenuItem->GetMenuItem();
|
||||
|
||||
foreach($oMenuItem->GetLinkedScripts() as $sLinkedScript)
|
||||
{
|
||||
|
||||
foreach ($oMenuItem->GetLinkedScripts() as $sLinkedScript) {
|
||||
$oContainerBlock->AddJsFileRelPath($sLinkedScript);
|
||||
}
|
||||
}
|
||||
@@ -1523,6 +1580,136 @@ class utils
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We cannot use iMenuId (corresponding values in {@see \iPopupMenuExtension} constants) as value is always {@see \iPopupMenuExtension::MENU_OBJLIST_TOOLKIT}
|
||||
* whenever we are in a datatable, whereas it is included in a object tab, a dashlet or a search.
|
||||
*
|
||||
* So a {@see \ContextTag} is set on the corresponding calls.
|
||||
*
|
||||
* @return bool true if we are in a search page context, either directly or by the datatable ajax call
|
||||
*
|
||||
* @since 3.1.0 N°3200
|
||||
*
|
||||
* @uses \ContextTag::TAG_OBJECT_SEARCH
|
||||
*/
|
||||
public static function IsCurrentPageASearch(): bool
|
||||
{
|
||||
if (ContextTag::Check(ContextTag::TAG_OBJECT_SEARCH)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DBObjectSearch $oFilter object list
|
||||
* @param array $aExtraParams
|
||||
*
|
||||
* @return string|null null if we are already in a search, otherwise the URL to open this list in a search
|
||||
*
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @uses utils::IsCurrentPageASearch()
|
||||
*
|
||||
* @since 3.1.0 N°3200
|
||||
*/
|
||||
public static function GetDataTableSearchUrl(DBSearch $oFilter, array $aExtraParams): ?string
|
||||
{
|
||||
if (static::IsCurrentPageASearch()) {
|
||||
// we don't want to add the link when already in a search page !
|
||||
return null;
|
||||
}
|
||||
|
||||
$bIsObjectRelation = isset($aExtraParams['object_id'], $aExtraParams['target_attr']);
|
||||
if ($bIsObjectRelation) {
|
||||
[$oDataTableSearchFilter, $aParams] = static::GetDataTableSearchForRelations($oFilter, $aExtraParams);
|
||||
} else {
|
||||
$oDataTableSearchFilter = $oFilter;
|
||||
$aParams = [];
|
||||
}
|
||||
|
||||
if (isset($aExtraParams['table_id'])) {
|
||||
$aParams['table_id'] = $aExtraParams['table_id'];
|
||||
}
|
||||
$sParams = json_encode($aParams);
|
||||
|
||||
$sAppRootUrl = static::GetAbsoluteUrlAppRoot();
|
||||
$oAppContext = new ApplicationContext();
|
||||
|
||||
$sUrl = $sAppRootUrl
|
||||
.'pages/UI.php?operation=search&'
|
||||
.$oAppContext->GetForLink()
|
||||
.'&filter='.rawurlencode($oDataTableSearchFilter->serialize());
|
||||
$sUrl .= '&aParams='.rawurlencode($sParams); // Not working... yet, cause not handled by UI.php
|
||||
|
||||
return $sUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrites filter for object relations, so that in the search page we will have the correct criteria and will be able to use "configure this list"
|
||||
*
|
||||
* @param \DBSearch $oFilter object list
|
||||
* @param array{link_attr: string, target_attr: string, object_id: string} $aExtraParams
|
||||
*
|
||||
* @return array{\DBObjectSearch, string[]}
|
||||
* @throws \CoreException
|
||||
* @throws \OQLException
|
||||
*
|
||||
* @since 3.1.0 N°3200
|
||||
*/
|
||||
private static function GetDataTableSearchForRelations(DBSearch $oFilter, array $aExtraParams): array
|
||||
{
|
||||
$sObjectId = $aExtraParams['object_id'];
|
||||
$bIsLinkedSetIndirect = isset($aExtraParams['link_attr']);
|
||||
if ($bIsLinkedSetIndirect) {
|
||||
//--- AttributeLinkedSetIndirect (n,n => lnk class)
|
||||
$sLnkClass = $oFilter->GetClass();
|
||||
$sExtKeyToObjectClass = $aExtraParams['link_attr'];
|
||||
$sExtKeyToRemoteClass = $aExtraParams['target_attr'];
|
||||
|
||||
/** @var \AttributeExternalKey $oLnkExtKeyToRemote */
|
||||
$oLnkExtKeyToRemote = MetaModel::GetAttributeDef($sLnkClass, $sExtKeyToRemoteClass);
|
||||
$sRemoteClass = $oLnkExtKeyToRemote->GetTargetClass();
|
||||
|
||||
/** @var \AttributeExternalKey $oLnkExtKeyToRemote */
|
||||
$oLnkExtKeyToObject = MetaModel::GetAttributeDef($sLnkClass, $sExtKeyToObjectClass);
|
||||
$sObjectClass = $oLnkExtKeyToObject->GetTargetClass();
|
||||
|
||||
/** @var \AttributeExternalKey $oLnkExtKeyToRemote */
|
||||
$oObjectExtKeyToLnk = $oLnkExtKeyToObject->GetMirrorLinkAttribute();
|
||||
$sObjectExtKeyToLnkClass = $oObjectExtKeyToLnk->GetCode();
|
||||
|
||||
$sRemoteClassAliasName = ormLinkSet::REMOTE_ALIAS;
|
||||
$sLnkClassAliasName = ormLinkSet::LINK_ALIAS;
|
||||
$sOql = <<<SQL
|
||||
SELECT {$sRemoteClassAliasName},{$sLnkClassAliasName}
|
||||
FROM {$sRemoteClass} AS {$sRemoteClassAliasName}
|
||||
JOIN {$sLnkClass} AS {$sLnkClassAliasName} ON {$sLnkClassAliasName}.$sExtKeyToRemoteClass = {$sRemoteClassAliasName}.id
|
||||
WHERE {$sLnkClassAliasName}.$sExtKeyToObjectClass = $sObjectId
|
||||
SQL;
|
||||
|
||||
$aAttCodesToDisplay = MetaModel::GetAttributeLinkedSetIndirectDatatableAttCodesToDisplay($sObjectClass, $sObjectExtKeyToLnkClass, $sRemoteClass, $sExtKeyToRemoteClass);
|
||||
/** @noinspection PhpUnnecessaryLocalVariableInspection */
|
||||
$sAttCodesToDisplay = implode(',', $aAttCodesToDisplay);
|
||||
$aParams = [
|
||||
'zlist' => false,
|
||||
'extra_fields' => $sAttCodesToDisplay,
|
||||
];
|
||||
} else {
|
||||
//--- AttributeLinkedSet (1,n => AttributeExternalKey)
|
||||
$sClass = $oFilter->GetClass();
|
||||
$sExtKeyCode = $aExtraParams['target_attr'];
|
||||
|
||||
$sOql = "SELECT $sClass WHERE $sExtKeyCode = $sObjectId";
|
||||
|
||||
$aParams = [];
|
||||
}
|
||||
|
||||
$oDataTableSearchFilter = DBSearch::FromOQL($sOql);
|
||||
|
||||
return [$oDataTableSearchFilter, $aParams];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sEnvironment
|
||||
*
|
||||
@@ -1530,10 +1717,10 @@ class utils
|
||||
*/
|
||||
public static function GetConfigFilePath($sEnvironment = null)
|
||||
{
|
||||
if (is_null($sEnvironment))
|
||||
{
|
||||
if (is_null($sEnvironment)) {
|
||||
$sEnvironment = self::GetCurrentEnvironment();
|
||||
}
|
||||
|
||||
return APPCONF.$sEnvironment.'/'.ITOP_CONFIG_FILE;
|
||||
}
|
||||
|
||||
@@ -1827,7 +2014,7 @@ class utils
|
||||
*/
|
||||
public static function HtmlEntities($sValue)
|
||||
{
|
||||
return htmlentities($sValue, ENT_QUOTES, 'UTF-8');
|
||||
return htmlentities($sValue ?? '', ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1843,7 +2030,7 @@ class utils
|
||||
public static function EscapeHtml($sValue)
|
||||
{
|
||||
return htmlspecialchars(
|
||||
$sValue,
|
||||
$sValue ?? '',
|
||||
ENT_QUOTES | ENT_DISALLOWED | ENT_HTML5,
|
||||
WebPage::PAGES_CHARSET,
|
||||
false
|
||||
@@ -1892,7 +2079,8 @@ class utils
|
||||
{
|
||||
$sText = str_replace("\r\n", "\n", $sText);
|
||||
$sText = str_replace("\r", "\n", $sText);
|
||||
return str_replace("\n", '<br/>', htmlentities($sText, ENT_QUOTES, 'UTF-8'));
|
||||
|
||||
return str_replace("\n", '<br/>', utils::EscapeHtml($sText));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1956,33 +2144,23 @@ class utils
|
||||
$iCurrentMaxExecTime = (int) ini_get('max_execution_time');
|
||||
set_time_limit(0);
|
||||
// Compiling SASS
|
||||
$sCss = $oSass->compileString($sSassContent);
|
||||
$oCompilationRes = $oSass->compileString($sSassContent);
|
||||
set_time_limit(intval($iCurrentMaxExecTime));
|
||||
|
||||
return $sCss->getCss();
|
||||
return $oCompilationRes->getCss();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the size of an image from a string.
|
||||
*
|
||||
* @see \getimagesizefromstring()
|
||||
* @param $sImageData string The image data, as a string.
|
||||
*
|
||||
* @return array|false
|
||||
*/
|
||||
public static function GetImageSize($sImageData)
|
||||
{
|
||||
if (function_exists('getimagesizefromstring')) // PHP 5.4.0 or higher
|
||||
{
|
||||
$aRet = @getimagesizefromstring($sImageData);
|
||||
}
|
||||
else if(ini_get('allow_url_fopen'))
|
||||
{
|
||||
// work around to avoid creating a tmp file
|
||||
$sUri = 'data://application/octet-stream;base64,'.base64_encode($sImageData);
|
||||
$aRet = @getimagesize($sUri);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Damned, need to create a tmp file
|
||||
$sTempFile = tempnam(SetupUtils::GetTmpDir(), 'img-');
|
||||
@file_put_contents($sTempFile, $sImageData);
|
||||
$aRet = @getimagesize($sTempFile);
|
||||
@unlink($sTempFile);
|
||||
}
|
||||
return $aRet;
|
||||
return @getimagesizefromstring($sImageData);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2728,10 +2906,24 @@ HTML;
|
||||
$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(), '');
|
||||
@@ -2739,7 +2931,7 @@ HTML;
|
||||
|
||||
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;
|
||||
@@ -3021,18 +3213,35 @@ HTML;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a snake_case $sInput into a CamelCase string
|
||||
*
|
||||
* @param string $sInput
|
||||
*
|
||||
* @return string
|
||||
* @return string Camel case representation of $sInput (eg. "something_new" becomes "SomethingNew")
|
||||
* @since 2.7.0
|
||||
*/
|
||||
public static function ToCamelCase($sInput)
|
||||
public static function ToCamelCase($sInput): string
|
||||
{
|
||||
return str_replace(' ', '', ucwords(strtr($sInput, '_-', ' ')));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sInput
|
||||
*
|
||||
* @return string Snake case representation of $sInput (eg. "SomethingNew" becomes "something_new")
|
||||
* @since 3.1.0
|
||||
* @link https://stackoverflow.com/a/19533226/2710325
|
||||
*/
|
||||
public static function ToSnakeCase(string $sInput): string
|
||||
{
|
||||
// Remove special chars to join words
|
||||
$sOutput = preg_replace('/(\W)/', '_', $sInput);
|
||||
// Transform camel case words to snake case
|
||||
$sOutput = preg_replace('/[A-Z]([A-Z](?![a-z]))*/', '_$0', $sOutput);
|
||||
// Lowercase everything
|
||||
$sOutput = mb_strtolower($sOutput);
|
||||
// Trim outer underscores
|
||||
return trim($sOutput, '_');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sInput
|
||||
*
|
||||
@@ -3145,15 +3354,50 @@ HTML;
|
||||
*/
|
||||
public static function AddParameterToUrl(string $sUrl, string $sParamName, string $sParamValue): string
|
||||
{
|
||||
if (strpos($sUrl, '?') === false)
|
||||
{
|
||||
if (strpos($sUrl, '?') === false) {
|
||||
$sUrl = $sUrl.'?'.urlencode($sParamName).'='.urlencode($sParamValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$sUrl = $sUrl.'&'.urlencode($sParamName).'='.urlencode($sParamValue);
|
||||
}
|
||||
|
||||
return $sUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return traits array used by a class and by parent classes hierarchy.
|
||||
*
|
||||
* @see https://www.php.net/manual/en/function.class-uses.php#110752
|
||||
*
|
||||
* @param string $sClass Class to scan
|
||||
* @param bool $bAutoload Autoload flag
|
||||
*
|
||||
* @return array traits used
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public static function TraitsUsedByClass(string $sClass, bool $bAutoload = true): array
|
||||
{
|
||||
$aTraits = [];
|
||||
do {
|
||||
$aTraits = array_merge(class_uses($sClass, $bAutoload), $aTraits);
|
||||
} while ($sClass = get_parent_class($sClass));
|
||||
foreach ($aTraits as $sTrait => $same) {
|
||||
$aTraits = array_merge(class_uses($sTrait, $bAutoload), $aTraits);
|
||||
}
|
||||
|
||||
return array_unique($aTraits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test trait usage by a class or by parent classes hierarchy.
|
||||
*
|
||||
* @param string $sTrait Trait to search for
|
||||
* @param string $sClass Class to check
|
||||
*
|
||||
* @return bool
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public static function IsTraitUsedByClass(string $sTrait, string $sClass): bool
|
||||
{
|
||||
return in_array($sTrait, self::TraitsUsedByClass($sClass, true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/WebPage.php, now loadable using autoloader
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/WebPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
*/
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/WebPage.php, now loadable using autoloader');
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/WebPage.php, now loadable using autoloader');
|
||||
@@ -60,16 +60,20 @@ class WizardHelper
|
||||
// special handling for lists
|
||||
// assumes this is handled as an array of objects
|
||||
// thus encoded in json like: [ { name:'link1', 'id': 123}, { name:'link2', 'id': 124}...]
|
||||
$aData = json_decode($value, true); // true means decode as a hash array (not an object)
|
||||
if (!is_array($value)) {
|
||||
$aData = json_decode($value, true); // true means decode as a hash array (not an object)
|
||||
} else {
|
||||
$aData = $value;
|
||||
}
|
||||
|
||||
// Check what are the meaningful attributes
|
||||
$aFields = $this->GetLinkedWizardStructure($oAttDef);
|
||||
$sLinkedClass = $oAttDef->GetLinkedClass();
|
||||
$aLinkedObjectsArray = array();
|
||||
if (!is_array($aData))
|
||||
{
|
||||
echo ("aData: '$aData' (value: '$value')\n");
|
||||
if (!is_array($aData)) {
|
||||
echo("aData: '$aData' (value: '$value')\n");
|
||||
}
|
||||
foreach($aData as $aLinkedObject)
|
||||
foreach ($aData as $aLinkedObject)
|
||||
{
|
||||
$oLinkedObj = MetaModel::NewObject($sLinkedClass);
|
||||
foreach($aFields as $sLinkedAttCode)
|
||||
@@ -359,6 +363,10 @@ class WizardHelper
|
||||
JS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function with an old pattern of code
|
||||
* @deprecated 3.1.0
|
||||
*/
|
||||
static function ParseJsonSet($oMe, $sLinkClass, $sExtKeyToMe, $sJsonSet)
|
||||
{
|
||||
$aSet = json_decode($sJsonSet, true); // true means hash array instead of object
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/XMLPage.php, now loadable using autoloader
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/XMLPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
*/
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/XMLPage.php, now loadable using autoloader');
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/XMLPage.php, now loadable using autoloader');
|
||||
@@ -11,7 +11,7 @@ define('APPCONF', APPROOT.'conf/');
|
||||
*
|
||||
* @see ITOP_CORE_VERSION to get full iTop core version
|
||||
*/
|
||||
define('ITOP_DESIGN_LATEST_VERSION', '3.0');
|
||||
define('ITOP_DESIGN_LATEST_VERSION', '3.1');
|
||||
|
||||
/**
|
||||
* Constant containing the iTop core version, whatever application was built
|
||||
@@ -23,6 +23,6 @@ define('ITOP_DESIGN_LATEST_VERSION', '3.0');
|
||||
* @used-by utils::GetItopVersionWikiSyntax()
|
||||
* @used-by iTopModulesPhpVersionIntegrationTest
|
||||
*/
|
||||
define('ITOP_CORE_VERSION', '3.0.2');
|
||||
define('ITOP_CORE_VERSION', '3.1.0');
|
||||
|
||||
require_once APPROOT.'bootstrap.inc.php';
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"type": "project",
|
||||
"license": "AGPL-3.0-only",
|
||||
"require": {
|
||||
"php": ">=7.1.3 <8.1.0",
|
||||
"php": ">=7.4.0 <8.2.0",
|
||||
"ext-ctype": "*",
|
||||
"ext-dom": "*",
|
||||
"ext-gd": "*",
|
||||
@@ -12,27 +12,26 @@
|
||||
"ext-json": "*",
|
||||
"ext-mysqli": "*",
|
||||
"ext-soap": "*",
|
||||
"apereo/phpcas" : "~1.6.0",
|
||||
"combodo/tcpdf": "~6.4.4",
|
||||
"guzzlehttp/guzzle": "^6.5.8",
|
||||
"guzzlehttp/guzzle": "^7.4.5",
|
||||
"laminas/laminas-mail": "^2.11",
|
||||
"laminas/laminas-servicemanager": "^3.5",
|
||||
"league/oauth2-google": "^3.0",
|
||||
"nikic/php-parser": "~4.13.2",
|
||||
"nikic/php-parser": "~4.14.0",
|
||||
"pear/archive_tar": "~1.4.14",
|
||||
"pelago/emogrifier": "~3.1.0",
|
||||
"pelago/emogrifier": "^6.0.0",
|
||||
"scssphp/scssphp": "^1.10.3",
|
||||
"swiftmailer/swiftmailer": "~6.3.0",
|
||||
"symfony/console": "~3.4.47",
|
||||
"symfony/dotenv": "~3.4.47",
|
||||
"symfony/framework-bundle": "~3.4.47",
|
||||
"symfony/twig-bundle": "~3.4.47",
|
||||
"symfony/yaml": "~3.4.47",
|
||||
"thenetworg/oauth2-azure": "^2.0",
|
||||
"twig/twig": "~1.43.1"
|
||||
"symfony/console": "5.4.*",
|
||||
"symfony/dotenv": "5.4.*",
|
||||
"symfony/framework-bundle": "5.4.*",
|
||||
"symfony/twig-bundle": "5.4.*",
|
||||
"symfony/yaml": "5.4.*",
|
||||
"thenetworg/oauth2-azure": "^2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/stopwatch": "~3.4.47",
|
||||
"symfony/web-profiler-bundle": "~3.4.47"
|
||||
"symfony/stopwatch": "5.4.*",
|
||||
"symfony/web-profiler-bundle": "5.4.*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-libsodium": "Required to use the AttributeEncryptedString.",
|
||||
@@ -44,7 +43,7 @@
|
||||
},
|
||||
"config": {
|
||||
"platform": {
|
||||
"php": "7.1.3"
|
||||
"php": "7.4.0"
|
||||
},
|
||||
"vendor-dir": "lib",
|
||||
"preferred-install": {
|
||||
@@ -61,6 +60,7 @@
|
||||
"sources"
|
||||
],
|
||||
"exclude-from-classmap": [
|
||||
"application/twigextension.class.inc.php",
|
||||
"core/oql/build/PHP/",
|
||||
"core/apc-emulation.php",
|
||||
"application/startup.inc.php",
|
||||
|
||||
3481
composer.lock
generated
3481
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -470,9 +470,9 @@ class Str
|
||||
public static function pure2html($pure, $maxLength = false)
|
||||
{
|
||||
// Check for HTML entities, but be careful the DB is in UTF-8
|
||||
return $maxLength
|
||||
? htmlentities(substr($pure, 0, $maxLength), ENT_QUOTES, 'UTF-8')
|
||||
: htmlentities($pure, ENT_QUOTES, 'UTF-8');
|
||||
return $maxLength
|
||||
? utils::EscapeHtml(substr($pure, 0, $maxLength))
|
||||
: utils::EscapeHtml($pure);
|
||||
}
|
||||
public static function pure2sql($pure, $maxLength = false)
|
||||
{
|
||||
|
||||
@@ -43,22 +43,24 @@ abstract class Action extends cmdbAbstractObject
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "grant_by_profile,core/cmdb",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array('name'),
|
||||
"db_table" => "priv_action",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "realclass",
|
||||
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-in-transit.svg'),
|
||||
"category" => "grant_by_profile,core/cmdb",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"complementary_name_attcode" => array('finalclass', 'description'),
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array('name'),
|
||||
"db_table" => "priv_action",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "realclass",
|
||||
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-in-transit.svg'),
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeString("name", array("allowed_values"=>null, "sql"=>"name", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
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 AttributeEnum("status", array("allowed_values"=>new ValueSetEnum(array('test'=>'Being tested' ,'enabled'=>'In production', 'disabled'=>'Inactive')), "sql"=>"status", "default_value"=>"test", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("trigger_list", array("linked_class"=>"lnkTriggerAction", "ext_key_to_me"=>"action_id", "ext_key_to_remote"=>"trigger_id", "allowed_values"=>null, "count_min"=>0, "count_max"=>0, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeEnum("status", array("allowed_values" => new ValueSetEnum(array('test' => 'Being tested', 'enabled' => 'In production', 'disabled' => 'Inactive')), "sql" => "status", "default_value" => "test", "is_null_allowed" => false, "depends_on" => array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("trigger_list",
|
||||
array("linked_class" => "lnkTriggerAction", "ext_key_to_me" => "action_id", "ext_key_to_remote" => "trigger_id", "allowed_values" => null, "count_min" => 0, "count_max" => 0, "depends_on" => array(), "display_style" => 'property')));
|
||||
|
||||
// Display lists
|
||||
// - Attributes to be displayed for the complete details
|
||||
|
||||
@@ -409,8 +409,6 @@ class AsyncSendEmail extends AsyncTask
|
||||
$oNew->Set('to', $oEMail->GetRecipientTO(true /* string */));
|
||||
$oNew->Set('subject', $oEMail->GetSubject());
|
||||
|
||||
// $oNew->Set('version', 1);
|
||||
// $sMessage = serialize($oEMail);
|
||||
$oNew->Set('version', 2);
|
||||
$sMessage = $oEMail->SerializeV2();
|
||||
$oNew->Set('message', $sMessage);
|
||||
|
||||
@@ -5,12 +5,15 @@
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Application\UI\Base\Component\FieldBadge\FieldBadgeUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Links\Indirect\BlockLinksSetDisplayAsProperty;
|
||||
use Combodo\iTop\Form\Field\LabelField;
|
||||
use Combodo\iTop\Form\Field\TextAreaField;
|
||||
use Combodo\iTop\Form\Form;
|
||||
use Combodo\iTop\Form\Validator\NotEmptyExtKeyValidator;
|
||||
use Combodo\iTop\Form\Validator\Validator;
|
||||
use Combodo\iTop\Renderer\BlockRenderer;
|
||||
use Combodo\iTop\Renderer\Console\ConsoleBlockRenderer;
|
||||
use Combodo\iTop\Service\Links\LinkSetModel;
|
||||
|
||||
require_once('MyHelpers.class.inc.php');
|
||||
require_once('ormdocument.class.inc.php');
|
||||
@@ -89,8 +92,15 @@ define('LINKSET_EDITMODE_ACTIONS', 2); // Show the usual 'Actions' popup menu
|
||||
define('LINKSET_EDITMODE_INPLACE', 3); // The "linked" objects can be created/modified/deleted in place
|
||||
define('LINKSET_EDITMODE_ADDREMOVE', 4); // The "linked" objects can be added/removed in place
|
||||
|
||||
define('LINKSET_RELATIONTYPE_PROPERTY', 'property');
|
||||
define('LINKSET_RELATIONTYPE_LINK', 'link');
|
||||
|
||||
define('LINKSET_DISPLAY_STYLE_PROPERTY', 'property');
|
||||
define('LINKSET_DISPLAY_STYLE_TAB', 'tab');
|
||||
|
||||
/**
|
||||
* Attributes implementing this interface won't be accepted as `group by` field
|
||||
*
|
||||
* @since 2.7.4 N°3473
|
||||
*/
|
||||
interface iAttributeNoGroupBy
|
||||
@@ -136,6 +146,15 @@ abstract class AttributeDefinition
|
||||
|
||||
abstract public function GetEditClass();
|
||||
|
||||
/**
|
||||
* @return array Css classes
|
||||
* @since 3.1.0 N°3190
|
||||
*/
|
||||
public function GetCssClasses(): array
|
||||
{
|
||||
return $this->aCSSClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the search widget type corresponding to this attribute
|
||||
*
|
||||
@@ -357,6 +376,18 @@ abstract class AttributeDefinition
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the attribute can be used in bulk modify.
|
||||
*
|
||||
* @return bool
|
||||
* @since 3.1.0 N°3190
|
||||
*
|
||||
*/
|
||||
public static function IsBulkModifyCompatible(): bool
|
||||
{
|
||||
return static::IsScalar();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the attribute value is a set of related objects (1-N or N-N)
|
||||
*
|
||||
@@ -620,6 +651,16 @@ abstract class AttributeDefinition
|
||||
return $sLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool True if the attribute has a description {@see \AttributeDefinition::GetDescription()}
|
||||
* @throws \Exception
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public function HasDescription(): bool
|
||||
{
|
||||
return utils::IsNotNullOrEmptyString($this->GetDescription());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $sDefault
|
||||
*
|
||||
@@ -854,6 +895,11 @@ abstract class AttributeDefinition
|
||||
//abstract protected GetBasicFilterHTMLInput();
|
||||
abstract public function GetBasicFilterSQLExpr($sOpCode, $value);
|
||||
|
||||
/**
|
||||
* since 3.1.0 return has changed (N°4690 - Deprecate "FilterCodes")
|
||||
*
|
||||
* @return array filtercode => attributecode
|
||||
*/
|
||||
public function GetFilterDefinitions()
|
||||
{
|
||||
return array();
|
||||
@@ -1408,6 +1454,7 @@ class AttributeLinkedSet extends AttributeDefinition
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
$this->aCSSClasses[] = 'attribute-set';
|
||||
}
|
||||
|
||||
public static function ListExpectedParams()
|
||||
@@ -1421,6 +1468,12 @@ class AttributeLinkedSet extends AttributeDefinition
|
||||
return "LinkedSet";
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public static function IsBulkModifyCompatible(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function IsWritable()
|
||||
{
|
||||
return true;
|
||||
@@ -1438,7 +1491,26 @@ class AttributeLinkedSet extends AttributeDefinition
|
||||
|
||||
public function GetValuesDef()
|
||||
{
|
||||
return $this->Get("allowed_values");
|
||||
$oValSetDef = $this->Get("allowed_values");
|
||||
if (!$oValSetDef) {
|
||||
// Let's propose every existing value
|
||||
$oValSetDef = new ValueSetObjects('SELECT '.LinkSetModel::GetTargetClass($this));
|
||||
}
|
||||
|
||||
return $oValSetDef;
|
||||
}
|
||||
|
||||
public function GetEditValue($value, $oHostObj = null)
|
||||
{
|
||||
/** @var ormLinkSet $value * */
|
||||
if ($value->Count() === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
/** Return linked objects key as string **/
|
||||
$aValues = $value->GetValues();
|
||||
|
||||
return implode(' ', $aValues);
|
||||
}
|
||||
|
||||
public function GetPrerequisiteAttributes($sClass = null)
|
||||
@@ -1505,11 +1577,42 @@ class AttributeLinkedSet extends AttributeDefinition
|
||||
return $this->GetOptional('tracking_level', MetaModel::GetConfig()->Get('tracking_level_linked_set_default'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string see LINKSET_EDITMODE_* constants
|
||||
* @since 3.1.0 N°5563 relations are edited using new attributes in details mode, but as nothing changed in edit mode we are still using edit_mode attribute
|
||||
*/
|
||||
public function GetEditMode()
|
||||
{
|
||||
return $this->GetOptional('edit_mode', LINKSET_EDITMODE_ACTIONS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string see LINKSET_RELATIONTYPE_* constants
|
||||
* @since 3.1.0 N°5563
|
||||
*/
|
||||
public function GetRelationType()
|
||||
{
|
||||
return $this->GetOptional('relation_type', LINKSET_RELATIONTYPE_LINK);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string see LINKSET_DISPLAY_STYLE_* constants
|
||||
* @since 3.1.0 N°3190
|
||||
*/
|
||||
public function GetDisplayStyle()
|
||||
{
|
||||
return $this->GetOptional('display_style', LINKSET_DISPLAY_STYLE_TAB);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
* @since 3.1.0 N°5563
|
||||
*/
|
||||
public function GetReadOnly()
|
||||
{
|
||||
return $this->GetOptional('read_only', false);
|
||||
}
|
||||
|
||||
public function GetLinkedClass()
|
||||
{
|
||||
return $this->Get('linked_class');
|
||||
@@ -1535,49 +1638,30 @@ class AttributeLinkedSet extends AttributeDefinition
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sValue
|
||||
* @param \DBObject $oHostObject
|
||||
* @param bool $bLocalize
|
||||
*
|
||||
* @return string|null
|
||||
*
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
/** @inheritDoc * */
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true): string
|
||||
{
|
||||
if (is_object($sValue) && ($sValue instanceof ormLinkSet))
|
||||
{
|
||||
$sValue->Rewind();
|
||||
$aItems = array();
|
||||
while ($oObj = $sValue->Fetch())
|
||||
{
|
||||
// Show only relevant information (hide the external key to the current object)
|
||||
$aAttributes = array();
|
||||
foreach(MetaModel::ListAttributeDefs($this->GetLinkedClass()) as $sAttCode => $oAttDef)
|
||||
{
|
||||
if ($sAttCode == $this->GetExtKeyToMe())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if ($oAttDef->IsExternalField())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
$sAttValue = $oObj->GetAsHTML($sAttCode);
|
||||
if (strlen($sAttValue) > 0)
|
||||
{
|
||||
$aAttributes[] = $sAttValue;
|
||||
}
|
||||
}
|
||||
$sAttributes = implode(', ', $aAttributes);
|
||||
$aItems[] = $sAttributes;
|
||||
try {
|
||||
|
||||
/** @var ormLinkSet $sValue */
|
||||
if ($sValue->Count() === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return implode('<br/>', $aItems);
|
||||
}
|
||||
$oLinkSetBlock = new BlockLinksSetDisplayAsProperty($this->GetCode(), $this, $sValue);
|
||||
|
||||
return null;
|
||||
return ConsoleBlockRenderer::RenderBlockTemplates($oLinkSetBlock);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$sMessage = "Error while displaying attribute {$this->GetCode()}";
|
||||
IssueLog::Error($sMessage, IssueLog::CHANNEL_DEFAULT, [
|
||||
'host_object_class' => $this->GetHostClass(),
|
||||
'host_object_key' => $oHostObject->GetKey(),
|
||||
'attribute' => $this->GetCode(),
|
||||
]);
|
||||
|
||||
return $sMessage;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2284,6 +2368,15 @@ class AttributeLinkedSetIndirect extends AttributeLinkedSet
|
||||
return $this->GetOptional("duplicates", false);
|
||||
} // The same object may be linked several times... or not...
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
* @since 3.1.0 N°5563
|
||||
*/
|
||||
public function GetReadOnly()
|
||||
{
|
||||
return $this->GetOptional('read_only', false);
|
||||
}
|
||||
|
||||
public function GetTrackingLevel()
|
||||
{
|
||||
return $this->GetOptional('tracking_level',
|
||||
@@ -2321,6 +2414,13 @@ class AttributeLinkedSetIndirect extends AttributeLinkedSet
|
||||
|
||||
return $oRet;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public static function IsBulkModifyCompatible(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2452,7 +2552,7 @@ class AttributeDBFieldVoid extends AttributeDefinition
|
||||
|
||||
public function GetFilterDefinitions()
|
||||
{
|
||||
return array($this->GetCode() => new FilterFromAttribute($this));
|
||||
return array($this->GetCode() => $this->GetCode());
|
||||
}
|
||||
|
||||
public function GetBasicFilterOperators()
|
||||
@@ -4174,20 +4274,21 @@ class AttributeText extends AttributeString
|
||||
|
||||
public function GetEditValue($sValue, $oHostObj = null)
|
||||
{
|
||||
if ($this->GetFormat() == 'text')
|
||||
{
|
||||
if (preg_match_all(WIKI_OBJECT_REGEXP, $sValue, $aAllMatches, PREG_SET_ORDER))
|
||||
{
|
||||
foreach($aAllMatches as $iPos => $aMatches)
|
||||
{
|
||||
// N°4517 - PHP 8.1 compatibility: str_replace call with null cause deprecated message
|
||||
if ($sValue == null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ($this->GetFormat() == 'text') {
|
||||
if (preg_match_all(WIKI_OBJECT_REGEXP, $sValue, $aAllMatches, PREG_SET_ORDER)) {
|
||||
foreach ($aAllMatches as $iPos => $aMatches) {
|
||||
$sClass = trim($aMatches[1]);
|
||||
$sName = trim($aMatches[2]);
|
||||
$sLabel = (!empty($aMatches[4])) ? trim($aMatches[4]) : null;
|
||||
|
||||
if (MetaModel::IsValidClass($sClass))
|
||||
{
|
||||
if (MetaModel::IsValidClass($sClass)) {
|
||||
$sClassLabel = MetaModel::GetName($sClass);
|
||||
$sReplacement = "[[$sClassLabel:$sName" . (!empty($sLabel) ? " | $sLabel" : "") . "]]";
|
||||
$sReplacement = "[[$sClassLabel:$sName".(!empty($sLabel) ? " | $sLabel" : "")."]]";
|
||||
$sValue = str_replace($aMatches[0], $sReplacement, $sValue);
|
||||
}
|
||||
}
|
||||
@@ -4224,31 +4325,31 @@ class AttributeText extends AttributeString
|
||||
public function MakeRealValue($proposedValue, $oHostObj)
|
||||
{
|
||||
$sValue = $proposedValue;
|
||||
switch ($this->GetFormat())
|
||||
{
|
||||
|
||||
// N°4517 - PHP 8.1 compatibility: str_replace call with null cause deprecated message
|
||||
if ($sValue == null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
switch ($this->GetFormat()) {
|
||||
case 'html':
|
||||
if (($sValue !== null) && ($sValue !== ''))
|
||||
{
|
||||
if (($sValue !== null) && ($sValue !== '')) {
|
||||
$sValue = HTMLSanitizer::Sanitize($sValue);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'text':
|
||||
default:
|
||||
if (preg_match_all(WIKI_OBJECT_REGEXP, $sValue, $aAllMatches, PREG_SET_ORDER))
|
||||
{
|
||||
foreach($aAllMatches as $iPos => $aMatches)
|
||||
{
|
||||
if (preg_match_all(WIKI_OBJECT_REGEXP, $sValue, $aAllMatches, PREG_SET_ORDER)) {
|
||||
foreach ($aAllMatches as $iPos => $aMatches) {
|
||||
$sClassLabel = trim($aMatches[1]);
|
||||
$sName = trim($aMatches[2]);
|
||||
$sLabel = (!empty($aMatches[4])) ? trim($aMatches[4]) : null;
|
||||
$sLabel = (!empty($aMatches[4])) ? trim($aMatches[4]) : null;
|
||||
|
||||
if (!MetaModel::IsValidClass($sClassLabel))
|
||||
{
|
||||
if (!MetaModel::IsValidClass($sClassLabel)) {
|
||||
$sClass = MetaModel::GetClassFromLabel($sClassLabel);
|
||||
if ($sClass)
|
||||
{
|
||||
$sReplacement = "[[$sClassLabel:$sName" . (!empty($sLabel) ? " | $sLabel" : "") . "]]";
|
||||
if ($sClass) {
|
||||
$sReplacement = "[[$sClassLabel:$sName".(!empty($sLabel) ? " | $sLabel" : "")."]]";
|
||||
$sValue = str_replace($aMatches[0], $sReplacement, $sValue);
|
||||
}
|
||||
}
|
||||
@@ -6016,7 +6117,9 @@ class AttributeDateTime extends AttributeDBField
|
||||
|
||||
public function GetDefaultValue(DBObject $oHostObject = null)
|
||||
{
|
||||
// null value will be replaced by the current date, if not already set, in DoComputeValues
|
||||
if (!$this->IsNullAllowed()) {
|
||||
return date($this->GetInternalFormat());
|
||||
}
|
||||
return $this->GetNullValue();
|
||||
}
|
||||
|
||||
@@ -7492,7 +7595,7 @@ class AttributeExternalField extends AttributeDefinition
|
||||
|
||||
public function GetFilterDefinitions()
|
||||
{
|
||||
return array($this->GetCode() => new FilterFromAttribute($this));
|
||||
return array($this->GetCode() => $this->GetCode());
|
||||
}
|
||||
|
||||
public function GetBasicFilterOperators()
|
||||
@@ -7806,7 +7909,7 @@ class AttributeBlob extends AttributeDefinition
|
||||
|
||||
public function GetDefaultValue(DBObject $oHostObject = null)
|
||||
{
|
||||
return "";
|
||||
return new ormDocument('', '', '');
|
||||
}
|
||||
|
||||
public function IsNullAllowed(DBObject $oHostObject = null)
|
||||
@@ -8155,6 +8258,11 @@ class AttributeImage extends AttributeBlob
|
||||
return $oDoc;
|
||||
}
|
||||
|
||||
public function GetDefaultValue(DBObject $oHostObject = null)
|
||||
{
|
||||
return new ormDocument('', '', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the supplied ormDocument actually contains an image
|
||||
* {@inheritDoc}
|
||||
@@ -8529,18 +8637,17 @@ class AttributeStopWatch extends AttributeDefinition
|
||||
public function GetFilterDefinitions()
|
||||
{
|
||||
$aRes = array(
|
||||
$this->GetCode() => new FilterFromAttribute($this),
|
||||
$this->GetCode().'_started' => new FilterFromAttribute($this, '_started'),
|
||||
$this->GetCode().'_laststart' => new FilterFromAttribute($this, '_laststart'),
|
||||
$this->GetCode().'_stopped' => new FilterFromAttribute($this, '_stopped')
|
||||
$this->GetCode() => $this->GetCode(),
|
||||
$this->GetCode().'_started' => $this->GetCode(),
|
||||
$this->GetCode().'_laststart' => $this->GetCode(),
|
||||
$this->GetCode().'_stopped' => $this->GetCode(),
|
||||
);
|
||||
foreach($this->ListThresholds() as $iThreshold => $aFoo)
|
||||
{
|
||||
foreach ($this->ListThresholds() as $iThreshold => $aFoo) {
|
||||
$sPrefix = $this->GetCode().'_'.$iThreshold;
|
||||
$aRes[$sPrefix.'_deadline'] = new FilterFromAttribute($this, '_deadline');
|
||||
$aRes[$sPrefix.'_passed'] = new FilterFromAttribute($this, '_passed');
|
||||
$aRes[$sPrefix.'_triggered'] = new FilterFromAttribute($this, '_triggered');
|
||||
$aRes[$sPrefix.'_overrun'] = new FilterFromAttribute($this, '_overrun');
|
||||
$aRes[$sPrefix.'_deadline'] = $this->GetCode();
|
||||
$aRes[$sPrefix.'_passed'] = $this->GetCode();
|
||||
$aRes[$sPrefix.'_triggered'] = $this->GetCode();
|
||||
$aRes[$sPrefix.'_overrun'] = $this->GetCode();
|
||||
}
|
||||
|
||||
return $aRes;
|
||||
@@ -9282,7 +9389,7 @@ class AttributeSubItem extends AttributeDefinition
|
||||
|
||||
public function GetFilterDefinitions()
|
||||
{
|
||||
return array($this->GetCode() => new FilterFromAttribute($this));
|
||||
return array($this->GetCode() => $this->GetCode());
|
||||
}
|
||||
|
||||
public function GetBasicFilterOperators()
|
||||
@@ -11347,6 +11454,13 @@ class AttributeTagSet extends AttributeSet
|
||||
return new ormTagSet(MetaModel::GetAttributeOrigin($this->GetHostClass(), $this->GetCode()), $this->GetCode(), $this->GetMaxItems());
|
||||
}
|
||||
|
||||
public function GetDefaultValue(DBObject $oHostObject = null)
|
||||
{
|
||||
$oTagSet = new ormTagSet(MetaModel::GetAttributeOrigin($this->GetHostClass(), $this->GetCode()), $this->GetCode(), $this->GetMaxItems());
|
||||
$oTagSet->SetValues([]);
|
||||
return $oTagSet;
|
||||
}
|
||||
|
||||
public function IsNull($proposedValue)
|
||||
{
|
||||
if (is_null($proposedValue))
|
||||
@@ -11941,7 +12055,7 @@ class AttributeFriendlyName extends AttributeDefinition
|
||||
|
||||
public function GetFilterDefinitions()
|
||||
{
|
||||
return array($this->GetCode() => new FilterFromAttribute($this));
|
||||
return array($this->GetCode() => $this->GetCode());
|
||||
}
|
||||
|
||||
public function GetBasicFilterOperators()
|
||||
@@ -12778,7 +12892,7 @@ class AttributeCustomFields extends AttributeDefinition
|
||||
$sRet = $value->GetAsHTML($bLocalize);
|
||||
} catch (Exception $e)
|
||||
{
|
||||
$sRet = 'Custom field error: '.htmlentities($e->getMessage(), ENT_QUOTES, 'UTF-8');
|
||||
$sRet = 'Custom field error: '.utils::EscapeHtml($e->getMessage());
|
||||
}
|
||||
|
||||
return $sRet;
|
||||
@@ -13078,7 +13192,7 @@ class AttributeObsolescenceFlag extends AttributeBoolean
|
||||
|
||||
public function GetDefaultValue(DBObject $oHostObject = null)
|
||||
{
|
||||
return $this->MakeRealValue("", $oHostObject);
|
||||
return $this->MakeRealValue(false, $oHostObject);
|
||||
}
|
||||
|
||||
public function IsNullAllowed()
|
||||
|
||||
@@ -21,6 +21,7 @@ MetaModel::IncludeModule('application/menunode.class.inc.php');
|
||||
MetaModel::IncludeModule('application/user.preferences.class.inc.php');
|
||||
MetaModel::IncludeModule('application/user.dashboard.class.inc.php');
|
||||
MetaModel::IncludeModule('application/audit.rule.class.inc.php');
|
||||
MetaModel::IncludeModule('application/audit.domain.class.inc.php');
|
||||
MetaModel::IncludeModule('application/query.class.inc.php');
|
||||
MetaModel::IncludeModule('setup/moduleinstallation.class.inc.php');
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ define('UTF8_BOM', chr(239).chr(187).chr(191)); // 0xEF, 0xBB, 0xBF
|
||||
|
||||
/**
|
||||
* CellChangeSpec
|
||||
* A series of classes, keeping the information about a given cell: could it be changed or not (and why)?
|
||||
* A series of classes, keeping the information about a given cell: could it be changed or not (and why)?
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
@@ -42,6 +42,17 @@ abstract class CellChangeSpec
|
||||
return $this->m_sOql;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.1.0 N°5305
|
||||
*/
|
||||
public function GetDisplayableValueAndDescription(): string
|
||||
{
|
||||
return sprintf("%s%s",
|
||||
$this->GetDisplayableValue(),
|
||||
$this->GetDescription()
|
||||
);
|
||||
}
|
||||
|
||||
abstract public function GetDescription();
|
||||
}
|
||||
|
||||
@@ -86,26 +97,90 @@ class CellStatus_Issue extends CellStatus_Modify
|
||||
parent::__construct($proposedValue, $previousValue);
|
||||
}
|
||||
|
||||
public function GetDescription()
|
||||
public function GetDisplayableValue()
|
||||
{
|
||||
if (is_null($this->m_proposedValue))
|
||||
{
|
||||
return Dict::Format('UI:CSVReport-Value-SetIssue', $this->m_sReason);
|
||||
return Dict::Format('UI:CSVReport-Value-SetIssue');
|
||||
}
|
||||
return Dict::Format('UI:CSVReport-Value-ChangeIssue', $this->m_proposedValue, $this->m_sReason);
|
||||
return Dict::Format('UI:CSVReport-Value-ChangeIssue', \utils::EscapeHtml($this->m_proposedValue));
|
||||
}
|
||||
|
||||
public function GetDescription()
|
||||
{
|
||||
return $this->m_sReason;
|
||||
}
|
||||
/*
|
||||
* @since 3.1.0 N°5305
|
||||
*/
|
||||
public function GetDisplayableValueAndDescription(): string
|
||||
{
|
||||
return sprintf("%s. %s",
|
||||
$this->GetDisplayableValue(),
|
||||
$this->GetDescription()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CellStatus_SearchIssue extends CellStatus_Issue
|
||||
{
|
||||
public function __construct()
|
||||
/** @var string|null $m_sAllowedValues */
|
||||
private $m_sAllowedValues;
|
||||
|
||||
/**
|
||||
* @since 3.1.0 N°5305
|
||||
* @var string $sSerializedSearch
|
||||
*/
|
||||
private $sSerializedSearch;
|
||||
|
||||
/** @var string|null $m_sTargetClass */
|
||||
private $m_sTargetClass;
|
||||
|
||||
/**
|
||||
* CellStatus_SearchIssue constructor.
|
||||
* @since 3.1.0 N°5305
|
||||
*
|
||||
* @param string $sOql : main message
|
||||
* @param string $sReason : main message
|
||||
* @param null $sClass : used for additional message that provides allowed values for current class $sClass
|
||||
* @param null $sAllowedValues : used for additional message that provides allowed values $sAllowedValues for current class
|
||||
*/
|
||||
public function __construct($sSerializedSearch, $sReason, $sClass=null, $sAllowedValues=null)
|
||||
{
|
||||
parent::__construct(null, null, null);
|
||||
parent::__construct(null, null, $sReason);
|
||||
$this->sSerializedSearch = $sSerializedSearch;
|
||||
$this->m_sAllowedValues = $sAllowedValues;
|
||||
$this->m_sTargetClass = $sClass;
|
||||
}
|
||||
|
||||
public function GetDisplayableValue()
|
||||
{
|
||||
if (null === $this->m_sReason) {
|
||||
return Dict::Format('UI:CSVReport-Value-NoMatch', '');
|
||||
}
|
||||
|
||||
return $this->m_sReason;
|
||||
}
|
||||
|
||||
public function GetDescription()
|
||||
{
|
||||
return Dict::S('UI:CSVReport-Value-NoMatch');
|
||||
if (\utils::IsNullOrEmptyString($this->m_sAllowedValues) ||
|
||||
\utils::IsNullOrEmptyString($this->m_sTargetClass)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return Dict::Format('UI:CSVReport-Value-NoMatch-PossibleValues', $this->m_sTargetClass, $this->m_sAllowedValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.1.0 N°5305
|
||||
* @return string
|
||||
*/
|
||||
public function GetSearchLinkUrl()
|
||||
{
|
||||
return sprintf("UI.php?operation=search&filter=%s",
|
||||
rawurlencode($this->sSerializedSearch)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,11 +201,24 @@ class CellStatus_NullIssue extends CellStatus_Issue
|
||||
class CellStatus_Ambiguous extends CellStatus_Issue
|
||||
{
|
||||
protected $m_iCount;
|
||||
/**
|
||||
* @since 3.1.0 N°5305
|
||||
* @var string
|
||||
*/
|
||||
protected $sSerializedSearch;
|
||||
|
||||
public function __construct($previousValue, $iCount, $sOql)
|
||||
/**
|
||||
* @since 3.1.0 N°5305
|
||||
*
|
||||
* @param $previousValue
|
||||
* @param int $iCount
|
||||
* @param string $sSerializedSearch
|
||||
*
|
||||
*/
|
||||
public function __construct($previousValue, $iCount, $sSerializedSearch)
|
||||
{
|
||||
$this->m_iCount = $iCount;
|
||||
$this->m_sQuery = $sOql;
|
||||
$this->sSerializedSearch = $sSerializedSearch;
|
||||
parent::__construct(null, $previousValue, '');
|
||||
}
|
||||
|
||||
@@ -139,12 +227,23 @@ class CellStatus_Ambiguous extends CellStatus_Issue
|
||||
$sCount = $this->m_iCount;
|
||||
return Dict::Format('UI:CSVReport-Value-Ambiguous', $sCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.1.0 N°5305
|
||||
* @return string
|
||||
*/
|
||||
public function GetSearchLinkUrl()
|
||||
{
|
||||
return sprintf("UI.php?operation=search&filter=%s",
|
||||
rawurlencode($this->sSerializedSearch)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* RowStatus
|
||||
* A series of classes, keeping the information about a given row: could it be changed or not (and why)?
|
||||
* A series of classes, keeping the information about a given row: could it be changed or not (and why)?
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
@@ -211,6 +310,26 @@ class RowStatus_Issue extends RowStatus
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* class dedicated to testability
|
||||
* not used/ignored in csv imports UI/CLI
|
||||
* @since 3.1.0 N°5305
|
||||
*/
|
||||
class RowStatus_Error extends RowStatus
|
||||
{
|
||||
/** @var string */
|
||||
protected $m_sError;
|
||||
|
||||
public function __construct($sError)
|
||||
{
|
||||
$this->m_sError = $sError;
|
||||
}
|
||||
|
||||
public function GetDescription()
|
||||
{
|
||||
return $this->m_sError;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* BulkChange
|
||||
@@ -220,17 +339,35 @@ class RowStatus_Issue extends RowStatus
|
||||
*/
|
||||
class BulkChange
|
||||
{
|
||||
protected $m_sClass;
|
||||
/** @var string */
|
||||
protected $m_sClass;
|
||||
protected $m_aData; // Note: hereafter, iCol maybe actually be any acceptable key (string)
|
||||
// #@# todo: rename the variables to sColIndex
|
||||
protected $m_aAttList; // attcode => iCol
|
||||
protected $m_aExtKeys; // aExtKeys[sExtKeyAttCode][sExtReconcKeyAttCode] = iCol;
|
||||
protected $m_aReconcilKeys; // attcode (attcode = 'id' for the pkey)
|
||||
protected $m_sSynchroScope; // OQL - if specified, then the missing items will be reported
|
||||
protected $m_aOnDisappear; // array of attcode => value, values to be set when an object gets out of scope (ignored if no scope has been defined)
|
||||
protected $m_sDateFormat; // Date format specification, see DateTime::createFromFormat
|
||||
protected $m_bLocalizedValues; // Values in the data set are localized (see AttributeEnum)
|
||||
protected $m_aExtKeysMappingCache; // Cache for resolving external keys based on the given search criterias
|
||||
/** @var array<string, string> attcode as key, iCol as value */
|
||||
protected $m_aAttList;
|
||||
/** @var array<string, array<string, string>> sExtKeyAttCode as key, array of sExtReconcKeyAttCode/iCol as value */
|
||||
protected $m_aExtKeys;
|
||||
/** @var string[] list of attcode (attcode = 'id' for the pkey) */
|
||||
protected $m_aReconcilKeys;
|
||||
/** @var string OQL - if specified, then the missing items will be reported */
|
||||
protected $m_sSynchroScope;
|
||||
/**
|
||||
* @var array<string, mixed> attcode as key, attvalue as value. Values to be set when an object gets out of scope
|
||||
* (ignored if no scope has been defined)
|
||||
*/
|
||||
protected $m_aOnDisappear;
|
||||
/**
|
||||
* @see DateTime::createFromFormat
|
||||
* @var string Date format specification
|
||||
*/
|
||||
protected $m_sDateFormat;
|
||||
/**
|
||||
* @see AttributeEnum
|
||||
* @var boolean true if Values in the data set are localized
|
||||
*/
|
||||
protected $m_bLocalizedValues;
|
||||
/** @var array Cache for resolving external keys based on the given search criterias */
|
||||
protected $m_aExtKeysMappingCache;
|
||||
|
||||
public function __construct($sClass, $aData, $aAttList, $aExtKeys, $aReconcilKeys, $sSynchroScope = null, $aOnDisappear = null, $sDateFormat = null, $bLocalize = false)
|
||||
{
|
||||
@@ -261,30 +398,30 @@ class BulkChange
|
||||
$this->m_sReportCsvSep = $sSeparator;
|
||||
$this->m_sReportCsvDelimiter = $sDelimiter;
|
||||
}
|
||||
|
||||
|
||||
protected function ResolveExternalKey($aRowData, $sAttCode, &$aResults)
|
||||
{
|
||||
$oExtKey = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
|
||||
$oReconFilter = new DBObjectSearch($oExtKey->GetTargetClass());
|
||||
foreach ($this->m_aExtKeys[$sAttCode] as $sForeignAttCode => $iCol)
|
||||
foreach ($this->m_aExtKeys[$sAttCode] as $sReconKeyAttCode => $iCol)
|
||||
{
|
||||
if ($sForeignAttCode == 'id')
|
||||
if ($sReconKeyAttCode == 'id')
|
||||
{
|
||||
$value = (int) $aRowData[$iCol];
|
||||
}
|
||||
else
|
||||
{
|
||||
// The foreign attribute is one of our reconciliation key
|
||||
$oForeignAtt = MetaModel::GetAttributeDef($oExtKey->GetTargetClass(), $sForeignAttCode);
|
||||
$oForeignAtt = MetaModel::GetAttributeDef($oExtKey->GetTargetClass(), $sReconKeyAttCode);
|
||||
$value = $oForeignAtt->MakeValueFromString($aRowData[$iCol], $this->m_bLocalizedValues);
|
||||
}
|
||||
$oReconFilter->AddCondition($sForeignAttCode, $value, '=');
|
||||
$oReconFilter->AddCondition($sReconKeyAttCode, $value, '=');
|
||||
$aResults[$iCol] = new CellStatus_Void(utils::HtmlEntities($aRowData[$iCol]));
|
||||
}
|
||||
|
||||
$oExtObjects = new CMDBObjectSet($oReconFilter);
|
||||
$aKeys = $oExtObjects->ToArray();
|
||||
return array($oReconFilter->ToOql(), $aKeys);
|
||||
return array($oReconFilter, $aKeys);
|
||||
}
|
||||
|
||||
// Returns true if the CSV data specifies that the external key must be left undefined
|
||||
@@ -318,10 +455,10 @@ class BulkChange
|
||||
{
|
||||
$aResults = array();
|
||||
$aErrors = array();
|
||||
|
||||
|
||||
// External keys reconciliation
|
||||
//
|
||||
foreach($this->m_aExtKeys as $sAttCode => $aKeyConfig)
|
||||
foreach($this->m_aExtKeys as $sAttCode => $aReconKeys)
|
||||
{
|
||||
// Skip external keys used for the reconciliation process
|
||||
// if (!array_key_exists($sAttCode, $this->m_aAttList)) continue;
|
||||
@@ -330,7 +467,7 @@ class BulkChange
|
||||
|
||||
if ($this->IsNullExternalKeySpec($aRowData, $sAttCode))
|
||||
{
|
||||
foreach ($aKeyConfig as $sForeignAttCode => $iCol)
|
||||
foreach ($aReconKeys as $sReconKeyAttCode => $iCol)
|
||||
{
|
||||
// Default reporting
|
||||
// $aRowData[$iCol] is always null
|
||||
@@ -352,25 +489,24 @@ class BulkChange
|
||||
$oReconFilter = new DBObjectSearch($oExtKey->GetTargetClass());
|
||||
|
||||
$aCacheKeys = array();
|
||||
foreach ($aKeyConfig as $sForeignAttCode => $iCol)
|
||||
foreach ($aReconKeys as $sReconKeyAttCode => $iCol)
|
||||
{
|
||||
// The foreign attribute is one of our reconciliation key
|
||||
if ($sForeignAttCode == 'id')
|
||||
if ($sReconKeyAttCode == 'id')
|
||||
{
|
||||
$value = $aRowData[$iCol];
|
||||
}
|
||||
else
|
||||
{
|
||||
$oForeignAtt = MetaModel::GetAttributeDef($oExtKey->GetTargetClass(), $sForeignAttCode);
|
||||
$oForeignAtt = MetaModel::GetAttributeDef($oExtKey->GetTargetClass(), $sReconKeyAttCode);
|
||||
$value = $oForeignAtt->MakeValueFromString($aRowData[$iCol], $this->m_bLocalizedValues);
|
||||
}
|
||||
$aCacheKeys[] = $value;
|
||||
$oReconFilter->AddCondition($sForeignAttCode, $value, '=');
|
||||
$oReconFilter->AddCondition($sReconKeyAttCode, $value, '=');
|
||||
$aResults[$iCol] = new CellStatus_Void(utils::HtmlEntities($aRowData[$iCol]));
|
||||
}
|
||||
$sCacheKey = implode('_|_', $aCacheKeys); // Unique key for this query...
|
||||
$iForeignKey = null;
|
||||
$sOQL = '';
|
||||
// TODO: check if *too long* keys can lead to collisions... and skip the cache in such a case...
|
||||
if (!array_key_exists($sAttCode, $this->m_aExtKeysMappingCache))
|
||||
{
|
||||
@@ -379,9 +515,8 @@ class BulkChange
|
||||
if (array_key_exists($sCacheKey, $this->m_aExtKeysMappingCache[$sAttCode]))
|
||||
{
|
||||
// Cache hit
|
||||
$iCount = $this->m_aExtKeysMappingCache[$sAttCode][$sCacheKey]['c'];
|
||||
$iObjectFoundCount = $this->m_aExtKeysMappingCache[$sAttCode][$sCacheKey]['c'];
|
||||
$iForeignKey = $this->m_aExtKeysMappingCache[$sAttCode][$sCacheKey]['k'];
|
||||
$sOQL = $this->m_aExtKeysMappingCache[$sAttCode][$sCacheKey]['oql'];
|
||||
// Record the hit
|
||||
$this->m_aExtKeysMappingCache[$sAttCode][$sCacheKey]['h']++;
|
||||
}
|
||||
@@ -389,34 +524,35 @@ class BulkChange
|
||||
{
|
||||
// Cache miss, let's initialize it
|
||||
$oExtObjects = new CMDBObjectSet($oReconFilter);
|
||||
$iCount = $oExtObjects->Count();
|
||||
if ($iCount == 1)
|
||||
$iObjectFoundCount = $oExtObjects->Count();
|
||||
if ($iObjectFoundCount == 1)
|
||||
{
|
||||
$oForeignObj = $oExtObjects->Fetch();
|
||||
$iForeignKey = $oForeignObj->GetKey();
|
||||
}
|
||||
$this->m_aExtKeysMappingCache[$sAttCode][$sCacheKey] = array(
|
||||
'c' => $iCount,
|
||||
'c' => $iObjectFoundCount,
|
||||
'k' => $iForeignKey,
|
||||
'oql' => $oReconFilter->ToOql(),
|
||||
'h' => 0, // number of hits on this cache entry
|
||||
);
|
||||
}
|
||||
switch($iCount)
|
||||
switch($iObjectFoundCount)
|
||||
{
|
||||
case 0:
|
||||
$aErrors[$sAttCode] = Dict::S('UI:CSVReport-Value-Issue-NotFound');
|
||||
$aResults[$sAttCode]= new CellStatus_SearchIssue();
|
||||
break;
|
||||
|
||||
$oCellStatus_SearchIssue = $this->GetCellSearchIssue($oReconFilter);
|
||||
$aResults[$sAttCode] = $oCellStatus_SearchIssue;
|
||||
$aErrors[$sAttCode] = Dict::S('UI:CSVReport-Value-Issue-NotFound');
|
||||
break;
|
||||
|
||||
case 1:
|
||||
// Do change the external key attribute
|
||||
$oTargetObj->Set($sAttCode, $iForeignKey);
|
||||
break;
|
||||
|
||||
// Do change the external key attribute
|
||||
$oTargetObj->Set($sAttCode, $iForeignKey);
|
||||
break;
|
||||
|
||||
default:
|
||||
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-FoundMany', $iCount);
|
||||
$aResults[$sAttCode]= new CellStatus_Ambiguous($oTargetObj->Get($sAttCode), $iCount, $sOQL);
|
||||
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-FoundMany', $iObjectFoundCount);
|
||||
$aResults[$sAttCode]= new CellStatus_Ambiguous($oTargetObj->Get($sAttCode), $iObjectFoundCount, $oReconFilter->serialize());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,7 +569,7 @@ class BulkChange
|
||||
else
|
||||
{
|
||||
$aResults[$sAttCode]= new CellStatus_Modify($iForeignObj, $oTargetObj->GetOriginal($sAttCode));
|
||||
foreach ($aKeyConfig as $sForeignAttCode => $iCol)
|
||||
foreach ($aReconKeys as $sReconKeyAttCode => $iCol)
|
||||
{
|
||||
// Report the change on reconciliation values as well
|
||||
$aResults[$iCol] = new CellStatus_Modify(utils::HtmlEntities($aRowData[$iCol]));
|
||||
@@ -446,7 +582,7 @@ class BulkChange
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Set the object attributes
|
||||
//
|
||||
foreach ($this->m_aAttList as $sAttCode => $iCol)
|
||||
@@ -487,7 +623,13 @@ class BulkChange
|
||||
$value = $oAttDef->MakeValueFromString($aRowData[$iCol], $this->m_bLocalizedValues);
|
||||
if (is_null($value) && (strlen($aRowData[$iCol]) > 0))
|
||||
{
|
||||
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-NoMatch', $sAttCode);
|
||||
if ($oAttDef instanceof AttributeEnum || $oAttDef instanceof AttributeTagSet){
|
||||
/** @var AttributeDefinition $oAttributeDefinition */
|
||||
$oAttributeDefinition = $oAttDef;
|
||||
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-AllowedValues', $sAttCode, implode(',', $oAttributeDefinition->GetAllowedValues()));
|
||||
} else {
|
||||
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-NoMatch', $sAttCode);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -504,7 +646,7 @@ class BulkChange
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Reporting on fields
|
||||
//
|
||||
$aChangedFields = $oTargetObj->ListChanges();
|
||||
@@ -556,7 +698,7 @@ class BulkChange
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Checks
|
||||
//
|
||||
$res = $oTargetObj->CheckConsistency();
|
||||
@@ -567,12 +709,101 @@ class BulkChange
|
||||
}
|
||||
return $aResults;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* search with current permissions did not match
|
||||
* let's search why and give some more feedbacks to the user through proper labels
|
||||
*
|
||||
* @param DBObjectSearch $oDbSearchWithConditions search used to find external key
|
||||
*
|
||||
* @return \CellStatus_SearchIssue
|
||||
* @throws \CoreException
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
*
|
||||
* @since 3.1.0 N°5305
|
||||
*/
|
||||
protected function GetCellSearchIssue($oDbSearchWithConditions) : CellStatus_SearchIssue {
|
||||
//current search with current permissions did not match
|
||||
//let's search why and give some more feedback to the user
|
||||
|
||||
$sSerializedSearch = $oDbSearchWithConditions->serialize();
|
||||
|
||||
// Count all objects with all permissions without any condition
|
||||
$oDbSearchWithoutAnyCondition = new DBObjectSearch($oDbSearchWithConditions->GetClass());
|
||||
$oDbSearchWithoutAnyCondition->AllowAllData(true);
|
||||
$oExtObjectSet = new CMDBObjectSet($oDbSearchWithoutAnyCondition);
|
||||
$iAllowAllDataObjectCount = $oExtObjectSet->Count();
|
||||
|
||||
if ($iAllowAllDataObjectCount === 0) {
|
||||
$sReason = Dict::Format('UI:CSVReport-Value-NoMatch-NoObject', $oDbSearchWithConditions->GetClass());
|
||||
return new CellStatus_SearchIssue($sSerializedSearch, $sReason);
|
||||
}
|
||||
|
||||
// Count all objects with current user permissions
|
||||
$oDbSearchWithoutAnyCondition->AllowAllData(false);
|
||||
$oExtObjectSetWithCurrentUserPermissions = new CMDBObjectSet($oDbSearchWithoutAnyCondition);
|
||||
$iCurrentUserRightsObjectCount = $oExtObjectSetWithCurrentUserPermissions->Count();
|
||||
|
||||
if ($iCurrentUserRightsObjectCount === 0){
|
||||
// No objects visible by current user
|
||||
$sReason = Dict::Format('UI:CSVReport-Value-NoMatch-NoObject-ForCurrentUser', $oDbSearchWithConditions->GetClass());
|
||||
return new CellStatus_SearchIssue($sSerializedSearch, $sReason);
|
||||
}
|
||||
|
||||
try{
|
||||
$aDisplayedAllowedValues = [];
|
||||
// Possibles values are displayed to UI user. we have to limit the amount of displayed values
|
||||
$oExtObjectSetWithCurrentUserPermissions->SetLimit(4);
|
||||
for($i = 0; $i < 3; $i++){
|
||||
/** @var \DBObject $oVisibleObject */
|
||||
$oVisibleObject = $oExtObjectSetWithCurrentUserPermissions->Fetch();
|
||||
if (is_null($oVisibleObject)){
|
||||
break;
|
||||
}
|
||||
|
||||
$aCurrentAllowedValueFields = [];
|
||||
foreach ($oDbSearchWithConditions->GetInternalParams() as $sForeignAttCode => $sValue){
|
||||
$aCurrentAllowedValueFields[] = $oVisibleObject->Get($sForeignAttCode);
|
||||
}
|
||||
$aDisplayedAllowedValues[] = implode(" ", $aCurrentAllowedValueFields);
|
||||
|
||||
}
|
||||
$allowedValues = implode(", ", $aDisplayedAllowedValues);
|
||||
if ($oExtObjectSetWithCurrentUserPermissions->Count() > 3){
|
||||
$allowedValues .= "...";
|
||||
}
|
||||
} catch(Exception $e) {
|
||||
IssueLog::Error("failure during CSV import when fetching few visible objects: ", null,
|
||||
[ 'target_class' => $oDbSearchWithConditions->GetClass(), 'criteria' => $oDbSearchWithConditions->GetCriteria(), 'message' => $e->getMessage()]
|
||||
);
|
||||
$sReason = Dict::Format('UI:CSVReport-Value-NoMatch-NoObject-ForCurrentUser', $oDbSearchWithConditions->GetClass());
|
||||
return new CellStatus_SearchIssue($sSerializedSearch, $sReason);
|
||||
}
|
||||
|
||||
if ($iAllowAllDataObjectCount != $iCurrentUserRightsObjectCount) {
|
||||
// No match and some objects NOT visible by current user. including current search maybe...
|
||||
$sReason = Dict::Format('UI:CSVReport-Value-NoMatch-SomeObjectNotVisibleForCurrentUser', $oDbSearchWithConditions->GetClass());
|
||||
return new CellStatus_SearchIssue($sSerializedSearch, $sReason, $oDbSearchWithConditions->GetClass(), $allowedValues);
|
||||
}
|
||||
|
||||
// No match. This is not linked to any right issue
|
||||
// Possible values: DD,DD
|
||||
$aCurrentValueFields = [];
|
||||
foreach ($oDbSearchWithConditions->GetInternalParams() as $sValue){
|
||||
$aCurrentValueFields[] = $sValue;
|
||||
}
|
||||
$value =implode(" ", $aCurrentValueFields);
|
||||
$sReason = Dict::Format('UI:CSVReport-Value-NoMatch', $value);
|
||||
return new CellStatus_SearchIssue($sSerializedSearch, $sReason, $oDbSearchWithConditions->GetClass(), $allowedValues);
|
||||
}
|
||||
|
||||
protected function PrepareMissingObject(&$oTargetObj, &$aErrors)
|
||||
{
|
||||
$aResults = array();
|
||||
$aErrors = array();
|
||||
|
||||
|
||||
// External keys
|
||||
//
|
||||
foreach($this->m_aExtKeys as $sAttCode => $aKeyConfig)
|
||||
@@ -585,7 +816,7 @@ class BulkChange
|
||||
$aResults[$iCol] = new CellStatus_Void('?');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Update attributes
|
||||
//
|
||||
foreach($this->m_aOnDisappear as $sAttCode => $value)
|
||||
@@ -596,7 +827,7 @@ class BulkChange
|
||||
}
|
||||
$oTargetObj->Set($sAttCode, $value);
|
||||
}
|
||||
|
||||
|
||||
// Reporting on fields
|
||||
//
|
||||
$aChangedFields = $oTargetObj->ListChanges();
|
||||
@@ -616,7 +847,7 @@ class BulkChange
|
||||
$aResults[$iCol]= new CellStatus_Void($oTargetObj->Get($sAttCode));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Checks
|
||||
//
|
||||
$res = $oTargetObj->CheckConsistency();
|
||||
@@ -674,14 +905,16 @@ class BulkChange
|
||||
}
|
||||
|
||||
$aResult[$iRow] = $this->PrepareObject($oTargetObj, $aRowData, $aErrors);
|
||||
|
||||
|
||||
if (count($aErrors) > 0)
|
||||
{
|
||||
$sErrors = implode(', ', $aErrors);
|
||||
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-Attribute'));
|
||||
//__ERRORS__ used by tests only
|
||||
$aResult[$iRow]["__ERRORS__"] = new RowStatus_Error($sErrors);
|
||||
return $oTargetObj;
|
||||
}
|
||||
|
||||
|
||||
// Check that any external key will have a value proposed
|
||||
$aMissingKeys = array();
|
||||
foreach (MetaModel::GetExternalKeys($this->m_sClass) as $sExtKeyAttCode => $oExtKey)
|
||||
@@ -689,7 +922,7 @@ class BulkChange
|
||||
if (!$oExtKey->IsNullAllowed())
|
||||
{
|
||||
if (!array_key_exists($sExtKeyAttCode, $this->m_aExtKeys) && !array_key_exists($sExtKeyAttCode, $this->m_aAttList))
|
||||
{
|
||||
{
|
||||
$aMissingKeys[] = $oExtKey->GetLabel();
|
||||
}
|
||||
}
|
||||
@@ -745,14 +978,16 @@ class BulkChange
|
||||
{
|
||||
$sErrors = implode(', ', $aErrors);
|
||||
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-Attribute'));
|
||||
//__ERRORS__ used by tests only
|
||||
$aResult[$iRow]["__ERRORS__"] = new RowStatus_Error($sErrors);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
$aChangedFields = $oTargetObj->ListChanges();
|
||||
if (count($aChangedFields) > 0)
|
||||
{
|
||||
$aResult[$iRow]["__STATUS__"] = new RowStatus_Modify(count($aChangedFields));
|
||||
|
||||
|
||||
// Optionaly record the results
|
||||
//
|
||||
if ($oChange)
|
||||
@@ -794,9 +1029,11 @@ class BulkChange
|
||||
{
|
||||
$sErrors = implode(', ', $aErrors);
|
||||
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-Attribute'));
|
||||
//__ERRORS__ used by tests only
|
||||
$aResult[$iRow]["__ERRORS__"] = new RowStatus_Error($sErrors);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
$aChangedFields = $oTargetObj->ListChanges();
|
||||
if (count($aChangedFields) > 0)
|
||||
{
|
||||
@@ -821,7 +1058,7 @@ class BulkChange
|
||||
$aResult[$iRow]["__STATUS__"] = new RowStatus_Disappeared(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function Process(CMDBChange $oChange = null)
|
||||
{
|
||||
if ($oChange)
|
||||
@@ -866,7 +1103,7 @@ class BulkChange
|
||||
foreach ($this->m_aAttList as $sAttCode => $iCol)
|
||||
{
|
||||
if ($sAttCode == 'id') continue;
|
||||
|
||||
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
|
||||
if ($oAttDef instanceof AttributeDateTime) // AttributeDate is derived from AttributeDateTime
|
||||
{
|
||||
@@ -881,14 +1118,18 @@ class BulkChange
|
||||
$sFormat = $sDateFormat;
|
||||
}
|
||||
$oFormat = new DateTimeFormat($sFormat);
|
||||
$sDateExample = $oFormat->Format(new DateTime('2022-10-23 16:25:33'));
|
||||
$sRegExp = $oFormat->ToRegExpr('/');
|
||||
if (!preg_match($sRegExp, $this->m_aData[$iRow][$iCol]))
|
||||
$sErrorMsg = Dict::Format('UI:CSVReport-Row-Issue-ExpectedDateFormat', $sDateExample);
|
||||
if (!preg_match($sRegExp, $sValue))
|
||||
{
|
||||
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-DateFormat'));
|
||||
$aResult[$iRow][$iCol] = new CellStatus_Issue(utils::HtmlEntities($sValue), null, $sErrorMsg);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
$oDate = DateTime::createFromFormat($sFormat, $this->m_aData[$iRow][$iCol]);
|
||||
$oDate = DateTime::createFromFormat($sFormat, $sValue);
|
||||
if ($oDate !== false)
|
||||
{
|
||||
$sNewDate = $oDate->format($oAttDef->GetInternalFormat());
|
||||
@@ -898,7 +1139,7 @@ class BulkChange
|
||||
{
|
||||
// Leave the cell unchanged
|
||||
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-DateFormat'));
|
||||
$aResult[$iRow][$sAttCode] = new CellStatus_Issue(null, utils::HtmlEntities($this->m_aData[$iRow][$iCol]), Dict::S('UI:CSVReport-Row-Issue-DateFormat'));
|
||||
$aResult[$iRow][$iCol] = new CellStatus_Issue($sValue, null, $sErrorMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -919,133 +1160,112 @@ class BulkChange
|
||||
}
|
||||
$iPreviousTimeLimit = ini_get('max_execution_time');
|
||||
$iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop');
|
||||
foreach($this->m_aData as $iRow => $aRowData)
|
||||
{
|
||||
set_time_limit(intval($iLoopTimeLimit));
|
||||
if (isset($aResult[$iRow]["__STATUS__"]))
|
||||
{
|
||||
// An issue at the earlier steps - skip the rest
|
||||
continue;
|
||||
|
||||
// Avoid too many events
|
||||
cmdbAbstractObject::SetEventDBLinksChangedBlocked(true);
|
||||
try {
|
||||
foreach ($this->m_aData as $iRow => $aRowData) {
|
||||
set_time_limit(intval($iLoopTimeLimit));
|
||||
if (isset($aResult[$iRow]["__STATUS__"])) {
|
||||
// An issue at the earlier steps - skip the rest
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
$oReconciliationFilter = new DBObjectSearch($this->m_sClass);
|
||||
$bSkipQuery = false;
|
||||
foreach ($this->m_aReconcilKeys as $sAttCode) {
|
||||
$valuecondition = null;
|
||||
if (array_key_exists($sAttCode, $this->m_aExtKeys)) {
|
||||
if ($this->IsNullExternalKeySpec($aRowData, $sAttCode)) {
|
||||
$oExtKey = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
|
||||
if ($oExtKey->IsNullAllowed()) {
|
||||
$valuecondition = $oExtKey->GetNullValue();
|
||||
$aResult[$iRow][$sAttCode] = new CellStatus_Void($oExtKey->GetNullValue());
|
||||
} else {
|
||||
$aResult[$iRow][$sAttCode] = new CellStatus_NullIssue();
|
||||
}
|
||||
} else {
|
||||
// The value has to be found or verified
|
||||
|
||||
/** var DBObjectSearch $oReconFilter */
|
||||
list($oReconFilter, $aMatches) = $this->ResolveExternalKey($aRowData, $sAttCode, $aResult[$iRow]);
|
||||
|
||||
if (count($aMatches) == 1) {
|
||||
$oRemoteObj = reset($aMatches); // first item
|
||||
$valuecondition = $oRemoteObj->GetKey();
|
||||
$aResult[$iRow][$sAttCode] = new CellStatus_Void($oRemoteObj->GetKey());
|
||||
} elseif (count($aMatches) == 0) {
|
||||
$oCellStatus_SearchIssue = $this->GetCellSearchIssue($oReconFilter);
|
||||
$aResult[$iRow][$sAttCode] = $oCellStatus_SearchIssue;
|
||||
} else {
|
||||
$aResult[$iRow][$sAttCode] = new CellStatus_Ambiguous(null, count($aMatches), $oReconFilter->serialize());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// The value is given in the data row
|
||||
$iCol = $this->m_aAttList[$sAttCode];
|
||||
if ($sAttCode == 'id') {
|
||||
$valuecondition = $aRowData[$iCol];
|
||||
} else {
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
|
||||
$valuecondition = $oAttDef->MakeValueFromString($aRowData[$iCol], $this->m_bLocalizedValues);
|
||||
}
|
||||
}
|
||||
if (is_null($valuecondition)) {
|
||||
$bSkipQuery = true;
|
||||
} else {
|
||||
$oReconciliationFilter->AddCondition($sAttCode, $valuecondition, '=');
|
||||
}
|
||||
}
|
||||
if ($bSkipQuery) {
|
||||
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-Reconciliation'));
|
||||
} else {
|
||||
$oReconciliationSet = new CMDBObjectSet($oReconciliationFilter);
|
||||
switch ($oReconciliationSet->Count()) {
|
||||
case 0:
|
||||
$oTargetObj = $this->CreateObject($aResult, $iRow, $aRowData, $oChange);
|
||||
// $aResult[$iRow]["__STATUS__"]=> set in CreateObject
|
||||
$aVisited[] = $oTargetObj->GetKey();
|
||||
break;
|
||||
case 1:
|
||||
$oTargetObj = $oReconciliationSet->Fetch();
|
||||
$this->UpdateObject($aResult, $iRow, $oTargetObj, $aRowData, $oChange);
|
||||
// $aResult[$iRow]["__STATUS__"]=> set in UpdateObject
|
||||
if (!is_null($this->m_sSynchroScope)) {
|
||||
$aVisited[] = $oTargetObj->GetKey();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Found several matches, ambiguous
|
||||
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-Ambiguous'));
|
||||
$aResult[$iRow]["id"] = new CellStatus_Ambiguous(0, $oReconciliationSet->Count(), $oReconciliationFilter->serialize());
|
||||
$aResult[$iRow]["finalclass"] = 'n/a';
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue(Dict::Format('UI:CSVReport-Row-Issue-Internal', get_class($e), $e->getMessage()));
|
||||
}
|
||||
}
|
||||
try
|
||||
{
|
||||
$oReconciliationFilter = new DBObjectSearch($this->m_sClass);
|
||||
$bSkipQuery = false;
|
||||
foreach($this->m_aReconcilKeys as $sAttCode)
|
||||
{
|
||||
$valuecondition = null;
|
||||
if (array_key_exists($sAttCode, $this->m_aExtKeys))
|
||||
{
|
||||
if ($this->IsNullExternalKeySpec($aRowData, $sAttCode))
|
||||
{
|
||||
$oExtKey = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
|
||||
if ($oExtKey->IsNullAllowed())
|
||||
{
|
||||
$valuecondition = $oExtKey->GetNullValue();
|
||||
$aResult[$iRow][$sAttCode] = new CellStatus_Void($oExtKey->GetNullValue());
|
||||
}
|
||||
else
|
||||
{
|
||||
$aResult[$iRow][$sAttCode] = new CellStatus_NullIssue();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The value has to be found or verified
|
||||
list($sQuery, $aMatches) = $this->ResolveExternalKey($aRowData, $sAttCode, $aResult[$iRow]);
|
||||
|
||||
if (count($aMatches) == 1)
|
||||
{
|
||||
$oRemoteObj = reset($aMatches); // first item
|
||||
$valuecondition = $oRemoteObj->GetKey();
|
||||
$aResult[$iRow][$sAttCode] = new CellStatus_Void($oRemoteObj->GetKey());
|
||||
}
|
||||
elseif (count($aMatches) == 0)
|
||||
{
|
||||
$aResult[$iRow][$sAttCode] = new CellStatus_SearchIssue();
|
||||
}
|
||||
else
|
||||
{
|
||||
$aResult[$iRow][$sAttCode] = new CellStatus_Ambiguous(null, count($aMatches), $sQuery);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The value is given in the data row
|
||||
$iCol = $this->m_aAttList[$sAttCode];
|
||||
if ($sAttCode == 'id')
|
||||
{
|
||||
$valuecondition = $aRowData[$iCol];
|
||||
}
|
||||
else
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
|
||||
$valuecondition = $oAttDef->MakeValueFromString($aRowData[$iCol], $this->m_bLocalizedValues);
|
||||
}
|
||||
}
|
||||
if (is_null($valuecondition))
|
||||
{
|
||||
$bSkipQuery = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oReconciliationFilter->AddCondition($sAttCode, $valuecondition, '=');
|
||||
}
|
||||
}
|
||||
if ($bSkipQuery)
|
||||
{
|
||||
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-Reconciliation'));
|
||||
}
|
||||
else
|
||||
{
|
||||
$oReconciliationSet = new CMDBObjectSet($oReconciliationFilter);
|
||||
switch($oReconciliationSet->Count())
|
||||
{
|
||||
case 0:
|
||||
$oTargetObj = $this->CreateObject($aResult, $iRow, $aRowData, $oChange);
|
||||
// $aResult[$iRow]["__STATUS__"]=> set in CreateObject
|
||||
$aVisited[] = $oTargetObj->GetKey();
|
||||
break;
|
||||
case 1:
|
||||
$oTargetObj = $oReconciliationSet->Fetch();
|
||||
$this->UpdateObject($aResult, $iRow, $oTargetObj, $aRowData, $oChange);
|
||||
// $aResult[$iRow]["__STATUS__"]=> set in UpdateObject
|
||||
if (!is_null($this->m_sSynchroScope))
|
||||
{
|
||||
$aVisited[] = $oTargetObj->GetKey();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Found several matches, ambiguous
|
||||
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-Ambiguous'));
|
||||
$aResult[$iRow]["id"]= new CellStatus_Ambiguous(0, $oReconciliationSet->Count(), $oReconciliationFilter->ToOql());
|
||||
$aResult[$iRow]["finalclass"]= 'n/a';
|
||||
|
||||
if (!is_null($this->m_sSynchroScope)) {
|
||||
// Compute the delta between the scope and visited objects
|
||||
$oScopeSearch = DBObjectSearch::FromOQL($this->m_sSynchroScope);
|
||||
$oScopeSet = new DBObjectSet($oScopeSearch);
|
||||
while ($oObj = $oScopeSet->Fetch()) {
|
||||
$iObj = $oObj->GetKey();
|
||||
if (!in_array($iObj, $aVisited)) {
|
||||
set_time_limit(intval($iLoopTimeLimit));
|
||||
$iRow++;
|
||||
$this->UpdateMissingObject($aResult, $iRow, $oObj, $oChange);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue(Dict::Format('UI:CSVReport-Row-Issue-Internal', get_class($e), $e->getMessage()));
|
||||
}
|
||||
} finally {
|
||||
// Send all the retained events for further computations
|
||||
cmdbAbstractObject::SetEventDBLinksChangedBlocked(false);
|
||||
cmdbAbstractObject::FireEventDbLinksChangedForAllObjects();
|
||||
}
|
||||
|
||||
if (!is_null($this->m_sSynchroScope))
|
||||
{
|
||||
// Compute the delta between the scope and visited objects
|
||||
$oScopeSearch = DBObjectSearch::FromOQL($this->m_sSynchroScope);
|
||||
$oScopeSet = new DBObjectSet($oScopeSearch);
|
||||
while ($oObj = $oScopeSet->Fetch())
|
||||
{
|
||||
$iObj = $oObj->GetKey();
|
||||
if (!in_array($iObj, $aVisited))
|
||||
{
|
||||
set_time_limit(intval($iLoopTimeLimit));
|
||||
$iRow++;
|
||||
$this->UpdateMissingObject($aResult, $iRow, $oObj, $oChange);
|
||||
}
|
||||
}
|
||||
}
|
||||
set_time_limit(intval($iPreviousTimeLimit));
|
||||
|
||||
// Fill in the blanks - the result matrix is expected to be 100% complete
|
||||
@@ -1110,7 +1330,7 @@ class BulkChange
|
||||
}
|
||||
}
|
||||
$oBulkChanges->Seek(0);
|
||||
|
||||
|
||||
$aDetails = array();
|
||||
while ($oChange = $oBulkChanges->Fetch())
|
||||
{
|
||||
@@ -1274,7 +1494,7 @@ EOF
|
||||
$oOldTarget = MetaModel::GetObject($oAttDef->GetTargetClass(), $oOperation->Get('oldvalue'));
|
||||
$sOldValue = $oOldTarget->GetHyperlink();
|
||||
}
|
||||
|
||||
|
||||
$sNewValue = Dict::S('UI:UndefinedObject');
|
||||
if ($oOperation->Get('newvalue') != 0)
|
||||
{
|
||||
@@ -1300,11 +1520,11 @@ EOF
|
||||
}
|
||||
else
|
||||
{
|
||||
$aAttributes[$sAttCode] = 1;
|
||||
$aAttributes[$sAttCode] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$aDetails = array();
|
||||
foreach($aObjects as $iUId => $aObjData)
|
||||
{
|
||||
@@ -1356,6 +1576,6 @@ EOF
|
||||
$aConfig[$sAttCode] = array('label' => MetaModel::GetLabel($sClass, $sAttCode), 'description' => MetaModel::GetDescription($sClass, $sAttCode));
|
||||
}
|
||||
$oPage->table($aConfig, $aDetails);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -885,7 +885,7 @@ class CMDBChangeOpSetAttributeCaseLog extends CMDBChangeOpSetAttribute
|
||||
*/
|
||||
protected function ToHtml($sRawText)
|
||||
{
|
||||
return str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sRawText, ENT_QUOTES, 'UTF-8'));
|
||||
return str_replace(array("\r\n", "\n", "\r"), "<br/>", utils::EscapeHtml($sRawText));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1177,9 +1177,8 @@ class CMDBChangeOpSetAttributeCustomFields extends CMDBChangeOpSetAttribute
|
||||
$oHandler = $oAttDef->GetHandler($aValues);
|
||||
$sValueDesc = $oHandler->GetAsHTML($aValues);
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$sValueDesc = 'Custom field error: '.htmlentities($e->getMessage(), ENT_QUOTES, 'UTF-8');
|
||||
catch (Exception $e) {
|
||||
$sValueDesc = 'Custom field error: '.utils::EscapeHtml($e->getMessage());
|
||||
}
|
||||
$sTextView = '<div>'.$sValueDesc.'</div>';
|
||||
|
||||
|
||||
@@ -499,7 +499,7 @@ abstract class CMDBObject extends DBObject
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
$oMyChangeOp->Set("oldvalue", $original);
|
||||
$oMyChangeOp->Set("newvalue", $value[$sAttCode]);
|
||||
$oMyChangeOp->Set("newvalue", $value);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeCustomFields)
|
||||
@@ -623,6 +623,9 @@ abstract class CMDBObject extends DBObject
|
||||
return $this->DBCloneTracked_Internal();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 3.1.0 N°5232 not used
|
||||
*/
|
||||
public function DBCloneTracked(CMDBChange $oChange, $newKey = null)
|
||||
{
|
||||
self::SetCurrentChange($oChange);
|
||||
@@ -637,20 +640,6 @@ abstract class CMDBObject extends DBObject
|
||||
return $newKey;
|
||||
}
|
||||
|
||||
public function DBUpdate()
|
||||
{
|
||||
// Copy the changes list before the update (the list should be reset afterwards)
|
||||
$aChanges = $this->ListChanges();
|
||||
if (count($aChanges) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$ret = parent::DBUpdate();
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param null $oDeletionPlan
|
||||
*
|
||||
@@ -685,46 +674,7 @@ abstract class CMDBObject extends DBObject
|
||||
protected function DBDeleteTracked_Internal(&$oDeletionPlan = null)
|
||||
{
|
||||
$ret = parent::DBDelete($oDeletionPlan);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public static function BulkUpdate(DBSearch $oFilter, array $aValues)
|
||||
{
|
||||
return static::BulkUpdateTracked_Internal($oFilter, $aValues);
|
||||
}
|
||||
|
||||
public static function BulkUpdateTracked(CMDBChange $oChange, DBSearch $oFilter, array $aValues)
|
||||
{
|
||||
self::SetCurrentChange($oChange);
|
||||
static::BulkUpdateTracked_Internal($oFilter, $aValues);
|
||||
}
|
||||
|
||||
protected static function BulkUpdateTracked_Internal(DBSearch $oFilter, array $aValues)
|
||||
{
|
||||
// $aValues is an array of $sAttCode => $value
|
||||
|
||||
// Get the list of objects to update (and load it before doing the change)
|
||||
$oObjSet = new CMDBObjectSet($oFilter);
|
||||
$oObjSet->Load();
|
||||
|
||||
// Keep track of the previous values (will be overwritten when the objects are synchronized with the DB)
|
||||
$aOriginalValues = array();
|
||||
$oObjSet->Rewind();
|
||||
while ($oItem = $oObjSet->Fetch())
|
||||
{
|
||||
$aOriginalValues[$oItem->GetKey()] = $oItem->m_aOrigValues;
|
||||
}
|
||||
|
||||
// Update in one single efficient query
|
||||
$ret = parent::BulkUpdate($oFilter, $aValues);
|
||||
|
||||
// Record... in many queries !!!
|
||||
$oObjSet->Rewind();
|
||||
while ($oItem = $oObjSet->Fetch())
|
||||
{
|
||||
$aChangedValues = $oItem->ListChangedValues($aValues);
|
||||
$oItem->RecordAttChanges($aChangedValues, $aOriginalValues[$oItem->GetKey()]);
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -157,7 +157,7 @@ class CMDBSource
|
||||
$iPort = null;
|
||||
self::InitServerAndPort($sDbHost, $sServer, $iPort);
|
||||
|
||||
$iFlags = null;
|
||||
$iFlags = 0;
|
||||
|
||||
// *some* errors (like connection errors) will throw mysqli_sql_exception instead of generating warnings printed to the output
|
||||
// but some other errors will still cause the query() method to return false !!!
|
||||
@@ -166,7 +166,6 @@ class CMDBSource
|
||||
try
|
||||
{
|
||||
$oMysqli = new mysqli();
|
||||
$oMysqli->init();
|
||||
|
||||
if ($bTlsEnabled)
|
||||
{
|
||||
@@ -351,19 +350,21 @@ class CMDBSource
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the version of the database server.
|
||||
*
|
||||
* @return string
|
||||
* @throws \MySQLException
|
||||
*
|
||||
* @uses \CMDBSource::QueryToCol() so needs a connection opened !
|
||||
* @uses \CMDBSource::QueryToScalar() so needs a connection opened !
|
||||
*/
|
||||
public static function GetDBVersion()
|
||||
{
|
||||
$aVersions = self::QueryToCol('SELECT Version() as version', 'version');
|
||||
return $aVersions[0];
|
||||
return static::QueryToScalar('SELECT VERSION()', 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @deprecated Use `CMDBSource::GetDBVersion` instead.
|
||||
* @uses mysqli_get_server_info
|
||||
*/
|
||||
public static function GetServerInfo()
|
||||
{
|
||||
@@ -1507,20 +1508,14 @@ class CMDBSource
|
||||
* Returns the value of the specified server variable
|
||||
* @param string $sVarName Name of the server variable
|
||||
* @return mixed Current value of the variable
|
||||
*/
|
||||
* @throws \MySQLQueryHasNoResultException|\MySQLException
|
||||
*/
|
||||
public static function GetServerVariable($sVarName)
|
||||
{
|
||||
$result = '';
|
||||
$sSql = "SELECT @@$sVarName as theVar";
|
||||
$aRows = self::QueryToArray($sSql);
|
||||
if (count($aRows) > 0)
|
||||
{
|
||||
$result = $aRows[0]['theVar'];
|
||||
}
|
||||
return $result;
|
||||
$sSql = 'SELECT @@'.$sVarName;
|
||||
return static::QueryToScalar($sSql, 0) ?: '';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the privileges of the current user
|
||||
* @return string privileges in a raw format
|
||||
@@ -1616,4 +1611,22 @@ class CMDBSource
|
||||
|
||||
return 'ALTER DATABASE'.CMDBSource::GetSqlStringColumnDefinition().';';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check which mysql client option (--ssl or --ssl-mode) to be used for encrypted connection
|
||||
*
|
||||
* @return bool true if --ssl-mode should be used, false otherwise
|
||||
* @throws \MySQLException
|
||||
*
|
||||
* @link https://dev.mysql.com/doc/refman/5.7/en/connection-options.html#encrypted-connection-options "Command Options for Encrypted Connections"
|
||||
*/
|
||||
public static function IsSslModeDBVersion()
|
||||
{
|
||||
if (static::GetDBVendor() === static::ENUM_DB_VENDOR_MYSQL)
|
||||
{
|
||||
//Mysql 5.7.0 and upper deprecated --ssl and uses --ssl-mode instead
|
||||
return version_compare(static::GetDBVersion(), '5.7.11', '>=');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ define('ITOP_APPLICATION_SHORT', 'iTop');
|
||||
*
|
||||
* @see ITOP_CORE_VERSION to get iTop core version
|
||||
*/
|
||||
define('ITOP_VERSION', '3.0.3-dev');
|
||||
define('ITOP_VERSION', '3.1.0-dev');
|
||||
|
||||
define('ITOP_VERSION_NAME', 'Fullmoon');
|
||||
define('ITOP_REVISION', 'svn');
|
||||
@@ -129,6 +129,22 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'event_service.debug.filter_events' => [
|
||||
'type' => 'array',
|
||||
'description' => 'List of events name to filter Event Service debug messages',
|
||||
'default' => [],
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'event_service.debug.filter_sources' => [
|
||||
'type' => 'array',
|
||||
'description' => 'List of event sources to filter Event Service debug messages',
|
||||
'default' => '',
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'app_env_label' => [
|
||||
'type' => 'string',
|
||||
'description' => 'Label displayed to describe the current application environment, defaults to the environment name (e.g. "production")',
|
||||
@@ -326,11 +342,11 @@ class Config
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'allow_menu_on_linkset' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'Display Action menus in view mode on any LinkedSet with edit_mode != none',
|
||||
'default' => false,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
'type' => 'bool',
|
||||
'description' => 'Display Action menus in view mode on any LinkedSet with edit_mode != none',
|
||||
'default' => true,
|
||||
'value' => true,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'allow_target_creation' => [
|
||||
@@ -1254,6 +1270,14 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'navigation_menu.show_organization_filter' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'Display organization filter in menu',
|
||||
'default' => true,
|
||||
'value' => true,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'quick_create.enabled' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'Whether or not the quick create is enabled',
|
||||
@@ -1521,11 +1545,11 @@ class Config
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'security.hide_administrators' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'If true, non-administrator users will not be able to see the administrator accounts, the Administrator profile and the links between the administrator accounts and their profiles.',
|
||||
'default' => false,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
'type' => 'bool',
|
||||
'description' => 'If true, non-administrator users will not be able to see the administrator accounts, the Administrator profile and the links between the administrator accounts and their profiles.',
|
||||
'default' => true,
|
||||
'value' => true,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'behind_reverse_proxy' => [
|
||||
@@ -1560,6 +1584,22 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'setup.launch_button.enabled' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'If true displays in the Application Upgrade screen a button allowing to launch the setup in a single click (no more manual config file permission change needed)',
|
||||
'default' => null,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'audit.enable_selection_landing_page' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'If true audit categories must be selected before results are computed (use this setting in case of a lot of audit categories)',
|
||||
'default' => false,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
];
|
||||
|
||||
public function IsProperty($sPropCode)
|
||||
@@ -1877,9 +1917,9 @@ class Config
|
||||
}
|
||||
if (strlen($sNoise) > 0)
|
||||
{
|
||||
// Note: sNoise is an html output, but so far it was ok for me (e.g. showing the entire call stack)
|
||||
// Note: sNoise is an html output, but so far it was ok for me (e.g. showing the entire call stack)
|
||||
throw new ConfigException('Syntax error in configuration file',
|
||||
array('file' => $sConfigFile, 'error' => '<tt>'.htmlentities($sNoise, ENT_QUOTES, 'UTF-8').'</tt>'));
|
||||
array('file' => $sConfigFile, 'error' => '<tt>'.utils::EscapeHtml($sNoise, ENT_QUOTES).'</tt>'));
|
||||
}
|
||||
|
||||
if (!isset($MySettings) || !is_array($MySettings))
|
||||
@@ -2262,7 +2302,7 @@ class Config
|
||||
$oHandle = null;
|
||||
$sConfig = null;
|
||||
|
||||
if (is_file($this->m_sFile))
|
||||
if ($this->m_sFile !== null && is_file($this->m_sFile))
|
||||
{
|
||||
$oHandle = fopen($this->m_sFile, 'r');
|
||||
$index = 0;
|
||||
@@ -2437,10 +2477,15 @@ class Config
|
||||
/**
|
||||
* Helper function to initialize a configuration from the page arguments
|
||||
*
|
||||
* @see \Parameters::GetParamForConfigArray() to get aParamValues from {@see Parameters} object hierarchy in setup
|
||||
* @see \WizardController::GetParamForConfigArray() to get aParamValues from {@see \WizardController} object hierarchy in setup
|
||||
*
|
||||
* @param array $aParamValues
|
||||
* @param string|null $sModulesDir
|
||||
* @param ?string $sModulesDir
|
||||
* @param bool $bPreserveModuleSettings
|
||||
*
|
||||
* @return void The current object is modified directly
|
||||
*
|
||||
* @throws \Exception
|
||||
* @throws \CoreException
|
||||
*/
|
||||
@@ -2687,7 +2732,7 @@ class ConfigPlaceholdersResolver
|
||||
}
|
||||
|
||||
$sPattern = '/\%(env|server)\((\w+)\)(?:\?:(\w*))?\%/'; //3 capturing groups, ie `%env(HTTP_PORT)?:8080%` produce: `env` `HTTP_PORT` and `8080`.
|
||||
|
||||
|
||||
if (! preg_match_all($sPattern, $rawValue, $aMatchesCollection, PREG_SET_ORDER))
|
||||
{
|
||||
return $rawValue;
|
||||
|
||||
@@ -52,17 +52,23 @@
|
||||
*/
|
||||
class ContextTag
|
||||
{
|
||||
const TAG_PORTAL = 'GUI:Portal';
|
||||
const TAG_CRON = 'CRON';
|
||||
const TAG_CONSOLE = 'GUI:Console';
|
||||
const TAG_SETUP = 'Setup';
|
||||
const TAG_SYNCHRO = 'Synchro';
|
||||
const TAG_REST = 'REST/JSON';
|
||||
public const TAG_PORTAL = 'GUI:Portal';
|
||||
public const TAG_CRON = 'CRON';
|
||||
public const TAG_CONSOLE = 'GUI:Console';
|
||||
public const TAG_SETUP = 'Setup';
|
||||
public const TAG_SYNCHRO = 'Synchro';
|
||||
public const TAG_REST = 'REST/JSON';
|
||||
/**
|
||||
* @var string
|
||||
* @since 3.1.0 N°3200
|
||||
*/
|
||||
public const TAG_OBJECT_SEARCH = 'ObjectSearch';
|
||||
|
||||
protected static $aStack = array();
|
||||
|
||||
/**
|
||||
* Store a context tag on the stack
|
||||
*
|
||||
* @param string $sTag
|
||||
*/
|
||||
public function __construct($sTag)
|
||||
|
||||
@@ -125,8 +125,8 @@ class CSVBulkExport extends TabularBulkExport
|
||||
$sRawSeparator = utils::ReadParam('separator', ',', true, 'raw_data');
|
||||
$sCustomDateTimeFormat = utils::ReadParam('', ',', true, 'raw_data');
|
||||
$aSep = array(
|
||||
';' => Dict::S('UI:CSVImport:SeparatorSemicolon+'),
|
||||
',' => Dict::S('UI:CSVImport:SeparatorComma+'),
|
||||
';' => Dict::S('UI:CSVImport:SeparatorSemicolon+'),
|
||||
',' => Dict::S('UI:CSVImport:SeparatorComma+'),
|
||||
'tab' => Dict::S('UI:CSVImport:SeparatorTab+'),
|
||||
);
|
||||
$sOtherSeparator = '';
|
||||
@@ -134,10 +134,10 @@ class CSVBulkExport extends TabularBulkExport
|
||||
$sOtherSeparator = $sRawSeparator;
|
||||
$sRawSeparator = 'other';
|
||||
}
|
||||
$aSep['other'] = Dict::S('UI:CSVImport:SeparatorOther').' <input type="text" size="3" name="other-separator" value="'.htmlentities($sOtherSeparator, ENT_QUOTES, 'UTF-8').'"/>';
|
||||
$aSep['other'] = Dict::S('UI:CSVImport:SeparatorOther').' <input type="text" size="3" name="other-separator" value="'.utils::EscapeHtml($sOtherSeparator).'"/>';
|
||||
|
||||
foreach ($aSep as $sVal => $sLabel) {
|
||||
$oRadio = InputUIBlockFactory::MakeForInputWithLabel($sLabel, "separator", htmlentities($sVal, ENT_QUOTES, 'UTF-8'), $sLabel, "radio");
|
||||
$oRadio = InputUIBlockFactory::MakeForInputWithLabel($sLabel, "separator", utils::EscapeHtml($sVal), $sLabel, "radio");
|
||||
$oRadio->GetInput()->SetIsChecked(($sVal == $sRawSeparator));
|
||||
$oRadio->SetBeforeInput(false);
|
||||
$oRadio->GetInput()->AddCSSClass('ibo-input--label-right');
|
||||
@@ -152,7 +152,7 @@ class CSVBulkExport extends TabularBulkExport
|
||||
|
||||
$sRawQualifier = utils::ReadParam('text-qualifier', '"', true, 'raw_data');
|
||||
$aQualifiers = array(
|
||||
'"' => Dict::S('UI:CSVImport:QualifierDoubleQuote+'),
|
||||
'"' => Dict::S('UI:CSVImport:QualifierDoubleQuote+'),
|
||||
'\'' => Dict::S('UI:CSVImport:QualifierSimpleQuote+'),
|
||||
);
|
||||
$sOtherQualifier = '';
|
||||
@@ -160,10 +160,10 @@ class CSVBulkExport extends TabularBulkExport
|
||||
$sOtherQualifier = $sRawQualifier;
|
||||
$sRawQualifier = 'other';
|
||||
}
|
||||
$aQualifiers['other'] = Dict::S('UI:CSVImport:QualifierOther').' <input type="text" size="3" name="other-text-qualifier" value="'.htmlentities($sOtherQualifier, ENT_QUOTES, 'UTF-8').'"/>';
|
||||
$aQualifiers['other'] = Dict::S('UI:CSVImport:QualifierOther').' <input type="text" size="3" name="other-text-qualifier" value="'.utils::EscapeHtml($sOtherQualifier).'"/>';
|
||||
|
||||
foreach ($aQualifiers as $sVal => $sLabel) {
|
||||
$oRadio = InputUIBlockFactory::MakeForInputWithLabel($sLabel, "text-qualifier", htmlentities($sVal, ENT_QUOTES, 'UTF-8'), $sLabel, "radio");
|
||||
$oRadio = InputUIBlockFactory::MakeForInputWithLabel($sLabel, "text-qualifier", utils::EscapeHtml($sVal), $sLabel, "radio");
|
||||
$oRadio->GetInput()->SetIsChecked(($sVal == $sRawSeparator));
|
||||
$oRadio->SetBeforeInput(false);
|
||||
$oRadio->GetInput()->AddCSSClass('ibo-input--label-right');
|
||||
@@ -209,8 +209,8 @@ class CSVBulkExport extends TabularBulkExport
|
||||
|
||||
$sDateTimeFormat = utils::ReadParam('date_format', (string)AttributeDateTime::GetFormat(), true, 'raw_data');
|
||||
|
||||
$sDefaultFormat = htmlentities((string)AttributeDateTime::GetFormat(), ENT_QUOTES, 'UTF-8');
|
||||
$sExample = htmlentities(date((string)AttributeDateTime::GetFormat()), ENT_QUOTES, 'UTF-8');
|
||||
$sDefaultFormat = utils::EscapeHtml((string)AttributeDateTime::GetFormat());
|
||||
$sExample = utils::EscapeHtml(date((string)AttributeDateTime::GetFormat()));
|
||||
$oRadioDefault = InputUIBlockFactory::MakeForInputWithLabel(Dict::Format('Core:BulkExport:DateTimeFormatDefault_Example', $sDefaultFormat, $sExample), "csv_date_format_radio", "default", "csv_date_time_format_default", "radio");
|
||||
$oRadioDefault->GetInput()->SetIsChecked(($sDateTimeFormat == (string)AttributeDateTime::GetFormat()));
|
||||
$oRadioDefault->SetBeforeInput(false);
|
||||
@@ -218,7 +218,7 @@ class CSVBulkExport extends TabularBulkExport
|
||||
$oFieldSetDate->AddSubBlock($oRadioDefault);
|
||||
$oFieldSetDate->AddSubBlock(new Html('</br>'));
|
||||
|
||||
$sFormatInput = '<input type="text" size="15" name="date_format" id="csv_custom_date_time_format" title="" value="'.htmlentities($sDateTimeFormat, ENT_QUOTES, 'UTF-8').'"/>';
|
||||
$sFormatInput = '<input type="text" size="15" name="date_format" id="csv_custom_date_time_format" title="" value="'.utils::EscapeHtml($sDateTimeFormat).'"/>';
|
||||
$oRadioCustom = InputUIBlockFactory::MakeForInputWithLabel(Dict::Format('Core:BulkExport:DateTimeFormatCustom_Format', $sFormatInput), "csv_date_format_radio", "custom", "csv_date_time_format_custom", "radio");
|
||||
$oRadioCustom->SetDescription(Dict::S('UI:CSVImport:CustomDateTimeFormatTooltip'));
|
||||
$oRadioCustom->GetInput()->SetIsChecked($sDateTimeFormat !== (string)AttributeDateTime::GetFormat());
|
||||
@@ -246,17 +246,18 @@ EOF
|
||||
}
|
||||
|
||||
protected function GetSampleData($oObj, $sAttCode)
|
||||
{
|
||||
if ($sAttCode != 'id')
|
||||
{
|
||||
{
|
||||
if ($sAttCode != 'id') {
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
|
||||
if ($oAttDef instanceof AttributeDateTime) // AttributeDate is derived from AttributeDateTime
|
||||
{
|
||||
$sClass = (get_class($oAttDef) == 'AttributeDateTime') ? 'user-formatted-date-time' : 'user-formatted-date';
|
||||
return '<div class="'.$sClass.'" data-date="'.$oObj->Get($sAttCode).'">'.htmlentities($oAttDef->GetEditValue($oObj->Get($sAttCode), $oObj), ENT_QUOTES, 'UTF-8').'</div>';
|
||||
|
||||
return '<div class="'.$sClass.'" data-date="'.$oObj->Get($sAttCode).'">'.utils::EscapeHtml($oAttDef->GetEditValue($oObj->Get($sAttCode), $oObj)).'</div>';
|
||||
}
|
||||
}
|
||||
return '<div class="text-preview">'.htmlentities($this->GetValue($oObj, $sAttCode), ENT_QUOTES, 'UTF-8').'</div>';
|
||||
|
||||
return '<div class="text-preview">'.utils::EscapeHtml($this->GetValue($oObj, $sAttCode)).'</div>';
|
||||
}
|
||||
|
||||
protected function GetValue($oObj, $sAttCode)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.0">
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.1">
|
||||
<user_rights>
|
||||
<profiles>
|
||||
<profile id="1024" _delta="define">
|
||||
@@ -270,4 +270,4 @@
|
||||
</class>
|
||||
</classes>
|
||||
</meta>
|
||||
</itop_design>
|
||||
</itop_design>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -38,7 +38,7 @@ interface iDBObjectSetIterator extends Countable
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function Count();
|
||||
public function Count(): int;
|
||||
|
||||
/**
|
||||
* Reset the cursor to the first item in the collection. Equivalent to Seek(0)
|
||||
@@ -52,7 +52,7 @@ interface iDBObjectSetIterator extends Countable
|
||||
*
|
||||
* @param int $iRow
|
||||
*/
|
||||
public function Seek($iPosition);
|
||||
public function Seek($iPosition): void;
|
||||
|
||||
/**
|
||||
* Fetch the object at the current position in the collection and move the cursor to the next position.
|
||||
|
||||
@@ -413,6 +413,10 @@ class DBObjectSearch extends DBSearch
|
||||
}
|
||||
|
||||
/**
|
||||
* Important: If you need to add a condition on the same $sFilterCode several times with different $value values; do not use this method as the previous $value occurences will be replaced by the last. Instead use:
|
||||
* * {@see \DBObjectSearch::AddConditionExpression()} in loops to add conditions one by one
|
||||
* * {@see \DBObjectSearch::AddConditionForInOperatorUsingParam()} for IN/NOT IN queries with lots of params at once
|
||||
*
|
||||
* @param string $sFilterCode
|
||||
* @param mixed $value
|
||||
* @param string $sOpCode operator to use : 'IN', 'NOT IN', 'Contains',' Begins with', 'Finishes with', ...
|
||||
@@ -423,22 +427,16 @@ class DBObjectSearch extends DBSearch
|
||||
* @param bool $bParseSearchString
|
||||
*
|
||||
* @throws \CoreException
|
||||
*
|
||||
* @see AddConditionForInOperatorUsingParam for IN/NOT IN queries with lots of params
|
||||
*/
|
||||
public function AddCondition($sFilterCode, $value, $sOpCode = null, $bParseSearchString = false)
|
||||
{
|
||||
MyHelpers::CheckKeyInArray('filter code in class: '.$this->GetClass(), $sFilterCode, MetaModel::GetClassFilterDefs($this->GetClass()));
|
||||
MyHelpers::CheckKeyInArray('filter code in class: '.$this->GetClass(), $sFilterCode, MetaModel::GetFilterAttribList($this->GetClass()));
|
||||
|
||||
$oField = new FieldExpression($sFilterCode, $this->GetClassAlias());
|
||||
if (empty($sOpCode))
|
||||
{
|
||||
if ($sFilterCode == 'id')
|
||||
{
|
||||
if (empty($sOpCode)) {
|
||||
if ($sFilterCode == 'id') {
|
||||
$sOpCode = '=';
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->GetClass(), $sFilterCode);
|
||||
$oNewCondition = $oAttDef->GetSmartConditionExpression($value, $oField, $this->m_aParams);
|
||||
$this->AddConditionExpression($oNewCondition);
|
||||
@@ -1900,16 +1898,13 @@ class DBObjectSearch extends DBSearch
|
||||
// Hide objects that are not visible to the current user
|
||||
//
|
||||
$oSearch = $this;
|
||||
if (!$this->IsAllDataAllowed() && !$this->IsDataFiltered())
|
||||
{
|
||||
if (!$this->IsAllDataAllowed() && !$this->IsDataFiltered()) {
|
||||
$oVisibleObjects = UserRights::GetSelectFilter($this->GetClass(), $this->GetModifierProperties('UserRightsGetSelectFilter'));
|
||||
if ($oVisibleObjects === false)
|
||||
{
|
||||
if ($oVisibleObjects === false) {
|
||||
// Make sure this is a valid search object, saying NO for all
|
||||
$oVisibleObjects = DBObjectSearch::FromEmptySet($this->GetClass());
|
||||
}
|
||||
if (is_object($oVisibleObjects))
|
||||
{
|
||||
if (is_object($oVisibleObjects)) {
|
||||
$oVisibleObjects->AllowAllData();
|
||||
$oSearch = $this->Intersect($oVisibleObjects);
|
||||
$oSearch->SetDataFiltered();
|
||||
@@ -1929,63 +1924,49 @@ class DBObjectSearch extends DBSearch
|
||||
if (isset($_SERVER['REQUEST_URI']))
|
||||
{
|
||||
$aContextData['sRequestUri'] = $_SERVER['REQUEST_URI'];
|
||||
}
|
||||
else if (isset($_SERVER['SCRIPT_NAME']))
|
||||
{
|
||||
} else if (isset($_SERVER['SCRIPT_NAME'])) {
|
||||
$aContextData['sRequestUri'] = $_SERVER['SCRIPT_NAME'];
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$aContextData['sRequestUri'] = '';
|
||||
}
|
||||
|
||||
// Need to identify the query
|
||||
$sOqlQuery = $oSearch->ToOql(false, null, true);
|
||||
if ((strpos($sOqlQuery, '`id` IN (') !== false) || (strpos($sOqlQuery, '`id` NOT IN (') !== false))
|
||||
{
|
||||
if ((strpos($sOqlQuery, '`id` IN (') !== false) || (strpos($sOqlQuery, '`id` NOT IN (') !== false)) {
|
||||
// Requests containing "id IN" are not worth caching
|
||||
$bCanCache = false;
|
||||
}
|
||||
|
||||
$aContextData['sOqlQuery'] = $sOqlQuery;
|
||||
|
||||
if (count($aModifierProperties))
|
||||
{
|
||||
if (count($aModifierProperties)) {
|
||||
array_multisort($aModifierProperties);
|
||||
$sModifierProperties = json_encode($aModifierProperties);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$sModifierProperties = '';
|
||||
}
|
||||
$aContextData['sModifierProperties'] = $sModifierProperties;
|
||||
|
||||
$sRawId = Dict::GetUserLanguage().'-'.$sOqlQuery.$sModifierProperties;
|
||||
if (!is_null($aAttToLoad))
|
||||
{
|
||||
if (!is_null($aAttToLoad)) {
|
||||
$sRawId .= json_encode($aAttToLoad);
|
||||
}
|
||||
$aContextData['aAttToLoad'] = $aAttToLoad;
|
||||
if (!is_null($aGroupByExpr))
|
||||
{
|
||||
foreach($aGroupByExpr as $sAlias => $oExpr)
|
||||
{
|
||||
$sRawId .= 'g:'.$sAlias.'!'.$oExpr->Render();
|
||||
if (!is_null($aGroupByExpr)) {
|
||||
foreach ($aGroupByExpr as $sAlias => $oExpr) {
|
||||
$sRawId .= 'g:'.$sAlias.'!'.$oExpr->RenderExpression();
|
||||
}
|
||||
}
|
||||
if (!is_null($aSelectExpr))
|
||||
{
|
||||
foreach($aSelectExpr as $sAlias => $oExpr)
|
||||
{
|
||||
$sRawId .= 'se:'.$sAlias.'!'.$oExpr->Render();
|
||||
if (!is_null($aSelectExpr)) {
|
||||
foreach ($aSelectExpr as $sAlias => $oExpr) {
|
||||
$sRawId .= 'se:'.$sAlias.'!'.$oExpr->RenderExpression();
|
||||
}
|
||||
}
|
||||
$aContextData['aGroupByExpr'] = $aGroupByExpr;
|
||||
$aContextData['aSelectExpr'] = $aSelectExpr;
|
||||
$sRawId .= $bGetCount;
|
||||
$aContextData['bGetCount'] = $bGetCount;
|
||||
if (is_array($aSelectedClasses))
|
||||
{
|
||||
if (is_array($aSelectedClasses)) {
|
||||
$sRawId .= implode(',', $aSelectedClasses); // Unions may alter the list of selected columns
|
||||
}
|
||||
$aContextData['aSelectedClasses'] = $aSelectedClasses;
|
||||
@@ -2096,17 +2077,13 @@ class DBObjectSearch extends DBSearch
|
||||
// 3rd step - position the attributes in the hierarchy of classes
|
||||
//
|
||||
$oSubClassExp->Browse(function($oNode) use ($sSubClass) {
|
||||
if ($oNode instanceof FieldExpression)
|
||||
{
|
||||
if ($oNode instanceof FieldExpression) {
|
||||
$sAttCode = $oNode->GetName();
|
||||
$oAttDef = MetaModel::GetAttributeDef($sSubClass, $sAttCode);
|
||||
if ($oAttDef->IsExternalField())
|
||||
{
|
||||
if ($oAttDef->IsExternalField()) {
|
||||
$sKeyAttCode = $oAttDef->GetKeyAttCode();
|
||||
$sClassOfAttribute = MetaModel::GetAttributeOrigin($sSubClass, $sKeyAttCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$sClassOfAttribute = MetaModel::GetAttributeOrigin($sSubClass, $sAttCode);
|
||||
}
|
||||
$sParent = MetaModel::GetAttributeOrigin($sClassOfAttribute, $oNode->GetName());
|
||||
@@ -2114,12 +2091,11 @@ class DBObjectSearch extends DBSearch
|
||||
}
|
||||
});
|
||||
|
||||
$sSignature = $oSubClassExp->Render();
|
||||
if (!array_key_exists($sSignature, $aExpressions))
|
||||
{
|
||||
$sSignature = $oSubClassExp->RenderExpression();
|
||||
if (!array_key_exists($sSignature, $aExpressions)) {
|
||||
$aExpressions[$sSignature] = array(
|
||||
'expression' => $oSubClassExp,
|
||||
'classes' => array(),
|
||||
'classes' => array(),
|
||||
);
|
||||
}
|
||||
$aExpressions[$sSignature]['classes'][] = $sSubClass;
|
||||
|
||||
@@ -53,6 +53,18 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
* @var array
|
||||
*/
|
||||
protected $m_aAttToLoad;
|
||||
/**
|
||||
* @var null|array
|
||||
*/
|
||||
protected $m_aExtendedDataSpec;
|
||||
/**
|
||||
* @var int Maximum number of elements to retrieve
|
||||
*/
|
||||
protected $m_iLimitCount;
|
||||
/**
|
||||
* @var int Offset from which elements should be retrieved
|
||||
*/
|
||||
protected $m_iLimitStart;
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
@@ -843,7 +855,7 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
*/
|
||||
public function Count()
|
||||
public function Count(): int
|
||||
{
|
||||
if (is_null($this->m_iNumTotalDBRows))
|
||||
{
|
||||
@@ -1078,14 +1090,13 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
*
|
||||
* @param int $iRow
|
||||
*
|
||||
* @return int|mixed
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
* @since 3.1.0 N°4517 Now returns void for return type to match parent class and be compatible with PHP 8.1
|
||||
*/
|
||||
public function Seek($iRow)
|
||||
public function Seek($iRow): void
|
||||
{
|
||||
if (!$this->m_bLoaded) $this->Load();
|
||||
|
||||
@@ -1094,7 +1105,6 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
{
|
||||
$this->m_oSQLResult->data_seek($this->m_iCurrRow);
|
||||
}
|
||||
return $this->m_iCurrRow;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,6 +30,7 @@ use DOMDocument;
|
||||
use DOMFormatException;
|
||||
use IssueLog;
|
||||
use LogAPI;
|
||||
use utils;
|
||||
|
||||
/**
|
||||
* Class \Combodo\iTop\DesignDocument
|
||||
@@ -83,6 +84,8 @@ class DesignDocument extends DOMDocument
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
// Return type union 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]
|
||||
public function save($filename, $options = null)
|
||||
{
|
||||
$this->documentElement->setAttribute('xmlns:xsi', "http://www.w3.org/2001/XMLSchema-instance");
|
||||
@@ -97,13 +100,12 @@ class DesignDocument extends DOMDocument
|
||||
public function Dump($bReturnRes = false)
|
||||
{
|
||||
$sXml = $this->saveXML();
|
||||
if ($bReturnRes)
|
||||
{
|
||||
if ($bReturnRes) {
|
||||
return $sXml;
|
||||
}
|
||||
|
||||
echo "<pre>\n";
|
||||
echo htmlentities($sXml);
|
||||
echo utils::EscapeHtml($sXml);
|
||||
echo "</pre>\n";
|
||||
|
||||
return '';
|
||||
@@ -181,6 +183,26 @@ class DesignElement extends \DOMElement
|
||||
return $this->ownerDocument->GetNodes($sXPath, $this);
|
||||
}
|
||||
|
||||
public static function ToArray(DesignElement $oNode)
|
||||
{
|
||||
$aRes = [];
|
||||
|
||||
if ($oNode->GetNodes('./*')->length == 0) {
|
||||
return $oNode->GetText('');
|
||||
}
|
||||
foreach ($oNode->GetNodes('./*') as $oSubNode) {
|
||||
/** @var \Combodo\iTop\DesignElement $oSubNode */
|
||||
$aSubArray = DesignElement::ToArray($oSubNode);
|
||||
if ($oSubNode->hasAttribute('id')) {
|
||||
$aRes[$oSubNode->getAttribute('id')] = $aSubArray;
|
||||
} else {
|
||||
$aRes[$oSubNode->tagName] = $aSubArray;
|
||||
}
|
||||
}
|
||||
|
||||
return $aRes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an HTML representation of the DOM, for debugging purposes
|
||||
*
|
||||
@@ -196,13 +218,13 @@ class DesignElement extends \DOMElement
|
||||
$oDoc->appendChild($oClone);
|
||||
|
||||
$sXml = $oDoc->saveXML($oClone);
|
||||
if ($bReturnRes)
|
||||
{
|
||||
if ($bReturnRes) {
|
||||
return $sXml;
|
||||
}
|
||||
echo "<pre>\n";
|
||||
echo htmlentities($sXml);
|
||||
echo utils::EscapeHtml($sXml);
|
||||
echo "</pre>\n";
|
||||
|
||||
return '';
|
||||
}
|
||||
/**
|
||||
|
||||
@@ -110,7 +110,7 @@ class Dict
|
||||
*
|
||||
* @param string $sStringCode The code identifying the dictionary entry
|
||||
* @param string $sDefault Default value if there is no match in the dictionary
|
||||
* @param bool $bUserLanguageOnly True to allow the use of the default language as a fallback, false otherwise
|
||||
* @param bool $bUserLanguageOnly False to allow the use of the default language as a fallback, true otherwise
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@@ -174,7 +174,7 @@ class Dict
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function Format($sFormatCode /*, ... arguments ....*/)
|
||||
public static function Format($sFormatCode /*, ... arguments ... */)
|
||||
{
|
||||
$sLocalizedFormat = self::S($sFormatCode);
|
||||
$aArguments = func_get_args();
|
||||
|
||||
@@ -459,11 +459,10 @@ class DisplayableNode extends GraphNode
|
||||
{
|
||||
$aContext = $aContextDefs[$key];
|
||||
$aRootCauses = array();
|
||||
foreach($aObjects as $oRootCause)
|
||||
{
|
||||
foreach ($aObjects as $oRootCause) {
|
||||
$aRootCauses[] = $oRootCause->GetHyperlink();
|
||||
}
|
||||
$sHtml .= '<p><img style="max-height: 24px; vertical-align:bottom;" class="ibo-class-icon ibo-is-small" src="'.utils::GetAbsoluteUrlModulesRoot().$aContext['icon'].'" title="'.htmlentities(Dict::S($aContext['dict'])).'"> '.implode(', ', $aRootCauses).'</p>';
|
||||
$sHtml .= '<p><img style="max-height: 24px; vertical-align:bottom;" class="ibo-class-icon ibo-is-small" src="'.utils::GetAbsoluteUrlModulesRoot().$aContext['icon'].'" title="'.utils::EscapeHtml(Dict::S($aContext['dict'])).'"> '.implode(', ', $aRootCauses).'</p>';
|
||||
}
|
||||
$sHtml .= '<hr/>';
|
||||
}
|
||||
@@ -1335,18 +1334,17 @@ class DisplayableGraph extends SimpleGraph
|
||||
}
|
||||
$oPdf->Rect($xMin, $yMin, $fMaxWidth + $fIconSize + 3*$fPadding, $yMax - $yMin, 'D');
|
||||
|
||||
if ($sComments != '')
|
||||
{
|
||||
if ($sComments != '') {
|
||||
// Draw the comment text (surrounded by a rectangle)
|
||||
$xPos = $xMin + $fMaxWidth + $fIconSize + 4*$fPadding;
|
||||
$w = $xMax - $xPos - 2*$fPadding;
|
||||
$xPos = $xMin + $fMaxWidth + $fIconSize + 4 * $fPadding;
|
||||
$w = $xMax - $xPos - 2 * $fPadding;
|
||||
$iNbLines = 1;
|
||||
$sText = '<p>'.str_replace("\n", '<br/>', htmlentities($sComments, ENT_QUOTES, 'UTF-8'), $iNbLines).'</p>';
|
||||
$sText = '<p>'.str_replace("\n", '<br/>', utils::EscapeHtml($sComments), $iNbLines).'</p>';
|
||||
$fLineHeight = $oPdf->getStringHeight($w, $sText);
|
||||
$h = (1+$iNbLines) * $fLineHeight;
|
||||
$yPos = $yMax - 2*$fPadding - $h;
|
||||
$h = (1 + $iNbLines) * $fLineHeight;
|
||||
$yPos = $yMax - 2 * $fPadding - $h;
|
||||
$oPdf->writeHTMLCell($w, $h, $xPos + $fPadding, $yPos + $fPadding, $sText, 0 /* border */, 1 /* ln */);
|
||||
$oPdf->Rect($xPos, $yPos, $w + 2*$fPadding, $h + 2*$fPadding, 'D');
|
||||
$oPdf->Rect($xPos, $yPos, $w + 2 * $fPadding, $h + 2 * $fPadding, 'D');
|
||||
$yMax = $yPos - $fPadding;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,9 +27,6 @@
|
||||
use Combodo\iTop\Core\Email\EmailFactory;
|
||||
use Combodo\iTop\Core\Email\iEMail;
|
||||
|
||||
Swift_Preferences::getInstance()->setCharset('UTF-8');
|
||||
|
||||
|
||||
define ('EMAIL_SEND_OK', 0);
|
||||
define ('EMAIL_SEND_PENDING', 1);
|
||||
define ('EMAIL_SEND_ERROR', 2);
|
||||
@@ -112,7 +109,7 @@ class EMail implements iEMail
|
||||
* @throws \CoreException
|
||||
* @throws \Symfony\Component\CssSelector\Exception\SyntaxErrorException
|
||||
*/
|
||||
static public function UnSerializeV2($sSerializedMessage)
|
||||
public static function UnSerializeV2($sSerializedMessage)
|
||||
{
|
||||
return EmailFactory::GetMailer()::UnSerializeV2($sSerializedMessage);
|
||||
}
|
||||
@@ -154,7 +151,7 @@ class EMail implements iEMail
|
||||
public function SetBody($sBody, $sMimeType = 'text/html', $sCustomStyles = null)
|
||||
{
|
||||
$this->oMailer->SetBody($sBody, $sMimeType, $sCustomStyles);
|
||||
}
|
||||
}
|
||||
|
||||
public function AddPart($sText, $sMimeType = 'text/html')
|
||||
{
|
||||
|
||||
@@ -100,8 +100,8 @@ class ExcelBulkExport extends TabularBulkExport
|
||||
|
||||
$sDateTimeFormat = utils::ReadParam('date_format', (string)AttributeDateTime::GetFormat(), true, 'raw_data');
|
||||
|
||||
$sDefaultFormat = htmlentities((string)AttributeDateTime::GetFormat(), ENT_QUOTES, 'UTF-8');
|
||||
$sExample = htmlentities(date((string)AttributeDateTime::GetFormat()), ENT_QUOTES, 'UTF-8');
|
||||
$sDefaultFormat = utils::EscapeHtml((string)AttributeDateTime::GetFormat());
|
||||
$sExample = utils::EscapeHtml(date((string)AttributeDateTime::GetFormat()));
|
||||
$oRadioDefault = InputUIBlockFactory::MakeForInputWithLabel(Dict::Format('Core:BulkExport:DateTimeFormatDefault_Example', $sDefaultFormat, $sExample), "excel_date_format_radio", "default", "excel_date_time_format_default", "radio");
|
||||
$oRadioDefault->GetInput()->SetIsChecked(($sDateTimeFormat == (string)AttributeDateTime::GetFormat()));
|
||||
$oRadioDefault->SetBeforeInput(false);
|
||||
@@ -109,7 +109,7 @@ class ExcelBulkExport extends TabularBulkExport
|
||||
$oFieldSetDate->AddSubBlock($oRadioDefault);
|
||||
$oFieldSetDate->AddSubBlock(new Html('</br>'));
|
||||
|
||||
$sFormatInput = '<input type="text" size="15" name="date_format" id="excel_custom_date_time_format" title="" value="'.htmlentities($sDateTimeFormat, ENT_QUOTES, 'UTF-8').'"/>';
|
||||
$sFormatInput = '<input type="text" size="15" name="date_format" id="excel_custom_date_time_format" title="" value="'.utils::EscapeHtml($sDateTimeFormat).'"/>';
|
||||
$oRadioCustom = InputUIBlockFactory::MakeForInputWithLabel(Dict::Format('Core:BulkExport:DateTimeFormatCustom_Format', $sFormatInput), "excel_date_format_radio", "custom", "excel_date_time_format_custom", "radio");
|
||||
$oRadioCustom->SetDescription(Dict::S('UI:CSVImport:CustomDateTimeFormatTooltip'));
|
||||
$oRadioCustom->GetInput()->SetIsChecked($sDateTimeFormat !== (string)AttributeDateTime::GetFormat());
|
||||
@@ -156,16 +156,17 @@ EOF
|
||||
|
||||
protected function GetSampleData($oObj, $sAttCode)
|
||||
{
|
||||
if ($sAttCode != 'id')
|
||||
{
|
||||
if ($sAttCode != 'id') {
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
|
||||
if ($oAttDef instanceof AttributeDateTime) // AttributeDate is derived from AttributeDateTime
|
||||
{
|
||||
$sClass = (get_class($oAttDef) == 'AttributeDateTime') ? 'user-formatted-date-time' : 'user-formatted-date';
|
||||
return '<div class="'.$sClass.'" data-date="'.$oObj->Get($sAttCode).'">'.htmlentities($oAttDef->GetEditValue($oObj->Get($sAttCode), $oObj), ENT_QUOTES, 'UTF-8').'</div>';
|
||||
|
||||
return '<div class="'.$sClass.'" data-date="'.$oObj->Get($sAttCode).'">'.utils::EscapeHtml($oAttDef->GetEditValue($oObj->Get($sAttCode), $oObj)).'</div>';
|
||||
}
|
||||
}
|
||||
return '<div class="text-preview">'.htmlentities($this->GetValue($oObj, $sAttCode), ENT_QUOTES, 'UTF-8').'</div>';
|
||||
|
||||
return '<div class="text-preview">'.utils::EscapeHtml($this->GetValue($oObj, $sAttCode)).'</div>';
|
||||
}
|
||||
|
||||
protected function GetValue($oObj, $sAttCode)
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
<?php
|
||||
// The file has been moved in iTop 2.2.0+ (revision 3803)
|
||||
// Preserve backward compatibility with some external tools (Cf. toolkit)
|
||||
|
||||
// @deprecated 3.1.0
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use core/oql/expression.class.inc.php instead');
|
||||
|
||||
require_once(APPROOT.'core/oql/expression.class.inc.php');
|
||||
|
||||
@@ -31,8 +31,9 @@ require_once('MyHelpers.class.inc.php');
|
||||
|
||||
|
||||
/**
|
||||
* Definition of a filter (could be made out of an existing attribute, or from an expression)
|
||||
* Definition of a filter (could be made out of an existing attribute, or from an expression)
|
||||
*
|
||||
* @deprecated 3.1.0 not used N°4690 - Deprecate "FilterCodes"
|
||||
* @package iTopORM
|
||||
*/
|
||||
abstract class FilterDefinition
|
||||
@@ -46,6 +47,7 @@ abstract class FilterDefinition
|
||||
|
||||
public function __construct($sCode, $aParams = array())
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod("Deprecated class ".$this->GetClass().". Do not use. Will be removed in next version.");
|
||||
$this->m_sCode = $sCode;
|
||||
$this->m_aParams = $aParams;
|
||||
$this->ConsistencyCheck();
|
||||
@@ -98,8 +100,9 @@ abstract class FilterDefinition
|
||||
}
|
||||
|
||||
/**
|
||||
* Match against the object unique identifier
|
||||
* Match against the object unique identifier
|
||||
*
|
||||
* @deprecated 3.1.0 N°4690 - Deprecate "FilterCodes"
|
||||
* @package iTopORM
|
||||
*/
|
||||
class FilterPrivateKey extends FilterDefinition
|
||||
@@ -145,8 +148,9 @@ class FilterPrivateKey extends FilterDefinition
|
||||
}
|
||||
|
||||
/**
|
||||
* Match against an existing attribute (the attribute type will determine the available operators)
|
||||
* Match against an existing attribute (the attribute type will determine the available operators)
|
||||
*
|
||||
* @deprecated 3.1.0 N°4690 - Deprecate "FilterCodes"
|
||||
* @package iTopORM
|
||||
*/
|
||||
class FilterFromAttribute extends FilterDefinition
|
||||
|
||||
@@ -62,7 +62,8 @@ class HTMLBulkExport extends TabularBulkExport
|
||||
if ($oAttDef instanceof AttributeDateTime) // AttributeDate is derived from AttributeDateTime
|
||||
{
|
||||
$sClass = (get_class($oAttDef) == 'AttributeDateTime') ? 'user-formatted-date-time' : 'user-formatted-date';
|
||||
return '<div class="'.$sClass.'" data-date="'.$oObj->Get($sAttCode).'">'.htmlentities($oAttDef->GetEditValue($oObj->Get($sAttCode), $oObj), ENT_QUOTES, 'UTF-8').'</div>';
|
||||
|
||||
return '<div class="'.$sClass.'" data-date="'.$oObj->Get($sAttCode).'">'.utils::EscapeHtml($oAttDef->GetEditValue($oObj->Get($sAttCode), $oObj)).'</div>';
|
||||
}
|
||||
}
|
||||
return $this->GetValue($oObj, $sAttCode);
|
||||
|
||||
@@ -134,7 +134,7 @@ abstract class DOMSanitizer extends HTMLSanitizer
|
||||
public function DoSanitize($sHTML)
|
||||
{
|
||||
$this->oDoc = new DOMDocument();
|
||||
$this->oDoc->preserveWhitespace = true;
|
||||
$this->oDoc->preserveWhiteSpace = true;
|
||||
|
||||
// MS outlook implements empty lines by the mean of <p><o:p></o:p></p>
|
||||
// We have to transform that into <p><br></p> (which is how Thunderbird implements empty lines)
|
||||
@@ -286,8 +286,8 @@ class HTMLDOMSanitizer extends DOMSanitizer
|
||||
'strong' => array(),
|
||||
'img' => array('src', 'style', 'alt', 'title'),
|
||||
'ul' => array('style'),
|
||||
'ol' => array('style'),
|
||||
'li' => array('style'),
|
||||
'ol' => array('reversed', 'start', 'style', 'type'),
|
||||
'li' => array('style', 'value'),
|
||||
'h1' => array('style'),
|
||||
'h2' => array('style'),
|
||||
'h3' => array('style'),
|
||||
@@ -402,7 +402,7 @@ class HTMLDOMSanitizer extends DOMSanitizer
|
||||
public function LoadDoc($sHTML)
|
||||
{
|
||||
@$this->oDoc->loadHTML('<?xml encoding="UTF-8"?>'.$sHTML); // For loading HTML chunks where the character set is not specified
|
||||
$this->oDoc->preserveWhitespace = true;
|
||||
$this->oDoc->preserveWhiteSpace = true;
|
||||
}
|
||||
|
||||
public function PrintDoc()
|
||||
|
||||
@@ -295,13 +295,12 @@ class InlineImage extends DBObject
|
||||
{
|
||||
$sImgTag = $aImgInfo[0][0];
|
||||
$sSecret = '';
|
||||
if (preg_match('/data-img-secret="([0-9a-f]+)"/', $sImgTag, $aSecretMatches))
|
||||
{
|
||||
if (preg_match('/data-img-secret="([0-9a-f]+)"/', $sImgTag, $aSecretMatches)) {
|
||||
$sSecret = '&s='.$aSecretMatches[1];
|
||||
}
|
||||
$sAttId = $aImgInfo[2][0];
|
||||
|
||||
$sNewImgTag = preg_replace('/src="[^"]+"/', 'src="'.htmlentities($sUrl.$sAttId.$sSecret, ENT_QUOTES, 'UTF-8').'"', $sImgTag); // preserve other attributes, must convert & to & to be idempotent with CKEditor
|
||||
|
||||
$sNewImgTag = preg_replace('/src="[^"]+"/', 'src="'.utils::EscapeHtml($sUrl.$sAttId.$sSecret).'"', $sImgTag); // preserve other attributes, must convert & to & to be idempotent with CKEditor
|
||||
$aNeedles[] = $sImgTag;
|
||||
$aReplacements[] = $sNewImgTag;
|
||||
}
|
||||
@@ -536,8 +535,8 @@ JS
|
||||
$iObjKey = $oObject->GetKey();
|
||||
|
||||
$sAbsoluteUrlAppRoot = utils::GetAbsoluteUrlAppRoot();
|
||||
$sToggleFullScreen = htmlentities(Dict::S('UI:ToggleFullScreen'), ENT_QUOTES, 'UTF-8');
|
||||
|
||||
$sToggleFullScreen = utils::EscapeHtml(Dict::S('UI:ToggleFullScreen'));
|
||||
|
||||
return
|
||||
<<<JS
|
||||
// Hook the file upload of all CKEditor instances
|
||||
|
||||
@@ -299,7 +299,7 @@ class ExecutionKPI
|
||||
*/
|
||||
private static function Push(ExecutionKPI $oExecutionKPI)
|
||||
{
|
||||
array_push(self::$m_aExecutionStack, $oExecutionKPI);
|
||||
self::$m_aExecutionStack[] = $oExecutionKPI;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -449,4 +449,3 @@ class ExecutionKPI
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -551,6 +551,12 @@ class LogChannels
|
||||
*/
|
||||
public const NOTIFICATIONS = 'notifications';
|
||||
|
||||
/**
|
||||
* @var string Everything related to the backup / restore
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public const BACKUP = 'backup';
|
||||
|
||||
/**
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@@ -570,11 +576,29 @@ class LogChannels
|
||||
|
||||
public const CORE = 'core';
|
||||
|
||||
/**
|
||||
* @var string Everything related to the datatable component
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public const DATATABLE = 'Datatable';
|
||||
|
||||
public const DEADLOCK = 'DeadLock';
|
||||
|
||||
public const INLINE_IMAGE = 'InlineImage';
|
||||
|
||||
public const PORTAL = 'portal';
|
||||
public const PORTAL = 'portal';
|
||||
|
||||
/**
|
||||
* @var string Everything related to the event service
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public const EVENT_SERVICE = 'EventService';
|
||||
|
||||
/**
|
||||
* @var string Everything related to the datamodel CRUD
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public const DM_CRUD = 'DMCRUD';
|
||||
}
|
||||
|
||||
|
||||
@@ -1018,6 +1042,11 @@ class DeadLockLog extends LogAPI
|
||||
class DeprecatedCallsLog extends LogAPI
|
||||
{
|
||||
public const ENUM_CHANNEL_PHP_METHOD = 'deprecated-php-method';
|
||||
/**
|
||||
* @var string
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public const ENUM_CHANNEL_PHP_ENDPOINT = 'deprecated-php-endpoint';
|
||||
public const ENUM_CHANNEL_PHP_LIBMETHOD = 'deprecated-php-libmethod';
|
||||
public const ENUM_CHANNEL_FILE = 'deprecated-file';
|
||||
public const CHANNEL_DEFAULT = self::ENUM_CHANNEL_PHP_METHOD;
|
||||
@@ -1235,6 +1264,35 @@ class DeprecatedCallsLog extends LogAPI
|
||||
static::Warning($sMessage, self::ENUM_CHANNEL_PHP_METHOD);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $sAdditionalMessage
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public static function NotifyDeprecatedPhpEndpoint(?string $sAdditionalMessage = null): void
|
||||
{
|
||||
try {
|
||||
if (!static::IsLogLevelEnabled(self::LEVEL_WARNING, self::ENUM_CHANNEL_PHP_ENDPOINT)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (ConfigException $e) {
|
||||
return;
|
||||
}
|
||||
|
||||
$aStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
|
||||
$iStackDeprecatedMethodLevel = 0; // level 0 = current method, level 1 = method containing the `NotifyDeprecatedPhpMethod` call
|
||||
$sDeprecatedUrl = $_SERVER['REQUEST_URI'];
|
||||
$sCallerFile = $aStack[$iStackDeprecatedMethodLevel]['file'];
|
||||
$sCallerLine = $aStack[$iStackDeprecatedMethodLevel]['line'];
|
||||
$sMessage = "Call to endpoint {$sDeprecatedUrl} in {$sCallerFile}#L{$sCallerLine}";
|
||||
|
||||
if (!is_null($sAdditionalMessage)) {
|
||||
$sMessage .= ' : '.$sAdditionalMessage;
|
||||
}
|
||||
|
||||
static::Warning($sMessage, self::ENUM_CHANNEL_PHP_ENDPOINT);
|
||||
}
|
||||
|
||||
public static function Log($sLevel, $sMessage, $sChannel = null, $aContext = array()): void
|
||||
{
|
||||
if (true === utils::IsDevelopmentEnvironment()) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -107,7 +107,7 @@ abstract class Expression {
|
||||
/**
|
||||
* recursive rendering
|
||||
*
|
||||
* @deprecated use RenderExpression
|
||||
* @deprecated 3.0.0 use RenderExpression
|
||||
*
|
||||
* @param array $aArgs used as input by default, or used as output if bRetrofitParams set to True
|
||||
* @param bool $bRetrofitParams
|
||||
@@ -118,7 +118,7 @@ abstract class Expression {
|
||||
public function Render(&$aArgs = null, $bRetrofitParams = false)
|
||||
{
|
||||
// cannot notify depreciation for now as this is still MASSIVELY used in iTop core !
|
||||
//DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use RenderExpression');
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use RenderExpression');
|
||||
|
||||
return $this->RenderExpression(false, $aArgs, $bRetrofitParams);
|
||||
}
|
||||
@@ -1765,6 +1765,7 @@ class FieldExpression extends UnaryExpression
|
||||
*/
|
||||
public function MakeValueLabel($oFilter, $sValue, $sDefault)
|
||||
{
|
||||
|
||||
$sAttCode = $this->GetName();
|
||||
$sParentAlias = $this->GetParent();
|
||||
|
||||
@@ -2859,9 +2860,26 @@ class FunctionExpression extends Expression
|
||||
{
|
||||
throw new \Exception("Function {$this->m_sVerb} requires 1 argument");
|
||||
}
|
||||
|
||||
// N°5985 - Since PHP 8.1+, a bug fix on \DateTimeInterval for a date anterior to "1937-05-23" now returns a different number of days. The workaround below aim at making the code work with PHP 7.4 => 8.2+
|
||||
//
|
||||
// $oDate = new DateTimeImmutable('2020-01-02');
|
||||
// $oZero = new DateTimeImmutable('1937-05-22');
|
||||
// $iRet = (int) $oDate->diff($oZero)->format('%a');
|
||||
// echo $iRet."\n"; // 30174 (PHP 8.0) vs 30175 (PHP 8.1+)
|
||||
//
|
||||
// $oDate = new DateTimeImmutable('2020-01-02');
|
||||
// $oZero = new DateTimeImmutable('1937-05-23');
|
||||
// $iRet = (int) $oDate->diff($oZero)->format('%a');
|
||||
// echo $iRet."\n"; // 30174 (PHP 8.0) vs 30174 (PHP 8.1+)
|
||||
//
|
||||
// To work around that we take 1970-01-01 as "zero date" and we offset it with the number of days between 1582-01-01 and 1970-01-01.
|
||||
// Note that as the "target" date could be between 1582-01-01 and 1970-01-01 we have to format the interval with the "-"/"+" sign in order to correct the number of days.
|
||||
|
||||
$oDate = new DateTime($this->m_aArgs[0]->Evaluate($aArgs));
|
||||
$oZero = new DateTime('1582-01-01');
|
||||
$iRet = (int) $oDate->diff($oZero)->format('%a') + 577815;
|
||||
$oZero = new DateTime('1970-01-01');
|
||||
$iDaysBetween19700101And15800101 = 141713;
|
||||
$iRet = (int) $oZero->diff($oDate)->format('%R%a') + 577815 + $iDaysBetween19700101And15800101;
|
||||
return $iRet;
|
||||
|
||||
case 'FROM_DAYS':
|
||||
@@ -3049,8 +3067,7 @@ class FunctionExpression extends Expression
|
||||
public function MakeValueLabel($oFilter, $sValue, $sDefault)
|
||||
{
|
||||
static $aWeekDayToString = null;
|
||||
if (is_null($aWeekDayToString))
|
||||
{
|
||||
if (is_null($aWeekDayToString)) {
|
||||
// Init the correspondance table
|
||||
$aWeekDayToString = array(
|
||||
0 => Dict::S('DayOfWeek-Sunday'),
|
||||
@@ -3059,7 +3076,7 @@ class FunctionExpression extends Expression
|
||||
3 => Dict::S('DayOfWeek-Wednesday'),
|
||||
4 => Dict::S('DayOfWeek-Thursday'),
|
||||
5 => Dict::S('DayOfWeek-Friday'),
|
||||
6 => Dict::S('DayOfWeek-Saturday')
|
||||
6 => Dict::S('DayOfWeek-Saturday'),
|
||||
);
|
||||
}
|
||||
static $aMonthToString = null;
|
||||
@@ -3067,15 +3084,15 @@ class FunctionExpression extends Expression
|
||||
{
|
||||
// Init the correspondance table
|
||||
$aMonthToString = array(
|
||||
1 => Dict::S('Month-01'),
|
||||
2 => Dict::S('Month-02'),
|
||||
3 => Dict::S('Month-03'),
|
||||
4 => Dict::S('Month-04'),
|
||||
5 => Dict::S('Month-05'),
|
||||
6 => Dict::S('Month-06'),
|
||||
7 => Dict::S('Month-07'),
|
||||
8 => Dict::S('Month-08'),
|
||||
9 => Dict::S('Month-09'),
|
||||
1 => Dict::S('Month-01'),
|
||||
2 => Dict::S('Month-02'),
|
||||
3 => Dict::S('Month-03'),
|
||||
4 => Dict::S('Month-04'),
|
||||
5 => Dict::S('Month-05'),
|
||||
6 => Dict::S('Month-06'),
|
||||
7 => Dict::S('Month-07'),
|
||||
8 => Dict::S('Month-08'),
|
||||
9 => Dict::S('Month-09'),
|
||||
10 => Dict::S('Month-10'),
|
||||
11 => Dict::S('Month-11'),
|
||||
12 => Dict::S('Month-12'),
|
||||
@@ -3083,30 +3100,22 @@ class FunctionExpression extends Expression
|
||||
}
|
||||
|
||||
$sRes = $sDefault;
|
||||
if (strtolower($this->m_sVerb) == 'date_format')
|
||||
{
|
||||
if (strtolower($this->m_sVerb) == 'date_format') {
|
||||
$oFormatExpr = $this->m_aArgs[1];
|
||||
if ($oFormatExpr->Render() == "'%w'")
|
||||
{
|
||||
if (isset($aWeekDayToString[(int)$sValue]))
|
||||
{
|
||||
|
||||
if ($oFormatExpr->RenderExpression() == "'%w'") {
|
||||
if (isset($aWeekDayToString[(int)$sValue])) {
|
||||
$sRes = $aWeekDayToString[(int)$sValue];
|
||||
}
|
||||
}
|
||||
elseif ($oFormatExpr->Render() == "'%Y-%m'")
|
||||
{
|
||||
} elseif ($oFormatExpr->RenderExpression() == "'%Y-%m'") {
|
||||
// yyyy-mm => "yyyy month"
|
||||
$iMonth = (int) substr($sValue, -2); // the two last chars
|
||||
$iMonth = (int)substr($sValue, -2); // the two last chars
|
||||
$sRes = substr($sValue, 0, 4).' '.$aMonthToString[$iMonth];
|
||||
}
|
||||
elseif ($oFormatExpr->Render() == "'%Y-%m-%d'")
|
||||
{
|
||||
} elseif ($oFormatExpr->RenderExpression() == "'%Y-%m-%d'") {
|
||||
// yyyy-mm-dd => "month d"
|
||||
$iMonth = (int) substr($sValue, 5, 2);
|
||||
$iMonth = (int)substr($sValue, 5, 2);
|
||||
$sRes = $aMonthToString[$iMonth].' '.(int)substr($sValue, -2);
|
||||
}
|
||||
elseif ($oFormatExpr->Render() == "'%H'")
|
||||
{
|
||||
} elseif ($oFormatExpr->RenderExpression() == "'%H'") {
|
||||
// H => "H Hour(s)"
|
||||
$sRes = $sValue.':00';
|
||||
}
|
||||
|
||||
@@ -33,17 +33,19 @@ class OQLParser_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 OQLParser_yyToken implements ArrayAccess
|
||||
}
|
||||
}
|
||||
|
||||
function offsetUnset($offset)
|
||||
function offsetUnset($offset): void
|
||||
{
|
||||
unset($this->metadata[$offset]);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,37 @@
|
||||
|
||||
class OQLException extends CoreException
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
* @since 3.1.0
|
||||
*/
|
||||
protected $m_MyIssue;
|
||||
/**
|
||||
* @var string
|
||||
* @since 3.1.0
|
||||
*/
|
||||
protected $m_sInput;
|
||||
/**
|
||||
* @var int
|
||||
* @since 3.1.0
|
||||
*/
|
||||
protected $m_iLine;
|
||||
/**
|
||||
* @var int
|
||||
* @since 3.1.0
|
||||
*/
|
||||
protected $m_iCol;
|
||||
/**
|
||||
* @var string
|
||||
* @since 3.1.0
|
||||
*/
|
||||
protected $m_sUnexpected;
|
||||
/**
|
||||
* @var array|null string
|
||||
* @since 3.1.0
|
||||
*/
|
||||
protected $m_aExpecting;
|
||||
|
||||
public function __construct($sIssue, $sInput, $iLine, $iCol, $sUnexpected, $aExpecting = null)
|
||||
{
|
||||
$this->m_MyIssue = $sIssue;
|
||||
@@ -58,22 +89,19 @@ class OQLException extends CoreException
|
||||
|
||||
public function getHtmlDesc($sHighlightHtmlBegin = '<span style="font-weight: bolder">', $sHighlightHtmlEnd = '</span>')
|
||||
{
|
||||
$sRet = htmlentities($this->m_MyIssue.", found '".$this->m_sUnexpected."' in: ", ENT_QUOTES, 'UTF-8');
|
||||
$sRet .= htmlentities(substr($this->m_sInput, 0, $this->m_iCol), ENT_QUOTES, 'UTF-8');
|
||||
$sRet .= $sHighlightHtmlBegin.htmlentities(substr($this->m_sInput, $this->m_iCol, strlen($this->m_sUnexpected)), ENT_QUOTES, 'UTF-8').$sHighlightHtmlEnd;
|
||||
$sRet .= htmlentities(substr($this->m_sInput, $this->m_iCol + strlen($this->m_sUnexpected)), ENT_QUOTES, 'UTF-8');
|
||||
$sRet = utils::EscapeHtml($this->m_MyIssue.", found '".$this->m_sUnexpected."' in: ");
|
||||
$sRet .= utils::EscapeHtml(substr($this->m_sInput, 0, $this->m_iCol));
|
||||
$sRet .= $sHighlightHtmlBegin.utils::EscapeHtml(substr($this->m_sInput, $this->m_iCol, strlen($this->m_sUnexpected))).$sHighlightHtmlEnd;
|
||||
$sRet .= utils::EscapeHtml(substr($this->m_sInput, $this->m_iCol + strlen($this->m_sUnexpected)));
|
||||
|
||||
if (!is_null($this->m_aExpecting) && (count($this->m_aExpecting) > 0))
|
||||
{
|
||||
if (count($this->m_aExpecting) < 30)
|
||||
{
|
||||
if (!is_null($this->m_aExpecting) && (count($this->m_aExpecting) > 0)) {
|
||||
if (count($this->m_aExpecting) < 30) {
|
||||
$sExpectations = '{'.implode(', ', $this->m_aExpecting).'}';
|
||||
$sRet .= ", expecting ".htmlentities($sExpectations, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
$sRet .= ", expecting ".utils::EscapeHtml($sExpectations);
|
||||
}
|
||||
$sSuggest = self::FindClosestString($this->m_sUnexpected, $this->m_aExpecting);
|
||||
if (strlen($sSuggest) > 0)
|
||||
{
|
||||
$sRet .= ", I would suggest to use '$sHighlightHtmlBegin".htmlentities($sSuggest, ENT_QUOTES, 'UTF-8')."$sHighlightHtmlEnd'";
|
||||
if (strlen($sSuggest) > 0) {
|
||||
$sRet .= ", I would suggest to use '$sHighlightHtmlBegin".utils::EscapeHtml($sSuggest)."$sHighlightHtmlEnd'";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -252,7 +252,7 @@ class ormCaseLog {
|
||||
if (!array_key_exists('format', $aIndex[$index]) || ($aIndex[$index]['format'] == static::ENUM_FORMAT_TEXT))
|
||||
{
|
||||
$sCSSClass = 'caselog_entry';
|
||||
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
|
||||
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", utils::EscapeHtml($sTextEntry));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -292,19 +292,15 @@ class ormCaseLog {
|
||||
}
|
||||
|
||||
// Process the case of an eventual remainder (quick migration of AttributeText fields)
|
||||
if ($iPos < (strlen($this->m_sLog) - 1))
|
||||
{
|
||||
if ($iPos < (strlen($this->m_sLog) - 1)) {
|
||||
$sTextEntry = substr($this->m_sLog, $iPos);
|
||||
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
|
||||
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", utils::EscapeHtml($sTextEntry));
|
||||
|
||||
if (count($this->m_aIndex) == 0)
|
||||
{
|
||||
if (count($this->m_aIndex) == 0) {
|
||||
$sHtml .= '<div class="caselog_entry" style="'.$sStyleCaseLogEntry.'"">';
|
||||
$sHtml .= $sTextEntry;
|
||||
$sHtml .= '</div>';
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$sHtml .= '<div class="caselog_header" style="'.$sStyleCaseLogHeader.'">';
|
||||
$sHtml .= Dict::S('UI:CaseLog:InitialValue');
|
||||
$sHtml .= '</div>';
|
||||
@@ -327,24 +323,18 @@ class ormCaseLog {
|
||||
$sHtml = '<ul class="case_log_simple_html">';
|
||||
$iPos = 0;
|
||||
$aIndex = $this->m_aIndex;
|
||||
for($index=count($aIndex)-1 ; $index >= 0 ; $index--)
|
||||
{
|
||||
for($index=count($aIndex)-1 ; $index >= 0 ; $index--) {
|
||||
$iPos += $aIndex[$index]['separator_length'];
|
||||
$sTextEntry = substr($this->m_sLog, $iPos, $aIndex[$index]['text_length']);
|
||||
$sCSSClass = 'case_log_simple_html_entry_html';
|
||||
if (!array_key_exists('format', $aIndex[$index]) || ($aIndex[$index]['format'] == static::ENUM_FORMAT_TEXT))
|
||||
{
|
||||
if (!array_key_exists('format', $aIndex[$index]) || ($aIndex[$index]['format'] == static::ENUM_FORMAT_TEXT)) {
|
||||
$sCSSClass = 'case_log_simple_html_entry';
|
||||
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
|
||||
if (!is_null($aTransfoHandler))
|
||||
{
|
||||
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", utils::EscapeHtml($sTextEntry));
|
||||
if (!is_null($aTransfoHandler)) {
|
||||
$sTextEntry = call_user_func($aTransfoHandler, $sTextEntry);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!is_null($aTransfoHandler))
|
||||
{
|
||||
} else {
|
||||
if (!is_null($aTransfoHandler)) {
|
||||
$sTextEntry = call_user_func($aTransfoHandler, $sTextEntry, true /* wiki "links" only */);
|
||||
}
|
||||
$sTextEntry = InlineImage::FixUrls($sTextEntry);
|
||||
@@ -383,19 +373,15 @@ class ormCaseLog {
|
||||
}
|
||||
|
||||
// Process the case of an eventual remainder (quick migration of AttributeText fields)
|
||||
if ($iPos < (strlen($this->m_sLog) - 1))
|
||||
{
|
||||
if ($iPos < (strlen($this->m_sLog) - 1)) {
|
||||
$sTextEntry = substr($this->m_sLog, $iPos);
|
||||
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
|
||||
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", utils::EscapeHtml($sTextEntry));
|
||||
|
||||
if (count($this->m_aIndex) == 0)
|
||||
{
|
||||
if (count($this->m_aIndex) == 0) {
|
||||
$sHtml .= '<li>';
|
||||
$sHtml .= $sTextEntry;
|
||||
$sHtml .= '</li>';
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$sHtml .= '<li>';
|
||||
$sHtml .= Dict::S('UI:CaseLog:InitialValue');
|
||||
$sHtml .= '<div class="case_log_simple_html_entry" style="'.$sStyleCaseLogEntry.'">';
|
||||
@@ -437,11 +423,9 @@ class ormCaseLog {
|
||||
}
|
||||
$iPos += $aIndex[$index]['separator_length'];
|
||||
$sTextEntry = substr($this->m_sLog, $iPos, $aIndex[$index]['text_length']);
|
||||
if (!array_key_exists('format', $aIndex[$index]) || ($aIndex[$index]['format'] == static::ENUM_FORMAT_TEXT))
|
||||
{
|
||||
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
|
||||
if (!is_null($aTransfoHandler))
|
||||
{
|
||||
if (!array_key_exists('format', $aIndex[$index]) || ($aIndex[$index]['format'] == static::ENUM_FORMAT_TEXT)) {
|
||||
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", utils::EscapeHtml($sTextEntry));
|
||||
if (!is_null($aTransfoHandler)) {
|
||||
$sTextEntry = call_user_func($aTransfoHandler, $sTextEntry);
|
||||
}
|
||||
}
|
||||
@@ -483,19 +467,16 @@ class ormCaseLog {
|
||||
$oBlock->AddSubBlock($oCollapsibleBlock);
|
||||
}
|
||||
// Process the case of an eventual remainder (quick migration of AttributeText fields)
|
||||
if ($iPos < (strlen($this->m_sLog) - 1))
|
||||
{
|
||||
if ($iPos < (strlen($this->m_sLog) - 1)) {
|
||||
// In this case the format is always "text"
|
||||
$sTextEntry = substr($this->m_sLog, $iPos);
|
||||
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
|
||||
if (!is_null($aTransfoHandler))
|
||||
{
|
||||
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", utils::EscapeHtml($sTextEntry));
|
||||
if (!is_null($aTransfoHandler)) {
|
||||
$sTextEntry = call_user_func($aTransfoHandler, $sTextEntry);
|
||||
}
|
||||
|
||||
if (count($this->m_aIndex) == 0)
|
||||
{
|
||||
$oCollapsibleBlock = CollapsibleSectionUIBlockFactory::MakeStandard( '');
|
||||
if (count($this->m_aIndex) == 0) {
|
||||
$oCollapsibleBlock = CollapsibleSectionUIBlockFactory::MakeStandard('');
|
||||
$oCollapsibleBlock->AddSubBlock(new Html($sTextEntry));
|
||||
$oCollapsibleBlock->SetOpenedByDefault(true);
|
||||
$oBlock->AddSubBlock($oCollapsibleBlock);
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Service\Events\EventData;
|
||||
use Combodo\iTop\Service\Events\EventService;
|
||||
|
||||
|
||||
/**
|
||||
* ormDocument
|
||||
@@ -109,17 +112,14 @@ class ormDocument
|
||||
public function GetAsHTML()
|
||||
{
|
||||
$sResult = '';
|
||||
if ($this->IsEmpty())
|
||||
{
|
||||
if ($this->IsEmpty()) {
|
||||
// If the filename is not empty, display it, this is used
|
||||
// by the creation wizard while the file has not yet been uploaded
|
||||
$sResult = htmlentities($this->GetFileName(), ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
else
|
||||
{
|
||||
$sResult = utils::EscapeHtml($this->GetFileName());
|
||||
} else {
|
||||
$data = $this->GetData();
|
||||
$sSize = utils::BytesToFriendlyFormat(strlen($data));
|
||||
$sResult = htmlentities($this->GetFileName(), ENT_QUOTES, 'UTF-8').' ('.$sSize.')<br/>';
|
||||
$sResult = utils::EscapeHtml($this->GetFileName()).' ('.$sSize.')<br/>';
|
||||
}
|
||||
return $sResult;
|
||||
}
|
||||
@@ -131,7 +131,8 @@ class ormDocument
|
||||
public function GetDisplayLink($sClass, $Id, $sAttCode)
|
||||
{
|
||||
$sUrl = $this->GetDisplayURL($sClass, $Id, $sAttCode);
|
||||
return "<a href=\"$sUrl\" target=\"_blank\" >".htmlentities($this->GetFileName(), ENT_QUOTES, 'UTF-8')."</a>\n";
|
||||
|
||||
return "<a href=\"$sUrl\" target=\"_blank\" >".utils::EscapeHtml($this->GetFileName())."</a>\n";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -141,7 +142,8 @@ class ormDocument
|
||||
public function GetDownloadLink($sClass, $Id, $sAttCode)
|
||||
{
|
||||
$sUrl = $this->GetDownloadURL($sClass, $Id, $sAttCode);
|
||||
return "<a href=\"$sUrl\">".htmlentities($this->GetFileName(), ENT_QUOTES, 'UTF-8')."</a>\n";
|
||||
|
||||
return "<a href=\"$sUrl\">".utils::EscapeHtml($this->GetFileName())."</a>\n";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -194,7 +196,6 @@ class ormDocument
|
||||
* @param string $sContentDisposition Either 'inline' or 'attachment'
|
||||
* @param string $sSecretField The attcode of the field containing a "secret" to be provided in order to retrieve the file
|
||||
* @param string $sSecretValue The value of the secret to be compared with the value of the attribute $sSecretField
|
||||
* @return none
|
||||
*/
|
||||
public static function DownloadDocument(WebPage $oPage, $sClass, $id, $sAttCode, $sContentDisposition = 'attachment', $sSecretField = null, $sSecretValue = null)
|
||||
{
|
||||
@@ -213,6 +214,12 @@ class ormDocument
|
||||
$oDocument = $oObj->Get($sAttCode);
|
||||
if (is_object($oDocument))
|
||||
{
|
||||
$aEventData = array(
|
||||
'debug_info' => $oDocument->GetFileName(),
|
||||
'object' => $oObj,
|
||||
'document' => $oDocument,
|
||||
);
|
||||
EventService::FireEvent(new EventData(EVENT_DOWNLOAD_DOCUMENT, $sClass, $aEventData));
|
||||
$oPage->TrashUnexpectedOutput();
|
||||
$oPage->SetContentType($oDocument->GetMimeType());
|
||||
$oPage->SetContentDisposition($sContentDisposition,$oDocument->GetFileName());
|
||||
|
||||
@@ -153,8 +153,7 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
||||
*/
|
||||
public function AddObject(DBObject $oObject, $sClassAlias = '')
|
||||
{
|
||||
// cannot notify depreciation for now as this is still MASSIVELY used in iTop core !
|
||||
//DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use \ormLinkSet::AddItem() instead');
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use \ormLinkSet::AddItem() instead');
|
||||
$this->AddItem($oObject);
|
||||
}
|
||||
|
||||
@@ -312,7 +311,7 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \MySQLException
|
||||
*/
|
||||
public function Count()
|
||||
public function Count(): int
|
||||
{
|
||||
$this->LoadOriginalIds();
|
||||
$iRet = count($this->aPreserved) + count($this->aAdded) + count($this->aModified);
|
||||
@@ -327,7 +326,7 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
||||
* @throws Exception
|
||||
* @internal param int $iRow
|
||||
*/
|
||||
public function Seek($iPosition)
|
||||
public function Seek($iPosition): void
|
||||
{
|
||||
$this->LoadOriginalIds();
|
||||
|
||||
@@ -375,6 +374,8 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
*/
|
||||
// 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]
|
||||
public function current()
|
||||
{
|
||||
$this->LoadOriginalIds();
|
||||
@@ -382,9 +383,8 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
||||
$iPreservedCount = count($this->aPreserved);
|
||||
if ($this->iCursor < $iPreservedCount)
|
||||
{
|
||||
$iRet = current($this->aPreserved);
|
||||
$this->oOriginalSet->Seek($iRet);
|
||||
$oRet = $this->oOriginalSet->Fetch();
|
||||
$sId = key($this->aPreserved);
|
||||
$oRet = MetaModel::GetObject($this->sClass, $sId);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -410,7 +410,7 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \MySQLException
|
||||
*/
|
||||
public function next()
|
||||
public function next(): void
|
||||
{
|
||||
$this->LoadOriginalIds();
|
||||
|
||||
@@ -440,6 +440,8 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
||||
* @link http://php.net/manual/en/iterator.key.php
|
||||
* @return mixed scalar on success, or null on failure.
|
||||
*/
|
||||
// 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]
|
||||
public function key()
|
||||
{
|
||||
return $this->iCursor;
|
||||
@@ -455,7 +457,7 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \MySQLException
|
||||
*/
|
||||
public function valid()
|
||||
public function valid(): bool
|
||||
{
|
||||
$this->LoadOriginalIds();
|
||||
|
||||
@@ -473,7 +475,7 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \MySQLException
|
||||
*/
|
||||
public function rewind()
|
||||
public function rewind(): void
|
||||
{
|
||||
$this->LoadOriginalIds();
|
||||
|
||||
@@ -733,6 +735,7 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
||||
$oLink->DBClone();
|
||||
}
|
||||
}
|
||||
$oLink->SetLinkHostObject($oHostObject);
|
||||
$oLink->DBWrite();
|
||||
|
||||
$this->aPreserved[$oLink->GetKey()] = $oLink;
|
||||
@@ -844,11 +847,30 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
||||
}
|
||||
$oLinkSet = new DBObjectSet($oLinkSearch);
|
||||
$oLinkSet->SetShowObsoleteData($bShowObsolete);
|
||||
if ($this->HasDelta())
|
||||
{
|
||||
if ($this->HasDelta()) {
|
||||
$oLinkSet->AddObjectArray($this->aAdded);
|
||||
}
|
||||
|
||||
return $oLinkSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* GetValues.
|
||||
*
|
||||
* @return array of tag codes
|
||||
*/
|
||||
public function GetValues()
|
||||
{
|
||||
$aValues = array();
|
||||
foreach ($this->aPreserved as $sTagCode => $oTag) {
|
||||
$aValues[] = $sTagCode;
|
||||
}
|
||||
foreach ($this->aAdded as $sTagCode => $oTag) {
|
||||
$aValues[] = $sTagCode;
|
||||
}
|
||||
|
||||
sort($aValues);
|
||||
|
||||
return $aValues;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,7 +242,7 @@ class ormStopWatch
|
||||
foreach ($aProperties as $sProperty => $sValue)
|
||||
{
|
||||
$sRes .= "<TR>";
|
||||
$sCell = str_replace("\n", "<br>\n", $sValue);
|
||||
$sCell = str_replace("\n", "<br>\n", $sValue ?? '');
|
||||
$sRes .= "<TD class=\"label\">$sProperty</TD><TD>$sCell</TD>";
|
||||
$sRes .= "</TR>";
|
||||
}
|
||||
@@ -596,10 +596,9 @@ class CheckStopWatchThresholds implements iBackgroundProcess
|
||||
$oSW = $oObj->Get($sAttCode);
|
||||
$oSW->MarkThresholdAsTriggered($iThreshold);
|
||||
$oObj->Set($sAttCode, $oSW);
|
||||
|
||||
if($oObj->IsModified())
|
||||
{
|
||||
CMDBObject::SetTrackInfo("Automatic - threshold triggered");
|
||||
|
||||
if ($oObj->IsModified()) {
|
||||
CMDBObject::SetCurrentChangeFromParams("Automatic - threshold triggered");
|
||||
|
||||
$oObj->DBUpdate();
|
||||
}
|
||||
|
||||
@@ -499,9 +499,9 @@ final class ormTagSet extends ormSet
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sTagCode
|
||||
* @param $sTagLabel
|
||||
*
|
||||
* @return DBObject tag
|
||||
* @return string Tag code
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \CoreException
|
||||
*/
|
||||
|
||||
@@ -75,7 +75,7 @@ class PDFBulkExport extends HTMLBulkExport
|
||||
$aPossibleFormat = ['A3', 'A4', 'Letter'];
|
||||
$sDefaultFormat = 'A4';
|
||||
foreach ($aPossibleFormat as $sVal) {
|
||||
$oSelectFormat->AddSubBlock(SelectOptionUIBlockFactory::MakeForSelectOption($sVal, htmlentities(Dict::S('Core:BulkExport:PageSize-'.$sVal), ENT_QUOTES, 'UTF-8'), ($sVal == $sDefaultFormat)));
|
||||
$oSelectFormat->AddSubBlock(SelectOptionUIBlockFactory::MakeForSelectOption($sVal, utils::EscapeHtml(Dict::S('Core:BulkExport:PageSize-'.$sVal)), ($sVal == $sDefaultFormat)));
|
||||
}
|
||||
$oFieldSetFormat->AddSubBlock(new Html('</br>'));
|
||||
|
||||
@@ -88,7 +88,7 @@ class PDFBulkExport extends HTMLBulkExport
|
||||
$aPossibleOrientation = ['P', 'L'];
|
||||
$sDefaultOrientation = 'L';
|
||||
foreach ($aPossibleOrientation as $sVal) {
|
||||
$oSelectOrientation->AddSubBlock(SelectOptionUIBlockFactory::MakeForSelectOption($sVal, htmlentities(Dict::S('Core:BulkExport:PageOrientation-'.$sVal), ENT_QUOTES, 'UTF-8'), ($sVal == $sDefaultOrientation)));
|
||||
$oSelectOrientation->AddSubBlock(SelectOptionUIBlockFactory::MakeForSelectOption($sVal, utils::EscapeHtml(Dict::S('Core:BulkExport:PageOrientation-'.$sVal)), ($sVal == $sDefaultOrientation)));
|
||||
}
|
||||
|
||||
//date format
|
||||
@@ -97,8 +97,8 @@ class PDFBulkExport extends HTMLBulkExport
|
||||
|
||||
$sDateTimeFormat = utils::ReadParam('date_format', (string)AttributeDateTime::GetFormat(), true, 'raw_data');
|
||||
|
||||
$sDefaultFormat = htmlentities((string)AttributeDateTime::GetFormat(), ENT_QUOTES, 'UTF-8');
|
||||
$sExample = htmlentities(date((string)AttributeDateTime::GetFormat()), ENT_QUOTES, 'UTF-8');
|
||||
$sDefaultFormat = utils::EscapeHtml((string)AttributeDateTime::GetFormat());
|
||||
$sExample = utils::EscapeHtml(date((string)AttributeDateTime::GetFormat()));
|
||||
$oRadioDefault = InputUIBlockFactory::MakeForInputWithLabel(Dict::Format('Core:BulkExport:DateTimeFormatDefault_Example', $sDefaultFormat, $sExample), "pdf_date_format_radio", "default", "pdf_date_time_format_default", "radio");
|
||||
$oRadioDefault->GetInput()->SetIsChecked(($sDateTimeFormat == (string)AttributeDateTime::GetFormat()));
|
||||
$oRadioDefault->SetBeforeInput(false);
|
||||
@@ -106,7 +106,7 @@ class PDFBulkExport extends HTMLBulkExport
|
||||
$oFieldSetDate->AddSubBlock($oRadioDefault);
|
||||
$oFieldSetDate->AddSubBlock(new Html('</br>'));
|
||||
|
||||
$sFormatInput = '<input type="text" size="15" name="date_format" id="pdf_custom_date_time_format" title="" value="'.htmlentities($sDateTimeFormat, ENT_QUOTES, 'UTF-8').'"/>';
|
||||
$sFormatInput = '<input type="text" size="15" name="date_format" id="pdf_custom_date_time_format" title="" value="'.utils::EscapeHtml($sDateTimeFormat).'"/>';
|
||||
$oRadioCustom = InputUIBlockFactory::MakeForInputWithLabel(Dict::Format('Core:BulkExport:DateTimeFormatCustom_Format', $sFormatInput), "pdf_date_format_radio", "custom", "pdf_date_time_format_custom", "radio");
|
||||
$oRadioCustom->SetDescription(Dict::S('UI:CSVImport:CustomDateTimeFormatTooltip'));
|
||||
$oRadioCustom->GetInput()->SetIsChecked($sDateTimeFormat !== (string)AttributeDateTime::GetFormat());
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
/**
|
||||
* Element of the response formed by RestResultWithObjects
|
||||
*
|
||||
* @package RESTExtensibilityAPI
|
||||
* @package RESTAPI
|
||||
* @api
|
||||
*/
|
||||
class ObjectResult
|
||||
@@ -163,11 +163,12 @@ class ObjectResult
|
||||
/**
|
||||
* REST response for services managing objects. Derive this structure to add information and/or constants
|
||||
*
|
||||
* @package RESTExtensibilityAPI
|
||||
* @package RESTAPI
|
||||
* @api
|
||||
*/
|
||||
class RestResultWithObjects extends RestResult
|
||||
{
|
||||
/** @var array "DBObject_class:DBObject_key" as key, {@see \ObjectResult} as value */
|
||||
public $objects;
|
||||
|
||||
/**
|
||||
@@ -223,7 +224,7 @@ class RestResultWithObjects extends RestResult
|
||||
}
|
||||
|
||||
/**
|
||||
* @package RESTExtensibilityAPI
|
||||
* @package RESTAPI
|
||||
* @api
|
||||
*/
|
||||
class RestResultWithRelations extends RestResultWithObjects
|
||||
@@ -259,7 +260,7 @@ class RestResultWithRelations extends RestResultWithObjects
|
||||
/**
|
||||
* Deletion result codes for a target object (either deleted or updated)
|
||||
*
|
||||
* @package RESTExtensibilityAPI
|
||||
* @package RESTAPI
|
||||
* @api
|
||||
* @since 2.0.1
|
||||
*/
|
||||
|
||||
@@ -745,26 +745,30 @@ class RelationTypeIterator implements Iterator
|
||||
}
|
||||
}
|
||||
|
||||
public function rewind()
|
||||
public function rewind(): void
|
||||
{
|
||||
$this->iCurrentIdx = 0;
|
||||
}
|
||||
|
||||
public function valid()
|
||||
public function valid(): bool
|
||||
{
|
||||
return array_key_exists($this->iCurrentIdx, $this->aList);
|
||||
}
|
||||
|
||||
public function next()
|
||||
public function next(): void
|
||||
{
|
||||
$this->iCurrentIdx++;
|
||||
}
|
||||
|
||||
// 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]
|
||||
public function current()
|
||||
{
|
||||
return $this->aList[$this->iCurrentIdx];
|
||||
}
|
||||
|
||||
// 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]
|
||||
public function key()
|
||||
{
|
||||
return $this->iCurrentIdx;
|
||||
|
||||
@@ -73,8 +73,8 @@ class SpreadsheetBulkExport extends TabularBulkExport
|
||||
|
||||
$sDateTimeFormat = utils::ReadParam('date_format', (string)AttributeDateTime::GetFormat(), true, 'raw_data');
|
||||
|
||||
$sDefaultFormat = htmlentities((string)AttributeDateTime::GetFormat(), ENT_QUOTES, 'UTF-8');
|
||||
$sExample = htmlentities(date((string)AttributeDateTime::GetFormat()), ENT_QUOTES, 'UTF-8');
|
||||
$sDefaultFormat = utils::EscapeHtml((string)AttributeDateTime::GetFormat());
|
||||
$sExample = utils::EscapeHtml(date((string)AttributeDateTime::GetFormat()));
|
||||
$oRadioDefault = InputUIBlockFactory::MakeForInputWithLabel(Dict::Format('Core:BulkExport:DateTimeFormatDefault_Example', $sDefaultFormat, $sExample), "spreadsheet_date_format_radio", "default", "spreadsheet_date_time_format_default", "radio");
|
||||
$oRadioDefault->GetInput()->SetIsChecked(($sDateTimeFormat == (string)AttributeDateTime::GetFormat()));
|
||||
$oRadioDefault->GetInput()->AddCSSClass('ibo-input-checkbox');
|
||||
@@ -82,7 +82,7 @@ class SpreadsheetBulkExport extends TabularBulkExport
|
||||
$oFieldSetDate->AddSubBlock($oRadioDefault);
|
||||
$oFieldSetDate->AddSubBlock(new Html('</br>'));
|
||||
|
||||
$sFormatInput = '<input type="text" size="15" name="date_format" id="spreadsheet_custom_date_time_format" title="" value="'.htmlentities($sDateTimeFormat, ENT_QUOTES, 'UTF-8').'"/>';
|
||||
$sFormatInput = '<input type="text" size="15" name="date_format" id="spreadsheet_custom_date_time_format" title="" value="'.utils::EscapeHtml($sDateTimeFormat).'"/>';
|
||||
$oRadioCustom = InputUIBlockFactory::MakeForInputWithLabel(Dict::Format('Core:BulkExport:DateTimeFormatCustom_Format', $sFormatInput), "spreadsheet_date_format_radio", "custom", "spreadsheet_date_time_format_custom", "radio");
|
||||
$oRadioCustom->SetDescription(Dict::S('UI:CSVImport:CustomDateTimeFormatTooltip'));
|
||||
$oRadioCustom->GetInput()->SetIsChecked($sDateTimeFormat !== (string)AttributeDateTime::GetFormat());
|
||||
@@ -140,7 +140,8 @@ EOF
|
||||
if ($oAttDef instanceof AttributeDateTime) // AttributeDate is derived from AttributeDateTime
|
||||
{
|
||||
$sClass = (get_class($oAttDef) == 'AttributeDateTime') ? 'user-formatted-date-time' : 'user-formatted-date';
|
||||
return '<div class="'.$sClass.'" data-date="'.$oObj->Get($sAttCode).'">'.htmlentities($oAttDef->GetEditValue($oObj->Get($sAttCode), $oObj), ENT_QUOTES, 'UTF-8').'</div>';
|
||||
|
||||
return '<div class="'.$sClass.'" data-date="'.$oObj->Get($sAttCode).'">'.utils::EscapeHtml($oAttDef->GetEditValue($oObj->Get($sAttCode), $oObj)).'</div>';
|
||||
}
|
||||
}
|
||||
return $this->GetValue($oObj, $sAttCode);
|
||||
@@ -158,19 +159,13 @@ EOF
|
||||
default:
|
||||
$value = $oObj->Get($sAttCode);
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
|
||||
if ($value instanceof ormCaseLog)
|
||||
{
|
||||
$sRet = str_replace("\n", "<br/>", htmlentities($value->__toString(), ENT_QUOTES, 'UTF-8'));
|
||||
}
|
||||
elseif ($value instanceof ormStopWatch)
|
||||
{
|
||||
if ($value instanceof ormCaseLog) {
|
||||
$sRet = str_replace("\n", "<br/>", utils::EscapeHtml($value->__toString()));
|
||||
} elseif ($value instanceof ormStopWatch) {
|
||||
$sRet = $value->GetTimeSpent();
|
||||
}
|
||||
elseif ($value instanceof ormDocument)
|
||||
{
|
||||
} elseif ($value instanceof ormDocument) {
|
||||
$sRet = '';
|
||||
}
|
||||
elseif ($oAttDef instanceof AttributeText)
|
||||
} elseif ($oAttDef instanceof AttributeText)
|
||||
{
|
||||
if ($bFormattedText)
|
||||
{
|
||||
@@ -191,15 +186,11 @@ EOF
|
||||
// Stick to the weird implementation made in GetNextChunk
|
||||
$sRet = utils::TextToHtml($oObj->GetEditValue($sAttCode));
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($this->bLocalizeOutput)
|
||||
{
|
||||
$sRet = htmlentities($oObj->GetEditValue(), ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
else
|
||||
{
|
||||
$sRet = htmlentities((string)$value, ENT_QUOTES, 'UTF-8');
|
||||
else {
|
||||
if ($this->bLocalizeOutput) {
|
||||
$sRet = utils::EscapeHtml($oObj->GetEditValue());
|
||||
} else {
|
||||
$sRet = utils::EscapeHtml((string)$value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -314,22 +305,16 @@ EOF
|
||||
$sData .= "<td>$sDate</td>";
|
||||
$sData .= "<td>$sTime</td>";
|
||||
}
|
||||
else if (get_class($oFinalAttDef) == 'AttributeDate')
|
||||
{
|
||||
else if (get_class($oFinalAttDef) == 'AttributeDate') {
|
||||
$sDate = $oDateFormat->Format($oObj->Get($sAttCode));
|
||||
$sData .= "<td>$sDate</td>";
|
||||
}
|
||||
else if($oAttDef instanceof AttributeCaseLog)
|
||||
{
|
||||
} else if ($oAttDef instanceof AttributeCaseLog) {
|
||||
$rawValue = $oObj->Get($sAttCode);
|
||||
$sField = str_replace("\n", "<br/>", htmlentities($rawValue->__toString(), ENT_QUOTES, 'UTF-8'));
|
||||
$sField = str_replace("\n", "<br/>", utils::EscapeHtml($rawValue->__toString()));
|
||||
// Trick for Excel: treat the content as text even if it begins with an equal sign
|
||||
$sData .= "<td x:str>$sField</td>";
|
||||
}
|
||||
elseif ($oAttDef instanceof AttributeText)
|
||||
{
|
||||
if ($bFormattedText)
|
||||
{
|
||||
} elseif ($oAttDef instanceof AttributeText) {
|
||||
if ($bFormattedText) {
|
||||
// Replace paragraphs (<p...>...</p>, etc) by line breaks (<br/>) since Excel (pre-2016) splits the cells when there is a paragraph
|
||||
$sField = static::HtmlToSpreadsheet($oObj->GetAsHTML($sAttCode));
|
||||
}
|
||||
@@ -356,16 +341,12 @@ EOF
|
||||
$sField = utils::HtmlEntities($oObj->GetAsCSV($sAttCode, $this->bLocalizeOutput, ''));
|
||||
$sData .= "<td x:str>$sField</td>";
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
$rawValue = $oObj->Get($sAttCode);
|
||||
if ($this->bLocalizeOutput)
|
||||
{
|
||||
$sField = htmlentities($oFinalAttDef->GetEditValue($rawValue), ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
else
|
||||
{
|
||||
$sField = htmlentities($rawValue, ENT_QUOTES, 'UTF-8');
|
||||
if ($this->bLocalizeOutput) {
|
||||
$sField = utils::EscapeHtml($oFinalAttDef->GetEditValue($rawValue));
|
||||
} else {
|
||||
$sField = utils::EscapeHtml($rawValue);
|
||||
}
|
||||
$sData .= "<td>$sField</td>";
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ class TemplateStringPlaceholder
|
||||
public function __construct($sToken)
|
||||
{
|
||||
$this->sToken = $sToken;
|
||||
$this->sAttcode = '';
|
||||
$this->sAttCode = '';
|
||||
$this->sFunction = '';
|
||||
$this->sParamName = '';
|
||||
$this->bIsValid = false; // Validity may be false in general, but it can work anyway (thanks to specialization) when rendering
|
||||
@@ -90,9 +90,9 @@ class TemplateString
|
||||
$oPlaceholder->sFunction = '';
|
||||
|
||||
$oPlaceholder->sParamName = $sParamName;
|
||||
$sAttcode = substr($sPlaceholder, strlen($sParamPrefix));
|
||||
$oPlaceholder->sAttcode = $sAttcode;
|
||||
$oPlaceholder->bIsValid = MetaModel::IsValidAttCode($sClass, $sAttcode, true /* extended */);
|
||||
$sAttCode = substr($sPlaceholder, strlen($sParamPrefix));
|
||||
$oPlaceholder->sAttCode = $sAttCode;
|
||||
$oPlaceholder->bIsValid = MetaModel::IsValidAttCode($sClass, $sAttCode, true /* extended */);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ class TemplateString
|
||||
$oRef = $aParamValues[$oPlaceholder->sParamName];
|
||||
try
|
||||
{
|
||||
$value = $oRef->Get($oPlaceholder->sAttcode);
|
||||
$value = $oRef->Get($oPlaceholder->sAttCode);
|
||||
$aSearch[] = '$'.$oPlaceholder->sToken.'$';
|
||||
$aReplace[] = $value;
|
||||
$oPlaceholder->bIsValid = true;
|
||||
|
||||
@@ -33,20 +33,22 @@ abstract class Trigger extends cmdbAbstractObject
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "grant_by_profile,core/cmdb",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "description",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array('description'),
|
||||
"db_table" => "priv_trigger",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "realclass",
|
||||
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-conflict.svg'),
|
||||
"category" => "grant_by_profile,core/cmdb",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "description",
|
||||
"complementary_name_attcode" => array('finalclass'),
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array('description'),
|
||||
"db_table" => "priv_trigger",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "realclass",
|
||||
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-conflict.svg'),
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values" => null, "sql" => "description", "default_value" => null, "is_null_allowed" => false, "depends_on" => array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("action_list", array("linked_class" => "lnkTriggerAction", "ext_key_to_me" => "trigger_id", "ext_key_to_remote" => "action_id", "allowed_values" => null, "count_min" => 1, "count_max" => 0, "depends_on" => array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("action_list",
|
||||
array("linked_class" => "lnkTriggerAction", "ext_key_to_me" => "trigger_id", "ext_key_to_remote" => "action_id", "allowed_values" => null, "count_min" => 1, "count_max" => 0, "depends_on" => array(), "display_style" => 'property')));
|
||||
$aTags = ContextTag::GetTags();
|
||||
MetaModel::Init_AddAttribute( new AttributeEnumSet("context", array("allowed_values" => null, "possible_values" => new ValueSetEnumPadded($aTags), "sql" => "context", "depends_on" => array(), "is_null_allowed" => true, "max_items" => 12)));
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user