mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-06 01:28:41 +02:00
Compare commits
564 Commits
support/2.
...
2.6.0-prod
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2ddb30f78 | ||
|
|
75fbb831c9 | ||
|
|
3219957eed | ||
|
|
44671a5085 | ||
|
|
bc4873dfe5 | ||
|
|
92657951c7 | ||
|
|
8b2c18ab8c | ||
|
|
18999f29c5 | ||
|
|
e8075bf5fd | ||
|
|
5338325a73 | ||
|
|
4d3810a10d | ||
|
|
672bc471be | ||
|
|
3089cbc2bc | ||
|
|
ce9416d887 | ||
|
|
473d1fb756 | ||
|
|
b880ca05a5 | ||
|
|
cbc9784d21 | ||
|
|
16cb1bbbbf | ||
|
|
a1c2b809ba | ||
|
|
c9691457b8 | ||
|
|
9ef6c78395 | ||
|
|
81f70c4c58 | ||
|
|
869f590bf4 | ||
|
|
3b6aa7eaf5 | ||
|
|
53ef2b0b5d | ||
|
|
4dfe6fc817 | ||
|
|
260143050e | ||
|
|
6adde38399 | ||
|
|
76a40b2f45 | ||
|
|
77c2537df7 | ||
|
|
6a88f622ad | ||
|
|
9b5d8b8a01 | ||
|
|
b4448c5bb9 | ||
|
|
7faf3258f7 | ||
|
|
7c0d03ea3b | ||
|
|
d1837377a4 | ||
|
|
1c3a503a82 | ||
|
|
daafa9123c | ||
|
|
5a1b6e43c9 | ||
|
|
81d502cae8 | ||
|
|
4a99ed2ad8 | ||
|
|
7016724abc | ||
|
|
0a8e3f099e | ||
|
|
45910dc115 | ||
|
|
afd6428411 | ||
|
|
7eb419507b | ||
|
|
b4a6d378ab | ||
|
|
44b7821015 | ||
|
|
52ac819c1f | ||
|
|
c4ba1d55ac | ||
|
|
85acac60c7 | ||
|
|
f54da5f9a6 | ||
|
|
36d47c2274 | ||
|
|
bd082c0a6e | ||
|
|
29dd196193 | ||
|
|
80a8f7a888 | ||
|
|
7f497fe3be | ||
|
|
53fe826f9f | ||
|
|
56b4ecb4ce | ||
|
|
d17717e4b5 | ||
|
|
68da3a4aad | ||
|
|
855480fd7b | ||
|
|
79200e8f96 | ||
|
|
540bc3a54b | ||
|
|
35752a8041 | ||
|
|
86dce21849 | ||
|
|
fca5a625d2 | ||
|
|
2766fad61a | ||
|
|
c6da1db72b | ||
|
|
843c06b007 | ||
|
|
7cf7628b8f | ||
|
|
41b096ba76 | ||
|
|
2a4bd4b0dc | ||
|
|
f4f5281769 | ||
|
|
a4a09cd8c7 | ||
|
|
831b18b4d2 | ||
|
|
e4e5f627c4 | ||
|
|
53e532cbaf | ||
|
|
3fa0cbf0fe | ||
|
|
af8b06dda6 | ||
|
|
72e2473444 | ||
|
|
6e31a040b2 | ||
|
|
6c07688c34 | ||
|
|
598c22a285 | ||
|
|
d392b9c0f6 | ||
|
|
387c166985 | ||
|
|
9115bc118d | ||
|
|
aca11ac966 | ||
|
|
c15d626095 | ||
|
|
79d00b9fe6 | ||
|
|
a92e2fd882 | ||
|
|
7ed51984c7 | ||
|
|
cdf11a3485 | ||
|
|
bbefd22950 | ||
|
|
e08369122d | ||
|
|
0ce0572c40 | ||
|
|
9d19556419 | ||
|
|
67c0e0eb1c | ||
|
|
c5d0dd05b9 | ||
|
|
4e1f877ab4 | ||
|
|
19d4de482d | ||
|
|
122c3a9542 | ||
|
|
15d9ba9906 | ||
|
|
95a22a1a7e | ||
|
|
5309aa225a | ||
|
|
c0cd450c5f | ||
|
|
1b4b71cb35 | ||
|
|
f37f3c4c22 | ||
|
|
83b3fb80d5 | ||
|
|
c242f90440 | ||
|
|
e4912ee175 | ||
|
|
83e1f35f9c | ||
|
|
c42333d085 | ||
|
|
c4c0b0f630 | ||
|
|
aec3c34eea | ||
|
|
c4ff20b9fb | ||
|
|
c7ce35a877 | ||
|
|
5ccfa24b27 | ||
|
|
1c6a6edf5a | ||
|
|
a659de9c9b | ||
|
|
29e28dedf1 | ||
|
|
0acc0b4a74 | ||
|
|
c300e46480 | ||
|
|
0a3f076335 | ||
|
|
12d9551845 | ||
|
|
0d6c799f9c | ||
|
|
a395f5b63c | ||
|
|
2e46fa50b3 | ||
|
|
f9bb2e7a14 | ||
|
|
57e8b9faaf | ||
|
|
c737b83eb5 | ||
|
|
5c91a1a612 | ||
|
|
0c49641094 | ||
|
|
99e909dae4 | ||
|
|
ecf34b47cb | ||
|
|
949b4495fc | ||
|
|
bd7e0174f3 | ||
|
|
ac89b2dc44 | ||
|
|
7e3fceb7dc | ||
|
|
b28ed08a85 | ||
|
|
a488c42dca | ||
|
|
fb233709e0 | ||
|
|
9169cced20 | ||
|
|
1db9708624 | ||
|
|
52b42d0210 | ||
|
|
67f5f09530 | ||
|
|
b9e0747825 | ||
|
|
e44ed248ef | ||
|
|
b0c120d7fd | ||
|
|
177b6d1757 | ||
|
|
ff39b7bc51 | ||
|
|
7401e88844 | ||
|
|
b1e1e29254 | ||
|
|
ffac3250af | ||
|
|
e9fb885d34 | ||
|
|
b87037d187 | ||
|
|
a97680fb04 | ||
|
|
798e526010 | ||
|
|
d344478b48 | ||
|
|
b898a09c4c | ||
|
|
17589e060a | ||
|
|
7867cc1dca | ||
|
|
114b18d7c8 | ||
|
|
5baff6257b | ||
|
|
8264d21520 | ||
|
|
8bd10a2d11 | ||
|
|
cce88f09b0 | ||
|
|
26475ad10f | ||
|
|
c1a81e4f0d | ||
|
|
4a5cb23730 | ||
|
|
f0e5128fb5 | ||
|
|
63b08b0e70 | ||
|
|
4072ed09ac | ||
|
|
70137808f0 | ||
|
|
36ade3b15c | ||
|
|
6b5f32611d | ||
|
|
43bfe06882 | ||
|
|
29df8f8f92 | ||
|
|
405b1b8e6f | ||
|
|
b050210737 | ||
|
|
ed02e3522e | ||
|
|
a327b5fb5e | ||
|
|
f9b2fda092 | ||
|
|
0d082bf378 | ||
|
|
d8cef95d56 | ||
|
|
2258e8c652 | ||
|
|
5e19d338b9 | ||
|
|
14ecfc4ce8 | ||
|
|
170d84d635 | ||
|
|
a962f6535e | ||
|
|
a4055c4a74 | ||
|
|
9ed550ee7f | ||
|
|
00bd7583f7 | ||
|
|
e4619fbc0f | ||
|
|
518d94f5f3 | ||
|
|
389a1791d7 | ||
|
|
54eb41b66f | ||
|
|
ede2673ba5 | ||
|
|
dd50c99ec4 | ||
|
|
357555e507 | ||
|
|
1f6a51b31f | ||
|
|
6d4cf71197 | ||
|
|
87f898d29d | ||
|
|
d5576522ae | ||
|
|
6addd9acec | ||
|
|
9b5942bf4e | ||
|
|
9faa4e4cbd | ||
|
|
8b80bd71e7 | ||
|
|
8989206461 | ||
|
|
89a21c8e44 | ||
|
|
a7ec6e5ca0 | ||
|
|
a08675352a | ||
|
|
7158b1f787 | ||
|
|
3925ad252f | ||
|
|
f3fd47a792 | ||
|
|
af92f58265 | ||
|
|
81b5f18579 | ||
|
|
184c0cb84b | ||
|
|
c892862822 | ||
|
|
b61d1feec4 | ||
|
|
a73ff16642 | ||
|
|
7763983ef4 | ||
|
|
35f7fe98ae | ||
|
|
533acbde8d | ||
|
|
d72da422de | ||
|
|
891608f812 | ||
|
|
e66da81a20 | ||
|
|
cae0cd00c2 | ||
|
|
32fc87023c | ||
|
|
0021482c1b | ||
|
|
276427e3df | ||
|
|
7b49bde3ad | ||
|
|
a94d940bd3 | ||
|
|
313c3c6fc3 | ||
|
|
de86f71c90 | ||
|
|
098b0531d8 | ||
|
|
34552214bf | ||
|
|
d61a8ba160 | ||
|
|
84ebca58ac | ||
|
|
def4c54d26 | ||
|
|
027b0fcff7 | ||
|
|
fc0cb44a84 | ||
|
|
574d72b0e7 | ||
|
|
cd5e1afb2b | ||
|
|
0298d6bc19 | ||
|
|
60d2fd4f7e | ||
|
|
5faa0ffe08 | ||
|
|
705ce02580 | ||
|
|
78c674a989 | ||
|
|
84d9be3293 | ||
|
|
30c622052e | ||
|
|
ada56da63e | ||
|
|
21c5638e1e | ||
|
|
02da84b4cf | ||
|
|
ebb5ede613 | ||
|
|
db4b8b2f43 | ||
|
|
c17f7caa29 | ||
|
|
d7df975971 | ||
|
|
f3f70d6296 | ||
|
|
256d4e2cb3 | ||
|
|
fb31c9006a | ||
|
|
bf000e6a89 | ||
|
|
94b4f10277 | ||
|
|
15671f6dd4 | ||
|
|
2460d2112f | ||
|
|
709badd0f7 | ||
|
|
99153d02ee | ||
|
|
4347b2c5ff | ||
|
|
c13b6ea13a | ||
|
|
6e5d4834f1 | ||
|
|
9f489d8a59 | ||
|
|
c9be1a8e71 | ||
|
|
c1e2f35c96 | ||
|
|
462148a12f | ||
|
|
7e4edc6e3a | ||
|
|
452e7cce01 | ||
|
|
cb7b5dfffb | ||
|
|
0a34fb7a7a | ||
|
|
460337954b | ||
|
|
74a8c3f5bd | ||
|
|
3347d32f2a | ||
|
|
4c2be6b2c5 | ||
|
|
860bb6d615 | ||
|
|
950ffcde2b | ||
|
|
0580c71749 | ||
|
|
c66b0bea41 | ||
|
|
7281bd4a1a | ||
|
|
c9d73fc0c8 | ||
|
|
8720ac2b08 | ||
|
|
33e8b6a64c | ||
|
|
d7cf339ab5 | ||
|
|
714fada9f0 | ||
|
|
d36fd6a47d | ||
|
|
c0d83e96d8 | ||
|
|
3da6251cc7 | ||
|
|
1bf39aa092 | ||
|
|
3387d8fdd2 | ||
|
|
9f926fccd8 | ||
|
|
e9a77c798e | ||
|
|
29691a09fe | ||
|
|
cb73c2a3f9 | ||
|
|
713a41909b | ||
|
|
9972e253d5 | ||
|
|
55309be9a1 | ||
|
|
fd75f3af28 | ||
|
|
af7e5eb03e | ||
|
|
b97ce7a25f | ||
|
|
89d617c152 | ||
|
|
4595c565cc | ||
|
|
8f55d4054a | ||
|
|
2e255b10a7 | ||
|
|
cf9c1b52ed | ||
|
|
d3b3c44cbd | ||
|
|
edb25b6dca | ||
|
|
4bf748641d | ||
|
|
16a6b70708 | ||
|
|
40355cb2d0 | ||
|
|
b52aaaadf2 | ||
|
|
f55c0aa1c9 | ||
|
|
333c51b0f9 | ||
|
|
46dee2919e | ||
|
|
799618dee7 | ||
|
|
96aaa0d8e1 | ||
|
|
5556e3efec | ||
|
|
d8b2d4435a | ||
|
|
a4315b4789 | ||
|
|
3e8dd61607 | ||
|
|
1d28a67d21 | ||
|
|
3a551e9cec | ||
|
|
cf6693a534 | ||
|
|
8c5c275952 | ||
|
|
5fa4b4cb88 | ||
|
|
58525f247e | ||
|
|
d78f19525e | ||
|
|
5559c8205f | ||
|
|
2a6a97526d | ||
|
|
1adea8f014 | ||
|
|
29177ec86b | ||
|
|
741e88e1f0 | ||
|
|
66bddc5730 | ||
|
|
6eaa7c0530 | ||
|
|
b257fae03e | ||
|
|
2d24324dea | ||
|
|
ec597f697a | ||
|
|
3bed62a473 | ||
|
|
b5f7227ecd | ||
|
|
615adf8281 | ||
|
|
cb7bb3242a | ||
|
|
4b08eea998 | ||
|
|
01551942b3 | ||
|
|
720d334053 | ||
|
|
9397d1ac2e | ||
|
|
1530bb89fe | ||
|
|
f09683949d | ||
|
|
e1f96974bb | ||
|
|
1fc261937f | ||
|
|
a94211d3d3 | ||
|
|
534f6b6a54 | ||
|
|
901764bf70 | ||
|
|
e67d6e8a80 | ||
|
|
16476f736a | ||
|
|
e9ecd89cda | ||
|
|
55036511ab | ||
|
|
3cf5d31f5d | ||
|
|
84ae36cf1a | ||
|
|
00515ac14a | ||
|
|
245612d0eb | ||
|
|
ae6e3b4f17 | ||
|
|
12a3f1c36c | ||
|
|
1af1e92b60 | ||
|
|
a1ad7a5def | ||
|
|
398d1aa820 | ||
|
|
38278f3432 | ||
|
|
baf4662ed8 | ||
|
|
cf07c9f1a2 | ||
|
|
446bd0ad1f | ||
|
|
3d5e46faac | ||
|
|
b8fba8997a | ||
|
|
a07c81d0d9 | ||
|
|
0252d091ca | ||
|
|
18d9ada58d | ||
|
|
055d2cc62c | ||
|
|
e15c5c58d8 | ||
|
|
b734aca4d0 | ||
|
|
e495b36dfb | ||
|
|
cdcfe03d67 | ||
|
|
2c6dc20046 | ||
|
|
a8c3b7ac2e | ||
|
|
98572c6efb | ||
|
|
70840c53aa | ||
|
|
9fbd27f3a8 | ||
|
|
c3c1897258 | ||
|
|
5403219746 | ||
|
|
fb08903a8a | ||
|
|
416005654e | ||
|
|
1debf77ab4 | ||
|
|
f1a8bb08da | ||
|
|
7edff12bbf | ||
|
|
34d601f49c | ||
|
|
ec38473d88 | ||
|
|
875c77ae65 | ||
|
|
75e24f9f79 | ||
|
|
16a4662129 | ||
|
|
1eda1eb9de | ||
|
|
d981fd35ef | ||
|
|
6f1dc44932 | ||
|
|
95b929dd27 | ||
|
|
0c75b98f48 | ||
|
|
ecc5a0bf8a | ||
|
|
d9315bec84 | ||
|
|
d1ee7f4353 | ||
|
|
979095337b | ||
|
|
34b528b1f4 | ||
|
|
5ea056a3fc | ||
|
|
2ba31244c2 | ||
|
|
3ea16694b4 | ||
|
|
35e872310d | ||
|
|
b86b24d444 | ||
|
|
45e1a6ffd6 | ||
|
|
c87f001956 | ||
|
|
1fc3b3a4ed | ||
|
|
c706a2d77c | ||
|
|
8577fc6701 | ||
|
|
ead3067d49 | ||
|
|
4772b8c5bb | ||
|
|
6817cfbeea | ||
|
|
f851238eab | ||
|
|
79157c465a | ||
|
|
64438f6b6d | ||
|
|
f2b914a4c5 | ||
|
|
da71004898 | ||
|
|
e9019b294a | ||
|
|
e2c3ea22e4 | ||
|
|
c7f3f20229 | ||
|
|
46dc024335 | ||
|
|
34c68cfef0 | ||
|
|
d6a564a38b | ||
|
|
31c69088ca | ||
|
|
49bb8fd515 | ||
|
|
b8f8a0d455 | ||
|
|
374e34a8b8 | ||
|
|
7ef1d72314 | ||
|
|
5666a3d74c | ||
|
|
8004b411da | ||
|
|
e473c46dc3 | ||
|
|
148309245b | ||
|
|
5dc39fe068 | ||
|
|
47c7a3c5e3 | ||
|
|
85b7e86e58 | ||
|
|
f871581997 | ||
|
|
3fca465f1d | ||
|
|
7955dd86f4 | ||
|
|
40a4e6d7b0 | ||
|
|
bef4ac83a4 | ||
|
|
bde83fc705 | ||
|
|
cad18bee73 | ||
|
|
98e5eaa4e0 | ||
|
|
3129e94363 | ||
|
|
7ac0e50bd9 | ||
|
|
7d502fae23 | ||
|
|
894eeef140 | ||
|
|
7991069282 | ||
|
|
39f3972a24 | ||
|
|
fc0e5ecd3b | ||
|
|
8897d9f82b | ||
|
|
1329b5f684 | ||
|
|
e58bc738d0 | ||
|
|
af63579f31 | ||
|
|
55fad3a9ec | ||
|
|
3250e0a1e6 | ||
|
|
8df287f45e | ||
|
|
f458a77449 | ||
|
|
1953c05b33 | ||
|
|
a03c553000 | ||
|
|
ecdc4076d9 | ||
|
|
cd2ea3793e | ||
|
|
d40ffd944f | ||
|
|
0ee4b52baa | ||
|
|
5e7db7a27e | ||
|
|
9631021f84 | ||
|
|
157193c831 | ||
|
|
1415f12506 | ||
|
|
4df497a768 | ||
|
|
9a13eb0f90 | ||
|
|
eaa49bce05 | ||
|
|
96334d859a | ||
|
|
13855b1501 | ||
|
|
277b24e2f0 | ||
|
|
abb2a2a6e2 | ||
|
|
961f5fd387 | ||
|
|
bdb62de81c | ||
|
|
422a850792 | ||
|
|
1dfcc77098 | ||
|
|
13097d7e0e | ||
|
|
ea9b7eddde | ||
|
|
07056613e5 | ||
|
|
4d45793ec7 | ||
|
|
4718133ab6 | ||
|
|
ba518fa975 | ||
|
|
0311e78690 | ||
|
|
9db47428db | ||
|
|
84dc3c2093 | ||
|
|
7d37b06555 | ||
|
|
6e6a89fb8c | ||
|
|
135d9c5e55 | ||
|
|
dd46048ea6 | ||
|
|
8fe38b03f6 | ||
|
|
0adadeb280 | ||
|
|
775a5122c9 | ||
|
|
5099060be2 | ||
|
|
201c93e20a | ||
|
|
c852d91a72 | ||
|
|
a3c6e62bd5 | ||
|
|
8cd18fe190 | ||
|
|
5e1452f9b9 | ||
|
|
b76de65886 | ||
|
|
48b3bd8bf3 | ||
|
|
ab1715edec | ||
|
|
3589783ee1 | ||
|
|
af43e22f03 | ||
|
|
956f6403b8 | ||
|
|
82ed1853fa | ||
|
|
eb0403945b | ||
|
|
3126af94ac | ||
|
|
e71ad536a9 | ||
|
|
d0322b471d | ||
|
|
a6af11e644 | ||
|
|
1809180d41 | ||
|
|
9612b1b9e9 | ||
|
|
526d4c4d15 | ||
|
|
3f4c824fd5 | ||
|
|
9c25feb67c | ||
|
|
e31faf81a9 | ||
|
|
6eb13c24aa | ||
|
|
dd20017b0a | ||
|
|
6630051ef3 | ||
|
|
a66d91c507 | ||
|
|
2f34f0458b | ||
|
|
076abc8ae7 | ||
|
|
5134e57109 | ||
|
|
3d1ccf2028 | ||
|
|
60b980fbff | ||
|
|
df20f1b5ab | ||
|
|
27144f07b1 | ||
|
|
8135fdb9d2 | ||
|
|
985ad18048 | ||
|
|
768ed2666d | ||
|
|
c289a53ed3 | ||
|
|
5c206718c8 | ||
|
|
d7f18e879a | ||
|
|
4881a2c274 | ||
|
|
5eb5fa05bc | ||
|
|
c7e0f36d7c | ||
|
|
54d54ca78e | ||
|
|
4cd591f81c | ||
|
|
d21d732545 | ||
|
|
ea4854d239 | ||
|
|
7ca95b9413 | ||
|
|
00d0d383f8 | ||
|
|
401a8cdd77 | ||
|
|
b2384855fd | ||
|
|
602c087c29 | ||
|
|
0d96ed5436 | ||
|
|
97fa3ac3b3 |
9
.gitflow
Normal file
9
.gitflow
Normal file
@@ -0,0 +1,9 @@
|
||||
[gitflow "branch"]
|
||||
master = master
|
||||
develop = develop
|
||||
[gitflow "prefix"]
|
||||
feature = feature/
|
||||
release = release/
|
||||
hotfix = hotfix/
|
||||
versiontag =
|
||||
support = support/
|
||||
38
.gitignore
vendored
38
.gitignore
vendored
@@ -1,37 +1,36 @@
|
||||
|
||||
# no slash at the end to handle also symlinks
|
||||
/toolkit
|
||||
/conf
|
||||
/env-*
|
||||
/conf/*
|
||||
/env-*/*
|
||||
|
||||
# composer reserver directory, from sources, populate/update using "composer install"
|
||||
vendor/*
|
||||
test/vendor/*
|
||||
|
||||
# all datas but listing prevention
|
||||
/data/**
|
||||
!/data/.htaccess
|
||||
!/data/index.php
|
||||
!/data/web.config
|
||||
data/*
|
||||
!data/.htaccess
|
||||
!data/index.php
|
||||
!data/web.config
|
||||
|
||||
# iTop extensions
|
||||
/extensions/**
|
||||
!/extensions/readme.txt
|
||||
extensions/*
|
||||
!extensions/readme.txt
|
||||
|
||||
# all logs but listing prevention
|
||||
/log/**
|
||||
!/log/.htaccess
|
||||
!/log/index.php
|
||||
!/log/web.config
|
||||
log/*
|
||||
!log/.htaccess
|
||||
!log/index.php
|
||||
!log/web.config
|
||||
|
||||
|
||||
# Jetbrains
|
||||
/.idea/**
|
||||
!/.idea/encodings.xml
|
||||
!/.idea/codeStyles
|
||||
!/.idea/codeStyles/*
|
||||
!/.idea/inspectionProfiles
|
||||
!/.idea/inspectionProfiles/*
|
||||
.idea/**
|
||||
!.idea/encodings.xml
|
||||
!.idea/codeStyles
|
||||
!.idea/codeStyles/*
|
||||
!.idea/inspectionProfiles
|
||||
!.idea/inspectionProfiles/*
|
||||
|
||||
|
||||
|
||||
@@ -121,3 +120,4 @@ local.properties
|
||||
.cache-main
|
||||
.scala_dependencies
|
||||
.worksheet
|
||||
|
||||
|
||||
12
.idea/codeStyles/Project.xml
generated
12
.idea/codeStyles/Project.xml
generated
@@ -1,5 +1,11 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<option name="LINE_SEPARATOR" value=" " />
|
||||
<option name="RIGHT_MARGIN" value="320" />
|
||||
<HTMLCodeStyleSettings>
|
||||
<option name="HTML_DO_NOT_INDENT_CHILDREN_OF" value="html,body,thead,tbody,tfoot,script,style" />
|
||||
<option name="HTML_DO_NOT_ALIGN_CHILDREN_OF_MIN_LINES" value="4" />
|
||||
</HTMLCodeStyleSettings>
|
||||
<PHPCodeStyleSettings>
|
||||
<option name="CONCAT_SPACES" value="false" />
|
||||
<option name="PHPDOC_BLANK_LINE_BEFORE_TAGS" value="true" />
|
||||
@@ -20,6 +26,7 @@
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="PHP">
|
||||
<option name="RIGHT_MARGIN" value="320" />
|
||||
<option name="BLANK_LINES_AFTER_PACKAGE" value="1" />
|
||||
<option name="BRACE_STYLE" value="2" />
|
||||
<option name="ELSE_ON_NEW_LINE" value="true" />
|
||||
@@ -41,5 +48,10 @@
|
||||
<option name="SMART_TABS" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="XML">
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
||||
2
.idea/encodings.xml
generated
2
.idea/encodings.xml
generated
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding">
|
||||
<component name="Encoding" defaultCharsetForPropertiesFiles="UTF-8">
|
||||
<file url="PROJECT" charset="UTF-8" />
|
||||
</component>
|
||||
</project>
|
||||
44
.idea/inspectionProfiles/Combodo.xml
generated
Normal file
44
.idea/inspectionProfiles/Combodo.xml
generated
Normal file
@@ -0,0 +1,44 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Combodo" />
|
||||
<inspection_tool class="InconsistentLineSeparators" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="MysqlParsingInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PhpIncludeInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpMethodParametersCountMismatchInspection" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpTooManyParametersInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="limit" value="7" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PhpUndefinedClassInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="DONT_REPORT_MULTI_RESOLVE" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PhpUndefinedMethodInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpUnhandledExceptionInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpUnusedLocalVariableInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||
<option name="DONT_REPORT_INSIDE_LIST" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PhpUnusedParameterInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||
<option name="DONT_REPORT_ABSTRACT_CLASS" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="SqlAddNotNullColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlAmbiguousColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlAutoIncrementDuplicateInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlCheckUsingColumnsInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlConstantConditionInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlDeprecateTypeInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlDerivedTableAliasInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlDialectInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlDropIndexedColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlIdentifierInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlInsertValuesInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlNoDataSourceInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlNullComparisonInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlPostgresqlSelectFromProcedureInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlResolveInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlShouldBeInGroupByInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlSideEffectsInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlSignatureInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlStorageInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlTypeInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlUnusedVariableInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
</profile>
|
||||
</component>
|
||||
155
.idea/inspectionProfiles/Project_Default.xml
generated
155
.idea/inspectionProfiles/Project_Default.xml
generated
@@ -1,154 +1,19 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="BladeControlDirectives" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CheckEmptyScriptTag" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CheckImageSize" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CheckNodeTest" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CheckTagEmptyBody" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CheckValidXmlInScriptTagBody" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="CoffeeScriptArgumentsOutsideFunction" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CoffeeScriptFunctionSignatures" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CoffeeScriptInfiniteLoop" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CoffeeScriptLiteralNotFunction" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="CoffeeScriptSillyAssignment" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CoffeeScriptSwitchStatementWithNoDefaultBranch" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CoffeeScriptUnusedLocalSymbols" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="ComposeFileStructure" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="CssFloatPxLength" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CssInvalidAtRule" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="CssInvalidCharsetRule" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CssInvalidElement" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="CssInvalidFunction" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="CssInvalidHtmlTagReference" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CssInvalidImport" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CssInvalidMediaFeature" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="CssInvalidPropertyValue" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="CssInvalidPseudoSelector" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="CssMissingComma" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CssNegativeValue" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="CssNoGenericFontName" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CssOptimizeSimilarProperties" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CssOverwrittenProperties" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CssRedundantUnit" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CssUnitlessNumber" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CssUnknownProperty" enabled="false" level="WARNING" enabled_by_default="false">
|
||||
<option name="myCustomPropertiesEnabled" value="false" />
|
||||
<option name="myIgnoreVendorSpecificProperties" value="false" />
|
||||
<option name="myCustomPropertiesList">
|
||||
<value>
|
||||
<list size="0" />
|
||||
</value>
|
||||
</option>
|
||||
<inspection_tool class="InconsistentLineSeparators" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpIncludeInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpMethodParametersCountMismatchInspection" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpTooManyParametersInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="limit" value="7" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="CssUnknownTarget" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="CssUnresolvedClass" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="CssUnresolvedCustomProperty" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="CssUnresolvedCustomPropertySet" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="CssUnusedSymbol" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CucumberExamplesColon" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="CucumberMissedExamples" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="CucumberTableInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CucumberUndefinedStep" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="DockerFileArgumentCount" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="DuplicateKeyInSection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="DuplicateSectionInFile" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="EmptyEventHandler" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="FileHeaderInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="GherkinBrokenTableInspection" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="GherkinMisplacedBackground" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="HamlNestedTagContent" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="HardwiredNamespacePrefix" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="HtmlDeprecatedTag" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="HtmlExtraClosingTag" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="HtmlFormInputWithoutLabel" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="HtmlMissingClosingTag" enabled="false" level="INFORMATION" enabled_by_default="false" />
|
||||
<inspection_tool class="HtmlPresentationalElement" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="HtmlUnknownAnchorTarget" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="HtmlUnknownAttribute" enabled="false" level="WARNING" enabled_by_default="false">
|
||||
<option name="myValues">
|
||||
<value>
|
||||
<list size="0" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="myCustomValuesEnabled" value="true" />
|
||||
<inspection_tool class="PhpUndefinedClassInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="DONT_REPORT_MULTI_RESOLVE" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="HtmlUnknownBooleanAttribute" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="HtmlUnknownTag" enabled="false" level="WARNING" enabled_by_default="false">
|
||||
<option name="myValues">
|
||||
<value>
|
||||
<list size="6">
|
||||
<item index="0" class="java.lang.String" itemvalue="nobr" />
|
||||
<item index="1" class="java.lang.String" itemvalue="noembed" />
|
||||
<item index="2" class="java.lang.String" itemvalue="comment" />
|
||||
<item index="3" class="java.lang.String" itemvalue="noscript" />
|
||||
<item index="4" class="java.lang.String" itemvalue="embed" />
|
||||
<item index="5" class="java.lang.String" itemvalue="script" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
<option name="myCustomValuesEnabled" value="true" />
|
||||
<inspection_tool class="PhpUnhandledExceptionInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpUnusedParameterInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||
<option name="DONT_REPORT_ABSTRACT_CLASS" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="HtmlUnknownTarget" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="ImplicitTypeConversion" enabled="false" level="WARNING" enabled_by_default="false">
|
||||
<option name="BITS" value="1720" />
|
||||
<option name="FLAG_EXPLICIT_CONVERSION" value="true" />
|
||||
<option name="IGNORE_NODESET_TO_BOOLEAN_VIA_STRING" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="IndexZeroUsage" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="JSLastCommaInArrayLiteral" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="JSLastCommaInObjectLiteral" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="JSUnresolvedFunction" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="JSUnresolvedVariable" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="LossyEncoding" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="MissingSinceTagDocInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="MysqlParsingInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="NonAsciiCharacters" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PhpDocMissingReturnTagInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PhpDocMissingThrowsInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PhpIncludeInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PhpRedundantClosingTagInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PhpUndefinedClassConstantInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PhpUndefinedClassInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PhpUndefinedConstantInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PhpUndefinedFieldInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PhpUndefinedFunctionInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PhpUndefinedGotoLabelInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PhpUndefinedMethodInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PhpUndefinedNamespaceInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PhpUnusedParameterInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="RedundantTypeConversion" enabled="false" level="WARNING" enabled_by_default="false">
|
||||
<option name="CHECK_ANY" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="RequiredAttributes" enabled="false" level="WARNING" enabled_by_default="false">
|
||||
<option name="myAdditionalRequiredHtmlAttributes" value="" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="SqlAddNotNullColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlAmbiguousColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlAutoIncrementDuplicateInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlCheckUsingColumnsInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlConstantConditionInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlDeprecateTypeInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlDerivedTableAliasInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlDialectInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlDropIndexedColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlIdentifierInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlInsertValuesInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlNoDataSourceInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlNullComparisonInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlPostgresqlSelectFromProcedureInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlResolveInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlShouldBeInGroupByInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlSignatureInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlStorageInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlTypeInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlUnusedVariableInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="TaskProblemsInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="TypeScriptPreferShortImport" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="XmlDefaultAttributeValue" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="XmlUnboundNsPrefix" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="XmlUnusedNamespaceDeclaration" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="XsltUnusedDeclaration" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="XsltVariableShadowing" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
</profile>
|
||||
</component>
|
||||
7
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
7
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="projectProfile" value="Combodo" />
|
||||
<option name="PROJECT_PROFILE" value="Combodo" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
||||
0
.jenkins/bin/archive/gather_external_files.sh
Executable file
0
.jenkins/bin/archive/gather_external_files.sh
Executable file
14
.jenkins/bin/init/append_files.sh
Executable file
14
.jenkins/bin/init/append_files.sh
Executable file
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -x
|
||||
|
||||
# create target dirs
|
||||
mkdir -p var
|
||||
mkdir -p toolkit
|
||||
|
||||
# cleanup target dirs
|
||||
rm -rf toolkit/*
|
||||
|
||||
# fill target dirs
|
||||
curl http://www.combodo.com/documentation/iTopDataModelToolkit-2.3.zip | tar xvz --directory toolkit
|
||||
cp -r .jenkins/configuration/default-environment/unattended_install/* toolkit
|
||||
11
.jenkins/bin/init/composer_install.sh
Executable file
11
.jenkins/bin/init/composer_install.sh
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -x
|
||||
|
||||
# on the root dir
|
||||
composer install
|
||||
|
||||
|
||||
# under the test dir
|
||||
cd test
|
||||
composer install
|
||||
13
.jenkins/bin/init/debug.sh
Executable file
13
.jenkins/bin/init/debug.sh
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -x
|
||||
|
||||
whoami
|
||||
pwd
|
||||
ls
|
||||
|
||||
echo "$BRANCH_NAME:${BRANCH_NAME}"
|
||||
|
||||
echo "printenv :"
|
||||
printenv
|
||||
|
||||
8
.jenkins/bin/tests/phpunit.sh
Executable file
8
.jenkins/bin/tests/phpunit.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
set -x
|
||||
|
||||
cd test
|
||||
|
||||
export DEBUG_UNIT_TEST="0"
|
||||
|
||||
php vendor/bin/phpunit --log-junit ../var/test/phpunit-log.junit.xml --teamcity
|
||||
6
.jenkins/bin/unattended_install/default_env.sh
Executable file
6
.jenkins/bin/unattended_install/default_env.sh
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -x
|
||||
|
||||
cd toolkit
|
||||
php unattended_install.php default-params.xml
|
||||
@@ -0,0 +1,285 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
*
|
||||
* Configuration file, generated by the iTop configuration wizard
|
||||
*
|
||||
* The file is used in MetaModel::LoadConfig() which does all the necessary initialization job
|
||||
*
|
||||
*/
|
||||
$MySettings = array(
|
||||
|
||||
// access_message: Message displayed to the users when there is any access restriction
|
||||
// default: 'iTop is temporarily frozen, please wait... (the admin team)'
|
||||
'access_message' => 'iTop is temporarily frozen, please wait... (the admin team)',
|
||||
|
||||
// access_mode: Access mode: ACCESS_READONLY = 0, ACCESS_ADMIN_WRITE = 2, ACCESS_FULL = 3
|
||||
// default: 3
|
||||
'access_mode' => 3,
|
||||
|
||||
'allowed_login_types' => 'form|basic|external',
|
||||
|
||||
// apc_cache.enabled: If set, the APC cache is allowed (the PHP extension must also be active)
|
||||
// default: true
|
||||
'apc_cache.enabled' => true,
|
||||
|
||||
// apc_cache.query_ttl: Time to live set in APC for the prepared queries (seconds - 0 means no timeout)
|
||||
// default: 3600
|
||||
'apc_cache.query_ttl' => 3600,
|
||||
|
||||
// app_root_url: Root URL used for navigating within the application, or from an email to the application (you can put $SERVER_NAME$ as a placeholder for the server's name)
|
||||
// default: ''
|
||||
'app_root_url' => 'http://127.0.0.1/itop/svn/trunk/',
|
||||
|
||||
// buttons_position: Position of the forms buttons: bottom | top | both
|
||||
// default: 'both'
|
||||
'buttons_position' => 'both',
|
||||
|
||||
// cas_include_path: The path where to find the phpCAS library
|
||||
// default: '/usr/share/php'
|
||||
'cas_include_path' => '/usr/share/php',
|
||||
|
||||
// cron_max_execution_time: Duration (seconds) of the page cron.php, must be shorter than php setting max_execution_time and shorter than the web server response timeout
|
||||
// default: 600
|
||||
'cron_max_execution_time' => 600,
|
||||
|
||||
// csv_file_default_charset: Character set used by default for downloading and uploading data as a CSV file. Warning: it is case sensitive (uppercase is preferable).
|
||||
// default: 'ISO-8859-1'
|
||||
'csv_file_default_charset' => 'ISO-8859-1',
|
||||
|
||||
'csv_import_charsets' => array (
|
||||
),
|
||||
|
||||
// csv_import_history_display: Display the history tab in the import wizard
|
||||
// default: false
|
||||
'csv_import_history_display' => false,
|
||||
|
||||
// date_and_time_format: Format for date and time display (per language)
|
||||
// default: array (
|
||||
// 'default' =>
|
||||
// array (
|
||||
// 'date' => 'Y-m-d',
|
||||
// 'time' => 'H:i:s',
|
||||
// 'date_time' => '$date $time',
|
||||
// ),
|
||||
// )
|
||||
'date_and_time_format' => array (
|
||||
'default' =>
|
||||
array (
|
||||
'date' => 'Y-m-d',
|
||||
'time' => 'H:i:s',
|
||||
'date_time' => '$date $time',
|
||||
),
|
||||
'FR FR' =>
|
||||
array (
|
||||
'date' => 'd/m/Y',
|
||||
'time' => 'H:i:s',
|
||||
'date_time' => '$date $time',
|
||||
),
|
||||
),
|
||||
|
||||
'db_host' => '',
|
||||
|
||||
'db_name' => 'itop_ci_main',
|
||||
|
||||
'db_pwd' => 'IKnowYouSeeMeInJenkinsConf',
|
||||
|
||||
'db_subname' => '',
|
||||
|
||||
'db_user' => 'jenkins_itop',
|
||||
|
||||
// deadline_format: The format used for displaying "deadline" attributes: any string with the following placeholders: $date$, $difference$
|
||||
// default: '$difference$'
|
||||
'deadline_format' => '$difference$',
|
||||
|
||||
'default_language' => 'EN US',
|
||||
|
||||
// disable_attachments_download_legacy_portal: Disable attachments download from legacy portal
|
||||
// default: true
|
||||
'disable_attachments_download_legacy_portal' => true,
|
||||
|
||||
// draft_attachments_lifetime: Lifetime (in seconds) of drafts' attachments and inline images: after this duration, the garbage collector will delete them.
|
||||
// default: 3600
|
||||
'draft_attachments_lifetime' => 3600,
|
||||
|
||||
// email_asynchronous: If set, the emails are sent off line, which requires cron.php to be activated. Exception: some features like the email test utility will force the serialized mode
|
||||
// default: false
|
||||
'email_asynchronous' => false,
|
||||
|
||||
// email_default_sender_address: Default address provided in the email from header field.
|
||||
// default: ''
|
||||
'email_default_sender_address' => '',
|
||||
|
||||
// email_default_sender_label: Default label provided in the email from header field.
|
||||
// default: ''
|
||||
'email_default_sender_label' => '',
|
||||
|
||||
// email_transport: Mean to send emails: PHPMail (uses the function mail()) or SMTP (implements the client protocole)
|
||||
// default: 'PHPMail'
|
||||
'email_transport' => 'SMTP',
|
||||
|
||||
// email_transport_smtp.host: host name or IP address (optional)
|
||||
// default: 'localhost'
|
||||
'email_transport_smtp.host' => 'smtp.combodo.com',
|
||||
|
||||
// email_transport_smtp.password: Authentication password (optional)
|
||||
// default: ''
|
||||
'email_transport_smtp.password' => 'IDoNotWork',
|
||||
|
||||
// email_transport_smtp.port: port number (optional)
|
||||
// default: 25
|
||||
'email_transport_smtp.port' => 25,
|
||||
|
||||
// email_transport_smtp.username: Authentication user (optional)
|
||||
// default: ''
|
||||
'email_transport_smtp.username' => 'test2@combodo.com',
|
||||
|
||||
// email_validation_pattern: Regular expression to validate/detect the format of an eMail address
|
||||
// default: '[a-zA-Z0-9._&\'-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9-]{2,}'
|
||||
'email_validation_pattern' => '[a-zA-Z0-9._&\'-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9-]{2,}',
|
||||
|
||||
'encryption_key' => '@iT0pEncr1pti0n!',
|
||||
|
||||
'ext_auth_variable' => '$_SERVER[\'REMOTE_USER\']',
|
||||
|
||||
'fast_reload_interval' => '60',
|
||||
|
||||
// graphviz_path: Path to the Graphviz "dot" executable for graphing objects lifecycle
|
||||
// default: '/usr/bin/dot'
|
||||
'graphviz_path' => '/usr/bin/dot',
|
||||
|
||||
// inline_image_max_display_width: The maximum width (in pixels) when displaying images inside an HTML formatted attribute. Images will be displayed using this this maximum width.
|
||||
// default: '250'
|
||||
'inline_image_max_display_width' => 250,
|
||||
|
||||
// inline_image_max_storage_width: The maximum width (in pixels) when uploading images to be used inside an HTML formatted attribute. Images larger than the given size will be downsampled before storing them in the database.
|
||||
// default: '1600'
|
||||
'inline_image_max_storage_width' => 1600,
|
||||
|
||||
// link_set_attribute_qualifier: Link set from string: attribute qualifier (encloses both the attcode and the value)
|
||||
// default: '\''
|
||||
'link_set_attribute_qualifier' => '\'',
|
||||
|
||||
// link_set_attribute_separator: Link set from string: attribute separator
|
||||
// default: ';'
|
||||
'link_set_attribute_separator' => ';',
|
||||
|
||||
// link_set_item_separator: Link set from string: line separator
|
||||
// default: '|'
|
||||
'link_set_item_separator' => '|',
|
||||
|
||||
// link_set_value_separator: Link set from string: value separator (between the attcode and the value itself
|
||||
// default: ':'
|
||||
'link_set_value_separator' => ':',
|
||||
|
||||
'log_global' => true,
|
||||
|
||||
'log_issue' => true,
|
||||
|
||||
'log_notification' => true,
|
||||
|
||||
'log_web_service' => true,
|
||||
|
||||
// max_combo_length: The maximum number of elements in a drop-down list. If more then an autocomplete will be used
|
||||
// default: 50
|
||||
'max_combo_length' => 50,
|
||||
|
||||
'max_display_limit' => '15',
|
||||
|
||||
// max_linkset_output: Maximum number of items shown when getting a list of related items in an email, using the form $this->some_list$. 0 means no limit.
|
||||
// default: 100
|
||||
'max_linkset_output' => 100,
|
||||
|
||||
'min_display_limit' => '10',
|
||||
|
||||
// online_help: Hyperlink to the online-help web page
|
||||
// default: 'http://www.combodo.com/itop-help'
|
||||
'online_help' => 'http://www.combodo.com/itop-help',
|
||||
|
||||
// php_path: Path to the php executable in CLI mode
|
||||
// default: 'php'
|
||||
'php_path' => 'php',
|
||||
|
||||
// portal_tickets: CSV list of classes supported in the portal
|
||||
// default: 'UserRequest'
|
||||
'portal_tickets' => 'UserRequest',
|
||||
|
||||
'query_cache_enabled' => true,
|
||||
|
||||
// search_manual_submit: Force manual submit of search requests (class => true)
|
||||
// default: false
|
||||
'search_manual_submit' => array (
|
||||
'Person' => true,
|
||||
),
|
||||
|
||||
'secure_connection_required' => false,
|
||||
|
||||
// session_name: The name of the cookie used to store the PHP session id
|
||||
// default: 'iTop'
|
||||
'session_name' => 'iTop',
|
||||
|
||||
// shortcut_actions: Actions that are available as direct buttons next to the "Actions" menu
|
||||
// default: 'UI:Menu:Modify,UI:Menu:New'
|
||||
'shortcut_actions' => 'UI:Menu:Modify,UI:Menu:New',
|
||||
|
||||
// source_dir: Source directory for the datamodel files. (which gets compiled to env-production).
|
||||
// default: ''
|
||||
'source_dir' => 'datamodels/2.x/',
|
||||
|
||||
'standard_reload_interval' => '300',
|
||||
|
||||
// synchro_trace: Synchronization details: none, display, save (includes 'display')
|
||||
// default: 'none'
|
||||
'synchro_trace' => 'none',
|
||||
|
||||
// timezone: Timezone (reference: http://php.net/manual/en/timezones.php). If empty, it will be left unchanged and MUST be explicitely configured in PHP
|
||||
// default: 'Europe/Paris'
|
||||
'timezone' => 'Europe/Paris',
|
||||
|
||||
// tracking_level_linked_set_default: Default tracking level if not explicitely set at the attribute level, for AttributeLinkedSet (defaults to NONE in case of a fresh install, LIST otherwise - this to preserve backward compatibility while upgrading from a version older than 2.0.3 - see TRAC #936)
|
||||
// default: 1
|
||||
'tracking_level_linked_set_default' => 0,
|
||||
|
||||
// url_validation_pattern: Regular expression to validate/detect the format of an URL (URL attributes and Wiki formatting for Text attributes)
|
||||
// default: '(https?|ftp)\\://([a-zA-Z0-9+!*(),;?&=\\$_.-]+(\\:[a-zA-Z0-9+!*(),;?&=\\$_.-]+)?@)?([a-zA-Z0-9-.]{3,})(\\:[0-9]{2,5})?(/([a-zA-Z0-9%+\\$_-]\\.?)+)*/?(\\?[a-zA-Z+&\\$_.-][a-zA-Z0-9;:[\\]@&%=+/\\$_.-]*)?(#[a-zA-Z_.-][a-zA-Z0-9+\\$_.-]*)?'
|
||||
'url_validation_pattern' => '(https?|ftp)\\://([a-zA-Z0-9+!*(),;?&=\\$_.-]+(\\:[a-zA-Z0-9+!*(),;?&=\\$_.-]+)?@)?([a-zA-Z0-9-.]{3,})(\\:[0-9]{2,5})?(/([a-zA-Z0-9%+\\$_-]\\.?)+)*/?(\\?[a-zA-Z+&\\$_.-][a-zA-Z0-9;:[\\]@&%=+/\\$_.-]*)?(#[a-zA-Z_.-][a-zA-Z0-9+\\$_.-]*)?',
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* Modules specific settings
|
||||
*
|
||||
*/
|
||||
$MyModuleSettings = array(
|
||||
'itop-attachments' => array (
|
||||
'allowed_classes' => array (
|
||||
0 => 'Ticket',
|
||||
),
|
||||
'position' => 'relations',
|
||||
'preview_max_width' => 290,
|
||||
),
|
||||
'itop-backup' => array (
|
||||
'mysql_bindir' => '',
|
||||
'week_days' => 'monday, tuesday, wednesday, thursday, friday',
|
||||
'time' => '23:30',
|
||||
'retention_count' => 5,
|
||||
'enabled' => true,
|
||||
'debug' => false,
|
||||
),
|
||||
'molkobain-console-tooltips' => array (
|
||||
'decoration_class' => 'fas fa-question',
|
||||
'enabled' => true,
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* Data model modules to be loaded. Names are specified as relative paths
|
||||
*
|
||||
*/
|
||||
$MyModules = array(
|
||||
'addons' => array (
|
||||
'user rights' => 'addons/userrights/userrightsprofile.class.inc.php',
|
||||
),
|
||||
);
|
||||
?>
|
||||
@@ -1,13 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<installation>
|
||||
<!-- On manual installs, this file is generated in setup/install-*.xml -->
|
||||
<mode>install</mode>
|
||||
<mode>upgrade</mode>
|
||||
<preinstall>
|
||||
<copies type="array"/>
|
||||
</preinstall>
|
||||
<source_dir>datamodels/2.x/</source_dir>
|
||||
<datamodel_version>2.5.0</datamodel_version>
|
||||
<previous_configuration_file>default-config-itop.php</previous_configuration_file>
|
||||
<previous_configuration_file>/var/lib/jenkins/workspace/iTop-CI/unattended_install/default-config-itop.php</previous_configuration_file>
|
||||
<extensions_dir>extensions</extensions_dir>
|
||||
<target_env>production</target_env>
|
||||
<workspace_dir></workspace_dir>
|
||||
@@ -20,7 +20,7 @@
|
||||
<db_tls_ca></db_tls_ca>
|
||||
<prefix></prefix>
|
||||
</database>
|
||||
<url>http://localhost/iTop/</url>
|
||||
<url>http://127.0.0.1/itop/svn/trunk/</url>
|
||||
<graphviz_path>/usr/bin/dot</graphviz_path>
|
||||
<admin_account>
|
||||
<user>admin</user>
|
||||
@@ -0,0 +1,190 @@
|
||||
<?php
|
||||
//this scrit will be run under the ./toolkit directory, relatively to the document root
|
||||
|
||||
require_once('../approot.inc.php');
|
||||
require_once(APPROOT.'/application/utils.inc.php');
|
||||
require_once(APPROOT.'/application/clipage.class.inc.php');
|
||||
require_once(APPROOT.'/core/config.class.inc.php');
|
||||
require_once(APPROOT.'/core/log.class.inc.php');
|
||||
require_once(APPROOT.'/core/kpi.class.inc.php');
|
||||
require_once(APPROOT.'/core/cmdbsource.class.inc.php');
|
||||
require_once(APPROOT.'/setup/setuppage.class.inc.php');
|
||||
require_once(APPROOT.'/setup/wizardcontroller.class.inc.php');
|
||||
require_once(APPROOT.'/setup/wizardsteps.class.inc.php');
|
||||
require_once(APPROOT.'/setup/applicationinstaller.class.inc.php');
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
$sParamFile = utils::ReadParam('response_file', 'default-params.xml', true /* CLI allowed */, 'raw_data');
|
||||
$bCheckConsistency = (utils::ReadParam('check_consistency', '0', true /* CLI allowed */) == '1');
|
||||
|
||||
$oParams = new XMLParameters($sParamFile);
|
||||
$sMode = $oParams->Get('mode');
|
||||
|
||||
if ($sMode == 'install')
|
||||
{
|
||||
echo "Installation mode detected.\n";
|
||||
$bClean = utils::ReadParam('clean', false, true /* CLI allowed */);
|
||||
if ($bClean)
|
||||
{
|
||||
echo "Cleanup mode detected.\n";
|
||||
$sTargetEnvironment = $oParams->Get('target_env', '');
|
||||
if ($sTargetEnvironment == '')
|
||||
{
|
||||
$sTargetEnvironment = 'production';
|
||||
}
|
||||
$sTargetDir = APPROOT.'env-'.$sTargetEnvironment;
|
||||
|
||||
// Configuration file
|
||||
$sConfigFile = APPCONF.$sTargetEnvironment.'/'.ITOP_CONFIG_FILE;
|
||||
if (file_exists($sConfigFile))
|
||||
{
|
||||
echo "Trying to delete the configuration file: '$sConfigFile'.\n";
|
||||
@chmod($sConfigFile, 0770); // RWX for owner and group, nothing for others
|
||||
unlink($sConfigFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "No config file to delete ($sConfigFile does not exist).\n";
|
||||
}
|
||||
|
||||
// env-xxx directory
|
||||
if (file_exists($sTargetDir))
|
||||
{
|
||||
if (is_dir($sTargetDir))
|
||||
{
|
||||
echo "Emptying the target directory '$sTargetDir'.\n";
|
||||
SetupUtils::tidydir($sTargetDir);
|
||||
}
|
||||
else
|
||||
{
|
||||
die("ERROR the target dir '$sTargetDir' exists, but is NOT a directory !!!\nExiting.\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "No target directory to delete ($sTargetDir does not exist).\n";
|
||||
}
|
||||
|
||||
// Database
|
||||
$aDBSettings = $oParams->Get('database', array());
|
||||
$sDBServer = $aDBSettings['server'];
|
||||
$sDBUser = $aDBSettings['user'];
|
||||
$sDBPwd = $aDBSettings['pwd'];
|
||||
$sDBName = $aDBSettings['name'];
|
||||
$sDBPrefix = $aDBSettings['prefix'];
|
||||
|
||||
if ($sDBPrefix != '')
|
||||
{
|
||||
die("Cleanup not implemented for a partial database (prefix= '$sDBPrefix')\nExiting.");
|
||||
}
|
||||
|
||||
$oMysqli = new mysqli($sDBServer, $sDBUser, $sDBPwd);
|
||||
if ($oMysqli->connect_errno)
|
||||
{
|
||||
die("Cannot connect to the MySQL server (".$mysqli->connect_errno . ") ".$mysqli->connect_error."\nExiting");
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($oMysqli->select_db($sDBName))
|
||||
{
|
||||
echo "Deleting database '$sDBName'\n";
|
||||
$oMysqli->query("DROP DATABASE `$sDBName`");
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "The database '$sDBName' does not seem to exist. Nothing to cleanup.\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$bHasErrors = false;
|
||||
$aChecks = SetupUtils::CheckBackupPrerequisites(APPROOT.'data'); // mmm should be the backup destination dir
|
||||
|
||||
$aSelectedModules = $oParams->Get('selected_modules');
|
||||
$sSourceDir = $oParams->Get('source_dir', 'datamodels/latest');
|
||||
$sExtensionDir = $oParams->Get('extensions_dir', 'extensions');
|
||||
$aChecks = array_merge($aChecks, SetupUtils::CheckSelectedModules($sSourceDir, $sExtensionDir, $aSelectedModules));
|
||||
|
||||
|
||||
foreach($aChecks as $oCheckResult)
|
||||
{
|
||||
switch($oCheckResult->iSeverity)
|
||||
{
|
||||
case CheckResult::ERROR:
|
||||
$bHasErrors = true;
|
||||
$sHeader = "Error";
|
||||
break;
|
||||
|
||||
case CheckResult::WARNING:
|
||||
$sHeader = "Warning";
|
||||
break;
|
||||
|
||||
case CheckResult::INFO:
|
||||
default:
|
||||
$sHeader = "Info";
|
||||
break;
|
||||
}
|
||||
echo $sHeader.": ".$oCheckResult->sLabel;
|
||||
if (strlen($oCheckResult->sDescription))
|
||||
{
|
||||
echo ' - '.$oCheckResult->sDescription;
|
||||
}
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
if ($bHasErrors)
|
||||
{
|
||||
echo "Encountered stopper issues. Aborting...\n";
|
||||
die;
|
||||
}
|
||||
|
||||
$bFoundIssues = false;
|
||||
|
||||
$bInstall = utils::ReadParam('install', true, true /* CLI allowed */);
|
||||
if ($bInstall)
|
||||
{
|
||||
echo "Starting the unattended installation...\n";
|
||||
$oWizard = new ApplicationInstaller($oParams);
|
||||
$bRes = $oWizard->ExecuteAllSteps();
|
||||
if (!$bRes)
|
||||
{
|
||||
echo "\nencountered installation issues!";
|
||||
$bFoundIssues = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "No installation requested.\n";
|
||||
}
|
||||
if (!$bFoundIssues && $bCheckConsistency)
|
||||
{
|
||||
echo "Checking data model consistency.\n";
|
||||
ob_start();
|
||||
$sCheckRes = '';
|
||||
try
|
||||
{
|
||||
MetaModel::CheckDefinitions(false);
|
||||
$sCheckRes = ob_get_clean();
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
$sCheckRes = ob_get_clean()."\nException: ".$e->getMessage();
|
||||
}
|
||||
if (strlen($sCheckRes) > 0)
|
||||
{
|
||||
echo $sCheckRes;
|
||||
echo "\nfound consistency issues!";
|
||||
$bFoundIssues = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$bFoundIssues)
|
||||
{
|
||||
// last line: used to check the install
|
||||
// the only way to track issues in case of Fatal error or even parsing error!
|
||||
echo "\ninstalled!";
|
||||
exit;
|
||||
}
|
||||
100
CONTRIBUTING.md
Normal file
100
CONTRIBUTING.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# Contributing to iTop
|
||||
|
||||
You want to contribute to iTop? Many thanks to you! 🎉 👍
|
||||
|
||||
Here are some guidelines that will help us integrate your work!
|
||||
|
||||
|
||||
## Contributions
|
||||
|
||||
### Subjects
|
||||
You are welcome to create pull requests on any of those subjects:
|
||||
|
||||
* 🐛 `:bug:` bug fix
|
||||
* 🔒 `:lock:` security
|
||||
* 🌐 `:globe_with_meridians:` translation
|
||||
|
||||
If you want to implement a **new feature**, please [create a corresponding ticket](https://sourceforge.net/p/itop/tickets/new/) for review.
|
||||
If you ever want to begin implementation, do so in a fork, and add a link to the corresponding commits in the ticket.
|
||||
|
||||
### License
|
||||
iTop is distributed under the AGPL-3.0 license (see the [license.txt] file),
|
||||
your code must comply with this license.
|
||||
|
||||
If you want to use another license, 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
|
||||
|
||||
|
||||
## Branch model
|
||||
|
||||
TL;DR:
|
||||
> **create a fork from iTop main repository,
|
||||
> create a branch based on either release branch if present, or develop otherwise**
|
||||
|
||||
We are using the [GitFlow](https://nvie.com/posts/a-successful-git-branching-model/) branch model. That means we have in our repo those
|
||||
main branches:
|
||||
|
||||
- develop: ongoing development version
|
||||
- release/*: if present, that means we are working on a beta version
|
||||
- master: previous stable version
|
||||
|
||||
For example, if no beta version is currently ongoing we could have:
|
||||
|
||||
- develop containing 2.8 version
|
||||
- master containing 2.7 version
|
||||
|
||||
In this example, when 2.8 beta is shipped that will become:
|
||||
|
||||
- develop: 2.9 version
|
||||
- release/2.8: 2.8 beta
|
||||
- master: 2.7 version
|
||||
|
||||
And when 2.8 final will be out:
|
||||
|
||||
- develop: 2.9 version
|
||||
- master: 2.8 version
|
||||
|
||||
|
||||
## Coding
|
||||
|
||||
### PHP styleguide
|
||||
|
||||
Please follow [our guidelines](https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Acoding_standards).
|
||||
|
||||
### Tests
|
||||
|
||||
Please create tests that covers as much as possible the code you're submitting.
|
||||
|
||||
Our tests are located in the `test/` directory, containing a PHPUnit config file : `phpunit.xml.dist`.
|
||||
|
||||
### Git Commit Messages
|
||||
|
||||
* Describe the functional change instead of the technical modifications
|
||||
* Use the present tense ("Add feature" not "Added feature")
|
||||
* Use the imperative mood ("Move cursor to..." not "Moves cursor to...")
|
||||
* Limit the first line to 72 characters or less
|
||||
* Please start the commit message with an applicable emoji (following the [Gitmoji guide](https://gitmoji.carloscuesta.me/).). For example :
|
||||
* 🌐 `:globe_with_meridians:` for translations
|
||||
* 🎨 `:art:` when improving the format/structure of the code
|
||||
* ⚡️ `:zap:` when improving performance
|
||||
* 🐛 `:bug:` when fixing a bug
|
||||
* 🔥 `:fire:` when removing code or files
|
||||
* 💚 `:green_heart:` when fixing the CI build
|
||||
* ✅ `:white_check_mark:` when adding tests
|
||||
* 🔒 `:lock:` when dealing with security
|
||||
* ⬆️ `:arrow_up:` when upgrading dependencies
|
||||
* ⬇️ `:arrow_down:` when downgrading dependencies
|
||||
* ♻️ `:recycle:` code refactoring
|
||||
* 💄 `:lipstick:` Updating the UI and style files.
|
||||
|
||||
## Pull request
|
||||
|
||||
When your code is working, please:
|
||||
|
||||
* stash 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/).
|
||||
70
Jenkinsfile
vendored
70
Jenkinsfile
vendored
@@ -1,11 +1,65 @@
|
||||
def infra
|
||||
pipeline {
|
||||
agent any
|
||||
stages {
|
||||
|
||||
node(){
|
||||
checkout scm
|
||||
stage('init') {
|
||||
parallel {
|
||||
stage('debug') {
|
||||
steps {
|
||||
sh './.jenkins/bin/init/debug.sh'
|
||||
}
|
||||
}
|
||||
stage('append files to project') {
|
||||
steps {
|
||||
sh './.jenkins/bin/init/append_files.sh'
|
||||
}
|
||||
}
|
||||
stage('composer install') {
|
||||
steps {
|
||||
sh './.jenkins/bin/init/composer_install.sh'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
infra = load '/var/lib/jenkins/workspace/itop-test-infra_master/src/Infra.groovy'
|
||||
stage('unattended_install') {
|
||||
parallel {
|
||||
stage('unattended_install default env') {
|
||||
steps {
|
||||
sh './.jenkins/bin/unattended_install/default_env.sh'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('test') {
|
||||
parallel {
|
||||
stage('phpunit') {
|
||||
steps {
|
||||
sh './.jenkins/bin/tests/phpunit.sh'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
post {
|
||||
always {
|
||||
junit 'var/test/phpunit-log.junit.xml'
|
||||
}
|
||||
failure {
|
||||
slackSend(channel: "#jenkins-itop", color: '#FF0000', message: "Ho no! Build failed! (${currentBuild.result}), Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
|
||||
}
|
||||
fixed {
|
||||
slackSend(channel: "#jenkins-itop", color: '#FFa500', message: "Yes! Build repaired! (${currentBuild.result}), Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
|
||||
}
|
||||
}
|
||||
|
||||
environment {
|
||||
DEBUG_UNIT_TEST = '0'
|
||||
}
|
||||
options {
|
||||
timeout(time: 20, unit: 'MINUTES')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
infra.call()
|
||||
|
||||
|
||||
141
README.md
Normal file
141
README.md
Normal file
@@ -0,0 +1,141 @@
|
||||
<p align="center"><a href="https://www.combodo.com/itop-193" target="_blank">
|
||||
<img src="https://www.combodo.com/logos/logo-itop.svg">
|
||||
</a></p>
|
||||
|
||||
|
||||
# iTop - ITSM & CMDB
|
||||
|
||||
iTop stands for *IT Operations Portal*.
|
||||
It is a complete open source, ITIL, web based service management tool including a fully customizable CMDB, a helpdesk system and a document management tool.
|
||||
iTop also offers mass import tools and web services to integrate with your IT
|
||||
|
||||
## Features
|
||||
- Fully configurable [Configuration Management (CMDB)][10]
|
||||
- [HelpDesk][11] and Incident Management
|
||||
- [Service and Contract Management][12]
|
||||
- [Change][13] Management
|
||||
- Configurable [SLA][14] Management
|
||||
- Graphical [impact analysis][15]
|
||||
- [CSV import][16] tool for any data
|
||||
- Consistency [audit][17] to check data quality
|
||||
- [Data synchronization][18] (for data federation)
|
||||
|
||||
|
||||
## Resources
|
||||
|
||||
- [iTop Forums][1]: for support request
|
||||
- [iTop Tickets][2]: for feature requests and bug reports
|
||||
- [Releases download][3]
|
||||
- [iTop documentation][4] for iTop and official extensions
|
||||
- [iTop extensions][5] for discovering and installing extensions
|
||||
|
||||
|
||||
## Releases
|
||||
### Version 2.6
|
||||
- [Changes since the previous version][58]
|
||||
- [New features][59]
|
||||
- [Migration notes][60]
|
||||
- [Download iTop 2.6.0-beta][61]
|
||||
|
||||
|
||||
### Version 2.5
|
||||
- [Changes since the previous version][54]
|
||||
- [New features][55]
|
||||
- [Migration notes][56]
|
||||
- [Download iTop 2.5.0][57]
|
||||
|
||||
|
||||
### Version 2.4
|
||||
- [Changes since the previous version][50]
|
||||
- [New features][51]
|
||||
- [Migration notes][52]
|
||||
- [Download iTop 2.4.1][53]
|
||||
|
||||
|
||||
# About Us
|
||||
|
||||
iTop development is sponsored, led and supported by [Combodo][0].
|
||||
|
||||
|
||||
# Contributors
|
||||
|
||||
We would like to give a special thank you to the people from the community who contributed to this project, including:
|
||||
- Alves, David
|
||||
- Beck, Pedro
|
||||
- Bilger, Jean-François
|
||||
- Bostoen, Jeffrey
|
||||
- Cardoso, Anderson
|
||||
- Cassaro, Bruno
|
||||
- Casteleyn, Thomas
|
||||
- Castro, Randall Badilla
|
||||
- Colantoni, Maria Laura
|
||||
- Dvořák, Lukáš
|
||||
- Goethals, Stefan
|
||||
- Gumble, David
|
||||
- Hippler, Lars
|
||||
- Khamit, Shamil
|
||||
- Konečný, Kamil
|
||||
- Kunin, Vladimir
|
||||
- Lassiter, Dennis
|
||||
- Lucas, Jonathan
|
||||
- Malik, Remie
|
||||
- Rosenke, Stephan
|
||||
- Seki, Shoji
|
||||
- Shilov, Vladimir
|
||||
- Tulio, Marco
|
||||
- Turrubiates, Miguel
|
||||
|
||||
#### Aliases
|
||||
- chifu1234
|
||||
- cprobst
|
||||
- jbostoen
|
||||
- Karkoff1212
|
||||
- larhip
|
||||
- Laura
|
||||
- Purple Grape
|
||||
- Schlobinux
|
||||
- theBigOne
|
||||
- ulmerspatz
|
||||
|
||||
#### Companies
|
||||
- Hardis
|
||||
- ITOMIG
|
||||
|
||||
|
||||
|
||||
[0]: https://www.combodo.com
|
||||
|
||||
[1]: https://sourceforge.net/p/itop/discussion/
|
||||
[2]: https://sourceforge.net/p/itop/tickets/
|
||||
[3]: https://sourceforge.net/projects/itop/files/itop/
|
||||
[4]: https://www.itophub.io/wiki
|
||||
[5]: https://store.itophub.io/en_US/
|
||||
|
||||
|
||||
|
||||
[10]: https://www.itophub.io/wiki/page?id=2_5_0%3Adatamodel%3Astart#configuration_management_cmdb
|
||||
[11]: https://www.itophub.io/wiki/page?id=2_5_0%3Adatamodel%3Astart#ticketing
|
||||
[12]: https://www.itophub.io/wiki/page?id=2_5_0%3Adatamodel%3Astart#service_management
|
||||
[13]: https://www.itophub.io/wiki/page?id=2_5_0%3Adatamodel%3Astart#change_management
|
||||
[14]: https://www.itophub.io/wiki/page?id=2_5_0%3Aimplementation%3Astart#service_level_agreements_and_targets
|
||||
[15]: https://www.itophub.io/wiki/page?id=2_5_0%3Auser%3Aactions#relations
|
||||
[16]: https://www.itophub.io/wiki/page?id=2_5_0%3Auser%3Abulk_modify#uploading_data
|
||||
[17]: https://www.itophub.io/wiki/page?id=2_5_0%3Aadmin%3Aaudit
|
||||
[18]: https://www.itophub.io/wiki/page?id=2_5_0%3Aadvancedtopics%3Adata_synchro_overview
|
||||
|
||||
|
||||
|
||||
[50]: https://www.itophub.io/wiki/page?id=2_4_0:release:change_log
|
||||
[51]: https://www.itophub.io/wiki/page?id=2_4_0:release:2_4_whats_new
|
||||
[52]: https://www.itophub.io/wiki/page?id=2_4_0:install:230_to_240_migration_notes
|
||||
[53]: https://sourceforge.net/projects/itop/files/itop/2.4.1/iTop-2.4.1-3714.zip/download
|
||||
|
||||
[54]: https://www.itophub.io/wiki/page?id=2_5_0:release:change_log
|
||||
[55]: https://www.itophub.io/wiki/page?id=2_5_0:release:2_5_whats_new
|
||||
[56]: https://www.itophub.io/wiki/page?id=2_5_0:install:240_to_250_migration_notes
|
||||
[57]: https://sourceforge.net/projects/itop/files/itop/2.5.0/iTop-2.5.0-3935.zip/download
|
||||
|
||||
[58]: https://www.itophub.io/wiki/page?id=2_6_0:release:change_log
|
||||
[59]: https://www.itophub.io/wiki/page?id=2_6_0:release:2_6_whats_new
|
||||
[60]: https://www.itophub.io/wiki/page?id=2_6_0:install:250_to_260_migration_notes
|
||||
[61]: https://sourceforge.net/projects/itop/files/itop/2.6.0-beta/iTop-2.6-beta-4146.zip/download
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2018 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* Class ApplicationContext
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2018 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -31,6 +31,12 @@ require_once(APPROOT."/application/utils.inc.php");
|
||||
*/
|
||||
interface iDBObjectURLMaker
|
||||
{
|
||||
/**
|
||||
* @param string $sClass
|
||||
* @param string $iId
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function MakeObjectURL($sClass, $iId);
|
||||
}
|
||||
|
||||
@@ -39,6 +45,13 @@ interface iDBObjectURLMaker
|
||||
*/
|
||||
class iTopStandardURLMaker implements iDBObjectURLMaker
|
||||
{
|
||||
/**
|
||||
* @param string $sClass
|
||||
* @param string $iId
|
||||
*
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function MakeObjectURL($sClass, $iId)
|
||||
{
|
||||
$sPage = DBObject::ComputeStandardUIPage($sClass);
|
||||
@@ -53,6 +66,13 @@ class iTopStandardURLMaker implements iDBObjectURLMaker
|
||||
*/
|
||||
class PortalURLMaker implements iDBObjectURLMaker
|
||||
{
|
||||
/**
|
||||
* @param string $sClass
|
||||
* @param string $iId
|
||||
*
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function MakeObjectURL($sClass, $iId)
|
||||
{
|
||||
$sAbsoluteUrl = utils::GetAbsoluteUrlAppRoot();
|
||||
@@ -74,10 +94,20 @@ class PortalURLMaker implements iDBObjectURLMaker
|
||||
*/
|
||||
class ApplicationContext
|
||||
{
|
||||
public static $m_sUrlMakerClass = null;
|
||||
protected static $m_aPluginProperties = null;
|
||||
protected static $aDefaultValues; // Cache shared among all instances
|
||||
|
||||
protected $aNames;
|
||||
protected $aValues;
|
||||
protected static $aDefaultValues; // Cache shared among all instances
|
||||
|
||||
|
||||
/**
|
||||
* ApplicationContext constructor.
|
||||
*
|
||||
* @param bool $bReadContext
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct($bReadContext = true)
|
||||
{
|
||||
$this->aNames = array(
|
||||
@@ -89,11 +119,13 @@ class ApplicationContext
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the context directly in the PHP parameters (either POST or GET)
|
||||
* return nothing
|
||||
*/
|
||||
|
||||
/**
|
||||
* Read the context directly in the PHP parameters (either POST or GET)
|
||||
* return nothing
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function ReadContext()
|
||||
{
|
||||
if (!isset(self::$aDefaultValues))
|
||||
@@ -110,20 +142,26 @@ class ApplicationContext
|
||||
}
|
||||
// Hmm, there must be a better (more generic) way to handle the case below:
|
||||
// When there is only one possible (allowed) organization, the context must be
|
||||
// fixed to this org
|
||||
// fixed to this org unless there is only one organization in the system then
|
||||
// no filter is applied
|
||||
if ($sName == 'org_id')
|
||||
{
|
||||
if (MetaModel::IsValidClass('Organization'))
|
||||
{
|
||||
$oSearchFilter = new DBObjectSearch('Organization');
|
||||
$oSearchFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', true);
|
||||
$oSet = new CMDBObjectSet($oSearchFilter);
|
||||
$iCount = $oSet->CountWithLimit(2);
|
||||
if ($iCount == 1)
|
||||
if ($iCount > 1)
|
||||
{
|
||||
// Only one possible value for org_id, set it in the context
|
||||
$oOrg = $oSet->Fetch();
|
||||
self::$aDefaultValues[$sName] = $oOrg->GetKey();
|
||||
$oSearchFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', true);
|
||||
$oSet = new CMDBObjectSet($oSearchFilter);
|
||||
$iCount = $oSet->CountWithLimit(2);
|
||||
if ($iCount == 1)
|
||||
{
|
||||
// Only one possible value for org_id, set it in the context
|
||||
$oOrg = $oSet->Fetch();
|
||||
self::$aDefaultValues[$sName] = $oOrg->GetKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -131,12 +169,15 @@ class ApplicationContext
|
||||
}
|
||||
$this->aValues = self::$aDefaultValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value for the given parameter
|
||||
* @param string $sParamName Name of the parameter to read
|
||||
* @return mixed The value for this parameter
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns the current value for the given parameter
|
||||
*
|
||||
* @param string $sParamName Name of the parameter to read
|
||||
* @param string $defaultValue
|
||||
*
|
||||
* @return mixed The value for this parameter
|
||||
*/
|
||||
public function GetCurrentValue($sParamName, $defaultValue = '')
|
||||
{
|
||||
if (isset($this->aValues[$sParamName]))
|
||||
@@ -148,7 +189,7 @@ class ApplicationContext
|
||||
|
||||
/**
|
||||
* Returns the context as string with the format name1=value1&name2=value2....
|
||||
* return string The context as a string to be appended to an href property
|
||||
* @return string The context as a string to be appended to an href property
|
||||
*/
|
||||
public function GetForLink()
|
||||
{
|
||||
@@ -162,7 +203,7 @@ class ApplicationContext
|
||||
|
||||
/**
|
||||
* Returns the context as sequence of input tags to be inserted inside a <form> tag
|
||||
* return string The context as a sequence of <input type="hidden" /> tags
|
||||
* @return string The context as a sequence of <input type="hidden" /> tags
|
||||
*/
|
||||
public function GetForForm()
|
||||
{
|
||||
@@ -176,7 +217,7 @@ class ApplicationContext
|
||||
|
||||
/**
|
||||
* Returns the context as a hash array 'parameter_name' => value
|
||||
* return array The context information
|
||||
* @return array The context information
|
||||
*/
|
||||
public function GetAsHash()
|
||||
{
|
||||
@@ -199,8 +240,7 @@ class ApplicationContext
|
||||
/**
|
||||
* Removes the specified parameter from the context, for example when the same parameter
|
||||
* is already a search parameter
|
||||
* @param string $sParamName Name of the parameter to remove
|
||||
* @return none
|
||||
* @param string $sParamName Name of the parameter to remove
|
||||
*/
|
||||
public function Reset($sParamName)
|
||||
{
|
||||
@@ -212,6 +252,11 @@ class ApplicationContext
|
||||
|
||||
/**
|
||||
* Initializes the given object with the default values provided by the context
|
||||
*
|
||||
* @param \DBObject $oObj
|
||||
*
|
||||
* @throws \Exception
|
||||
* @throws \CoreUnexpectedValue
|
||||
*/
|
||||
public function InitObjectFromContext(DBObject &$oObj)
|
||||
{
|
||||
@@ -238,13 +283,11 @@ class ApplicationContext
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static $m_sUrlMakerClass = null;
|
||||
|
||||
/**
|
||||
* Set the current application url provider
|
||||
* @param sClass string Class implementing iDBObjectURLMaker
|
||||
* @return void
|
||||
* @param string $sClass Class implementing iDBObjectURLMaker
|
||||
* @return string
|
||||
*/
|
||||
public static function SetUrlMakerClass($sClass = 'iTopStandardURLMaker')
|
||||
{
|
||||
@@ -278,7 +321,14 @@ class ApplicationContext
|
||||
|
||||
/**
|
||||
* Get the current application url provider
|
||||
*
|
||||
* @param string $sObjClass
|
||||
* @param string $sObjKey
|
||||
* @param null $sUrlMakerClass
|
||||
* @param bool $bWithNavigationContext
|
||||
*
|
||||
* @return string the name of the class
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function MakeObjectUrl($sObjClass, $sObjKey, $sUrlMakerClass = null, $bWithNavigationContext = true)
|
||||
{
|
||||
@@ -306,8 +356,6 @@ class ApplicationContext
|
||||
}
|
||||
}
|
||||
|
||||
protected static $m_aPluginProperties = null;
|
||||
|
||||
/**
|
||||
* Load plugin properties for the current session
|
||||
* @return void
|
||||
@@ -326,9 +374,9 @@ class ApplicationContext
|
||||
|
||||
/**
|
||||
* Set plugin properties
|
||||
* @param sPluginClass string Class implementing any plugin interface
|
||||
* @param sProperty string Name of the property
|
||||
* @param value scalar Value (numeric or string)
|
||||
* @param string $sPluginClass Class implementing any plugin interface
|
||||
* @param string $sProperty Name of the property
|
||||
* @param mixed $value Value (numeric or string)
|
||||
* @return void
|
||||
*/
|
||||
public static function SetPluginProperty($sPluginClass, $sProperty, $value)
|
||||
@@ -341,7 +389,7 @@ class ApplicationContext
|
||||
|
||||
/**
|
||||
* Get plugin properties
|
||||
* @param sPluginClass string Class implementing any plugin interface
|
||||
* @param string $sPluginClass Class implementing any plugin interface
|
||||
* @return array of sProperty=>value pairs
|
||||
*/
|
||||
public static function GetPluginProperties($sPluginClass)
|
||||
@@ -359,4 +407,3 @@ class ApplicationContext
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
require_once(APPROOT.'application/newsroomprovider.class.inc.php');
|
||||
|
||||
/**
|
||||
* Management of application plugins
|
||||
*
|
||||
@@ -122,7 +124,8 @@ interface iApplicationUIExtension
|
||||
* Sorry, the verb has been reserved. You must implement it, but it is not called as of now.
|
||||
*
|
||||
* @param DBObject $oObject The object being displayed
|
||||
* @return type desc
|
||||
*
|
||||
* @return string[] desc
|
||||
*/
|
||||
public function EnumUsedAttributes($oObject); // Not yet implemented
|
||||
|
||||
@@ -441,8 +444,9 @@ abstract class ApplicationPopupMenuItem
|
||||
|
||||
/**
|
||||
* Returns the components to create a popup menu item in HTML
|
||||
* @return Hash A hash array: array('label' => , 'url' => , 'target' => , 'onclick' => )
|
||||
* @ignore
|
||||
*
|
||||
* @return array A hash array: array('label' => , 'url' => , 'target' => , 'onclick' => )
|
||||
* @ignore
|
||||
*/
|
||||
abstract public function GetMenuItem();
|
||||
|
||||
@@ -759,11 +763,15 @@ interface iRestServiceProvider
|
||||
* @return array An array of hash 'verb' => verb, 'description' => description
|
||||
*/
|
||||
public function ListOperations($sVersion);
|
||||
|
||||
/**
|
||||
* Enumerate services delivered by this class
|
||||
*
|
||||
* @param string $sVersion The version (e.g. 1.0) supported by the services
|
||||
* @param string $sVerb
|
||||
* @param array $aParams
|
||||
*
|
||||
* @return RestResult The standardized result structure (at least a message)
|
||||
* @throws Exception in case of internal failure.
|
||||
*/
|
||||
public function ExecOperation($sVersion, $sVerb, $aParams);
|
||||
}
|
||||
@@ -817,10 +825,6 @@ class RestResult
|
||||
* Result: the requested operation cannot be performed because it can cause data (integrity) loss
|
||||
*/
|
||||
const UNSAFE = 12;
|
||||
/**
|
||||
* Result: the request page number is not valid. It must be an integer greater than 0
|
||||
*/
|
||||
const INVALID_PAGE = 13;
|
||||
/**
|
||||
* Result: the operation could not be performed, see the message for troubleshooting
|
||||
*/
|
||||
@@ -870,7 +874,7 @@ class RestUtils
|
||||
* @param StdClass $oData Structured input data. Must contain the entry defined by sParamName.
|
||||
* @param string $sParamName Name of the parameter to fetch from the input data
|
||||
*
|
||||
* @return string
|
||||
* @return mixed parameter value if present
|
||||
* @throws Exception If the parameter is missing
|
||||
* @api
|
||||
*/
|
||||
@@ -893,7 +897,8 @@ class RestUtils
|
||||
* @param StdClass $oData Structured input data.
|
||||
* @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
|
||||
* @return void
|
||||
*
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
* @api
|
||||
*/
|
||||
@@ -937,7 +942,8 @@ class RestUtils
|
||||
* @param string $sClass Name of the class
|
||||
* @param StdClass $oData Structured input data.
|
||||
* @param string $sParamName Name of the parameter to fetch from the input data
|
||||
* @return An array of class => list of attributes (see RestResultWithObjects::AddObject that uses it)
|
||||
*
|
||||
* @return array of class => list of attributes (see RestResultWithObjects::AddObject that uses it)
|
||||
* @throws Exception
|
||||
* @api
|
||||
*/
|
||||
@@ -1085,13 +1091,10 @@ class RestUtils
|
||||
*
|
||||
* @param string $sClass Name of the class
|
||||
* @param mixed $key Either search criteria (substructure), or an object or an OQL string.
|
||||
* @param int $iLimit The limit of results to return
|
||||
* @param int $iOffset The offset of results to return
|
||||
*
|
||||
* @return DBObjectSet The search result set
|
||||
* @throws Exception If the input structure is not valid
|
||||
*/
|
||||
public static function GetObjectSetFromKey($sClass, $key, $iLimit = 0, $iOffset = 0)
|
||||
public static function GetObjectSetFromKey($sClass, $key)
|
||||
{
|
||||
if (is_object($key))
|
||||
{
|
||||
@@ -1120,13 +1123,12 @@ class RestUtils
|
||||
{
|
||||
// OQL
|
||||
$oSearch = DBObjectSearch::FromOQL($key);
|
||||
$oObjectSet = new DBObjectSet($oSearch);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Wrong format for key");
|
||||
}
|
||||
$oObjectSet = new DBObjectSet($oSearch, array(), array(), null, $iLimit, $iOffset);
|
||||
$oObjectSet = new DBObjectSet($oSearch);
|
||||
return $oObjectSet;
|
||||
}
|
||||
|
||||
@@ -1169,6 +1171,14 @@ class RestUtils
|
||||
}
|
||||
$value = DBObjectSet::FromArray($sLnkClass, $aLinks);
|
||||
}
|
||||
elseif ($oAttDef instanceof AttributeTagSet)
|
||||
{
|
||||
if (!is_array($value))
|
||||
{
|
||||
throw new Exception("A tag set must be defined by an array of tag codes");
|
||||
}
|
||||
$value = $oAttDef->FromJSONToValue($value);
|
||||
}
|
||||
else
|
||||
{
|
||||
$value = $oAttDef->FromJSONToValue($value);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -21,6 +21,7 @@ require_once(APPROOT.'application/dashlet.class.inc.php');
|
||||
require_once(APPROOT.'core/modelreflection.class.inc.php');
|
||||
|
||||
/**
|
||||
*
|
||||
* A user editable dashboard page
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
@@ -49,6 +50,11 @@ abstract class Dashboard
|
||||
$this->sId = $sId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sXml
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function FromXml($sXml)
|
||||
{
|
||||
$this->aCells = array(); // reset the content of the dashboard
|
||||
@@ -100,10 +106,10 @@ abstract class Dashboard
|
||||
$oCellsList = $oCellsNode->getElementsByTagName('cell');
|
||||
$aCellOrder = array();
|
||||
$iCellRank = 0;
|
||||
/** @var \DOMElement $oCellNode */
|
||||
foreach($oCellsList as $oCellNode)
|
||||
{
|
||||
$aDashletList = array();
|
||||
$oCellRank = $oCellNode->getElementsByTagName('rank')->item(0);
|
||||
$oCellRank = $oCellNode->getElementsByTagName('rank')->item(0);
|
||||
if ($oCellRank)
|
||||
{
|
||||
$iCellRank = (float)$oCellRank->textContent;
|
||||
@@ -113,6 +119,7 @@ abstract class Dashboard
|
||||
$oDashletList = $oDashletsNode->getElementsByTagName('dashlet');
|
||||
$iRank = 0;
|
||||
$aDashletOrder = array();
|
||||
/** @var \DOMElement $oDomNode */
|
||||
foreach($oDashletList as $oDomNode)
|
||||
{
|
||||
$oRank = $oDomNode->getElementsByTagName('rank')->item(0);
|
||||
@@ -145,6 +152,11 @@ abstract class Dashboard
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DOMElement $oDomNode
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function InitDashletFromDOMNode($oDomNode)
|
||||
{
|
||||
$sId = $oDomNode->getAttribute('id');
|
||||
@@ -152,7 +164,8 @@ abstract class Dashboard
|
||||
|
||||
// Test if dashlet can be instanciated, otherwise (uninstalled, broken, ...) we display a placeholder
|
||||
$sClass = static::GetDashletClassFromType($sDashletType);
|
||||
$oNewDashlet = new $sClass($this->oMetaModel, $sId);
|
||||
/** @var \Dashlet $oNewDashlet */
|
||||
$oNewDashlet = new $sClass($this->oMetaModel, $sId);
|
||||
$oNewDashlet->SetDashletType($sDashletType);
|
||||
$oNewDashlet->FromDOMNode($oDomNode);
|
||||
|
||||
@@ -163,8 +176,17 @@ abstract class Dashboard
|
||||
{
|
||||
return ($aItem1['rank'] > $aItem2['rank']) ? +1 : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Error handler to turn XML loading warnings into exceptions
|
||||
*
|
||||
* @param $errno
|
||||
* @param $errstr
|
||||
* @param $errfile
|
||||
* @param $errline
|
||||
*
|
||||
* @return bool
|
||||
* @throws \DOMException
|
||||
*/
|
||||
public static function ErrorHandler($errno, $errstr, $errfile, $errline)
|
||||
{
|
||||
@@ -194,8 +216,12 @@ abstract class Dashboard
|
||||
return $sXml;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DOMElement $oDefinition
|
||||
*/
|
||||
public function ToDOMNode($oDefinition)
|
||||
{
|
||||
/** @var \DOMDocument $oDoc */
|
||||
$oDoc = $oDefinition->ownerDocument;
|
||||
|
||||
$oNode = $oDoc->createElement('layout', $this->sLayoutClass);
|
||||
@@ -227,6 +253,7 @@ abstract class Dashboard
|
||||
$iDashletRank = 0;
|
||||
$oDashletsNode = $oDoc->createElement('dashlets');
|
||||
$oCellNode->appendChild($oDashletsNode);
|
||||
/** @var \Dashlet $oDashlet */
|
||||
foreach ($aCell as $oDashlet)
|
||||
{
|
||||
$oNode = $oDoc->createElement('dashlet');
|
||||
@@ -256,6 +283,7 @@ abstract class Dashboard
|
||||
{
|
||||
$sDashletClass = $aDashletParams['dashlet_class'];
|
||||
$sId = $aDashletParams['dashlet_id'];
|
||||
/** @var \Dashlet $oNewDashlet */
|
||||
$oNewDashlet = new $sDashletClass($this->oMetaModel, $sId);
|
||||
if (isset($aDashletParams['dashlet_type']))
|
||||
{
|
||||
@@ -318,31 +346,28 @@ abstract class Dashboard
|
||||
$this->iAutoReloadSec = max(MetaModel::GetConfig()->Get('min_reload_interval'), (int)$iAutoReloadSec);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Dashlet $oDashlet
|
||||
*/
|
||||
public function AddDashlet($oDashlet)
|
||||
{
|
||||
$sId = $this->GetNewDashletId();
|
||||
$oDashlet->SetId($sId);
|
||||
$this->aCells[] = array($oDashlet);
|
||||
}
|
||||
|
||||
public function Render($oPage, $bEditMode = false, $aExtraParams = array())
|
||||
{
|
||||
$oPage->add('<h1>'.htmlentities(Dict::S($this->sTitle), ENT_QUOTES, 'UTF-8', false).'</h1>');
|
||||
$oLayout = new $this->sLayoutClass;
|
||||
$oLayout->Render($oPage, $this->aCells, $bEditMode, $aExtraParams);
|
||||
if (!$bEditMode)
|
||||
{
|
||||
$oPage->add_linked_script('../js/dashlet.js');
|
||||
$oPage->add_linked_script('../js/dashboard.js');
|
||||
}
|
||||
}
|
||||
|
||||
public function RenderProperties($oPage)
|
||||
|
||||
/**
|
||||
* @param \WebPage $oPage *
|
||||
* @param array $aExtraParams
|
||||
*
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function RenderProperties($oPage, $aExtraParams = array())
|
||||
{
|
||||
// menu to pick a layout and edit other properties of the dashboard
|
||||
$oPage->add('<div class="ui-widget-content ui-corner-all"><div class="ui-widget-header ui-corner-all" style="text-align:center; padding: 2px;">'.Dict::S('UI:DashboardEdit:Properties').'</div>');
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot();
|
||||
|
||||
|
||||
$oPage->add('<div style="text-align:center">'.Dict::S('UI:DashboardEdit:Layout').'</div>');
|
||||
$oPage->add('<div id="select_layout" style="text-align:center">');
|
||||
foreach( get_declared_classes() as $sLayoutClass)
|
||||
@@ -360,13 +385,13 @@ abstract class Dashboard
|
||||
}
|
||||
}
|
||||
$oPage->add('</div>');
|
||||
|
||||
|
||||
$oForm = new DesignerForm();
|
||||
|
||||
$oField = new DesignerHiddenField('dashboard_id', '', $this->sId);
|
||||
$oForm->AddField($oField);
|
||||
|
||||
$oField = new DesignerLongTextField('dashboard_title', Dict::S('UI:DashboardEdit:DashboardTitle'), $this->sTitle);
|
||||
$oField = new DesignerTextField('dashboard_title', Dict::S('UI:DashboardEdit:DashboardTitle'), $this->sTitle);
|
||||
$oForm->AddField($oField);
|
||||
|
||||
$oField = new DesignerBooleanField('auto_reload', Dict::S('UI:DashboardEdit:AutoReload'), $this->bAutoReload);
|
||||
@@ -377,8 +402,8 @@ abstract class Dashboard
|
||||
$oForm->AddField($oField);
|
||||
|
||||
|
||||
$this->SetFormParams($oForm);
|
||||
$oForm->RenderAsPropertySheet($oPage, false, '.itop-dashboard');
|
||||
$this->SetFormParams($oForm, $aExtraParams);
|
||||
$oForm->RenderAsPropertySheet($oPage, false, '.itop-dashboard');
|
||||
|
||||
$oPage->add('</div>');
|
||||
|
||||
@@ -422,8 +447,28 @@ abstract class Dashboard
|
||||
EOF
|
||||
);
|
||||
}
|
||||
|
||||
public function RenderDashletsSelection($oPage)
|
||||
|
||||
/**
|
||||
* @param \iTopWebPage $oPage
|
||||
* @param bool $bEditMode
|
||||
* @param array $aExtraParams
|
||||
* @param bool $bCanEdit
|
||||
*/
|
||||
public function Render($oPage, $bEditMode = false, $aExtraParams = array(), $bCanEdit = true)
|
||||
{
|
||||
$oPage->add('<div class="dashboard-title-line"><div class="dashboard-title">'.htmlentities(Dict::S($this->sTitle), ENT_QUOTES, 'UTF-8', false).'</div></div>');
|
||||
|
||||
$oLayout = new $this->sLayoutClass;
|
||||
/** @var \DashboardLayoutMultiCol $oLayout */
|
||||
$oLayout->Render($oPage, $this->aCells, $bEditMode, $aExtraParams);
|
||||
if (!$bEditMode)
|
||||
{
|
||||
$oPage->add_linked_script('../js/dashlet.js');
|
||||
$oPage->add_linked_script('../js/dashboard.js');
|
||||
}
|
||||
}
|
||||
|
||||
public function RenderDashletsSelection(WebPage $oPage)
|
||||
{
|
||||
// Toolbox/palette to drag and drop dashlets
|
||||
$oPage->add('<div class="ui-widget-content ui-corner-all"><div class="ui-widget-header ui-corner-all" style="text-align:center; padding: 2px;">'.Dict::S('UI:DashboardEdit:Dashlets').'</div>');
|
||||
@@ -441,7 +486,7 @@ EOF
|
||||
$oPage->add_ready_script("$('.dashlet_icon').draggable({helper: 'clone', appendTo: 'body', zIndex: 10000, revert:'invalid'});");
|
||||
}
|
||||
|
||||
public function RenderDashletsProperties($oPage)
|
||||
public function RenderDashletsProperties(WebPage $oPage, $aExtraParams = array())
|
||||
{
|
||||
// Toolbox/palette to edit the properties of each dashlet
|
||||
$oPage->add('<div class="ui-widget-content ui-corner-all"><div class="ui-widget-header ui-corner-all" style="text-align:center; padding: 2px;">'.Dict::S('UI:DashboardEdit:DashletProperties').'</div>');
|
||||
@@ -449,15 +494,15 @@ EOF
|
||||
$oPage->add('<div id="dashlet_properties" style="text-align:center">');
|
||||
foreach($this->aCells as $aCell)
|
||||
{
|
||||
/** @var \Dashlet $oDashlet */
|
||||
foreach($aCell as $oDashlet)
|
||||
{
|
||||
$sId = $oDashlet->GetID();
|
||||
$sClass = get_class($oDashlet);
|
||||
if ($oDashlet->IsVisible())
|
||||
{
|
||||
$oPage->add('<div class="dashlet_properties" id="dashlet_properties_'.$sId.'" style="display:none">');
|
||||
$oForm = $oDashlet->GetForm();
|
||||
$this->SetFormParams($oForm);
|
||||
$this->SetFormParams($oForm, $aExtraParams);
|
||||
$oForm->RenderAsPropertySheet($oPage, false, '.itop-dashboard');
|
||||
$oPage->add('</div>');
|
||||
}
|
||||
@@ -472,6 +517,7 @@ EOF
|
||||
* Return an array of dashlets available for selection.
|
||||
*
|
||||
* @return array
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
protected function GetAvailableDashlets()
|
||||
{
|
||||
@@ -505,6 +551,7 @@ EOF
|
||||
$iNewId = 0;
|
||||
foreach($this->aCells as $aDashlets)
|
||||
{
|
||||
/** @var \Dashlet $oDashlet */
|
||||
foreach($aDashlets as $oDashlet)
|
||||
{
|
||||
$iNewId = max($iNewId, (int)$oDashlet->GetID());
|
||||
@@ -512,8 +559,14 @@ EOF
|
||||
}
|
||||
return $iNewId + 1;
|
||||
}
|
||||
|
||||
abstract protected function SetFormParams($oForm);
|
||||
|
||||
/**
|
||||
* @param $oForm
|
||||
* @param array $aExtraParams
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract protected function SetFormParams($oForm, $aExtraParams = array());
|
||||
|
||||
public static function GetDashletClassFromType($sType, $oFactory = null)
|
||||
{
|
||||
@@ -523,11 +576,22 @@ EOF
|
||||
}
|
||||
return 'DashletUnknown';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function GetId()
|
||||
{
|
||||
return $this->sId;
|
||||
}
|
||||
}
|
||||
|
||||
class RuntimeDashboard extends Dashboard
|
||||
{
|
||||
protected $bCustomized;
|
||||
private $sDefinitionFile = '';
|
||||
private $sReloadURL = null;
|
||||
|
||||
|
||||
public function __construct($sId)
|
||||
{
|
||||
@@ -540,10 +604,17 @@ class RuntimeDashboard extends Dashboard
|
||||
{
|
||||
$this->bCustomized = $bCustomized;
|
||||
}
|
||||
|
||||
protected function SetFormParams($oForm)
|
||||
|
||||
/**
|
||||
* @param \DesignerForm $oForm
|
||||
*
|
||||
* @param array $aExtraParams
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function SetFormParams($oForm, $aExtraParams = array())
|
||||
{
|
||||
$oForm->SetSubmitParams(utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php', array('operation' => 'update_dashlet_property'));
|
||||
$oForm->SetSubmitParams(utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php', array('operation' => 'update_dashlet_property', 'extra_params' => $aExtraParams));
|
||||
}
|
||||
|
||||
public function Save()
|
||||
@@ -587,41 +658,265 @@ class RuntimeDashboard extends Dashboard
|
||||
utils::PopArchiveMode();
|
||||
}
|
||||
}
|
||||
|
||||
public function RenderEditionTools($oPage)
|
||||
|
||||
/**
|
||||
* @param string $sDashboardFile file name relative to the current module folder
|
||||
* @param string $sDashBoardId code of the dashboard either menu_id or <class>__<attcode>
|
||||
*
|
||||
* @return null|RuntimeDashboard
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
*/
|
||||
public static function GetDashboard($sDashboardFile, $sDashBoardId)
|
||||
{
|
||||
$bCustomized = false;
|
||||
|
||||
if (!appUserPreferences::GetPref('display_original_dashboard_'.$sDashBoardId, false))
|
||||
{
|
||||
// Search for an eventual user defined dashboard
|
||||
$oUDSearch = new DBObjectSearch('UserDashboard');
|
||||
$oUDSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
|
||||
$oUDSearch->AddCondition('menu_code', $sDashBoardId, '=');
|
||||
$oUDSet = new DBObjectSet($oUDSearch);
|
||||
if ($oUDSet->Count() > 0)
|
||||
{
|
||||
// Assuming there is at most one couple {user, menu}!
|
||||
$oUserDashboard = $oUDSet->Fetch();
|
||||
$sDashboardDefinition = $oUserDashboard->Get('contents');
|
||||
$bCustomized = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sDashboardDefinition = @file_get_contents($sDashboardFile);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$sDashboardDefinition = @file_get_contents($sDashboardFile);
|
||||
}
|
||||
|
||||
if ($sDashboardDefinition !== false)
|
||||
{
|
||||
$oDashboard = new RuntimeDashboard($sDashBoardId);
|
||||
$oDashboard->FromXml($sDashboardDefinition);
|
||||
$oDashboard->SetCustomFlag($bCustomized);
|
||||
$oDashboard->SetDefinitionFile($sDashboardFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oDashboard = null;
|
||||
}
|
||||
return $oDashboard;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \iTopWebPage $oPage
|
||||
* @param bool $bEditMode
|
||||
* @param array $aExtraParams (class and id of the current object
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function Render($oPage, $bEditMode = false, $aExtraParams = array(), $bCanEdit = true)
|
||||
{
|
||||
if (!isset($aExtraParams['query_params']) && isset($aExtraParams['this->class']))
|
||||
{
|
||||
$oObj = MetaModel::GetObject($aExtraParams['this->class'], $aExtraParams['this->id']);
|
||||
$aRenderParams = array('query_params' => $oObj->ToArgsForQuery());
|
||||
}
|
||||
else
|
||||
{
|
||||
$aRenderParams = $aExtraParams;
|
||||
}
|
||||
|
||||
parent::Render($oPage, $bEditMode, $aRenderParams);
|
||||
|
||||
if (isset($aExtraParams['query_params']['this->object()']))
|
||||
{
|
||||
/** @var \DBObject $oObj */
|
||||
$oObj = $aExtraParams['query_params']['this->object()'];
|
||||
$aAjaxParams = array('this->class' => get_class($oObj), 'this->id' => $oObj->GetKey());
|
||||
}
|
||||
else
|
||||
{
|
||||
$aAjaxParams = $aExtraParams;
|
||||
}
|
||||
if (!$bEditMode && !$oPage->IsPrintableVersion())
|
||||
{
|
||||
$sId = $this->GetId();
|
||||
$sDivId = preg_replace('/[^a-zA-Z0-9_]/', '', $sId);
|
||||
if ($this->GetAutoReload())
|
||||
{
|
||||
$sFile = addslashes($this->GetDefinitionFile());
|
||||
$sExtraParams = json_encode($aAjaxParams);
|
||||
$iReloadInterval = 1000 * $this->GetAutoReloadInterval();
|
||||
$sReloadURL = $this->GetReloadURL();
|
||||
$oPage->add_script(
|
||||
<<<EOF
|
||||
if (typeof(AutoReloadDashboardId$sDivId) !== 'undefined')
|
||||
{
|
||||
clearInterval(AutoReloadDashboardId$sDivId);
|
||||
delete AutoReloadDashboardId$sDivId;
|
||||
}
|
||||
|
||||
AutoReloadDashboardId$sDivId = setInterval("ReloadDashboard$sDivId();", $iReloadInterval);
|
||||
|
||||
function ReloadDashboard$sDivId()
|
||||
{
|
||||
// Do not reload when a dialog box is active
|
||||
if (!($('.ui-dialog:visible').length > 0) && $('.dashboard_contents#$sDivId').is(':visible'))
|
||||
{
|
||||
$('.dashboard_contents#$sDivId').block();
|
||||
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php',
|
||||
{ operation: 'reload_dashboard', dashboard_id: '$sId', file: '$sFile', extra_params: $sExtraParams, reload_url: '$sReloadURL'},
|
||||
function(data){
|
||||
$('.dashboard_contents#$sDivId').html(data);
|
||||
$('.dashboard_contents#$sDivId').unblock();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
EOF
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oPage->add_script(
|
||||
<<<EOF
|
||||
if (typeof(AutoReloadDashboardId$sDivId) !== 'undefined')
|
||||
{
|
||||
clearInterval(AutoReloadDashboardId$sDivId);
|
||||
delete AutoReloadDashboardId$sDivId;
|
||||
}
|
||||
EOF
|
||||
);
|
||||
}
|
||||
|
||||
if ($bCanEdit)
|
||||
{
|
||||
$this->RenderSelector($oPage, $aAjaxParams);
|
||||
$this->RenderEditionTools($oPage, $aAjaxParams);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \iTopWebPage $oPage
|
||||
* @param array $aAjaxParams
|
||||
*/
|
||||
protected function RenderSelector($oPage, $aAjaxParams = array())
|
||||
{
|
||||
$sId = $this->GetId();
|
||||
$sDivId = preg_replace('/[^a-zA-Z0-9_]/', '', $sId);
|
||||
$sExtraParams = json_encode($aAjaxParams);
|
||||
|
||||
$sSelectorHtml = '<div class="dashboard-selector">';
|
||||
if ($this->HasCustomDashboard())
|
||||
{
|
||||
$bStandardSelected = appUserPreferences::GetPref('display_original_dashboard_'.$sId, false);
|
||||
$sStandard = Dict::S('UI:Toggle:StandardDashboard');
|
||||
$sSelectorHtml .= '<div class="selector-label">'.$sStandard.'</div>';
|
||||
$sSelectorHtml .= '<label class="switch"><input type="checkbox" onchange="ToggleDashboardSelector'.$sDivId.'();" '.($bStandardSelected ? '' : 'checked').'><span class="slider round"></span></label></input></label>';
|
||||
$sCustom = Dict::S('UI:Toggle:CustomDashboard');
|
||||
$sSelectorHtml .= '<div class="selector-label">'.$sCustom.'</div>';
|
||||
|
||||
}
|
||||
$sSelectorHtml .= '</div>';
|
||||
$sSelectorHtml = addslashes($sSelectorHtml);
|
||||
$sFile = addslashes($this->GetDefinitionFile());
|
||||
$sReloadURL = $this->GetReloadURL();
|
||||
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
$('.dashboard-title').after('$sSelectorHtml');
|
||||
EOF
|
||||
);
|
||||
|
||||
$oPage->add_script(
|
||||
<<<EOF
|
||||
function ToggleDashboardSelector$sDivId()
|
||||
{
|
||||
$('.dashboard_contents#$sDivId').block();
|
||||
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php',
|
||||
{ operation: 'toggle_dashboard', dashboard_id: '$sId', file: '$sFile', extra_params: $sExtraParams, reload_url: '$sReloadURL' },
|
||||
function(data) {
|
||||
$('.dashboard_contents#$sDivId').html(data);
|
||||
$('.dashboard_contents#$sDivId').unblock();
|
||||
}
|
||||
);
|
||||
}
|
||||
EOF
|
||||
);
|
||||
}
|
||||
|
||||
protected function HasCustomDashboard()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Search for an eventual user defined dashboard
|
||||
$oUDSearch = new DBObjectSearch('UserDashboard');
|
||||
$oUDSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
|
||||
$oUDSearch->AddCondition('menu_code', $this->GetId(), '=');
|
||||
$oUDSet = new DBObjectSet($oUDSearch);
|
||||
|
||||
return ($oUDSet->Count() > 0);
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \WebPage $oPage
|
||||
* @param array $aExtraParams
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function RenderEditionTools(WebPage $oPage, $aExtraParams)
|
||||
{
|
||||
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.iframe-transport.js');
|
||||
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.fileupload.js');
|
||||
$sEditMenu = "<td><span id=\"DashboardMenu\"><ul><li><img src=\"../images/pencil-menu.png\"><ul>";
|
||||
$sEditMenu = "<div id=\"DashboardMenu\"><ul><li><img src=\"../images/pencil-menu.png\"><ul>";
|
||||
|
||||
$aActions = array();
|
||||
$oEdit = new JSPopupMenuItem('UI:Dashboard:Edit', Dict::S('UI:Dashboard:Edit'), "return EditDashboard('{$this->sId}')");
|
||||
$aActions[$oEdit->GetUID()] = $oEdit->GetMenuItem();
|
||||
$sFile = addslashes($this->sDefinitionFile);
|
||||
$sJSExtraParams = json_encode($aExtraParams);
|
||||
$bCanEdit = true;
|
||||
if ($this->HasCustomDashboard())
|
||||
{
|
||||
$bCanEdit = !appUserPreferences::GetPref('display_original_dashboard_'.$this->GetId(), false);
|
||||
}
|
||||
if ($bCanEdit)
|
||||
{
|
||||
$oEdit = new JSPopupMenuItem('UI:Dashboard:Edit', Dict::S('UI:Dashboard:Edit'), "return EditDashboard('{$this->sId}', '$sFile', $sJSExtraParams)");
|
||||
$aActions[$oEdit->GetUID()] = $oEdit->GetMenuItem();
|
||||
}
|
||||
|
||||
if ($this->bCustomized)
|
||||
{
|
||||
$oRevert = new JSPopupMenuItem('UI:Dashboard:RevertConfirm', Dict::S('UI:Dashboard:Revert'),
|
||||
"if (confirm('".addslashes(Dict::S('UI:Dashboard:RevertConfirm'))."')) return RevertDashboard('{$this->sId}'); else return false");
|
||||
"if (confirm('".addslashes(Dict::S('UI:Dashboard:RevertConfirm'))."')) return RevertDashboard('{$this->sId}', $sJSExtraParams); else return false");
|
||||
$aActions[$oRevert->GetUID()] = $oRevert->GetMenuItem();
|
||||
}
|
||||
utils::GetPopupMenuItems($oPage, iPopupMenuExtension::MENU_DASHBOARD_ACTIONS, $this, $aActions);
|
||||
$sEditMenu .= $oPage->RenderPopupMenuItems($aActions);
|
||||
|
||||
|
||||
$sEditMenu = addslashes($sEditMenu);
|
||||
//$sEditBtn = addslashes('<div style="display: inline-block; height: 55px; width:200px;vertical-align:center;line-height:60px;text-align:left;"><button onclick="EditDashboard(\''.$this->sId.'\');">Edit This Page</button></div>');
|
||||
$sReloadURL = $this->GetReloadURL();
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
$('#logOffBtn').parent().before('$sEditMenu');
|
||||
$('.dashboard-title').after('$sEditMenu');
|
||||
$('#DashboardMenu>ul').popupmenu();
|
||||
|
||||
EOF
|
||||
);
|
||||
$oPage->add_script(
|
||||
<<<EOF
|
||||
function EditDashboard(sId)
|
||||
function EditDashboard(sId, sDashboardFile, aExtraParams)
|
||||
{
|
||||
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', {operation: 'dashboard_editor', id: sId},
|
||||
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', {operation: 'dashboard_editor', id: sId, file: sDashboardFile, extra_params: aExtraParams, reload_url: '$sReloadURL'},
|
||||
function(data)
|
||||
{
|
||||
$('body').append(data);
|
||||
@@ -629,12 +924,12 @@ function EditDashboard(sId)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
function RevertDashboard(sId)
|
||||
function RevertDashboard(sId, aExtraParams)
|
||||
{
|
||||
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', {operation: 'revert_dashboard', dashboard_id: sId},
|
||||
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', {operation: 'revert_dashboard', dashboard_id: sId, extra_params: aExtraParams, reload_url: '$sReloadURL'},
|
||||
function(data)
|
||||
{
|
||||
$('body').append(data);
|
||||
location.reload();
|
||||
}
|
||||
);
|
||||
return false;
|
||||
@@ -643,9 +938,14 @@ EOF
|
||||
);
|
||||
}
|
||||
|
||||
public function RenderProperties($oPage)
|
||||
/**
|
||||
* @param \WebPage $oPage
|
||||
*
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function RenderProperties($oPage, $aExtraParams = array())
|
||||
{
|
||||
parent::RenderProperties($oPage);
|
||||
parent::RenderProperties($oPage, $aExtraParams);
|
||||
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
@@ -676,16 +976,36 @@ EOF
|
||||
}
|
||||
|
||||
|
||||
public function RenderEditor($oPage)
|
||||
/**
|
||||
* @param \iTopWebPage $oPage
|
||||
*
|
||||
* @param array $aExtraParams
|
||||
*
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \ReflectionException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function RenderEditor($oPage, $aExtraParams = array())
|
||||
{
|
||||
if (isset($aExtraParams['this->class']))
|
||||
{
|
||||
$oObj = MetaModel::GetObject($aExtraParams['this->class'], $aExtraParams['this->id']);
|
||||
$aRenderParams = array('query_params' => $oObj->ToArgsForQuery());
|
||||
}
|
||||
else
|
||||
{
|
||||
$aRenderParams = $aExtraParams;
|
||||
}
|
||||
$sJSExtraParams = json_encode($aExtraParams);
|
||||
$oPage->add('<div id="dashboard_editor">');
|
||||
$oPage->add('<div class="ui-layout-center">');
|
||||
$this->Render($oPage, true);
|
||||
$this->Render($oPage, true, $aRenderParams);
|
||||
$oPage->add('</div>');
|
||||
$oPage->add('<div class="ui-layout-east">');
|
||||
$this->RenderProperties($oPage);
|
||||
$this->RenderProperties($oPage, $aExtraParams);
|
||||
$this->RenderDashletsSelection($oPage);
|
||||
$this->RenderDashletsProperties($oPage);
|
||||
$this->RenderDashletsProperties($oPage, $aExtraParams);
|
||||
$oPage->add('</div>');
|
||||
$oPage->add('<div id="event_bus"/>'); // For exchanging messages between the panes, same as in the designer
|
||||
$oPage->add('</div>');
|
||||
@@ -699,7 +1019,9 @@ EOF
|
||||
$sAutoReload = $this->bAutoReload ? 'true' : 'false';
|
||||
$sAutoReloadSec = (string) $this->iAutoReloadSec;
|
||||
$sTitle = addslashes($this->sTitle);
|
||||
$sFile = addslashes($this->GetDefinitionFile());
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php';
|
||||
$sReloadURL = $this->GetReloadURL();
|
||||
|
||||
$sExitConfirmationMessage = addslashes(Dict::S('UI:NavigateAwayConfirmationMessage'));
|
||||
$sCancelConfirmationMessage = addslashes(Dict::S('UI:CancelConfirmationMessage'));
|
||||
@@ -729,7 +1051,7 @@ $('#dashboard_editor').dialog({
|
||||
}
|
||||
}
|
||||
window.bLeavingOnUserAction = true;
|
||||
oDashboard.save();
|
||||
oDashboard.save($(this));
|
||||
} },
|
||||
{ text: "$sCancelButtonLabel", click: function() {
|
||||
var oDashboard = $('.itop-dashboard').data('itopRuntimedashboard');
|
||||
@@ -751,8 +1073,8 @@ $('#dashboard_editor').dialog({
|
||||
$('#dashboard_editor .ui-layout-center').runtimedashboard({
|
||||
dashboard_id: '$sId', layout_class: '$sLayoutClass', title: '$sTitle',
|
||||
auto_reload: $sAutoReload, auto_reload_sec: $sAutoReloadSec,
|
||||
submit_to: '$sUrl', submit_parameters: {operation: 'save_dashboard'},
|
||||
render_to: '$sUrl', render_parameters: {operation: 'render_dashboard'},
|
||||
submit_to: '$sUrl', submit_parameters: {operation: 'save_dashboard', file: '$sFile', extra_params: $sJSExtraParams, reload_url: '$sReloadURL'},
|
||||
render_to: '$sUrl', render_parameters: {operation: 'render_dashboard', file: '$sFile', extra_params: $sJSExtraParams, reload_url: '$sReloadURL'},
|
||||
new_dashlet_parameters: {operation: 'new_dashlet'}
|
||||
});
|
||||
|
||||
@@ -835,7 +1157,8 @@ EOF
|
||||
$sParentId = $aParentMenu['parent'];
|
||||
$aParentMenu = $aParentMenus[$sParentId];
|
||||
}
|
||||
$oParentMenu = $aParentMenu['node'];
|
||||
/** @var \MenuNode $oParentMenu */
|
||||
$oParentMenu = $aParentMenu['node'];
|
||||
if ($oMenu->IsEnabled() && $oParentMenu->IsEnabled())
|
||||
{
|
||||
$sMenuLabel = $oMenu->GetTitle();
|
||||
@@ -890,6 +1213,7 @@ EOF
|
||||
{
|
||||
$oSubForm = new DesignerForm();
|
||||
$oMetaModel = new ModelReflectionRuntime();
|
||||
/** @var \Dashlet $oDashlet */
|
||||
$oDashlet = new $sDashletClass($oMetaModel, 0);
|
||||
$oDashlet->GetPropertiesFieldsFromOQL($oSubForm, $sOQL);
|
||||
|
||||
@@ -900,7 +1224,11 @@ EOF
|
||||
|
||||
return $oForm;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param \WebPage $oPage
|
||||
* @param $sOQL
|
||||
*/
|
||||
public static function GetDashletCreationDlgFromOQL($oPage, $sOQL)
|
||||
{
|
||||
$oPage->add('<div id="dashlet_creation_dlg">');
|
||||
@@ -947,4 +1275,30 @@ $('#dashlet_creation_dlg').dialog({
|
||||
EOF
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function GetDefinitionFile()
|
||||
{
|
||||
return $this->sDefinitionFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sDefinitionFile
|
||||
*/
|
||||
public function SetDefinitionFile($sDefinitionFile)
|
||||
{
|
||||
$this->sDefinitionFile = $sDefinitionFile;
|
||||
}
|
||||
|
||||
public function GetReloadURL()
|
||||
{
|
||||
return $this->sReloadURL;
|
||||
}
|
||||
|
||||
public function SetReloadURL($sReloadURL)
|
||||
{
|
||||
$this->sReloadURL = $sReloadURL;
|
||||
}
|
||||
}
|
||||
@@ -59,6 +59,7 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
|
||||
$bNoVisibleFound = true;
|
||||
while($idx < count($aKeys) && $bNoVisibleFound)
|
||||
{
|
||||
/** @var \Dashlet $oDashlet */
|
||||
$oDashlet = $aDashlets[$aKeys[$idx]];
|
||||
if ($oDashlet->IsVisible())
|
||||
{
|
||||
@@ -98,7 +99,13 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
|
||||
return $aCells;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param \WebPage $oPage
|
||||
* @param $aCells
|
||||
* @param bool $bEditMode
|
||||
* @param array $aExtraParams
|
||||
*/
|
||||
public function Render($oPage, $aCells, $bEditMode = false, $aExtraParams = array())
|
||||
{
|
||||
// Trim the list of cells to remove the invisible/empty ones at the end of the array
|
||||
@@ -122,6 +129,7 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
|
||||
$aDashlets = $aCells[$iCellIdx];
|
||||
if (count($aDashlets) > 0)
|
||||
{
|
||||
/** @var \Dashlet $oDashlet */
|
||||
foreach($aDashlets as $oDashlet)
|
||||
{
|
||||
if ($oDashlet->IsVisible())
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.5">
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.6">
|
||||
<portals>
|
||||
<portal id="legacy_portal" _delta="define">
|
||||
<url>portal/index.php</url>
|
||||
|
||||
@@ -431,7 +431,7 @@ EOF;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aRow['form::select'] = "<input type=\"checkbox\" $sDisabled class=\"selectList{$this->iListId}\" name=\"selectObject[]\" value=\"".$aObjects[$sAlias]->GetKey()."\"></input>";
|
||||
$aRow['form::select'] = "<input type=\"checkBox\" $sDisabled class=\"selectList{$this->iListId}\" name=\"selectObject[]\" value=\"".$aObjects[$sAlias]->GetKey()."\"></input>";
|
||||
}
|
||||
}
|
||||
foreach($aColumns[$sAlias] as $sAttCode => $aData)
|
||||
@@ -565,7 +565,7 @@ EOF;
|
||||
<<<EOF
|
||||
var oTable = $('#{$this->iListId} table.listResults');
|
||||
oTable.tableHover();
|
||||
oTable.tablesorter( { $sHeaders widgets: ['myZebra', 'truncatedList']} ).tablesorterPager({container: $('#pager{$this->iListId}'), totalRows:$iCount, size: $iPageSize, filter: '$sOQL', extra_params: '$sExtraParams', select_mode: '$sSelectModeJS', displayKey: $sDisplayKey, table_id: '{$this->iListId}', columns: $sJSColumns, class_aliases: $sJSClassAliases $sCssCount});
|
||||
oTable.tablesorter( { $sHeaders widgets: ['myZebra', 'truncatedList']} ).tablesorterPager({container: $('#pager{$this->iListId}'), totalRows:$iCount, size: $iPageSize, filter: '$sOQL', extra_params: '$sExtraParams', select_mode: '$sSelectModeJS', displayKey: $sDisplayKey, columns: $sJSColumns, class_aliases: $sJSClassAliases $sCssCount});
|
||||
EOF
|
||||
);
|
||||
if ($sFakeSortList != '')
|
||||
@@ -756,6 +756,10 @@ class DataTableSettings implements Serializable
|
||||
{
|
||||
foreach($this->aClassAliases as $sAlias => $sClass)
|
||||
{
|
||||
if (!isset($this->aColumns[$sAlias]))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
foreach($this->aColumns[$sAlias] as $sAttCode => $aData)
|
||||
{
|
||||
// Remove non-existent columns
|
||||
@@ -771,7 +775,7 @@ class DataTableSettings implements Serializable
|
||||
$aTempData = array();
|
||||
foreach($aList as $sAttCode => $oAttDef)
|
||||
{
|
||||
if ( (!array_key_exists($sAttCode, $this->aColumns[$sAlias])) && (!$oAttDef instanceof AttributeLinkSet))
|
||||
if ( (!array_key_exists($sAttCode, $this->aColumns[$sAlias])) && (!($oAttDef instanceof AttributeLinkedSet || $oAttDef instanceof AttributeDashboard)))
|
||||
{
|
||||
$aFieldData = $this->GetFieldData($sAlias, $sAttCode, $oAttDef, false /* bChecked */, 'none');
|
||||
if ($aFieldData) $aTempData[$aFieldData['label']] = $aFieldData;
|
||||
|
||||
@@ -220,9 +220,26 @@ class DisplayBlock
|
||||
$aExtraParams['currentId'] = $sId;
|
||||
$sExtraParams = addslashes(str_replace('"', "'", json_encode($aExtraParams))); // JSON encode, change the style of the quotes and escape them
|
||||
|
||||
if (isset($aExtraParams['query_params']))
|
||||
{
|
||||
$aQueryParams = $aExtraParams['query_params'];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isset($aExtraParams['this->id']) && isset($aExtraParams['this->class']))
|
||||
{
|
||||
$sClass = $aExtraParams['this->class'];
|
||||
$iKey = $aExtraParams['this->id'];
|
||||
$oObj = MetaModel::GetObject($sClass, $iKey);
|
||||
$aQueryParams = array('this->object()' => $oObj);
|
||||
}
|
||||
else
|
||||
{
|
||||
$aQueryParams = array();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$sFilter = $this->m_oFilter->serialize(); // Used either for asynchronous or auto_reload
|
||||
$sFilter = addslashes($this->m_oFilter->serialize(false, $aQueryParams)); // Used either for asynchronous or auto_reload
|
||||
if (!$this->m_bAsynchronous)
|
||||
{
|
||||
// render now
|
||||
@@ -314,20 +331,31 @@ class DisplayBlock
|
||||
* @throws MySQLException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function GetRenderContent(WebPage $oPage, $aExtraParams = array(), $sId)
|
||||
public function GetRenderContent(WebPage $oPage, $aExtraParams, $sId)
|
||||
{
|
||||
$sHtml = '';
|
||||
// Add the extra params into the filter if they make sense for such a filter
|
||||
$bDoSearch = utils::ReadParam('dosearch', false);
|
||||
$aQueryParams = array();
|
||||
if (isset($aExtraParams['query_params']))
|
||||
{
|
||||
$aQueryParams = $aExtraParams['query_params'];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isset($aExtraParams['this->id']) && isset($aExtraParams['this->class']))
|
||||
{
|
||||
$sClass = $aExtraParams['this->class'];
|
||||
$iKey = $aExtraParams['this->id'];
|
||||
$oObj = MetaModel::GetObject($sClass, $iKey);
|
||||
$aQueryParams = array('this->object()' => $oObj);
|
||||
}
|
||||
}
|
||||
if ($this->m_oSet == null)
|
||||
{
|
||||
$aQueryParams = array();
|
||||
if (isset($aExtraParams['query_params']))
|
||||
{
|
||||
$aQueryParams = $aExtraParams['query_params'];
|
||||
}
|
||||
|
||||
// In case of search, the context filtering is done by the search itself
|
||||
if (($this->m_sStyle != 'links') && ($this->m_sStyle != 'search'))
|
||||
if (($this->m_sStyle != 'links') && ($this->m_sStyle != 'search') && ($this->m_sStyle != 'list_search'))
|
||||
{
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sClass = $this->m_oFilter->GetClass();
|
||||
@@ -457,7 +485,15 @@ class DisplayBlock
|
||||
$oSubsetSearch = $this->m_oFilter->DeepClone();
|
||||
$oCondition = new BinaryExpression($oGroupByExp, '=', new ScalarExpression($aValues[$iRow]));
|
||||
$oSubsetSearch->AddConditionExpression($oCondition);
|
||||
$sFilter = urlencode($oSubsetSearch->serialize());
|
||||
if (isset($aExtraParams['query_params']))
|
||||
{
|
||||
$aQueryParams = $aExtraParams['query_params'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$aQueryParams = array();
|
||||
}
|
||||
$sFilter = rawurlencode($oSubsetSearch->serialize(false, $aQueryParams));
|
||||
|
||||
$aData[] = array ('group' => $aLabels[$iRow],
|
||||
'value' => "<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=search&dosearch=1&$sParams&filter=$sFilter\">$iCount</a>"); // TO DO: add the context information
|
||||
@@ -580,6 +616,7 @@ class DisplayBlock
|
||||
}
|
||||
break;
|
||||
|
||||
case 'list_search':
|
||||
case 'list':
|
||||
$aClasses = $this->m_oSet->GetSelectedClasses();
|
||||
$aAuthorizedClasses = array();
|
||||
@@ -715,7 +752,7 @@ class DisplayBlock
|
||||
$sClass = $this->m_oFilter->GetClass();
|
||||
$oAppContext = new ApplicationContext();
|
||||
$bContextFilter = isset($aExtraParams['context_filter']) ? isset($aExtraParams['context_filter']) != 0 : false;
|
||||
if ($bContextFilter)
|
||||
if ($bContextFilter && is_null($this->m_oSet))
|
||||
{
|
||||
foreach($oAppContext->GetNames() as $sFilterCode)
|
||||
{
|
||||
@@ -734,7 +771,7 @@ class DisplayBlock
|
||||
$this->m_oSet->SetShowObsoleteData($this->m_bShowObsoleteData);
|
||||
}
|
||||
$iCount = $this->m_oSet->Count();
|
||||
$sHyperlink = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&'.$oAppContext->GetForLink().'&filter='.urlencode($this->m_oFilter->serialize());
|
||||
$sHyperlink = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&'.$oAppContext->GetForLink().'&filter='.rawurlencode($this->m_oFilter->serialize());
|
||||
$sHtml .= '<p><a class="actions" href="'.$sHyperlink.'">';
|
||||
// Note: border set to 0 due to various browser interpretations (IE9 adding a 2px border)
|
||||
$sHtml .= MetaModel::GetClassIcon($sClass, true, 'float;left;margin-right:10px;border:0;');
|
||||
@@ -823,7 +860,7 @@ class DisplayBlock
|
||||
}
|
||||
$sHyperlink = utils::GetAbsoluteUrlAppRoot()
|
||||
.'pages/UI.php?operation=search&'.$oAppContext->GetForLink()
|
||||
.'&filter='.urlencode($oSingleGroupByValueFilter->serialize());
|
||||
.'&filter='.rawurlencode($oSingleGroupByValueFilter->serialize());
|
||||
$aCounts[$sStateValue] = "<a href=\"$sHyperlink\">{$aCounts[$sStateValue]}</a>";
|
||||
}
|
||||
}
|
||||
@@ -832,7 +869,7 @@ class DisplayBlock
|
||||
$sHtml .= '<tr><td>'.implode('</td><td>', $aCounts).'</td></tr></table></div>';
|
||||
// Title & summary
|
||||
$iCount = $this->m_oSet->Count();
|
||||
$sHyperlink = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&'.$oAppContext->GetForLink().'&filter='.urlencode($this->m_oFilter->serialize());
|
||||
$sHyperlink = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&'.$oAppContext->GetForLink().'&filter='.rawurlencode($this->m_oFilter->serialize());
|
||||
$sHtml .= '<h1>'.Dict::S(str_replace('_', ':', $sTitle)).'</h1>';
|
||||
$sHtml .= '<a class="summary" href="'.$sHyperlink.'">'.Dict::Format(str_replace('_', ':', $sLabel), $iCount).'</a>';
|
||||
$sHtml .= '<div style="clear:both;"></div>';
|
||||
@@ -843,7 +880,7 @@ class DisplayBlock
|
||||
|
||||
$sCsvFile = strtolower($this->m_oFilter->GetClass()).'.csv';
|
||||
$sDownloadLink = utils::GetAbsoluteUrlAppRoot().'webservices/export.php?expression='.urlencode($this->m_oFilter->ToOQL(true)).'&format=csv&filename='.urlencode($sCsvFile);
|
||||
$sLinkToToggle = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&'.$oAppContext->GetForLink().'&filter='.urlencode($this->m_oFilter->serialize()).'&format=csv';
|
||||
$sLinkToToggle = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&'.$oAppContext->GetForLink().'&filter='.rawurlencode($this->m_oFilter->serialize()).'&format=csv';
|
||||
// Pass the parameters via POST, since expression may be very long
|
||||
$aParamsToPost = array(
|
||||
'expression' => $this->m_oFilter->ToOQL(true),
|
||||
@@ -913,10 +950,10 @@ class DisplayBlock
|
||||
|
||||
$sChartType = isset($aExtraParams['chart_type']) ? $aExtraParams['chart_type'] : 'pie';
|
||||
$sTitle = isset($aExtraParams['chart_title']) ? '<h1 style="text-align:center">'.htmlentities(Dict::S($aExtraParams['chart_title']), ENT_QUOTES, 'UTF-8').'</h1>' : '';
|
||||
$sHtml = "$sTitle<div style=\"height:200px;width:100%\" id=\"my_chart_$sId{$iChartCounter}\"><div style=\"height:200px;line-height:200px;vertical-align:center;text-align:center;width:100%\"><img src=\"../images/indicator.gif\"></div></div>\n";
|
||||
$sHtml = "$sTitle<div style=\"height:200px;width:100%\" class=\"dashboard_chart\" id=\"my_chart_$sId{$iChartCounter}\"><div style=\"height:200px;line-height:200px;vertical-align:center;text-align:center;width:100%\"><img src=\"../images/indicator.gif\"></div></div>\n";
|
||||
$sGroupBy = isset($aExtraParams['group_by']) ? $aExtraParams['group_by'] : '';
|
||||
$sGroupByExpr = isset($aExtraParams['group_by_expr']) ? '¶ms[group_by_expr]='.$aExtraParams['group_by_expr'] : '';
|
||||
$sFilter = $this->m_oFilter->serialize();
|
||||
$sFilter = $this->m_oFilter->serialize(false, $aQueryParams);
|
||||
$oContext = new ApplicationContext();
|
||||
$sContextParam = $oContext->GetForLink();
|
||||
$sAggregationFunction = isset($aExtraParams['aggregation_function']) ? $aExtraParams['aggregation_function'] : '';
|
||||
@@ -927,11 +964,11 @@ class DisplayBlock
|
||||
|
||||
if (isset($aExtraParams['group_by_label']))
|
||||
{
|
||||
$sUrl = json_encode(utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart¶ms[group_by]=$sGroupBy{$sGroupByExpr}¶ms[group_by_label]={$aExtraParams['group_by_label']}¶ms[chart_type]=$sChartType¶ms[currentId]=$sId{$iChartCounter}¶ms[order_direction]=$sOrderDirection¶ms[order_by]=$sOrderBy¶ms[limit]=$sLimit¶ms[aggregation_function]=$sAggregationFunction¶ms[aggregation_attribute]=$sAggregationAttr&id=$sId{$iChartCounter}&filter=".urlencode($sFilter).'&'.$sContextParam);
|
||||
$sUrl = json_encode(utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart¶ms[group_by]=$sGroupBy{$sGroupByExpr}¶ms[group_by_label]={$aExtraParams['group_by_label']}¶ms[chart_type]=$sChartType¶ms[currentId]=$sId{$iChartCounter}¶ms[order_direction]=$sOrderDirection¶ms[order_by]=$sOrderBy¶ms[limit]=$sLimit¶ms[aggregation_function]=$sAggregationFunction¶ms[aggregation_attribute]=$sAggregationAttr&id=$sId{$iChartCounter}&filter=".rawurlencode($sFilter).'&'.$sContextParam);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sUrl = json_encode(utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart¶ms[group_by]=$sGroupBy{$sGroupByExpr}¶ms[chart_type]=$sChartType¶ms[currentId]=$sId{$iChartCounter}¶ms[order_direction]=$sOrderDirection¶ms[order_by]=$sOrderBy¶ms[limit]=$sLimit¶ms[aggregation_function]=$sAggregationFunction¶ms[aggregation_attribute]=$sAggregationAttr&id=$sId{$iChartCounter}&filter=".urlencode($sFilter).'&'.$sContextParam);
|
||||
$sUrl = json_encode(utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart¶ms[group_by]=$sGroupBy{$sGroupByExpr}¶ms[chart_type]=$sChartType¶ms[currentId]=$sId{$iChartCounter}¶ms[order_direction]=$sOrderDirection¶ms[order_by]=$sOrderBy¶ms[limit]=$sLimit¶ms[aggregation_function]=$sAggregationFunction¶ms[aggregation_attribute]=$sAggregationAttr&id=$sId{$iChartCounter}&filter=".rawurlencode($sFilter).'&'.$sContextParam);
|
||||
}
|
||||
|
||||
$oPage->add_ready_script(
|
||||
@@ -971,7 +1008,7 @@ EOF
|
||||
$oSubsetSearch = $this->m_oFilter->DeepClone();
|
||||
$oCondition = new BinaryExpression($oGroupByExp, '=', new ScalarExpression($sValue));
|
||||
$oSubsetSearch->AddConditionExpression($oCondition);
|
||||
$aURLs[] = utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=search&format=html&filter=".urlencode($oSubsetSearch->serialize()).'&'.$sContextParam;
|
||||
$aURLs[] = utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=search&format=html&filter=".rawurlencode($oSubsetSearch->serialize()).'&'.$sContextParam;
|
||||
}
|
||||
$sJSURLs = json_encode($aURLs);
|
||||
}
|
||||
@@ -989,6 +1026,7 @@ EOF
|
||||
$sJson = json_encode($aValues);
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
|
||||
var chart = c3.generate({
|
||||
bindto: d3.select('#my_chart_$sId'),
|
||||
data: {
|
||||
@@ -1036,6 +1074,12 @@ var chart = c3.generate({
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (typeof(charts) === "undefined")
|
||||
{
|
||||
charts = [];
|
||||
}
|
||||
charts.push(chart);
|
||||
EOF
|
||||
);
|
||||
break;
|
||||
@@ -1074,6 +1118,12 @@ var chart = c3.generate({
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (typeof(charts) === "undefined")
|
||||
{
|
||||
charts = [];
|
||||
}
|
||||
charts.push(chart);
|
||||
EOF
|
||||
);
|
||||
break;
|
||||
@@ -1122,7 +1172,7 @@ EOF
|
||||
}
|
||||
if (($bAutoReload) && ($this->m_sStyle != 'search')) // Search form do NOT auto-reload
|
||||
{
|
||||
$sFilter = $this->m_oFilter->serialize(); // Used either for asynchronous or auto_reload
|
||||
$sFilter = addslashes(str_replace('"', "'", $this->m_oFilter->serialize())); // Used either for asynchronous or auto_reload
|
||||
$sExtraParams = addslashes(str_replace('"', "'", json_encode($aExtraParams))); // JSON encode, change the style of the quotes and escape them
|
||||
|
||||
$oPage->add_script('if (typeof window.oAutoReloadBlock == "undefined") {
|
||||
@@ -1131,7 +1181,7 @@ EOF
|
||||
if (typeof window.oAutoReloadBlock[\''.$sId.'\'] != "undefined") {
|
||||
clearInterval(window.oAutoReloadBlock[\''.$sId.'\']);
|
||||
}
|
||||
window.oAutoReloadBlock[\''.$sId.'\'] = setInterval("ReloadBlock(\''.$sId.'\', \''.$this->m_sStyle.'\', \''.$sFilter.'\', \"'.$sExtraParams.'\")", '.$iReloadInterval.');');
|
||||
window.oAutoReloadBlock[\''.$sId.'\'] = setInterval("ReloadBlock(\''.$sId.'\', \''.$this->m_sStyle.'\', \"'.$sFilter.'\", \"'.$sExtraParams.'\")", '.$iReloadInterval.');');
|
||||
}
|
||||
|
||||
return $sHtml;
|
||||
@@ -1391,7 +1441,7 @@ class HistoryBlock extends DisplayBlock
|
||||
default:
|
||||
if ($bTruncated)
|
||||
{
|
||||
$sFilter = $this->m_oFilter->serialize();
|
||||
$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 .= ' ';
|
||||
|
||||
@@ -378,9 +378,9 @@ $('#$sDialogId').dialog({
|
||||
}
|
||||
}
|
||||
} },
|
||||
{ text: "$sCancelButtonLabel", click: function() { KillAllMenus(); $(this).dialog( "close" ); $(this).remove(); } },
|
||||
{ text: "$sCancelButtonLabel", click: function() { $(this).dialog( "close" ); $(this).remove(); } },
|
||||
],
|
||||
close: function() { KillAllMenus(); $(this).remove(); }
|
||||
close: function() { $(this).remove(); }
|
||||
});
|
||||
var oForm = $('#$sDialogId form');
|
||||
var sFormId = oForm.attr('id');
|
||||
@@ -682,6 +682,7 @@ class DesignerFormField
|
||||
protected $sLabel;
|
||||
protected $sCode;
|
||||
protected $defaultValue;
|
||||
/** @var \DesignerForm $oForm */
|
||||
protected $oForm;
|
||||
protected $bMandatory;
|
||||
protected $bReadOnly;
|
||||
@@ -707,8 +708,11 @@ class DesignerFormField
|
||||
{
|
||||
return $this->sCode;
|
||||
}
|
||||
|
||||
public function SetForm($oForm)
|
||||
|
||||
/**
|
||||
* @param \DesignerForm $oForm
|
||||
*/
|
||||
public function SetForm(\DesignerForm $oForm)
|
||||
{
|
||||
$this->oForm = $oForm;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -50,6 +50,9 @@ class LoginWebPage extends NiceWebPage
|
||||
self::$sHandlerClass = $sClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \LoginWebPage
|
||||
*/
|
||||
public static function NewLoginWebPage()
|
||||
{
|
||||
return new self::$sHandlerClass;
|
||||
@@ -704,7 +707,6 @@ EOF
|
||||
{
|
||||
// No rights to be here, redirect to the portal
|
||||
header('Location: '.$ret);
|
||||
die();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -844,8 +846,8 @@ EOF
|
||||
{
|
||||
$sAuthUser = $_SESSION['auth_user'];
|
||||
UserRights::Login($sAuthUser); // Set the user's language
|
||||
$sOldPwd = utils::ReadPostedParam('old_pwd', '', 'raw_data');
|
||||
$sNewPwd = utils::ReadPostedParam('new_pwd', '', 'raw_data');
|
||||
$sOldPwd = utils::ReadPostedParam('old_pwd', '', false, 'raw_data');
|
||||
$sNewPwd = utils::ReadPostedParam('new_pwd', '', false, 'raw_data');
|
||||
if (UserRights::CanChangePassword() && ((!UserRights::CheckCredentials($sAuthUser, $sOldPwd)) || (!UserRights::ChangePassword($sOldPwd, $sNewPwd))))
|
||||
{
|
||||
$oPage = self::NewLoginWebPage();
|
||||
|
||||
@@ -84,14 +84,11 @@ class ApplicationMenu
|
||||
{
|
||||
// Build menus from module handlers
|
||||
//
|
||||
foreach(get_declared_classes() as $sPHPClass)
|
||||
{
|
||||
if (is_subclass_of($sPHPClass, 'ModuleHandlerAPI'))
|
||||
{
|
||||
$aCallSpec = array($sPHPClass, 'OnMenuCreation');
|
||||
call_user_func($aCallSpec);
|
||||
}
|
||||
}
|
||||
/** @var \ModuleHandlerApiInterface $oPHPClass */
|
||||
foreach(MetaModel::EnumPlugins('ModuleHandlerApiInterface') as $oPHPClass)
|
||||
{
|
||||
$oPHPClass::OnMenuCreation();
|
||||
}
|
||||
|
||||
// Build menus from the menus themselves (e.g. the ShortcutContainerMenuNode will do that)
|
||||
//
|
||||
@@ -125,9 +122,11 @@ class ApplicationMenu
|
||||
}
|
||||
|
||||
/**
|
||||
* Check wether a menu Id is enabled or not
|
||||
* Check whether a menu Id is enabled or not
|
||||
*
|
||||
* @param $sMenuId
|
||||
* @throws DictExceptionMissingString
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
static public function CheckMenuIdEnabled($sMenuId)
|
||||
{
|
||||
@@ -167,7 +166,9 @@ class ApplicationMenu
|
||||
}
|
||||
else
|
||||
{
|
||||
$sParentId = self::$aMenusIndex[$iParentIndex]['node']->GetMenuId();
|
||||
/** @var \MenuNode $oNode */
|
||||
$oNode = self::$aMenusIndex[$iParentIndex]['node'];
|
||||
$sParentId = $oNode->GetMenuId();
|
||||
self::$aMenusIndex[$iParentIndex]['children'][] = array ('rank' => $fRank, 'index' => $index);
|
||||
}
|
||||
|
||||
@@ -181,7 +182,9 @@ class ApplicationMenu
|
||||
else
|
||||
{
|
||||
// the menu already exists, let's combine the conditions that make it visible
|
||||
self::$aMenusIndex[$index]['node']->AddCondition($oMenuNode);
|
||||
/** @var \MenuNode $oNode */
|
||||
$oNode = self::$aMenusIndex[$index]['node'];
|
||||
$oNode->AddCondition($oMenuNode);
|
||||
}
|
||||
|
||||
return $index;
|
||||
@@ -198,7 +201,7 @@ class ApplicationMenu
|
||||
|
||||
/**
|
||||
* Entry point to display the whole menu into the web page, used by iTopWebPage
|
||||
* @param $oPage
|
||||
* @param \iTopWebPage $oPage
|
||||
* @param $aExtraParams
|
||||
* @throws DictExceptionMissingString
|
||||
*/
|
||||
@@ -208,6 +211,7 @@ class ApplicationMenu
|
||||
// Sort the root menu based on the rank
|
||||
usort(self::$aRootMenus, array('ApplicationMenu', 'CompareOnRank'));
|
||||
$iAccordion = 0;
|
||||
$iActiveAccordion = $iAccordion;
|
||||
$iActiveMenu = self::GetMenuIndexById(self::GetActiveNodeId());
|
||||
foreach(self::$aRootMenus as $aMenu)
|
||||
{
|
||||
@@ -221,16 +225,18 @@ class ApplicationMenu
|
||||
$oPage->AddToMenu('</ul>');
|
||||
if ($bActive)
|
||||
{
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
// Accordion Menu
|
||||
$("#accordion").css({display:'block'}).accordion({ header: "h3", navigation: true, heightStyle: "content", collapsible: true, active: $iAccordion, icons: false, animate:true }); // collapsible will be enabled once the item will be selected
|
||||
EOF
|
||||
);
|
||||
$iActiveAccordion = $iAccordion;
|
||||
}
|
||||
$oPage->AddToMenu('</div>');
|
||||
$iAccordion++;
|
||||
}
|
||||
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
// Accordion Menu
|
||||
$("#accordion").css({display:'block'}).accordion({ header: "h3", heightStyle: "content", collapsible: true, active: $iActiveAccordion, icons: false, animate: true }); // collapsible will be enabled once the item will be selected
|
||||
EOF
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -264,7 +270,7 @@ EOF
|
||||
|
||||
/**
|
||||
* Handles the display of the sub-menus (called recursively if necessary)
|
||||
* @param WebPage $oPage
|
||||
* @param \iTopWebPage $oPage
|
||||
* @param array $aMenus
|
||||
* @param array $aExtraParams
|
||||
* @param int $iActiveMenu
|
||||
@@ -356,6 +362,7 @@ EOF
|
||||
static public function GetMenuIndexById($sTitle)
|
||||
{
|
||||
$index = -1;
|
||||
/** @var MenuNode[] $aMenu */
|
||||
foreach(self::$aMenusIndex as $aMenu)
|
||||
{
|
||||
if ($aMenu['node']->GetMenuId() == $sTitle)
|
||||
@@ -837,7 +844,7 @@ class OQLMenuNode extends MenuNode
|
||||
|
||||
/**
|
||||
* Set some extra parameters to be passed to the display block to fine tune its appearence
|
||||
* @param Hash $aParams paramCode => value. See DisplayBlock::GetDisplay for the meaning of the parameters
|
||||
* @param array $aParams paramCode => value. See DisplayBlock::GetDisplay for the meaning of the parameters
|
||||
*/
|
||||
public function SetParameters($aParams)
|
||||
{
|
||||
@@ -950,7 +957,7 @@ class SearchMenuNode extends MenuNode
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param \iTopWebPage $oPage
|
||||
* @param array $aExtraParams
|
||||
* @return mixed|void
|
||||
* @throws DictExceptionMissingString
|
||||
@@ -1054,7 +1061,9 @@ class NewObjectMenuNode extends MenuNode
|
||||
|
||||
/**
|
||||
* @param string[] $aExtraParams
|
||||
*
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function GetHyperlink($aExtraParams)
|
||||
{
|
||||
@@ -1143,37 +1152,11 @@ class DashboardMenuNode extends MenuNode
|
||||
*/
|
||||
public function GetDashboard()
|
||||
{
|
||||
$sDashboardDefinition = @file_get_contents($this->sDashboardFile);
|
||||
if ($sDashboardDefinition !== false)
|
||||
{
|
||||
$bCustomized = false;
|
||||
|
||||
// Search for an eventual user defined dashboard, overloading the existing one
|
||||
$oUDSearch = new DBObjectSearch('UserDashboard');
|
||||
$oUDSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
|
||||
$oUDSearch->AddCondition('menu_code', $this->sMenuId, '=');
|
||||
$oUDSet = new DBObjectSet($oUDSearch);
|
||||
if ($oUDSet->Count() > 0)
|
||||
{
|
||||
// Assuming there is at most one couple {user, menu}!
|
||||
$oUserDashboard = $oUDSet->Fetch();
|
||||
$sDashboardDefinition = $oUserDashboard->Get('contents');
|
||||
$bCustomized = true;
|
||||
|
||||
}
|
||||
$oDashboard = new RuntimeDashboard($this->sMenuId);
|
||||
$oDashboard->FromXml($sDashboardDefinition);
|
||||
$oDashboard->SetCustomFlag($bCustomized);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oDashboard = null;
|
||||
}
|
||||
return $oDashboard;
|
||||
return RuntimeDashboard::GetDashboard($this->sDashboardFile, $this->sMenuId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param \iTopWebPage $oPage
|
||||
* @param string[] $aExtraParams
|
||||
* @throws CoreException
|
||||
* @throws Exception
|
||||
@@ -1186,38 +1169,9 @@ class DashboardMenuNode extends MenuNode
|
||||
{
|
||||
$sDivId = preg_replace('/[^a-zA-Z0-9_]/', '', $this->sMenuId);
|
||||
$oPage->add('<div class="dashboard_contents" id="'.$sDivId.'">');
|
||||
$oDashboard->SetReloadURL($this->GetHyperlink($aExtraParams));
|
||||
$oDashboard->Render($oPage, false, $aExtraParams);
|
||||
$oPage->add('</div>');
|
||||
$oDashboard->RenderEditionTools($oPage);
|
||||
|
||||
if ($oDashboard->GetAutoReload())
|
||||
{
|
||||
$sId = $this->sMenuId;
|
||||
$sExtraParams = json_encode($aExtraParams);
|
||||
$iReloadInterval = 1000 * $oDashboard->GetAutoReloadInterval();
|
||||
$oPage->add_script(
|
||||
<<<EOF
|
||||
setInterval("ReloadDashboard('$sDivId');", $iReloadInterval);
|
||||
|
||||
function ReloadDashboard(sDivId)
|
||||
{
|
||||
var oExtraParams = $sExtraParams;
|
||||
// Do not reload when a dialog box is active
|
||||
if (!($('.ui-dialog:visible').length > 0))
|
||||
{
|
||||
$('.dashboard_contents#'+sDivId).block();
|
||||
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php',
|
||||
{ operation: 'reload_dashboard', dashboard_id: '$sId', extra_params: oExtraParams},
|
||||
function(data){
|
||||
$('.dashboard_contents#'+sDivId).html(data);
|
||||
$('.dashboard_contents#'+sDivId).unblock();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
EOF
|
||||
);
|
||||
}
|
||||
|
||||
$bEdit = utils::ReadParam('edit', false);
|
||||
if ($bEdit)
|
||||
|
||||
159
application/newsroomprovider.class.inc.php
Normal file
159
application/newsroomprovider.class.inc.php
Normal file
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2015 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/>
|
||||
|
||||
/**
|
||||
* A provider for messages to be displayed in the iTop Newsroom
|
||||
*/
|
||||
interface iNewsroomProvider
|
||||
{
|
||||
/**
|
||||
* Inject the current configuration in the provider
|
||||
* @param Config $oConfig
|
||||
* @return void
|
||||
*/
|
||||
public function SetConfig(Config $oConfig);
|
||||
|
||||
/**
|
||||
* Tells if this provider is enabled for the given user
|
||||
* @param User $oUser The user for who to check if the provider is applicable.
|
||||
* return bool
|
||||
*/
|
||||
public function IsApplicable(User $oUser = null);
|
||||
|
||||
/**
|
||||
* The human readable (localized) label for this provider
|
||||
* @return string
|
||||
*/
|
||||
public function GetLabel();
|
||||
|
||||
/**
|
||||
* The URL to query (from the browser, using jsonp) to fetch all unread messages
|
||||
* @return string
|
||||
*/
|
||||
public function GetFetchURL();
|
||||
|
||||
/**
|
||||
* The URL to navigate to in order to display all messages
|
||||
* @return string
|
||||
*/
|
||||
public function GetViewAllURL();
|
||||
|
||||
/**
|
||||
* The URL to query(from the browser, using jsonp) to mark all unread messages as read
|
||||
* @return string
|
||||
*/
|
||||
public function GetMarkAllAsReadURL();
|
||||
|
||||
/**
|
||||
* Return the URL to configure the preferences for this provider or null is there is nothing to configure
|
||||
* @return string|null
|
||||
*/
|
||||
public function GetPreferencesUrl();
|
||||
|
||||
/**
|
||||
* Return an array key => value to be replaced in URL of the messages
|
||||
* Example: '%itop_root%' => utils::GetAbsoluteUrlAppRoot();
|
||||
* @return string[]
|
||||
*/
|
||||
public function GetPlaceholders();
|
||||
|
||||
/**
|
||||
* The duration between to refreshes of the cache (in seconds)
|
||||
* @return int
|
||||
*/
|
||||
public function GetTTL();
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic implementation of a Newsroom provider, to be overloaded by your own provider implementation
|
||||
*
|
||||
*/
|
||||
abstract class NewsroomProviderBase implements iNewsroomProvider
|
||||
{
|
||||
/**
|
||||
* The current configuration parameters
|
||||
* @var Config
|
||||
*/
|
||||
protected $oConfig;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->oConfig = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see iNewsroomProvider::SetConfig()
|
||||
*/
|
||||
public function SetConfig(Config $oConfig)
|
||||
{
|
||||
$this->oConfig = $oConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see iNewsroomProvider::GetPreferencesUrl()
|
||||
*/
|
||||
public function GetPreferencesUrl()
|
||||
{
|
||||
return null; // No preferences
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see iNewsroomProvider::GetLabel()
|
||||
*/
|
||||
public abstract function GetLabel();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see iNewsroomProvider::GetFetchURL()
|
||||
*/
|
||||
public abstract function GetFetchURL();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see iNewsroomProvider::GetMarkAllURL()
|
||||
*/
|
||||
public abstract function GetMarkAllAsReadURL();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see iNewsroomProvider::GetViewAllURL()
|
||||
*/
|
||||
public abstract function GetViewAllURL();
|
||||
|
||||
public function IsApplicable(User $oUser = null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see iNewsroomProvider::GetPlaceholders()
|
||||
*/
|
||||
public function GetPlaceholders()
|
||||
{
|
||||
return array(); // By default, empty set of placeholders
|
||||
}
|
||||
|
||||
public function GetTTL()
|
||||
{
|
||||
return 10*60; // Refresh every 10 minutes
|
||||
}
|
||||
}
|
||||
@@ -37,8 +37,15 @@ class NiceWebPage extends WebPage
|
||||
{
|
||||
parent::__construct($s_title, $bPrintable);
|
||||
$this->m_aReadyScripts = array();
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-1.12.4.min.js');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-migrate-1.4.1.min.js'); // Needed since many other plugins still rely on oldies like $.browser
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-3.3.1.min.js');
|
||||
if(utils::IsDevelopmentEnvironment()) // Needed since many other plugins still rely on oldies like $.browser
|
||||
{
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-migrate-3.0.1.dev.js');
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-migrate-3.0.1.prod.min.js');
|
||||
}
|
||||
$this->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/ui-lightness/jquery-ui-1.11.4.custom.css');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-ui-1.11.4.custom.min.js');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/utils.js');
|
||||
@@ -47,6 +54,7 @@ class NiceWebPage extends WebPage
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.tablesorter.js');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.tablesorter.pager.js');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.tablehover.js');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/table-selectable-lines.js');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/field_sorter.js');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/datatable.js');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.positionBy.js');
|
||||
@@ -61,6 +69,7 @@ class NiceWebPage extends WebPage
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_external_field.js');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_numeric.js');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_enum.js');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_tag_set.js');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_external_key.js');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_hierarchical_key.js');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_date_abstract.js');
|
||||
|
||||
@@ -35,7 +35,7 @@ class iTopPDF extends TCPDF
|
||||
|
||||
// Display the page number (right aligned)
|
||||
// Warning: the 'R'ight alignment does not work when using placeholders like $this->getAliasNumPage() or $this->getAliasNbPages()
|
||||
$this->MultiCell($iPageNumberWidth, 15, 'Page '.$this->page, 0, 'R', false, 0 /* $ln */, '', '', true, 0, false, true, 15, 'M' /* $valign */);
|
||||
$this->MultiCell($iPageNumberWidth, 15, Dict::Format('Core:BulkExport:PDF:PageNumber' ,$this->page), 0, 'R', false, 0 /* $ln */, '', '', true, 0, false, true, 15, 'M' /* $valign */);
|
||||
|
||||
// Branding logo
|
||||
$sBrandingIcon = APPROOT.'images/itop-logo.png';
|
||||
|
||||
@@ -807,7 +807,7 @@ EOF
|
||||
*/
|
||||
public function DoUpdateObjectFromPostedForm(DBObject $oObj, $aAttList = null)
|
||||
{
|
||||
$sTransactionId = utils::ReadPostedParam('transaction_id', '');
|
||||
$sTransactionId = utils::ReadPostedParam('transaction_id', '', 'transaction_id');
|
||||
if (!utils::IsTransactionValid($sTransactionId))
|
||||
{
|
||||
throw new TransactionException();
|
||||
|
||||
@@ -47,13 +47,12 @@ abstract class Query extends cmdbAbstractObject
|
||||
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 AttributeText("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeText("fields", array("allowed_values"=>null, "sql"=>"fields", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('name', 'description', 'fields')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('details', array('name', 'description')); // 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', 'fields')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('standard_search', array('name', 'description')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('default_search', array('name', 'description')); // Criteria of the default search form
|
||||
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
|
||||
}
|
||||
@@ -78,6 +77,9 @@ class QueryOQL extends Query
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeOQL("oql", array("allowed_values"=>null, "sql"=>"oql", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeText("fields", array("allowed_values"=>null, "sql"=>"fields", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
// Rolled back to AttributeText until AttributeQueryAttCodeSet can manage fields order correctly
|
||||
//MetaModel::Init_AddAttribute(new AttributeQueryAttCodeSet("fields", array("allowed_values"=>null,"max_items" => 1000, "query_field" => "oql", "sql"=>"fields", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array('oql'))));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('name', 'description', 'oql', 'fields')); // Attributes to be displayed for the complete details
|
||||
@@ -136,6 +138,41 @@ class QueryOQL extends Query
|
||||
}
|
||||
return $aFieldsMap;
|
||||
}
|
||||
|
||||
// Rolled back until 'fields' can be properly managed by AttributeQueryAttCodeSet
|
||||
//
|
||||
// public function ComputeValues()
|
||||
// {
|
||||
// parent::ComputeValues();
|
||||
//
|
||||
// // Remove unwanted attribute codes
|
||||
// $aChanges = $this->ListChanges();
|
||||
// if (isset($aChanges['fields']))
|
||||
// {
|
||||
// $oAttDef = MetaModel::GetAttributeDef(get_class($this), 'fields');
|
||||
// $aArgs = array('this' => $this);
|
||||
// $aAllowedValues = $oAttDef->GetAllowedValues($aArgs);
|
||||
//
|
||||
// /** @var \ormSet $oValue */
|
||||
// $oValue = $this->Get('fields');
|
||||
// $aValues = $oValue->GetValues();
|
||||
// $bChanged = false;
|
||||
// foreach($aValues as $key => $sValue)
|
||||
// {
|
||||
// if (!isset($aAllowedValues[$sValue]))
|
||||
// {
|
||||
// unset($aValues[$key]);
|
||||
// $bChanged = true;
|
||||
// }
|
||||
// }
|
||||
// if ($bChanged)
|
||||
// {
|
||||
// $oValue->SetValues($aValues);
|
||||
// $this->Set('fields', $oValue);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
@@ -24,6 +24,26 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
// This storage is freed on error (case of allowed memory exhausted)
|
||||
$sReservedMemory = str_repeat('*', 1024 * 1024);
|
||||
register_shutdown_function(function()
|
||||
{
|
||||
global $sReservedMemory;
|
||||
$sReservedMemory = null;
|
||||
if (!is_null($err = error_get_last()) && ($err['type'] == E_ERROR))
|
||||
{
|
||||
if (strpos($err['message'], 'Allowed memory size of') !== false)
|
||||
{
|
||||
$sLimit = ini_get('memory_limit');
|
||||
echo "<p>iTop: Allowed memory size of $sLimit exhausted, contact your administrator to increase memory_limit in php.ini</p>\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "<p>iTop: An error occurred, check server error log for more information.</p>\n";
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
require_once(APPROOT.'/core/cmdbobject.class.inc.php');
|
||||
require_once(APPROOT.'/application/utils.inc.php');
|
||||
require_once(APPROOT.'/core/contexttag.class.inc.php');
|
||||
|
||||
@@ -119,7 +119,7 @@ class privUITransactionSession
|
||||
// Strictly speaking, the two lines below should be grouped together
|
||||
// by a critical section
|
||||
// sem_acquire($rSemIdentified);
|
||||
$id = str_replace(array('.', ' '), '', microtime()); //1 + count($_SESSION['transactions']);
|
||||
$id = static::GetUserPrefix() . str_replace(array('.', ' '), '', microtime()); //1 + count($_SESSION['transactions']);
|
||||
$_SESSION['transactions'][$id] = true;
|
||||
// sem_release($rSemIdentified);
|
||||
|
||||
@@ -174,6 +174,17 @@ class privUITransactionSession
|
||||
// sem_release($rSemIdentified);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string to prefix transaction ID with info from the current user.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function GetUserPrefix()
|
||||
{
|
||||
$sPrefix = 'u'.UserRights::GetUserId();
|
||||
return $sPrefix.'-';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -206,7 +217,7 @@ class privUITransactionFile
|
||||
throw new Exception('The directory "'.APPROOT.'data/transactions" must be writable to the application.');
|
||||
}
|
||||
self::CleanupOldTransactions();
|
||||
$id = basename(tempnam(APPROOT.'data/transactions', self::GetUserPrefix()));
|
||||
$id = basename(tempnam(APPROOT.'data/transactions', static::GetUserPrefix()));
|
||||
self::Info('GetNewTransactionId: Created transaction: '.$id);
|
||||
|
||||
return (string)$id;
|
||||
@@ -310,6 +321,11 @@ class privUITransactionFile
|
||||
return $aResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a prefix based on the user login instead of its ID for a better usage in tempnam()
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected static function GetUserPrefix()
|
||||
{
|
||||
$sPrefix = substr(UserRights::GetUser(), 0, 10);
|
||||
|
||||
@@ -332,7 +332,8 @@ EOF
|
||||
if ( ($oCurrObject != null) && ($this->sAttCode != ''))
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($oCurrObject), $this->sAttCode);
|
||||
$aArgs = array('this' => $oCurrObject);
|
||||
/** @var \DBObject $oCurrObject */
|
||||
$aArgs = $oCurrObject->ToArgsForQuery();
|
||||
$aParams = array('query_params' => $aArgs);
|
||||
$oSet = $oAttDef->GetAllowedValuesAsObjectSet($aArgs);
|
||||
$oFilter = $oSet->GetFilter();
|
||||
@@ -402,7 +403,7 @@ EOF
|
||||
// Current extkey value, so we can display event if it is not available anymore (eg. archived).
|
||||
$iCurrentExtKeyId = (is_null($oObj)) ? 0 : $oObj->Get($this->sAttCode);
|
||||
|
||||
$oBlock = new DisplayBlock($oFilter, 'list', false, array('query_params' => array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId)));
|
||||
$oBlock = new DisplayBlock($oFilter, 'list_search', false, array('query_params' => array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId)));
|
||||
$oBlock->Display($oP, $this->iId.'_results', array('this' => $oObj, 'cssCount'=> '#count_'.$this->iId, 'menu' => false, 'selection_mode' => true, 'selection_type' => 'single', 'table_id' => 'select_'.$this->sAttCode)); // Don't display the 'Actions' menu on the results
|
||||
}
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ class UIHTMLEditorWidget
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
$('#$iId').bind('update', function(evt){
|
||||
BlockField('cke_$iId', $('#$iId').attr('disabled'));
|
||||
BlockField('cke_$iId', $('#$iId').prop('disabled'));
|
||||
//Delayed execution - ckeditor must be properly initialized before setting readonly
|
||||
var retryCount = 0;
|
||||
var oMe = $('#$iId');
|
||||
|
||||
@@ -133,15 +133,14 @@ class UILinksWidget
|
||||
$sPrefix = "$this->m_sAttCode{$this->m_sNameSuffix}";
|
||||
$aRow = array();
|
||||
$aFieldsMap = array();
|
||||
$iKey = 0;
|
||||
if(is_object($linkObjOrId) && (!$linkObjOrId->IsNew()))
|
||||
{
|
||||
$iKey = $linkObjOrId->GetKey();
|
||||
$key = $linkObjOrId->GetKey();
|
||||
$iRemoteObjKey = $linkObjOrId->Get($this->m_sExtKeyToRemote);
|
||||
$sPrefix .= "[$iKey][";
|
||||
$sPrefix .= "[$key][";
|
||||
$sNameSuffix = "]"; // To make a tabular form
|
||||
$aArgs['prefix'] = $sPrefix;
|
||||
$aArgs['wizHelper'] = "oWizardHelper{$this->m_iInputId}{$iKey}";
|
||||
$aArgs['wizHelper'] = "oWizardHelper{$this->m_iInputId}{$key}";
|
||||
$aArgs['this'] = $linkObjOrId;
|
||||
|
||||
if($bReadOnly)
|
||||
@@ -155,7 +154,7 @@ class UILinksWidget
|
||||
}
|
||||
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\">";
|
||||
$aRow['form::checkbox'] = "<input class=\"selection\" data-remote-id=\"$iRemoteObjKey\" data-link-id=\"$key\" data-unique-id=\"$iUniqueId\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"$key\">";
|
||||
foreach($this->m_aEditableFields as $sFieldCode)
|
||||
{
|
||||
$sFieldId = $this->m_iInputId.'_'.$sFieldCode.'['.$linkObjOrId->GetKey().']';
|
||||
@@ -196,30 +195,7 @@ class UILinksWidget
|
||||
$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
|
||||
);
|
||||
}
|
||||
|
||||
$aRow['form::checkbox'] = "<input class=\"selection\" data-remote-id=\"$iRemoteObjKey\" data-link-id=\"\" data-unique-id=\"$iUniqueId\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"$sInputValue\">";
|
||||
foreach($this->m_aEditableFields as $sFieldCode)
|
||||
{
|
||||
$sFieldId = $this->m_iInputId.'_'.$sFieldCode.'['.-$iUniqueId.']';
|
||||
@@ -231,12 +207,20 @@ EOF
|
||||
cmdbAbstractObject::GetFormElementForField($oP, $this->m_sLinkedClass, $sFieldCode, $oAttDef, $sValue, $sDisplayValue, $sSafeId /* id */, $sNameSuffix, 0, $aArgs).
|
||||
'</div></div></div>';
|
||||
$aFieldsMap[$sFieldCode] = $sSafeId;
|
||||
$oP->add_ready_script(<<<EOF
|
||||
oWidget{$this->m_iInputId}.OnValueChange($iKey, $iUniqueId, '$sFieldCode', '$sValue');
|
||||
EOF
|
||||
);
|
||||
}
|
||||
$sState = '';
|
||||
|
||||
// Rows created with ajax call need OnLinkAdded call.
|
||||
// Rows added before loading the form cannot call OnLinkAdded.
|
||||
if ($iUniqueId > 0)
|
||||
{
|
||||
$oP->add_ready_script(
|
||||
<<<EOF
|
||||
PrepareWidgets();
|
||||
oWidget{$this->m_iInputId}.OnLinkAdded($iUniqueId, $iRemoteObjKey);
|
||||
EOF
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if(!$bReadOnly)
|
||||
@@ -353,19 +337,8 @@ EOF
|
||||
$sHtmlValue .= "<input type=\"hidden\" id=\"{$sFormPrefix}{$this->m_iInputId}\">\n";
|
||||
$oValue->Rewind();
|
||||
$aForm = array();
|
||||
$iAddedId = -1; // Unique id for new links
|
||||
|
||||
$sDuplicates = ($this->m_bDuplicatesAllowed) ? 'true' : 'false';
|
||||
// Don't automatically launch the search if the table is huge
|
||||
$bDoSearch = !utils::IsHighCardinality($this->m_sRemoteClass);
|
||||
$sJSDoSearch = $bDoSearch ? 'true' : 'false';
|
||||
$sWizHelper = 'oWizardHelper'.$sFormPrefix;
|
||||
$oPage->add_ready_script(<<<EOF
|
||||
oWidget{$this->m_iInputId} = new LinksWidget('{$this->m_sAttCode}{$this->m_sNameSuffix}', '{$this->m_sClass}', '{$this->m_sAttCode}', '{$this->m_iInputId}', '{$this->m_sNameSuffix}', $sDuplicates, $sWizHelper, '{$this->m_sExtKeyToRemote}', $sJSDoSearch);
|
||||
oWidget{$this->m_iInputId}.Init();
|
||||
EOF
|
||||
);
|
||||
|
||||
$iAddedId = 1; // Unique id for new links
|
||||
$aAddedLinks = array();
|
||||
while($oCurrentLink = $oValue->Fetch())
|
||||
{
|
||||
// We try to retrieve the remote object as usual
|
||||
@@ -384,7 +357,9 @@ EOF
|
||||
|
||||
if ($oCurrentLink->IsNew())
|
||||
{
|
||||
$key = $iAddedId--;
|
||||
$key = -($iAddedId++);
|
||||
$iUniqueId = -$key;
|
||||
$aAddedLinks[] = array('iAddedId' => $iUniqueId, 'iRemote' => $oCurrentLink->Get($this->m_sExtKeyToRemote));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -393,6 +368,24 @@ EOF
|
||||
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs, $oCurrentObj, $key, $bReadOnly);
|
||||
}
|
||||
$sHtmlValue .= $this->DisplayFormTable($oPage, $this->m_aTableConfig, $aForm);
|
||||
$sDuplicates = ($this->m_bDuplicatesAllowed) ? 'true' : 'false';
|
||||
// Don't automatically launch the search if the table is huge
|
||||
$bDoSearch = !utils::IsHighCardinality($this->m_sRemoteClass);
|
||||
$sJSDoSearch = $bDoSearch ? 'true' : 'false';
|
||||
$sWizHelper = 'oWizardHelper'.$sFormPrefix;
|
||||
$oPage->add_ready_script(<<<EOF
|
||||
oWidget{$this->m_iInputId} = new LinksWidget('{$this->m_sAttCode}{$this->m_sNameSuffix}', '{$this->m_sClass}', '{$this->m_sAttCode}', '{$this->m_iInputId}', '{$this->m_sNameSuffix}', $sDuplicates, $sWizHelper, '{$this->m_sExtKeyToRemote}', $sJSDoSearch);
|
||||
oWidget{$this->m_iInputId}.Init();
|
||||
EOF
|
||||
);
|
||||
|
||||
foreach ($aAddedLinks as $aAddedLink)
|
||||
{
|
||||
$oPage->add_ready_script(<<<EOF
|
||||
oWidget{$this->m_iInputId}.AddLink({$aAddedLink['iAddedId']}, {$aAddedLink['iRemote']});
|
||||
EOF
|
||||
);
|
||||
}
|
||||
|
||||
$sHtmlValue .= "<span style=\"float:left;\"> <img src=\"../images/tv-item-last.gif\"> <input id=\"{$this->m_sAttCode}{$this->m_sNameSuffix}_btnRemove\" type=\"button\" value=\"".Dict::S('UI:RemoveLinkedObjectsOf_Class')."\" onClick=\"oWidget{$this->m_iInputId}.RemoveSelected();\" >";
|
||||
$sHtmlValue .= " <input id=\"{$this->m_sAttCode}{$this->m_sNameSuffix}_btnAdd\" type=\"button\" value=\"".Dict::Format('UI:AddLinkedObjectsOf_Class', MetaModel::GetName($this->m_sRemoteClass))."\" onClick=\"oWidget{$this->m_iInputId}.AddObjects();\"><span id=\"{$this->m_sAttCode}{$this->m_sNameSuffix}_indicatorAdd\"></span></span>\n";
|
||||
|
||||
@@ -73,17 +73,17 @@ class UIPasswordWidget
|
||||
$oPage->add_ready_script("$('#{$this->iId}_confirm').bind('keyup change', function(evt, sFormId) { return ValidatePasswordField('$this->iId', sFormId) } );"); // Bind to a custom event: validate
|
||||
$oPage->add_ready_script("$('#{$this->iId}').bind('update', function(evt, sFormId)
|
||||
{
|
||||
if ($(this).attr('disabled'))
|
||||
if ($(this).prop('disabled'))
|
||||
{
|
||||
$('#{$this->iId}_confirm').attr('disabled', 'disabled');
|
||||
$('#{$this->iId}_changed').attr('disabled', 'disabled');
|
||||
$('#{$this->iId}_reset').attr('disabled', 'disabled');
|
||||
$('#{$this->iId}_confirm').prop('disabled', true);
|
||||
$('#{$this->iId}_changed').prop('disabled', true);
|
||||
$('#{$this->iId}_reset').prop('disabled', true);
|
||||
}
|
||||
else
|
||||
{
|
||||
$('#{$this->iId}_confirm').removeAttr('disabled');
|
||||
$('#{$this->iId}_changed').removeAttr('disabled');
|
||||
$('#{$this->iId}_reset').removeAttr('disabled');
|
||||
$('#{$this->iId}_confirm').prop('disabled', false);
|
||||
$('#{$this->iId}_changed').prop('disabled', false);
|
||||
$('#{$this->iId}_reset').prop('disabled', false);
|
||||
}
|
||||
}
|
||||
);"); // Bind to a custom event: update to handle enabling/disabling
|
||||
|
||||
@@ -62,11 +62,11 @@ class utils
|
||||
{
|
||||
if (!file_exists($sParamFile))
|
||||
{
|
||||
throw new Exception("Could not find the parameter file: '".utils::HtmlEntities($sParamFile)."'");
|
||||
throw new Exception("Could not find the parameter file: '$sParamFile'");
|
||||
}
|
||||
if (!is_readable($sParamFile))
|
||||
{
|
||||
throw new Exception("Could not load parameter file: '".utils::HtmlEntities($sParamFile)."'");
|
||||
throw new Exception("Could not load parameter file: '$sParamFile'");
|
||||
}
|
||||
$sParams = file_get_contents($sParamFile);
|
||||
|
||||
@@ -273,18 +273,7 @@ class utils
|
||||
}
|
||||
return $retValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|string[] $value
|
||||
* @param string $sSanitizationFilter one of : integer, class, string, context_param, parameter, field_name,
|
||||
* transaction_id, parameter, raw_data
|
||||
*
|
||||
* @return string|string[]|bool boolean for :
|
||||
* * the 'class' filter (true if valid, false otherwise)
|
||||
* * if the filter fails (@see \filter_var())
|
||||
*
|
||||
* @since 2.5.2 2.6.0 new 'transaction_id' filter
|
||||
*/
|
||||
|
||||
protected static function Sanitize_Internal($value, $sSanitizationFilter)
|
||||
{
|
||||
switch($sSanitizationFilter)
|
||||
@@ -331,12 +320,14 @@ class utils
|
||||
// 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;
|
||||
break;
|
||||
|
||||
case 'parameter':
|
||||
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP, array("options"=>array("regexp"=>'/^([ A-Za-z0-9_=-]|%3D|%2B|%2F)*$/'))); // 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;
|
||||
|
||||
$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 'field_name':
|
||||
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP, array("options"=>array("regexp"=>'/^[A-Za-z0-9_]+(->[A-Za-z0-9_]+)*$/'))); // att_code or att_code->name or AttCode->Name or AttCode->Key2->Name
|
||||
break;
|
||||
@@ -536,7 +527,7 @@ class utils
|
||||
/**
|
||||
* Returns a unique tmp id for the current upload based on the transaction system (db).
|
||||
*
|
||||
* Build as session_id() . '_' . static::GetNewTransactionId()
|
||||
* Build as static::GetNewTransactionId()
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@@ -546,7 +537,7 @@ class utils
|
||||
{
|
||||
$sTransactionId = static::GetNewTransactionId();
|
||||
}
|
||||
return session_id() . '_' . $sTransactionId;
|
||||
return $sTransactionId;
|
||||
}
|
||||
|
||||
public static function ReadFromFile($sFileName)
|
||||
@@ -581,6 +572,18 @@ class utils
|
||||
}
|
||||
return $iReturn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the memory limit is at least what is required
|
||||
*
|
||||
* @param int $memoryLimit set limit in bytes
|
||||
* @param int $requiredLimit required limit in bytes
|
||||
* @return bool
|
||||
*/
|
||||
public static function IsMemoryLimitOk($memoryLimit, $requiredLimit)
|
||||
{
|
||||
return ($memoryLimit >= $requiredLimit) || ($memoryLimit == -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a value into a more friendly format (KB, MB, GB, TB) instead a juste a Bytes amount.
|
||||
@@ -725,7 +728,11 @@ class utils
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string The absolute URL to the application, including host and path
|
||||
* Returns the absolute URL to the application root path
|
||||
*
|
||||
* @return string The absolute URL to the application root, without the first slash
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
static public function GetAbsoluteUrlAppRoot()
|
||||
{
|
||||
@@ -754,7 +761,15 @@ class utils
|
||||
return $sUrl;
|
||||
}
|
||||
|
||||
static public function GetDefaultUrlAppRoot()
|
||||
/**
|
||||
* Builds an root url from the server's variables.
|
||||
* For most usages, when an root url is needed, use utils::GetAbsoluteUrlAppRoot() instead as uses this only as a fallback when the app_root_url conf parameter is not defined.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
static public function GetDefaultUrlAppRoot()
|
||||
{
|
||||
// Build an absolute URL to this page on this server/port
|
||||
$sServerName = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '';
|
||||
@@ -823,7 +838,7 @@ class utils
|
||||
|
||||
/**
|
||||
* Helper to handle the variety of HTTP servers
|
||||
* See #286 (fixed in [896]), and #634 (this fix)
|
||||
* See N°286 (fixed in [896]), and N°634 (this fix)
|
||||
*
|
||||
* Though the official specs says 'a non empty string', some servers like IIS do set it to 'off' !
|
||||
* nginx set it to an empty string
|
||||
@@ -1076,12 +1091,7 @@ class utils
|
||||
// $param is a DBObject
|
||||
$oObj = $param;
|
||||
$sOQL = "SELECT ".get_class($oObj)." WHERE id=".$oObj->GetKey();
|
||||
$oFilter = DBObjectSearch::FromOQL($sOQL);
|
||||
$sFilter = $oFilter->serialize();
|
||||
$sUrl = ApplicationContext::MakeObjectUrl(get_class($oObj), $oObj->GetKey());
|
||||
$sUIPage = cmdbAbstractObject::ComputeStandardUIPage(get_class($oObj));
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sContext = $oAppContext->GetForLink();
|
||||
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/tabularfieldsselector.js');
|
||||
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.dragtable.js');
|
||||
$oPage->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/dragtable.css');
|
||||
@@ -1101,21 +1111,29 @@ class utils
|
||||
break;
|
||||
|
||||
case iPopupMenuExtension::MENU_DASHBOARD_ACTIONS:
|
||||
// $param is a Dashboard
|
||||
$oAppContext = new ApplicationContext();
|
||||
$aParams = $oAppContext->GetAsHash();
|
||||
$sMenuId = ApplicationMenu::GetActiveNodeId();
|
||||
$sDlgTitle = addslashes(Dict::S('UI:ImportDashboardTitle'));
|
||||
$sDlgText = addslashes(Dict::S('UI:ImportDashboardText'));
|
||||
$sCloseBtn = addslashes(Dict::S('UI:Button:Cancel'));
|
||||
// $param is a Dashboard
|
||||
/** @var \RuntimeDashboard $oDashboard */
|
||||
$oDashboard = $param;
|
||||
$sDashboardId = $oDashboard->GetId();
|
||||
$sDashboardFile = $oDashboard->GetDefinitionFile();
|
||||
$sDlgTitle = addslashes(Dict::S('UI:ImportDashboardTitle'));
|
||||
$sDlgText = addslashes(Dict::S('UI:ImportDashboardText'));
|
||||
$sCloseBtn = addslashes(Dict::S('UI:Button:Cancel'));
|
||||
$sDashboardFileJS = addslashes($sDashboardFile);
|
||||
$sDashboardFileURL = urlencode($sDashboardFile);
|
||||
$sUploadDashboardTransactId = utils::GetNewTransactionId();
|
||||
$aResult = array(
|
||||
new SeparatorPopupMenuItem(),
|
||||
new URLPopupMenuItem('UI:ExportDashboard', Dict::S('UI:ExportDashBoard'), utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=export_dashboard&id='.$sMenuId),
|
||||
new JSPopupMenuItem('UI:ImportDashboard', Dict::S('UI:ImportDashBoard'),
|
||||
"UploadDashboard({dashboard_id: '$sMenuId', title: '$sDlgTitle', text: '$sDlgText', close_btn: '$sCloseBtn', transaction: '$sUploadDashboardTransactId' })"),
|
||||
);
|
||||
break;
|
||||
$aResult = array(
|
||||
new SeparatorPopupMenuItem(),
|
||||
new URLPopupMenuItem('UI:ExportDashboard', Dict::S('UI:ExportDashBoard'), utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=export_dashboard&id='.$sDashboardId.'&file='.$sDashboardFileURL),
|
||||
new JSPopupMenuItem('UI:ImportDashboard', Dict::S('UI:ImportDashBoard'), "UploadDashboard({dashboard_id: '$sDashboardId', file: '$sDashboardFileJS', title: '$sDlgTitle', text: '$sDlgText', close_btn: '$sCloseBtn', transaction: '$sUploadDashboardTransactId' })"),
|
||||
);
|
||||
if ($oDashboard->GetReloadURL())
|
||||
{
|
||||
$aResult[] = new SeparatorPopupMenuItem();
|
||||
$aResult[] = new URLPopupMenuItem('UI:Menu:PrintableVersion', Dict::S('UI:Menu:PrintableVersion'), $oDashboard->GetReloadURL().'&printable=1', '_blank');
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
// Unknown type of menu, do nothing
|
||||
@@ -1153,7 +1171,7 @@ class utils
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @return string target configuration file name (including full path)
|
||||
* Get target configuration file name (including full path)
|
||||
*/
|
||||
public static function GetConfigFilePath($sEnvironment = null)
|
||||
{
|
||||
@@ -1163,17 +1181,6 @@ class utils
|
||||
}
|
||||
return APPCONF.$sEnvironment.'/'.ITOP_CONFIG_FILE;
|
||||
}
|
||||
/**
|
||||
* @return string target configuration file name (including relative path)
|
||||
*/
|
||||
public static function GetConfigFilePathRelative($sEnvironment = null)
|
||||
{
|
||||
if (is_null($sEnvironment))
|
||||
{
|
||||
$sEnvironment = self::GetCurrentEnvironment();
|
||||
}
|
||||
return "conf/".$sEnvironment.'/'.ITOP_CONFIG_FILE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string the absolute URL to the modules root path
|
||||
@@ -1482,7 +1489,7 @@ class utils
|
||||
static public function GetCSSFromSASS($sSassRelPath, $aImportPaths = null)
|
||||
{
|
||||
// Avoiding compilation if file is already a css file.
|
||||
if (preg_match('/\.css$/', $sSassRelPath))
|
||||
if (preg_match('/\.css(\?.*)?$/', $sSassRelPath))
|
||||
{
|
||||
return $sSassRelPath;
|
||||
}
|
||||
@@ -1990,4 +1997,14 @@ class utils
|
||||
$aHugeClasses = MetaModel::GetConfig()->Get('high_cardinality_classes');
|
||||
return in_array($sClass, $aHugeClasses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if iTop is in a development environment (VCS vs build number)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function IsDevelopmentEnvironment()
|
||||
{
|
||||
return ITOP_REVISION === 'svn';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -311,7 +311,10 @@ class WebPage implements Page
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a script (as an include, i.e. link) to the header of the page
|
||||
* Add a script (as an include, i.e. link) to the header of the page.<br>
|
||||
* Handles duplicates : calling twice with the same script will add the script only once
|
||||
*
|
||||
* @param string $s_linked_script
|
||||
*/
|
||||
public function add_linked_script($s_linked_script)
|
||||
{
|
||||
|
||||
@@ -154,7 +154,7 @@ class WizardHelper
|
||||
}
|
||||
else
|
||||
{
|
||||
// May happen for security reasons (portal, see ticket #1074)
|
||||
// May happen for security reasons (portal, see ticket N°1074)
|
||||
$oObj->Set($sAttCode, $value);
|
||||
}
|
||||
}
|
||||
@@ -174,6 +174,30 @@ class WizardHelper
|
||||
}
|
||||
$oObj->Set($sAttCode, $value);
|
||||
}
|
||||
else if ($oAttDef instanceof AttributeTagSet) // AttributeDate is derived from AttributeDateTime
|
||||
{
|
||||
if (is_null($value))
|
||||
{
|
||||
// happens if field is hidden (see N°1827)
|
||||
$value = array();
|
||||
}
|
||||
else
|
||||
{
|
||||
$value = json_decode($value, true);
|
||||
}
|
||||
$oTagSet = new ormTagSet(get_class($oObj), $sAttCode, $oAttDef->GetMaxItems());
|
||||
$oTagSet->SetValues($value['orig_value']);
|
||||
$oTagSet->ApplyDelta($value);
|
||||
$oObj->Set($sAttCode, $oTagSet);
|
||||
}
|
||||
else if ($oAttDef instanceof AttributeSet) // AttributeDate is derived from AttributeDateTime
|
||||
{
|
||||
$value = json_decode($value, true);
|
||||
$oTagSet = new ormSet(get_class($oObj), $sAttCode, $oAttDef->GetMaxItems());
|
||||
$oTagSet->SetValues($value['orig_value']);
|
||||
$oTagSet->ApplyDelta($value);
|
||||
$oObj->Set($sAttCode, $oTagSet);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oObj->Set($sAttCode, $value);
|
||||
@@ -205,11 +229,10 @@ class WizardHelper
|
||||
// this as to be handled as an array of objects
|
||||
// thus encoded in json like: [ { name:'link1', 'id': 123}, { name:'link2', 'id': 124}...]
|
||||
// NOT YET IMPLEMENTED !!
|
||||
$sLinkedClass = $oAttDef->GetLinkedClass();
|
||||
$oSet = $value;
|
||||
$aData = array();
|
||||
$aFields = $this->GetLinkedWizardStructure($oAttDef);
|
||||
while($oSet->fetch())
|
||||
while($oLinkedObj = $oSet->fetch())
|
||||
{
|
||||
foreach($aFields as $sLinkedAttCode)
|
||||
{
|
||||
|
||||
@@ -40,7 +40,7 @@ Class XLSXWriter
|
||||
|
||||
protected function tempFilename()
|
||||
{
|
||||
$filename = tempnam("/tmp", "xlsx_writer_");
|
||||
$filename = tempnam(SetupUtils::GettmpDir(), 'xlsx_writer_');
|
||||
$this->temp_files[] = $filename;
|
||||
return $filename;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
"ext-json": "*",
|
||||
"ext-zip": "*",
|
||||
"ext-mysqli": "*",
|
||||
"ext-dom": "*",
|
||||
"ext-iconv": "*",
|
||||
"ext-gd": "*"
|
||||
},
|
||||
"config": {
|
||||
|
||||
@@ -223,7 +223,14 @@ class ActionEmail extends ActionNotification
|
||||
return implode(', ', $aRecipients);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param \Trigger $oTrigger
|
||||
* @param array $aContextArgs
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \CoreWarning
|
||||
*/
|
||||
public function DoExecute($oTrigger, $aContextArgs)
|
||||
{
|
||||
if (MetaModel::IsLogEnabledNotification())
|
||||
@@ -292,6 +299,14 @@ class ActionEmail extends ActionNotification
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Trigger $oTrigger
|
||||
* @param array $aContextArgs
|
||||
* @param \EventNotification $oLog
|
||||
*
|
||||
* @return string
|
||||
* @throws \CoreException
|
||||
*/
|
||||
protected function _DoExecute($oTrigger, $aContextArgs, &$oLog)
|
||||
{
|
||||
$sPreviousUrlMaker = ApplicationContext::SetUrlMakerClass();
|
||||
|
||||
@@ -168,7 +168,6 @@ abstract class AsyncTask extends DBObject
|
||||
$aConfig = $aRetries[get_class($this)];
|
||||
$iMaxRetries = $aConfig['max_retries'];
|
||||
}
|
||||
return $iMaxRetries;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -29,6 +29,7 @@ MetaModel::IncludeModule('core/action.class.inc.php');
|
||||
MetaModel::IncludeModule('core/trigger.class.inc.php');
|
||||
MetaModel::IncludeModule('core/bulkexport.class.inc.php');
|
||||
MetaModel::IncludeModule('core/ownershiplock.class.inc.php');
|
||||
MetaModel::IncludeModule('core/tagsetfield.class.inc.php');
|
||||
MetaModel::IncludeModule('synchro/synchrodatasource.class.inc.php');
|
||||
MetaModel::IncludeModule('core/backgroundtask.class.inc.php');
|
||||
MetaModel::IncludeModule('core/inlineimage.class.inc.php');
|
||||
|
||||
@@ -332,18 +332,6 @@ class BulkChange
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DBObject $oTargetObj
|
||||
* @param array $aRowData
|
||||
* @param array $aErrors
|
||||
*
|
||||
* @return array
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
*/
|
||||
protected function PrepareObject(&$oTargetObj, $aRowData, &$aErrors)
|
||||
{
|
||||
$aResults = array();
|
||||
@@ -489,9 +477,7 @@ class BulkChange
|
||||
if (!$oAttDef->IsWritable() && in_array($sAttCode, $this->m_aReconcilKeys)){ continue; }
|
||||
|
||||
$aReasons = array();
|
||||
$iFlags = ($oTargetObj->IsNew())
|
||||
? $oTargetObj->GetInitialStateAttributeFlags($sAttCode, $aReasons)
|
||||
: $oTargetObj->GetAttributeFlags($sAttCode, $aReasons);
|
||||
$iFlags = $oTargetObj->GetAttributeFlags($sAttCode, $aReasons);
|
||||
if ( (($iFlags & OPT_ATT_READONLY) == OPT_ATT_READONLY) && ( $oTargetObj->Get($sAttCode) != $aRowData[$iCol]) )
|
||||
{
|
||||
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-Readonly', $sAttCode, $oTargetObj->Get($sAttCode), $aRowData[$iCol]);
|
||||
|
||||
@@ -242,6 +242,64 @@ class CMDBChangeOpSetAttributeScalar extends CMDBChangeOpSetAttribute
|
||||
return $sResult;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Record the modification of a tag set attribute
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class CMDBChangeOpSetAttributeTagSet extends CMDBChangeOpSetAttribute
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_setatt_tagset",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeText("oldvalue", array("allowed_values"=>null, "sql"=>"oldvalue", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeText("newvalue", array("allowed_values"=>null, "sql"=>"newvalue", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'attcode', 'oldvalue', 'newvalue')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'attcode', 'oldvalue', 'newvalue')); // Attributes to be displayed for a list
|
||||
}
|
||||
|
||||
/**
|
||||
* Describe (as a text string) the modifications corresponding to this change
|
||||
*/
|
||||
public function GetDescription()
|
||||
{
|
||||
$sResult = '';
|
||||
$sTargetObjectClass = $this->Get('objclass');
|
||||
$oTargetObjectKey = $this->Get('objkey');
|
||||
$sAttCode = $this->Get('attcode');
|
||||
$oTargetSearch = new DBObjectSearch($sTargetObjectClass);
|
||||
$oTargetSearch->AddCondition('id', $oTargetObjectKey, '=');
|
||||
|
||||
$oMonoObjectSet = new DBObjectSet($oTargetSearch);
|
||||
if (UserRights::IsActionAllowedOnAttribute($sTargetObjectClass, $sAttCode, UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES)
|
||||
{
|
||||
if (!MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode'))) return ''; // Protects against renamed attributes...
|
||||
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
|
||||
$sAttName = $oAttDef->GetLabel();
|
||||
$sNewValue = $this->Get('newvalue');
|
||||
$sOldValue = $this->Get('oldvalue');
|
||||
$sResult = $oAttDef->DescribeChangeAsHTML($sOldValue, $sNewValue);
|
||||
}
|
||||
return $sResult;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Record the modification of an URL
|
||||
*
|
||||
|
||||
@@ -409,7 +409,19 @@ abstract class CMDBObject extends DBObject
|
||||
$oMyChangeOp->Set("newvalue", $value);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
else
|
||||
elseif ($oAttDef instanceOf AttributeSet)
|
||||
{
|
||||
// Tag Set
|
||||
//
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeTagSet");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
$oMyChangeOp->Set("oldvalue", implode(' ', $original->GetValues()));
|
||||
$oMyChangeOp->Set("newvalue", implode(' ', $value->GetValues()));
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Scalars
|
||||
//
|
||||
@@ -551,6 +563,12 @@ abstract class CMDBObject extends DBObject
|
||||
$this->DBUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null $oDeletionPlan
|
||||
*
|
||||
* @return \DeletionPlan|null
|
||||
* @throws \DeleteException
|
||||
*/
|
||||
public function DBDelete(&$oDeletionPlan = null)
|
||||
{
|
||||
return $this->DBDeleteTracked_Internal($oDeletionPlan);
|
||||
@@ -563,6 +581,12 @@ abstract class CMDBObject extends DBObject
|
||||
$this->DBDeleteTracked_Internal($oDeletionPlan);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null $oDeletionPlan
|
||||
*
|
||||
* @return \DeletionPlan|null
|
||||
* @throws \DeleteException
|
||||
*/
|
||||
protected function DBDeleteTracked_Internal(&$oDeletionPlan = null)
|
||||
{
|
||||
$prevkey = $this->GetKey();
|
||||
|
||||
@@ -108,18 +108,22 @@ class MySQLHasGoneAwayException extends MySQLException
|
||||
*/
|
||||
class CMDBSource
|
||||
{
|
||||
const ENUM_DB_VENDOR_MYSQL = 'MySQL';
|
||||
const ENUM_DB_VENDOR_MARIADB = 'MariaDB';
|
||||
const ENUM_DB_VENDOR_PERCONA = 'Percona';
|
||||
|
||||
protected static $m_sDBHost;
|
||||
protected static $m_sDBUser;
|
||||
protected static $m_sDBPwd;
|
||||
protected static $m_sDBName;
|
||||
/**
|
||||
* @var boolean
|
||||
* @since 2.5 #1260 MySQL TLS first implementation
|
||||
* @since 2.5 N°1260 MySQL TLS first implementation
|
||||
*/
|
||||
protected static $m_bDBTlsEnabled;
|
||||
/**
|
||||
* @var string
|
||||
* @since 2.5 #1260 MySQL TLS first implementation
|
||||
* @since 2.5 N°1260 MySQL TLS first implementation
|
||||
*/
|
||||
protected static $m_sDBTlsCA;
|
||||
|
||||
@@ -133,7 +137,7 @@ class CMDBSource
|
||||
* use expression as value)
|
||||
*
|
||||
* @see https://dev.mysql.com/doc/refman/5.7/en/charset-column.html
|
||||
* @since 2.5 #1001 switch to utf8mb4
|
||||
* @since 2.5 N°1001 switch to utf8mb4
|
||||
*/
|
||||
public static function GetSqlStringColumnDefinition()
|
||||
{
|
||||
@@ -406,6 +410,27 @@ class CMDBSource
|
||||
return $aVersions[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the DB vendor between MySQL and its main forks
|
||||
* @return string
|
||||
*/
|
||||
static public function GetDBVendor()
|
||||
{
|
||||
$sDBVendor = static::ENUM_DB_VENDOR_MYSQL;
|
||||
|
||||
$sVersionComment = static::GetServerVariable('version') . ' - ' . static::GetServerVariable('version_comment');
|
||||
if(preg_match('/mariadb/i', $sVersionComment) === 1)
|
||||
{
|
||||
$sDBVendor = static::ENUM_DB_VENDOR_MARIADB;
|
||||
}
|
||||
else if(preg_match('/percona/i', $sVersionComment) === 1)
|
||||
{
|
||||
$sDBVendor = static::ENUM_DB_VENDOR_PERCONA;
|
||||
}
|
||||
|
||||
return $sDBVendor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sSource
|
||||
*
|
||||
@@ -455,10 +480,15 @@ class CMDBSource
|
||||
public static function DropTable($sTable)
|
||||
{
|
||||
$res = self::Query("DROP TABLE `$sTable`");
|
||||
self::_TablesInfoCacheReset(true); // reset the table info cache!
|
||||
self::_TablesInfoCacheReset(); // reset the table info cache!
|
||||
return $res;
|
||||
}
|
||||
|
||||
public static function CacheReset($sTable)
|
||||
{
|
||||
self::_TablesInfoCacheReset($sTable);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \mysqli
|
||||
*/
|
||||
@@ -870,6 +900,20 @@ class CMDBSource
|
||||
return ($aFieldData["Type"]);
|
||||
}
|
||||
|
||||
private static function IsNumericType($aFieldData)
|
||||
{
|
||||
$aNumericTypes = array('tinyint(', 'decimal(', 'int(' );
|
||||
$sType = strtolower($aFieldData["Type"]);
|
||||
foreach ($aNumericTypes as $sNumericType)
|
||||
{
|
||||
if (strpos($sType, $sNumericType) === 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sTable
|
||||
* @param string $sField
|
||||
@@ -908,8 +952,15 @@ class CMDBSource
|
||||
}
|
||||
else
|
||||
{
|
||||
$default = $aFieldData["Default"] + 0; // Coerce to a numeric variable
|
||||
$sRet .= ' DEFAULT '.self::Quote($default);
|
||||
if (self::IsNumericType($aFieldData))
|
||||
{
|
||||
$sRet .= ' DEFAULT '.$aFieldData["Default"];
|
||||
}
|
||||
else
|
||||
{
|
||||
$default = $aFieldData["Default"] + 0; // Coerce to a numeric variable
|
||||
$sRet .= ' DEFAULT '.self::Quote($default);
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif (is_string($aFieldData["Default"]) == 'string')
|
||||
@@ -967,9 +1018,16 @@ class CMDBSource
|
||||
|
||||
// Cache the information about existing tables, and their fields
|
||||
private static $m_aTablesInfo = array();
|
||||
private static function _TablesInfoCacheReset()
|
||||
private static function _TablesInfoCacheReset($sTableName = null)
|
||||
{
|
||||
self::$m_aTablesInfo = array();
|
||||
if (is_null($sTableName))
|
||||
{
|
||||
self::$m_aTablesInfo = array();
|
||||
}
|
||||
else
|
||||
{
|
||||
self::$m_aTablesInfo[strtolower($sTableName)] = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1042,7 +1100,7 @@ class CMDBSource
|
||||
* @return string query to upgrade table charset and collation if needed, null if not
|
||||
* @throws \MySQLException
|
||||
*
|
||||
* @since 2.5 #1001 switch to utf8mb4
|
||||
* @since 2.5 N°1001 switch to utf8mb4
|
||||
* @see https://dev.mysql.com/doc/refman/5.7/en/charset-table.html
|
||||
*/
|
||||
public static function DBCheckTableCharsetAndCollation($sTableName)
|
||||
@@ -1192,7 +1250,7 @@ class CMDBSource
|
||||
* @return string query to upgrade database charset and collation if needed, null if not
|
||||
* @throws \MySQLException
|
||||
*
|
||||
* @since 2.5 #1001 switch to utf8mb4
|
||||
* @since 2.5 N°1001 switch to utf8mb4
|
||||
* @see https://dev.mysql.com/doc/refman/5.7/en/charset-database.html
|
||||
*/
|
||||
public static function DBCheckCharsetAndCollation()
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
define('ITOP_APPLICATION', 'iTop');
|
||||
define('ITOP_APPLICATION_SHORT', 'iTop');
|
||||
define('ITOP_VERSION', '2.5.0');
|
||||
define('ITOP_VERSION', '2.6.0');
|
||||
define('ITOP_REVISION', 'svn');
|
||||
define('ITOP_BUILD_DATE', '$WCNOW$');
|
||||
|
||||
@@ -37,6 +37,7 @@ define('ACCESS_READONLY', 0);
|
||||
|
||||
require_once('coreexception.class.inc.php');
|
||||
require_once('attributedef.class.inc.php'); // For the defines
|
||||
require_once('simplecrypt.class.inc.php');
|
||||
|
||||
class ConfigException extends CoreException
|
||||
{
|
||||
@@ -64,8 +65,8 @@ define('DEFAULT_FAST_RELOAD_INTERVAL', 1 * 60);
|
||||
define('DEFAULT_SECURE_CONNECTION_REQUIRED', false);
|
||||
define('DEFAULT_ALLOWED_LOGIN_TYPES', 'form|basic|external');
|
||||
define('DEFAULT_EXT_AUTH_VARIABLE', '$_SERVER[\'REMOTE_USER\']');
|
||||
define('DEFAULT_ENCRYPTION_KEY', '@iT0pEncr1pti0n!'); // We'll use a random value, later...
|
||||
|
||||
define('DEFAULT_ENCRYPTION_KEY', '@iT0pEncr1pti0n!'); // We'll use a random generated key later (if possible)
|
||||
define('DEFAULT_ENCRYPTION_LIB', 'Mcrypt'); // We'll define the best encryption available later
|
||||
/**
|
||||
* Config
|
||||
* configuration data (this class cannot not be localized, because it is responsible for loading the dictionaries)
|
||||
@@ -93,7 +94,7 @@ class Config
|
||||
protected $m_aSettings = array(
|
||||
'app_env_label' => array(
|
||||
'type' => 'string',
|
||||
'description' => 'Label displayed to describe the current application environnment, defaults to the environment name (e.g. "production")',
|
||||
'description' => 'Label displayed to describe the current application environment, defaults to the environment name (e.g. "production")',
|
||||
'default' => '',
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
@@ -166,7 +167,7 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'db_character_set' => array( // @deprecated to remove in 2.7 ? #1001 utf8mb4 switch
|
||||
'db_character_set' => array( // @deprecated to remove in 2.7 ? N°1001 utf8mb4 switch
|
||||
'type' => 'string',
|
||||
'description' => 'Deprecated since iTop 2.5 : now using utf8mb4',
|
||||
'default' => 'DEPRECATED_2.5',
|
||||
@@ -174,7 +175,7 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'db_collation' => array( // @deprecated to remove in 2.7 ? #1001 utf8mb4 switch
|
||||
'db_collation' => array( // @deprecated to remove in 2.7 ? N°1001 utf8mb4 switch
|
||||
'type' => 'string',
|
||||
'description' => 'Deprecated since iTop 2.5 : now using utf8mb4_unicode_ci',
|
||||
'default' => 'DEPRECATED_2.5',
|
||||
@@ -200,7 +201,7 @@ class Config
|
||||
),
|
||||
'skip_strong_security' => array(
|
||||
'type' => 'bool',
|
||||
'description' => 'Disable strong security - TEMPORY: this flag should be removed when we are more confident in the recent change in security',
|
||||
'description' => 'Disable strong security - TEMPORARY: this flag should be removed when we are more confident in the recent change in security',
|
||||
'default' => true,
|
||||
'value' => true,
|
||||
'source_of_value' => '',
|
||||
@@ -216,7 +217,7 @@ class Config
|
||||
),
|
||||
'query_indentation_enabled' => array(
|
||||
'type' => 'bool',
|
||||
'description' => 'For developpers: format the SQL queries for human analysis',
|
||||
'description' => 'For developers: format the SQL queries for human analysis',
|
||||
'default' => false,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
@@ -224,7 +225,7 @@ class Config
|
||||
),
|
||||
'disable_mandatory_ext_keys' => array(
|
||||
'type' => 'bool',
|
||||
'description' => 'For developpers: allow every external keys to be undefined',
|
||||
'description' => 'For developers: allow every external keys to be undefined',
|
||||
'default' => false,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
@@ -407,6 +408,14 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
),
|
||||
'tag_set_item_separator' => array(
|
||||
'type' => 'string',
|
||||
'description' => 'Tag set from string: tag label separator',
|
||||
'default' => '|',
|
||||
'value' => '|',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
),
|
||||
'cron_max_execution_time' => array(
|
||||
'type' => 'integer',
|
||||
'description' => 'Duration (seconds) of the page cron.php, must be shorter than php setting max_execution_time and shorter than the web server response timeout',
|
||||
@@ -441,7 +450,7 @@ class Config
|
||||
),
|
||||
'email_transport' => array(
|
||||
'type' => 'string',
|
||||
'description' => 'Mean to send emails: PHPMail (uses the function mail()) or SMTP (implements the client protocole)',
|
||||
'description' => 'Mean to send emails: PHPMail (uses the function mail()) or SMTP (implements the client protocol)',
|
||||
'default' => "PHPMail",
|
||||
'value' => "PHPMail",
|
||||
'source_of_value' => '',
|
||||
@@ -537,7 +546,7 @@ class Config
|
||||
),
|
||||
'timezone' => array(
|
||||
'type' => 'string',
|
||||
'description' => 'Timezone (reference: http://php.net/manual/en/timezones.php). If empty, it will be left unchanged and MUST be explicitely configured in PHP',
|
||||
'description' => 'Timezone (reference: http://php.net/manual/en/timezones.php). If empty, it will be left unchanged and MUST be explicitly configured in PHP',
|
||||
// examples... not used (nor 'description')
|
||||
'examples' => array(
|
||||
'America/Sao_Paulo',
|
||||
@@ -935,7 +944,7 @@ class Config
|
||||
),
|
||||
'tracking_level_linked_set_default' => array(
|
||||
'type' => 'integer',
|
||||
'description' => 'Default tracking level if not explicitely set at the attribute level, for AttributeLinkedSet (defaults to NONE in case of a fresh install, LIST otherwise - this to preserve backward compatibility while upgrading from a version older than 2.0.3 - see TRAC #936)',
|
||||
'description' => 'Default tracking level if not explicitly set at the attribute level, for AttributeLinkedSet (defaults to NONE in case of a fresh install, LIST otherwise - this to preserve backward compatibility while upgrading from a version older than 2.0.3 - see TRAC #936)',
|
||||
'default' => LINKSET_TRACKING_LIST,
|
||||
'value' => LINKSET_TRACKING_LIST,
|
||||
'source_of_value' => '',
|
||||
@@ -943,7 +952,7 @@ class Config
|
||||
),
|
||||
'tracking_level_linked_set_indirect_default' => array(
|
||||
'type' => 'integer',
|
||||
'description' => 'Default tracking level if not explicitely set at the attribute level, for AttributeLinkedSetIndirect',
|
||||
'description' => 'Default tracking level if not explicitly set at the attribute level, for AttributeLinkedSetIndirect',
|
||||
'default' => LINKSET_TRACKING_ALL,
|
||||
'value' => LINKSET_TRACKING_ALL,
|
||||
'source_of_value' => '',
|
||||
@@ -984,7 +993,7 @@ class Config
|
||||
'transaction_storage' => array(
|
||||
'type' => 'string',
|
||||
'description' => 'The type of mechanism to use for storing the unique identifiers for transactions (Session|File).',
|
||||
'default' => 'Session',
|
||||
'default' => 'File',
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
@@ -1056,7 +1065,7 @@ class Config
|
||||
'draft_attachments_lifetime' => array(
|
||||
'type' => 'integer',
|
||||
'description' => 'Lifetime (in seconds) of drafts\' attachments and inline images: after this duration, the garbage collector will delete them.',
|
||||
'default' => 3600,
|
||||
'default' => 86400,
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
@@ -1117,6 +1126,14 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
),
|
||||
'optimize_requests_for_join_count' => array(
|
||||
'type' => 'bool',
|
||||
'description' => 'Optimize request joins to minimize the count (default is true, try to set it to false in case of performance issues)',
|
||||
'default' => true,
|
||||
'value' => true,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
),
|
||||
'high_cardinality_classes' => array(
|
||||
'type' => 'array',
|
||||
'description' => 'List of classes with high cardinality (Force manual submit of search)',
|
||||
@@ -1125,6 +1142,22 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
),
|
||||
'newsroom_enabled' => array(
|
||||
'type' => 'bool',
|
||||
'description' => 'Whether or not the whole newsroom is enabled',
|
||||
'default' => true,
|
||||
'value' => true,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'regenerate_session_id_enabled' => array(
|
||||
'type' => 'bool',
|
||||
'description' => 'If true then session id will be regenerated on each login, to prevent session fixation.',
|
||||
'default' => true,
|
||||
'value' => true,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
);
|
||||
|
||||
public function IsProperty($sPropCode)
|
||||
@@ -1184,6 +1217,11 @@ class Config
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sPropCode
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function Get($sPropCode)
|
||||
{
|
||||
return $this->m_aSettings[$sPropCode]['value'];
|
||||
@@ -1246,13 +1284,29 @@ class Config
|
||||
*/
|
||||
protected $m_sEncryptionKey;
|
||||
|
||||
/**
|
||||
* @var string Encryption key used for all attributes of type "encrypted string". Can be set to a random value
|
||||
* unless you want to import a database from another iTop instance, in which case you must use
|
||||
* the same encryption key in order to properly decode the encrypted fields
|
||||
*/
|
||||
protected $m_sEncryptionLibrary;
|
||||
|
||||
/**
|
||||
* @var array Additional character sets to be supported by the interactive CSV import
|
||||
* 'iconv_code' => 'display name'
|
||||
*/
|
||||
protected $m_aCharsets;
|
||||
|
||||
public function __construct($sConfigFile = null, $bLoadConfig = true)
|
||||
/**
|
||||
* Config constructor.
|
||||
*
|
||||
* @param string|null $sConfigFile
|
||||
* @param bool $bLoadConfig
|
||||
*
|
||||
* @throws \ConfigException
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function __construct($sConfigFile = null, $bLoadConfig = true)
|
||||
{
|
||||
$this->m_sFile = $sConfigFile;
|
||||
if (is_null($sConfigFile))
|
||||
@@ -1282,10 +1336,14 @@ class Config
|
||||
$this->m_sDefaultLanguage = 'EN US';
|
||||
$this->m_sAllowedLoginTypes = DEFAULT_ALLOWED_LOGIN_TYPES;
|
||||
$this->m_sExtAuthVariable = DEFAULT_EXT_AUTH_VARIABLE;
|
||||
$this->m_sEncryptionKey = DEFAULT_ENCRYPTION_KEY;
|
||||
$this->m_aCharsets = array();
|
||||
$this->m_bQueryCacheEnabled = DEFAULT_QUERY_CACHE_ENABLED;
|
||||
|
||||
//define default encryption params according to php install
|
||||
$aEncryptParams = SimpleCrypt::GetNewDefaultParams();
|
||||
$this->m_sEncryptionLibrary = isset($aEncryptParams['lib']) ? $aEncryptParams['lib'] : DEFAULT_ENCRYPTION_LIB;
|
||||
$this->m_sEncryptionKey= isset($aEncryptParams['key']) ? $aEncryptParams['key'] : DEFAULT_ENCRYPTION_KEY;
|
||||
|
||||
$this->m_aModuleSettings = array();
|
||||
|
||||
if ($bLoadConfig)
|
||||
@@ -1310,7 +1368,13 @@ class Config
|
||||
*/
|
||||
}
|
||||
|
||||
protected function CheckFile($sPurpose, $sFileName)
|
||||
/**
|
||||
* @param string $sPurpose
|
||||
* @param string $sFileName
|
||||
*
|
||||
* @throws \ConfigException
|
||||
*/
|
||||
protected function CheckFile($sPurpose, $sFileName)
|
||||
{
|
||||
if (!file_exists($sFileName))
|
||||
{
|
||||
@@ -1425,7 +1489,8 @@ class Config
|
||||
$this->m_sDefaultLanguage = isset($MySettings['default_language']) ? trim($MySettings['default_language']) : 'EN US';
|
||||
$this->m_sAllowedLoginTypes = isset($MySettings['allowed_login_types']) ? trim($MySettings['allowed_login_types']) : DEFAULT_ALLOWED_LOGIN_TYPES;
|
||||
$this->m_sExtAuthVariable = isset($MySettings['ext_auth_variable']) ? trim($MySettings['ext_auth_variable']) : DEFAULT_EXT_AUTH_VARIABLE;
|
||||
$this->m_sEncryptionKey = isset($MySettings['encryption_key']) ? trim($MySettings['encryption_key']) : DEFAULT_ENCRYPTION_KEY;
|
||||
$this->m_sEncryptionKey = isset($MySettings['encryption_key']) ? trim($MySettings['encryption_key']) : $this->m_sEncryptionKey;
|
||||
$this->m_sEncryptionLibrary = isset($MySettings['encryption_library']) ? trim($MySettings['encryption_library']) : $this->m_sEncryptionLibrary;
|
||||
$this->m_aCharsets = isset($MySettings['csv_import_charsets']) ? $MySettings['csv_import_charsets'] : array();
|
||||
}
|
||||
|
||||
@@ -1446,7 +1511,14 @@ class Config
|
||||
return $this->GetModuleParameter($sModule, $sProperty, $defaultvalue);
|
||||
}
|
||||
|
||||
public function GetModuleParameter($sModule, $sProperty, $defaultvalue = null)
|
||||
/**
|
||||
* @param string $sModule
|
||||
* @param string $sProperty
|
||||
* @param mixed|null $defaultvalue
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function GetModuleParameter($sModule, $sProperty, $defaultvalue = null)
|
||||
{
|
||||
$ret = $defaultvalue;
|
||||
if (class_exists('ModulesXMLParameters'))
|
||||
@@ -1478,6 +1550,7 @@ class Config
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*
|
||||
* @deprecated 2.5 will be removed in 2.6
|
||||
* @see Config::Get() as a replacement
|
||||
*/
|
||||
@@ -1488,6 +1561,7 @@ class Config
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*
|
||||
* @deprecated 2.5 will be removed in 2.6
|
||||
* @see Config::Get() as a replacement
|
||||
*/
|
||||
@@ -1498,6 +1572,7 @@ class Config
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*
|
||||
* @deprecated 2.5 will be removed in 2.6
|
||||
* @see Config::Get() as a replacement
|
||||
*/
|
||||
@@ -1508,7 +1583,8 @@ class Config
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @deprecated 2.5 will be removed in 2.6 #1001 utf8mb4 switch
|
||||
*
|
||||
* @deprecated 2.5 will be removed in 2.6 N°1001 utf8mb4 switch
|
||||
* @see Config::DEFAULT_CHARACTER_SET
|
||||
*/
|
||||
public function GetDBCharacterSet()
|
||||
@@ -1518,7 +1594,8 @@ class Config
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @deprecated 2.5 will be removed in 2.6 #1001 utf8mb4 switch
|
||||
*
|
||||
* @deprecated 2.5 will be removed in 2.6 N°1001 utf8mb4 switch
|
||||
* @see Config::DEFAULT_COLLATION
|
||||
*/
|
||||
public function GetDBCollation()
|
||||
@@ -1528,6 +1605,7 @@ class Config
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*
|
||||
* @deprecated 2.5 will be removed in 2.6
|
||||
* @see Config::Get() as a replacement
|
||||
*/
|
||||
@@ -1538,6 +1616,7 @@ class Config
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*
|
||||
* @deprecated 2.5 will be removed in 2.6
|
||||
* @see Config::Get() as a replacement
|
||||
*/
|
||||
@@ -1611,6 +1690,11 @@ class Config
|
||||
return $this->m_sEncryptionKey;
|
||||
}
|
||||
|
||||
public function GetEncryptionLibrary()
|
||||
{
|
||||
return $this->m_sEncryptionLibrary;
|
||||
}
|
||||
|
||||
public function GetAllowedLoginTypes()
|
||||
{
|
||||
return explode('|', $this->m_sAllowedLoginTypes);
|
||||
@@ -1739,6 +1823,7 @@ class Config
|
||||
$aSettings['allowed_login_types'] = $this->m_sAllowedLoginTypes;
|
||||
$aSettings['ext_auth_variable'] = $this->m_sExtAuthVariable;
|
||||
$aSettings['encryption_key'] = $this->m_sEncryptionKey;
|
||||
$aSettings['encryption_library'] = $this->m_sEncryptionLibrary;
|
||||
$aSettings['csv_import_charsets'] = $this->m_aCharsets;
|
||||
|
||||
foreach ($this->m_aModuleSettings as $sModule => $aProperties)
|
||||
@@ -1756,14 +1841,16 @@ class Config
|
||||
return $aSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the configuration to a file (php format) that can be reloaded later
|
||||
* By default write to the same file that was specified when constructing the object
|
||||
/**
|
||||
* Write the configuration to a file (php format) that can be reloaded later
|
||||
* By default write to the same file that was specified when constructing the object
|
||||
*
|
||||
* @param string $sFileName string Name of the file to write to (emtpy to write to the same file)
|
||||
*
|
||||
* @return boolean True otherwise throws an Exception
|
||||
*
|
||||
* @param $sFileName string Name of the file to write to (emtpy to write to the same file)
|
||||
*
|
||||
* @return boolean True otherwise throws an Exception
|
||||
*/
|
||||
* @throws \ConfigException
|
||||
*/
|
||||
public function WriteToFile($sFileName = '')
|
||||
{
|
||||
if (empty($sFileName))
|
||||
@@ -1825,6 +1912,7 @@ class Config
|
||||
'allowed_login_types' => $this->m_sAllowedLoginTypes,
|
||||
'ext_auth_variable' => $this->m_sExtAuthVariable,
|
||||
'encryption_key' => $this->m_sEncryptionKey,
|
||||
'encryption_library' => $this->m_sEncryptionLibrary,
|
||||
'csv_import_charsets' => $this->m_aCharsets,
|
||||
);
|
||||
foreach ($aOtherValues as $sKey => $value)
|
||||
@@ -1910,9 +1998,16 @@ class Config
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to initialize a configuration from the page arguments
|
||||
*/
|
||||
/**
|
||||
* Helper function to initialize a configuration from the page arguments
|
||||
*
|
||||
* @param array $aParamValues
|
||||
* @param string|null $sModulesDir
|
||||
* @param bool $bPreserveModuleSettings
|
||||
*
|
||||
* @throws \Exception
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function UpdateFromParams($aParamValues, $sModulesDir = null, $bPreserveModuleSettings = false)
|
||||
{
|
||||
if (isset($aParamValues['application_path']))
|
||||
@@ -2042,9 +2137,13 @@ class Config
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: for an array of string, change the prefix when found
|
||||
*/
|
||||
/**
|
||||
* Helper: for an array of string, change the prefix when found
|
||||
*
|
||||
* @param array $aStrings
|
||||
* @param string $sSearchPrefix
|
||||
* @param string $sNewPrefix
|
||||
*/
|
||||
protected static function ChangePrefix(&$aStrings, $sSearchPrefix, $sNewPrefix)
|
||||
{
|
||||
foreach ($aStrings as &$sFile)
|
||||
@@ -2056,10 +2155,13 @@ class Config
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obsolete: kept only for backward compatibility of the Toolkit
|
||||
* Quick and dirty way to clone a config file into another environment
|
||||
*/
|
||||
/**
|
||||
* Obsolete: kept only for backward compatibility of the Toolkit
|
||||
* Quick and dirty way to clone a config file into another environment
|
||||
*
|
||||
* @param string $sSourceEnv
|
||||
* @param string $sTargetEnv
|
||||
*/
|
||||
public function ChangeModulesPath($sSourceEnv, $sTargetEnv)
|
||||
{
|
||||
// Now does nothing since the includes are built into the environment itself
|
||||
@@ -2096,5 +2198,3 @@ class Config
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
@@ -107,6 +107,81 @@ class CoreException extends Exception
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class CoreCannotSaveObjectException
|
||||
*
|
||||
* Specialized exception to raise if {@link DBObject::CheckToWrite()} fails, which allow easy data retrieval
|
||||
*
|
||||
* @see \DBObject::DBInsertNoReload()
|
||||
* @see \DBObject::DBUpdate()
|
||||
*
|
||||
* @since 2.6 N°659 uniqueness constraint
|
||||
*/
|
||||
class CoreCannotSaveObjectException extends CoreException
|
||||
{
|
||||
/** @var string[] */
|
||||
private $aIssues;
|
||||
/** @var int */
|
||||
private $iObjectId;
|
||||
/** @var string */
|
||||
private $sObjectClass;
|
||||
|
||||
/**
|
||||
* CoreCannotSaveObjectException constructor.
|
||||
*
|
||||
* @param array $aContextData containing at least those keys : issues, id, class
|
||||
*/
|
||||
public function __construct($aContextData)
|
||||
{
|
||||
$this->aIssues = $aContextData['issues'];
|
||||
$this->iObjectId = $aContextData['id'];
|
||||
$this->sObjectClass = $aContextData['class'];
|
||||
|
||||
$sIssues = implode(', ', $this->aIssues);
|
||||
parent::__construct($sIssues, $aContextData);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getHtmlMessage()
|
||||
{
|
||||
$sTitle = Dict::S('UI:Error:SaveFailed');
|
||||
$sContent = "<span><strong>{$sTitle}</strong></span>";
|
||||
|
||||
if (count($this->aIssues) == 1)
|
||||
{
|
||||
$sContent .= " <span>{$this->aIssues[0]}</span>";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sContent .= '<ul>';
|
||||
foreach ($this->aIssues as $sError)
|
||||
{
|
||||
$sContent .= "<li>$sError</li>";
|
||||
}
|
||||
$sContent .= '</ul>';
|
||||
}
|
||||
|
||||
return $sContent;
|
||||
}
|
||||
|
||||
public function getIssues()
|
||||
{
|
||||
return $this->aIssues;
|
||||
}
|
||||
|
||||
public function getObjectId()
|
||||
{
|
||||
return $this->iObjectId;
|
||||
}
|
||||
|
||||
public function getObjectClass()
|
||||
{
|
||||
return $this->sObjectClass;
|
||||
}
|
||||
}
|
||||
|
||||
class CoreWarning extends CoreException
|
||||
{
|
||||
}
|
||||
|
||||
@@ -266,7 +266,7 @@ EOF
|
||||
if ($this->aStatusInfo['charset'] != 'UTF-8')
|
||||
{
|
||||
// Note: due to bugs in the glibc library it's safer to call iconv on the smallest possible string
|
||||
// and thus to convert field by field and not the whole row or file at once (see ticket #991)
|
||||
// and thus to convert field by field and not the whole row or file at once (see ticket N°991)
|
||||
$aData[$idx] = @iconv('UTF-8', $this->aStatusInfo['charset'].'//IGNORE//TRANSLIT', $aData[$idx]);
|
||||
}
|
||||
}
|
||||
@@ -325,7 +325,7 @@ EOF
|
||||
if ($this->aStatusInfo['charset'] != 'UTF-8')
|
||||
{
|
||||
// Note: due to bugs in the glibc library it's safer to call iconv on the smallest possible string
|
||||
// and thus to convert field by field and not the whole row or file at once (see ticket #991)
|
||||
// and thus to convert field by field and not the whole row or file at once (see ticket N°991)
|
||||
$aData[] = @iconv('UTF-8', $this->aStatusInfo['charset'].'//IGNORE//TRANSLIT', $sField);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.5">
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.6">
|
||||
<user_rights>
|
||||
<profiles>
|
||||
<profile id="1024" _delta="define">
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -480,6 +480,11 @@ class DBObjectSearch extends DBSearch
|
||||
$oNewCondition = Expression::FromOQL($sOQLCondition);
|
||||
break;
|
||||
|
||||
case 'MATCHES':
|
||||
$oRightExpr = new ScalarExpression($value);
|
||||
$oNewCondition = new MatchExpression($oField, $oRightExpr);
|
||||
break;
|
||||
|
||||
case 'Contains':
|
||||
case 'Begins with':
|
||||
case 'Finishes with':
|
||||
@@ -1299,6 +1304,13 @@ class DBObjectSearch extends DBSearch
|
||||
$oRight = $this->OQLExpressionToCondition($sQuery, $oExpression->GetRightExpr(), $aClassAliases);
|
||||
return new BinaryExpression($oLeft, $sOperator, $oRight);
|
||||
}
|
||||
elseif ($oExpression instanceof MatchOqlExpression)
|
||||
{
|
||||
$oLeft = $this->OQLExpressionToCondition($sQuery, $oExpression->GetLeftExpr(), $aClassAliases);
|
||||
$oRight = $this->OQLExpressionToCondition($sQuery, $oExpression->GetRightExpr(), $aClassAliases);
|
||||
|
||||
return new MatchExpression($oLeft, $oRight);
|
||||
}
|
||||
elseif ($oExpression instanceof FieldOqlExpression)
|
||||
{
|
||||
$sClassAlias = $oExpression->GetParent();
|
||||
@@ -1925,46 +1937,86 @@ class DBObjectSearch extends DBSearch
|
||||
}
|
||||
}
|
||||
|
||||
// First query built from the root, adding all tables including the leaf
|
||||
// Before N.1065 we were joining from the leaf first, but this wasn't a good choice :
|
||||
// most of the time (obsolescence, friendlyname, ...) we want to get a root attribute !
|
||||
//
|
||||
$oSelectBase = null;
|
||||
$aClassHierarchy = MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL, true);
|
||||
$bIsClassStandaloneClass = (count($aClassHierarchy) == 1);
|
||||
foreach($aClassHierarchy as $sSomeClass)
|
||||
$bRootFirst = MetaModel::GetConfig()->Get('optimize_requests_for_join_count');
|
||||
if ($bRootFirst)
|
||||
{
|
||||
if (!MetaModel::HasTable($sSomeClass))
|
||||
// First query built from the root, adding all tables including the leaf
|
||||
// Before N.1065 we were joining from the leaf first, but this wasn't a good choice :
|
||||
// most of the time (obsolescence, friendlyname, ...) we want to get a root attribute !
|
||||
//
|
||||
$oSelectBase = null;
|
||||
$aClassHierarchy = MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL, true);
|
||||
$bIsClassStandaloneClass = (count($aClassHierarchy) == 1);
|
||||
foreach($aClassHierarchy as $sSomeClass)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
self::DbgTrace("Adding join from root to leaf: $sSomeClass... let's call MakeSQLObjectQuerySingleTable()");
|
||||
$oSelectParentTable = $this->MakeSQLObjectQuerySingleTable($oBuild, $aAttToLoad, $sSomeClass, $aExtKeys, $aValues);
|
||||
if (is_null($oSelectBase))
|
||||
{
|
||||
$oSelectBase = $oSelectParentTable;
|
||||
if (!$bIsClassStandaloneClass && (MetaModel::IsRootClass($sSomeClass)))
|
||||
if (!MetaModel::HasTable($sSomeClass))
|
||||
{
|
||||
// As we're linking to root class first, we're adding a where clause on the finalClass attribute :
|
||||
// COALESCE($sRootClassFinalClass IN ('$sExpectedClasses'), 1)
|
||||
// If we don't, the child classes can be removed in the query optimisation phase, including the leaf that was queried
|
||||
// So we still need to filter records to only those corresponding to the child classes !
|
||||
// The coalesce is mandatory if we have a polymorphic query (left join)
|
||||
$oClassListExpr = ListExpression::FromScalars(MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL));
|
||||
$sFinalClassSqlColumnName = MetaModel::DBGetClassField($sSomeClass);
|
||||
$oClassExpr = new FieldExpression($sFinalClassSqlColumnName, $oSelectBase->GetTableAlias());
|
||||
$oInExpression = new BinaryExpression($oClassExpr, 'IN', $oClassListExpr);
|
||||
$oTrueExpression = new TrueExpression();
|
||||
$aCoalesceAttr = array($oInExpression, $oTrueExpression);
|
||||
$oFinalClassRestriction = new FunctionExpression("COALESCE", $aCoalesceAttr);
|
||||
|
||||
$oBuild->m_oQBExpressions->AddCondition($oFinalClassRestriction);
|
||||
continue;
|
||||
}
|
||||
|
||||
self::DbgTrace("Adding join from root to leaf: $sSomeClass... let's call MakeSQLObjectQuerySingleTable()");
|
||||
$oSelectParentTable = $this->MakeSQLObjectQuerySingleTable($oBuild, $aAttToLoad, $sSomeClass, $aExtKeys, $aValues);
|
||||
if (is_null($oSelectBase))
|
||||
{
|
||||
$oSelectBase = $oSelectParentTable;
|
||||
if (!$bIsClassStandaloneClass && (MetaModel::IsRootClass($sSomeClass)))
|
||||
{
|
||||
// As we're linking to root class first, we're adding a where clause on the finalClass attribute :
|
||||
// COALESCE($sRootClassFinalClass IN ('$sExpectedClasses'), 1)
|
||||
// If we don't, the child classes can be removed in the query optimisation phase, including the leaf that was queried
|
||||
// So we still need to filter records to only those corresponding to the child classes !
|
||||
// The coalesce is mandatory if we have a polymorphic query (left join)
|
||||
$oClassListExpr = ListExpression::FromScalars(MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL));
|
||||
$sFinalClassSqlColumnName = MetaModel::DBGetClassField($sSomeClass);
|
||||
$oClassExpr = new FieldExpression($sFinalClassSqlColumnName, $oSelectBase->GetTableAlias());
|
||||
$oInExpression = new BinaryExpression($oClassExpr, 'IN', $oClassListExpr);
|
||||
$oTrueExpression = new TrueExpression();
|
||||
$aCoalesceAttr = array($oInExpression, $oTrueExpression);
|
||||
$oFinalClassRestriction = new FunctionExpression("COALESCE", $aCoalesceAttr);
|
||||
|
||||
$oBuild->m_oQBExpressions->AddCondition($oFinalClassRestriction);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$oSelectBase->AddInnerJoin($oSelectParentTable, $sKeyField, MetaModel::DBGetKey($sSomeClass));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// First query built upon on the leaf (ie current) class
|
||||
//
|
||||
self::DbgTrace("Main (=leaf) class, call MakeSQLObjectQuerySingleTable()");
|
||||
if (MetaModel::HasTable($sClass))
|
||||
{
|
||||
$oSelectBase = $this->MakeSQLObjectQuerySingleTable($oBuild, $aAttToLoad, $sClass, $aExtKeys, $aValues);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oSelectBase->AddInnerJoin($oSelectParentTable, $sKeyField, MetaModel::DBGetKey($sSomeClass));
|
||||
$oSelectBase = null;
|
||||
|
||||
// As the join will not filter on the expected classes, we have to specify it explicitely
|
||||
$sExpectedClasses = implode("', '", MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL));
|
||||
$oFinalClassRestriction = Expression::FromOQL("`$sClassAlias`.finalclass IN ('$sExpectedClasses')");
|
||||
$oBuild->m_oQBExpressions->AddCondition($oFinalClassRestriction);
|
||||
}
|
||||
|
||||
// Then we join the queries of the eventual parent classes (compound model)
|
||||
foreach(MetaModel::EnumParentClasses($sClass) as $sParentClass)
|
||||
{
|
||||
if (!MetaModel::HasTable($sParentClass)) continue;
|
||||
|
||||
self::DbgTrace("Parent class: $sParentClass... let's call MakeSQLObjectQuerySingleTable()");
|
||||
$oSelectParentTable = $this->MakeSQLObjectQuerySingleTable($oBuild, $aAttToLoad, $sParentClass, $aExtKeys, $aValues);
|
||||
if (is_null($oSelectBase))
|
||||
{
|
||||
$oSelectBase = $oSelectParentTable;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oSelectBase->AddInnerJoin($oSelectParentTable, $sKeyField, MetaModel::DBGetKey($sParentClass));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
*/
|
||||
protected $m_aAddedIds; // Ids of objects added (discrete lists)
|
||||
/**
|
||||
* @var hash array of (row => array of (classalias) => object/null) storing the objects added "in memory"
|
||||
* @var array array of (row => array of (classalias) => object/null) storing the objects added "in memory"
|
||||
*/
|
||||
protected $m_aAddedObjects;
|
||||
/**
|
||||
@@ -118,12 +118,19 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*
|
||||
* @throws \Exception
|
||||
* @throws \CoreException
|
||||
* @throws \MissingQueryArgument
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
$sRet = '';
|
||||
$this->Rewind();
|
||||
$sRet .= "Set (".$this->m_oFilter->ToOQL().")<br/>\n";
|
||||
$sRet .= "Query: <pre style=\"font-size: smaller; display:inline;\">".$this->m_oFilter->MakeSelectQuery().")</pre>\n";
|
||||
$sRet .= "Query: <pre style=\"font-size: smaller; display:inline;\">".$this->m_oFilter->MakeSelectQuery().")</pre>\n";
|
||||
|
||||
$sRet .= $this->Count()." records<br/>\n";
|
||||
if ($this->Count() > 0)
|
||||
@@ -171,13 +178,16 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
return $this->m_oFilter->GetShowObsoleteData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the subset of attributes to load (for each class of objects) before performing the SQL query for retrieving the rows from the DB
|
||||
*
|
||||
* @param array $aAttToLoad Format: alias => array of attribute_codes
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
/**
|
||||
* Specify the subset of attributes to load (for each class of objects) before performing the SQL query for retrieving the rows from the DB
|
||||
*
|
||||
* @param array $aAttToLoad Format: alias => array of attribute_codes
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws \Exception
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function OptimizeColumnLoad($aAttToLoad)
|
||||
{
|
||||
if (is_null($aAttToLoad))
|
||||
@@ -249,13 +259,15 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a set (in-memory) containing just the given object
|
||||
*
|
||||
* @param DBobject $oObject
|
||||
*
|
||||
* @return DBObjectSet The singleton set
|
||||
*/
|
||||
/**
|
||||
* Create a set (in-memory) containing just the given object
|
||||
*
|
||||
* @param \DBobject $oObject
|
||||
*
|
||||
* @return \DBObjectSet The singleton set
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
static public function FromObject($oObject)
|
||||
{
|
||||
$oRetSet = self::FromScratch(get_class($oObject));
|
||||
@@ -263,13 +275,15 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
return $oRetSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an empty set (in-memory), for the given class (and its subclasses) of objects
|
||||
*
|
||||
* @param string $sClass The class (or an ancestor) for the objects to be added in this set
|
||||
*
|
||||
* @return DBObjectSet The empty set
|
||||
*/
|
||||
/**
|
||||
* Create an empty set (in-memory), for the given class (and its subclasses) of objects
|
||||
*
|
||||
* @param string $sClass The class (or an ancestor) for the objects to be added in this set
|
||||
*
|
||||
* @return \DBObjectSet The empty set
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
static public function FromScratch($sClass)
|
||||
{
|
||||
$oFilter = new DBObjectSearch($sClass);
|
||||
@@ -278,34 +292,38 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
$oRetSet->m_bLoaded = true; // no DB load
|
||||
$oRetSet->m_iNumTotalDBRows = 0; // Nothing from the DB
|
||||
return $oRetSet;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a set (in-memory) with just one column (i.e. one object per row) and filled with the given array of objects
|
||||
*
|
||||
* @param string $sClass The class of the objects (must be a common ancestor to all objects in the set)
|
||||
* @param array $aObjects The list of objects to add into the set
|
||||
*
|
||||
* @return DBObjectSet
|
||||
*/
|
||||
/**
|
||||
* Create a set (in-memory) with just one column (i.e. one object per row) and filled with the given array of objects
|
||||
*
|
||||
* @param string $sClass The class of the objects (must be a common ancestor to all objects in the set)
|
||||
* @param array $aObjects The list of objects to add into the set
|
||||
*
|
||||
* @return \DBObjectSet
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
static public function FromArray($sClass, $aObjects)
|
||||
{
|
||||
$oRetSet = self::FromScratch($sClass);
|
||||
$oRetSet->AddObjectArray($aObjects, $sClass);
|
||||
return $oRetSet;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a set in-memory with several classes of objects per row (with one alias per "column")
|
||||
*
|
||||
* Limitation:
|
||||
* The filter/OQL query representing such a set can not be rebuilt (only the first column will be taken into account)
|
||||
*
|
||||
* @param hash $aClasses Format: array of (alias => class)
|
||||
* @param hash $aObjects Format: array of (array of (classalias => object))
|
||||
*
|
||||
* @return DBObjectSet
|
||||
*/
|
||||
/**
|
||||
* Create a set in-memory with several classes of objects per row (with one alias per "column")
|
||||
*
|
||||
* Limitation:
|
||||
* The filter/OQL query representing such a set can not be rebuilt (only the first column will be taken into account)
|
||||
*
|
||||
* @param array $aClasses Format: array of (alias => class)
|
||||
* @param array $aObjects Format: array of (array of (classalias => object))
|
||||
*
|
||||
* @return \DBObjectSet
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
static public function FromArrayAssoc($aClasses, $aObjects)
|
||||
{
|
||||
// In a perfect world, we should create a complete tree of DBObjectSearch,
|
||||
@@ -324,9 +342,19 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
$oRetSet->AddObjectExtended($aObjectsByClassAlias);
|
||||
}
|
||||
return $oRetSet;
|
||||
}
|
||||
}
|
||||
|
||||
static public function FromLinkSet($oObject, $sLinkSetAttCode, $sExtKeyToRemote)
|
||||
/**
|
||||
* @param $oObject
|
||||
* @param string $sLinkSetAttCode
|
||||
* @param string $sExtKeyToRemote
|
||||
*
|
||||
* @return \DBObjectSet
|
||||
*
|
||||
* @throws \Exception
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
*/static public function FromLinkSet($oObject, $sLinkSetAttCode, $sExtKeyToRemote)
|
||||
{
|
||||
$oLinkAttCode = MetaModel::GetAttributeDef(get_class($oObject), $sLinkSetAttCode);
|
||||
$oExtKeyAttDef = MetaModel::GetAttributeDef($oLinkAttCode->GetLinkedClass(), $sExtKeyToRemote);
|
||||
@@ -346,7 +374,13 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
* Note: After calling this method, the set cursor will be at the end of the set. You might want to rewind it.
|
||||
*
|
||||
* @param bool $bWithId
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws \Exception
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \MySQLException
|
||||
*/
|
||||
public function ToArray($bWithId = true)
|
||||
{
|
||||
@@ -364,8 +398,16 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
}
|
||||
}
|
||||
return $aRet;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*
|
||||
* @throws \Exception
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \MySQLException
|
||||
*/
|
||||
public function ToArrayOfValues()
|
||||
{
|
||||
if (!$this->m_bLoaded) $this->Load();
|
||||
@@ -421,7 +463,11 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
*
|
||||
* @param string $sAttCode
|
||||
* @param bool $bWithId
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws \Exception
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function GetColumnAsArray($sAttCode, $bWithId = true)
|
||||
{
|
||||
@@ -441,14 +487,16 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
return $aRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the DBSearch corresponding to the objects present in this set
|
||||
*
|
||||
* Limitation:
|
||||
* This method will NOT work for sets with several columns (i.e. several objects per row)
|
||||
*
|
||||
* @return DBObjectSearch
|
||||
*/
|
||||
/**
|
||||
* Retrieve the DBSearch corresponding to the objects present in this set
|
||||
*
|
||||
* Limitation:
|
||||
* This method will NOT work for sets with several columns (i.e. several objects per row)
|
||||
*
|
||||
* @return \DBObjectSearch
|
||||
*
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function GetFilter()
|
||||
{
|
||||
// Make sure that we carry on the parameters of the set with the filter
|
||||
@@ -495,18 +543,20 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
/**
|
||||
* The list of all classes (one per column) which are part of this set
|
||||
*
|
||||
* @return hash Format: alias => class
|
||||
* @return array Format: alias => class
|
||||
*/
|
||||
public function GetSelectedClasses()
|
||||
{
|
||||
return $this->m_oFilter->GetSelectedClasses();
|
||||
}
|
||||
|
||||
/**
|
||||
* The root class (i.e. highest ancestor in the MeaModel class hierarchy) for the first column on this set
|
||||
*
|
||||
* @return string The root class for the objects in the first column of the set
|
||||
*/
|
||||
/**
|
||||
* The root class (i.e. highest ancestor in the MeaModel class hierarchy) for the first column on this set
|
||||
*
|
||||
* @return string The root class for the objects in the first column of the set
|
||||
*
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function GetRootClass()
|
||||
{
|
||||
return MetaModel::GetRootClass($this->GetClass());
|
||||
@@ -515,7 +565,7 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
/**
|
||||
* The arguments used for building this set
|
||||
*
|
||||
* @return hash Format: parameter_name => value
|
||||
* @return array Format: parameter_name => value
|
||||
*/
|
||||
public function GetArgs()
|
||||
{
|
||||
@@ -533,11 +583,13 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
$this->m_iLimitStart = $iLimitStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the sort order for loading the rows from the DB. Changing the order by causes a Reload.
|
||||
*
|
||||
* @param hash $aOrderBy Format: [alias.]attcode => boolean (true = ascending, false = descending)
|
||||
*/
|
||||
/**
|
||||
* Sets the sort order for loading the rows from the DB. Changing the order by causes a Reload.
|
||||
*
|
||||
* @param array $aOrderBy Format: [alias.]attcode => boolean (true = ascending, false = descending)
|
||||
*
|
||||
* @throws \MySQLException
|
||||
*/
|
||||
public function SetOrderBy($aOrderBy)
|
||||
{
|
||||
if ($this->m_aOrderBy != $aOrderBy)
|
||||
@@ -551,11 +603,14 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the sort order for loading the rows from the DB. Changing the order by causes a Reload.
|
||||
*
|
||||
* @param hash $aAliases Format: alias => boolean (true = ascending, false = descending). If omitted, then it defaults to all the selected classes
|
||||
*/
|
||||
/**
|
||||
* Sets the sort order for loading the rows from the DB. Changing the order by causes a Reload.
|
||||
*
|
||||
* @param array $aAliases Format: alias => boolean (true = ascending, false = descending). If omitted, then it defaults to all the selected classes
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \MySQLException
|
||||
*/
|
||||
public function SetOrderByClasses($aAliases = null)
|
||||
{
|
||||
if ($aAliases === null)
|
||||
@@ -599,13 +654,15 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
return $this->m_iLimitStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sort order used for loading this set from the database
|
||||
*
|
||||
* Limitation: the sort order has no effect on objects added in-memory
|
||||
*
|
||||
* @return array Format: field_code => boolean (true = ascending, false = descending)
|
||||
*/
|
||||
/**
|
||||
* Get the sort order used for loading this set from the database
|
||||
*
|
||||
* Limitation: the sort order has no effect on objects added in-memory
|
||||
*
|
||||
* @return array Format: field_code => boolean (true = ascending, false = descending)
|
||||
*
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function GetRealSortOrder()
|
||||
{
|
||||
if (!$this->m_bSort)
|
||||
@@ -625,9 +682,12 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the set from the database. Actually performs the SQL query to retrieve the records from the DB.
|
||||
*/
|
||||
/**
|
||||
* Loads the set from the database. Actually performs the SQL query to retrieve the records from the DB.
|
||||
*
|
||||
* @throws \Exception
|
||||
* @throws \MySQLException
|
||||
*/
|
||||
public function Load()
|
||||
{
|
||||
if ($this->m_bLoaded) return;
|
||||
@@ -682,11 +742,14 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
$this->m_iNumLoadedDBRows = $this->m_oSQLResult->num_rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $aAttToLoad
|
||||
*
|
||||
* @return string SQL query
|
||||
*/
|
||||
/**
|
||||
* @param string[] $aAttToLoad
|
||||
*
|
||||
* @return string SQL query
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \MissingQueryArgument
|
||||
*/
|
||||
private function _makeSelectQuery($aAttToLoad)
|
||||
{
|
||||
if ($this->m_iLimitCount > 0)
|
||||
@@ -703,15 +766,20 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
return $sSQL;
|
||||
}
|
||||
|
||||
/**
|
||||
* The total number of rows in this set. Independently of the SetLimit used for loading the set and taking into
|
||||
* account the rows added in-memory.
|
||||
*
|
||||
* May actually perform the SQL query SELECT COUNT... if the set was not previously loaded, or loaded with a
|
||||
* SetLimit
|
||||
*
|
||||
* @return int The total number of rows for this set.
|
||||
*/
|
||||
/**
|
||||
* The total number of rows in this set. Independently of the SetLimit used for loading the set and taking into
|
||||
* account the rows added in-memory.
|
||||
*
|
||||
* May actually perform the SQL query SELECT COUNT... if the set was not previously loaded, or loaded with a
|
||||
* SetLimit
|
||||
*
|
||||
* @return int The total number of rows for this set.
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
*/
|
||||
public function Count()
|
||||
{
|
||||
if (is_null($this->m_iNumTotalDBRows))
|
||||
@@ -732,6 +800,7 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
* @param $iLimit
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
@@ -746,8 +815,8 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
if ($resQuery)
|
||||
{
|
||||
$aRow = CMDBSource::FetchArray($resQuery);
|
||||
CMDBSource::FreeResult($resQuery);
|
||||
$iCount = intval($aRow['COUNT']);
|
||||
CMDBSource::FreeResult($resQuery);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -766,6 +835,7 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
* @param $iLimit
|
||||
*
|
||||
* @return int
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
@@ -806,12 +876,17 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
return $this->m_iNumLoadedDBRows + count($this->m_aAddedObjects);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the object (with the given class alias) at the current position in the set and move the cursor to the next position.
|
||||
*
|
||||
* @param string $sRequestedClassAlias The class alias to fetch (if there are several objects/classes per row)
|
||||
* @return DBObject The fetched object or null when at the end
|
||||
*/
|
||||
/**
|
||||
* Fetch the object (with the given class alias) at the current position in the set and move the cursor to the next position.
|
||||
*
|
||||
* @param string $sRequestedClassAlias The class alias to fetch (if there are several objects/classes per row)
|
||||
*
|
||||
* @return \DBObject The fetched object or null when at the end
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \MySQLException
|
||||
*/
|
||||
public function Fetch($sRequestedClassAlias = '')
|
||||
{
|
||||
if (!$this->m_bLoaded) $this->Load();
|
||||
@@ -855,11 +930,15 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
return $oRetObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the whole row of objects (if several classes have been specified in the query) and move the cursor to the next position
|
||||
*
|
||||
* @return hash A hash with the format 'classAlias' => $oObj representing the current row of the set. Returns null when at the end.
|
||||
*/
|
||||
/**
|
||||
* Fetch the whole row of objects (if several classes have been specified in the query) and move the cursor to the next position
|
||||
*
|
||||
* @return array An associative with the format 'classAlias' => $oObj representing the current row of the set. Returns null when at the end.
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \MySQLException
|
||||
*/
|
||||
public function FetchAssoc()
|
||||
{
|
||||
if (!$this->m_bLoaded) $this->Load();
|
||||
@@ -902,6 +981,8 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
|
||||
/**
|
||||
* Position the cursor (for iterating in the set) to the first position (equivalent to Seek(0))
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function Rewind()
|
||||
{
|
||||
@@ -911,11 +992,18 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Position the cursor (for iterating in the set) to the given position
|
||||
*
|
||||
* @param int $iRow
|
||||
*/
|
||||
/**
|
||||
* Position the cursor (for iterating in the set) to the given position
|
||||
*
|
||||
* @param int $iRow
|
||||
*
|
||||
* @return int|mixed
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
*/
|
||||
public function Seek($iRow)
|
||||
{
|
||||
if (!$this->m_bLoaded) $this->Load();
|
||||
@@ -928,15 +1016,17 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
return $this->m_iCurrRow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an object to the current set (in-memory only, nothing is written to the database)
|
||||
*
|
||||
* Limitation:
|
||||
* Sets with several objects per row are NOT supported
|
||||
*
|
||||
* @param DBObject $oObject The object to add
|
||||
* @param string $sClassAlias The alias for the class of the object
|
||||
*/
|
||||
/**
|
||||
* Add an object to the current set (in-memory only, nothing is written to the database)
|
||||
*
|
||||
* Limitation:
|
||||
* Sets with several objects per row are NOT supported
|
||||
*
|
||||
* @param \DBObject $oObject The object to add
|
||||
* @param string $sClassAlias The alias for the class of the object
|
||||
*
|
||||
* @throws \MySQLException
|
||||
*/
|
||||
public function AddObject($oObject, $sClassAlias = '')
|
||||
{
|
||||
if (!$this->m_bLoaded) $this->Load();
|
||||
@@ -954,16 +1044,18 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a hash containig objects into the current set.
|
||||
*
|
||||
* The expected format for the hash is: $aObjectArray[$idx][$sClassAlias] => $oObject
|
||||
* Limitation:
|
||||
* The aliases MUST match the ones used in the current set
|
||||
* Only the ID of the objects associated to the first alias (column) is remembered.. in case we have to rebuild a filter
|
||||
*
|
||||
* @param hash $aObjectArray
|
||||
*/
|
||||
/**
|
||||
* Add a hash containig objects into the current set.
|
||||
*
|
||||
* The expected format for the hash is: $aObjectArray[$idx][$sClassAlias] => $oObject
|
||||
* Limitation:
|
||||
* The aliases MUST match the ones used in the current set
|
||||
* Only the ID of the objects associated to the first alias (column) is remembered.. in case we have to rebuild a filter
|
||||
*
|
||||
* @param array $aObjectArray
|
||||
*
|
||||
* @throws \MySQLException
|
||||
*/
|
||||
protected function AddObjectExtended($aObjectArray)
|
||||
{
|
||||
if (!$this->m_bLoaded) $this->Load();
|
||||
@@ -983,15 +1075,17 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an array of objects into the current set
|
||||
*
|
||||
* Limitation:
|
||||
* Sets with several classes per row are not supported (use AddObjectExtended instead)
|
||||
*
|
||||
* @param array $aObjects The array of objects to add
|
||||
* @param string $sClassAlias The Alias of the class for the added objects
|
||||
*/
|
||||
/**
|
||||
* Add an array of objects into the current set
|
||||
*
|
||||
* Limitation:
|
||||
* Sets with several classes per row are not supported (use AddObjectExtended instead)
|
||||
*
|
||||
* @param array $aObjects The array of objects to add
|
||||
* @param string $sClassAlias The Alias of the class for the added objects
|
||||
*
|
||||
* @throws \MySQLException
|
||||
*/
|
||||
public function AddObjectArray($aObjects, $sClassAlias = '')
|
||||
{
|
||||
if (!$this->m_bLoaded) $this->Load();
|
||||
@@ -1009,8 +1103,9 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
* Limitation:
|
||||
* The added objects are not checked for duplicates (i.e. one cann add several times the same object, or add an object already present in the set).
|
||||
*
|
||||
* @param DBObjectSet $oObjectSet The set to append
|
||||
* @throws CoreException
|
||||
* @param \DBObjectSet $oObjectSet The set to append
|
||||
*
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function Append(DBObjectSet $oObjectSet)
|
||||
{
|
||||
@@ -1027,17 +1122,24 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a set containing the objects present in both the current set and another specified set
|
||||
*
|
||||
* Limitations:
|
||||
* Will NOT work if only a subset of the sets was loaded with SetLimit.
|
||||
* Works only with sets made of objects loaded from the database since the comparison is based on the objects identifiers
|
||||
*
|
||||
* @param DBObjectSet $oObjectSet The set to intersect with. The current position inside the set will be lost (= at the end)
|
||||
* @throws CoreException
|
||||
* @return DBObjectSet A new set of objects, containing the objects present in both sets (based on their identifier)
|
||||
*/
|
||||
/**
|
||||
* Create a set containing the objects present in both the current set and another specified set
|
||||
*
|
||||
* Limitations:
|
||||
* Will NOT work if only a subset of the sets was loaded with SetLimit.
|
||||
* Works only with sets made of objects loaded from the database since the comparison is based on the objects identifiers
|
||||
*
|
||||
* @param \DBObjectSet $oObjectSet The set to intersect with. The current position inside the set will be lost (= at the end)
|
||||
*
|
||||
* @return \DBObjectSet A new set of objects, containing the objects present in both sets (based on their identifier)
|
||||
*
|
||||
* @throws \Exception
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
*/
|
||||
public function CreateIntersect(DBObjectSet $oObjectSet)
|
||||
{
|
||||
if ($this->GetRootClass() != $oObjectSet->GetRootClass())
|
||||
@@ -1069,16 +1171,19 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
return $oNewSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two sets of objects to determine if their content is identical or not.
|
||||
*
|
||||
* Limitation:
|
||||
* Works only for sets of 1 column (i.e. one class of object selected)
|
||||
*
|
||||
* @param DBObjectSet $oObjectSet
|
||||
* @param array $aExcludeColumns The list of columns to exclude frop the comparison
|
||||
* @return boolean True if the sets are identical, false otherwise
|
||||
*/
|
||||
/**
|
||||
* Compare two sets of objects to determine if their content is identical or not.
|
||||
*
|
||||
* Limitation:
|
||||
* Works only for sets of 1 column (i.e. one class of object selected)
|
||||
*
|
||||
* @param \DBObjectSet $oObjectSet
|
||||
* @param array $aExcludeColumns The list of columns to exclude frop the comparison
|
||||
*
|
||||
* @return boolean True if the sets are identical, false otherwise
|
||||
*
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function HasSameContents(DBObjectSet $oObjectSet, $aExcludeColumns = array())
|
||||
{
|
||||
$oComparator = new DBObjectSetComparator($this, $oObjectSet, $aExcludeColumns);
|
||||
@@ -1092,10 +1197,12 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
* The objects inside the set must be written in the database since the comparison is based on their identifiers
|
||||
* Sets with several objects per row are NOT supported
|
||||
*
|
||||
* @param DBObjectSet $oObjectSet
|
||||
* @throws CoreException
|
||||
* @param \DBObjectSet $oObjectSet
|
||||
*
|
||||
* @return DBObjectSet The "delta" set.
|
||||
* @return \DBObjectSet The "delta" set.
|
||||
*
|
||||
* @throws \Exception
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function CreateDelta(DBObjectSet $oObjectSet)
|
||||
{
|
||||
@@ -1128,9 +1235,11 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
return $oNewSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will be deprecated soon - use MetaModel::GetRelatedObjectsDown/Up instead to take redundancy into account
|
||||
*/
|
||||
/**
|
||||
* Will be deprecated soon - use MetaModel::GetRelatedObjectsDown/Up instead to take redundancy into account
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function GetRelatedObjects($sRelCode, $iMaxDepth = 99)
|
||||
{
|
||||
$aRelatedObjs = array();
|
||||
@@ -1154,16 +1263,21 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
return $aRelatedObjs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the "RelatedObjects" (forward or "down" direction) for the set
|
||||
* for the specified relation
|
||||
*
|
||||
* @param string $sRelCode The code of the relation to use for the computation
|
||||
* @param int $iMaxDepth Maximum recursion depth
|
||||
* @param boolean $bEnableReduncancy Whether or not to take into account the redundancy
|
||||
*
|
||||
* @return RelationGraph The graph of all the related objects
|
||||
*/
|
||||
/**
|
||||
* Compute the "RelatedObjects" (forward or "down" direction) for the set
|
||||
* for the specified relation
|
||||
*
|
||||
* @param string $sRelCode The code of the relation to use for the computation
|
||||
* @param int $iMaxDepth Maximum recursion depth
|
||||
* @param bool $bEnableRedundancy
|
||||
*
|
||||
* @return \RelationGraph The graph of all the related objects
|
||||
*
|
||||
* @throws \Exception
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \MySQLException
|
||||
*/
|
||||
public function GetRelatedObjectsDown($sRelCode, $iMaxDepth = 99, $bEnableRedundancy = true)
|
||||
{
|
||||
$oGraph = new RelationGraph();
|
||||
@@ -1175,17 +1289,22 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
$oGraph->ComputeRelatedObjectsDown($sRelCode, $iMaxDepth, $bEnableRedundancy);
|
||||
return $oGraph;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the "RelatedObjects" (reverse or "up" direction) for the set
|
||||
* for the specified relation
|
||||
*
|
||||
* @param string $sRelCode The code of the relation to use for the computation
|
||||
* @param int $iMaxDepth Maximum recursion depth
|
||||
* @param boolean $bEnableReduncancy Whether or not to take into account the redundancy
|
||||
*
|
||||
* @return RelationGraph The graph of all the related objects
|
||||
*/
|
||||
|
||||
/**
|
||||
* Compute the "RelatedObjects" (reverse or "up" direction) for the set
|
||||
* for the specified relation
|
||||
*
|
||||
* @param string $sRelCode The code of the relation to use for the computation
|
||||
* @param int $iMaxDepth Maximum recursion depth
|
||||
* @param bool $bEnableRedundancy
|
||||
*
|
||||
* @return \RelationGraph The graph of all the related objects
|
||||
*
|
||||
* @throws \Exception
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \MySQLException
|
||||
*/
|
||||
public function GetRelatedObjectsUp($sRelCode, $iMaxDepth = 99, $bEnableRedundancy = true)
|
||||
{
|
||||
$oGraph = new RelationGraph();
|
||||
@@ -1197,14 +1316,21 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
$oGraph->ComputeRelatedObjectsUp($sRelCode, $iMaxDepth, $bEnableRedundancy);
|
||||
return $oGraph;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an object that contains the values that are common to all the objects
|
||||
* in the set. If for a given attribute, objects in the set have various values
|
||||
* then the resulting object will contain null for this value.
|
||||
* @param $aValues Hash Output: the distribution of the values, in the set, for each attribute
|
||||
* @return DBObject The object with the common values
|
||||
*/
|
||||
|
||||
/**
|
||||
* Builds an object that contains the values that are common to all the objects
|
||||
* in the set. If for a given attribute, objects in the set have various values
|
||||
* then the resulting object will contain null for this value.
|
||||
*
|
||||
* @param array $aValues Hash Output: the distribution of the values, in the set, for each attribute
|
||||
*
|
||||
* @return \DBObject The object with the common values
|
||||
*
|
||||
* @throws \Exception
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \MySQLException
|
||||
*/
|
||||
public function ComputeCommonObject(&$aValues)
|
||||
{
|
||||
$sClass = $this->GetClass();
|
||||
@@ -1288,7 +1414,7 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
|
||||
/**
|
||||
* List the constant fields (and their value) in the given query
|
||||
* @return Hash [Alias][AttCode] => value
|
||||
* @return array [Alias][AttCode] => value
|
||||
*/
|
||||
public function ListConstantFields()
|
||||
{
|
||||
@@ -1378,10 +1504,12 @@ class DBObjectSetComparator
|
||||
$this->oSet1 = $oSet1;
|
||||
$this->oSet2 = $oSet2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the lists of fingerprints and initializes internal structures, if it was not already done
|
||||
*/
|
||||
|
||||
/**
|
||||
* Builds the lists of fingerprints and initializes internal structures, if it was not already done
|
||||
*
|
||||
* @throws \CoreException
|
||||
*/
|
||||
protected function ComputeFingerprints()
|
||||
{
|
||||
if ($this->aFingerprints1 === null)
|
||||
@@ -1426,11 +1554,13 @@ class DBObjectSetComparator
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if the sets are equivalent or not. Returns as soon as the first difference is found.
|
||||
* @return boolean true if the set have an equivalent content, false otherwise
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tells if the sets are equivalent or not. Returns as soon as the first difference is found.
|
||||
* @return boolean true if the set have an equivalent content, false otherwise
|
||||
*
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function SetsAreEquivalent()
|
||||
{
|
||||
if (($this->oSet1 === null) && ($this->oSet2 === null))
|
||||
@@ -1469,13 +1599,16 @@ class DBObjectSetComparator
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of differences between the two sets. In ordeer to write back into the database only the minimum changes
|
||||
* THE FIRST SET MUST BE THE ONE LOADED FROM THE DATABASE
|
||||
* Returns a hash: 'added' => DBObject(s), 'removed' => DBObject(s), 'modified' => DBObjects(s)
|
||||
* @return Ambigous <int:DBObject: , unknown>
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the list of differences between the two sets. In ordeer to write back into the database only the minimum changes
|
||||
* THE FIRST SET MUST BE THE ONE LOADED FROM THE DATABASE
|
||||
* Returns a hash: 'added' => DBObject(s), 'removed' => DBObject(s), 'modified' => DBObjects(s)
|
||||
* @return array
|
||||
*
|
||||
* @throws \Exception
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function GetDifferences()
|
||||
{
|
||||
$aResult = array('added' => array(), 'removed' => array(), 'modified' => array());
|
||||
@@ -1524,13 +1657,19 @@ class DBObjectSetComparator
|
||||
}
|
||||
return $aResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helpr to clone (in memory) an object and to apply to it the values taken from a second object
|
||||
* @param DBObject $oObjToClone
|
||||
* @param DBObject $oObjWithValues
|
||||
* @return DBObject The modified clone
|
||||
*/
|
||||
|
||||
/**
|
||||
* Helpr to clone (in memory) an object and to apply to it the values taken from a second object
|
||||
*
|
||||
* @param \DBObject $oObjToClone
|
||||
* @param \DBObject $oObjWithValues
|
||||
*
|
||||
* @return \DBObject The modified clone
|
||||
*
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
*/
|
||||
protected function CopyFrom($oObjToClone, $oObjWithValues)
|
||||
{
|
||||
$oObj = MetaModel::GetObject(get_class($oObjToClone), $oObjToClone->GetKey());
|
||||
|
||||
@@ -235,29 +235,96 @@ abstract class DBSearch
|
||||
abstract public function GetInternalParams();
|
||||
abstract public function GetQueryParams($bExcludeMagicParams = true);
|
||||
abstract public function ListConstantFields();
|
||||
|
||||
|
||||
/**
|
||||
* Turn the parameters (:xxx) into scalar values in order to easily
|
||||
* serialize a search
|
||||
*
|
||||
* @param array $aArgs
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function ApplyParameters($aArgs);
|
||||
|
||||
public function serialize($bDevelopParams = false, $aContextParams = null)
|
||||
public function serialize($bDevelopParams = false, $aContextParams = array())
|
||||
{
|
||||
$aQueryParams = $this->GetQueryParams();
|
||||
|
||||
$aContextParams = array_merge($this->GetInternalParams(), $aContextParams);
|
||||
|
||||
foreach($aQueryParams as $sParam => $sValue)
|
||||
{
|
||||
if (isset($aContextParams[$sParam]))
|
||||
{
|
||||
$aQueryParams[$sParam] = $aContextParams[$sParam];
|
||||
}
|
||||
elseif (($iPos = strpos($sParam, '->')) !== false)
|
||||
{
|
||||
$sParamName = substr($sParam, 0, $iPos);
|
||||
if (isset($aContextParams[$sParamName.'->object()']))
|
||||
{
|
||||
$sAttCode = substr($sParam, $iPos + 2);
|
||||
/** @var \DBObject $oObj */
|
||||
$oObj = $aContextParams[$sParamName.'->object()'];
|
||||
if ($oObj->IsModified())
|
||||
{
|
||||
if ($sAttCode == 'id')
|
||||
{
|
||||
$aQueryParams[$sParam] = $oObj->GetKey();
|
||||
}
|
||||
else
|
||||
{
|
||||
$aQueryParams[$sParam] = $oObj->Get($sAttCode);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unset($aQueryParams[$sParam]);
|
||||
// For database objects, serialize only class, key
|
||||
$aQueryParams[$sParamName.'->id'] = $oObj->GetKey();
|
||||
$aQueryParams[$sParamName.'->class'] = get_class($oObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$sOql = $this->ToOql($bDevelopParams, $aContextParams);
|
||||
return rawurlencode(base64_encode(serialize(array($sOql, $this->GetInternalParams(), $this->m_aModifierProperties))));
|
||||
return json_encode(array($sOql, $aQueryParams, $this->m_aModifierProperties));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sValue Serialized OQL query
|
||||
*
|
||||
* @return \DBSearch
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \OQLException
|
||||
*/
|
||||
static public function unserialize($sValue)
|
||||
{
|
||||
$aData = unserialize(base64_decode(rawurldecode($sValue)));
|
||||
$aData = json_decode($sValue, true);
|
||||
if (is_null($aData))
|
||||
{
|
||||
throw new CoreException("Invalid filter parameter");
|
||||
}
|
||||
$sOql = $aData[0];
|
||||
$aParams = $aData[1];
|
||||
$aExtraParams = array();
|
||||
foreach($aParams as $sParam => $sValue)
|
||||
{
|
||||
if (($iPos = strpos($sParam, '->class')) !== false)
|
||||
{
|
||||
$sParamName = substr($sParam, 0, $iPos);
|
||||
if (isset($aParams[$sParamName.'->id']))
|
||||
{
|
||||
$sClass = $aParams[$sParamName.'->class'];
|
||||
$iKey = $aParams[$sParamName.'->id'];
|
||||
$oObj = MetaModel::GetObject($sClass, $iKey);
|
||||
$aExtraParams[$sParamName.'->object()'] = $oObj;
|
||||
}
|
||||
}
|
||||
}
|
||||
$aParams = array_merge($aExtraParams, $aParams);
|
||||
// We've tried to use gzcompress/gzuncompress, but for some specific queries
|
||||
// it was not working at all (See Trac #193)
|
||||
// gzuncompress was issuing a warning "data error" and the return object was null
|
||||
@@ -335,6 +402,7 @@ abstract class DBSearch
|
||||
}
|
||||
}
|
||||
|
||||
/** @var DBObjectSearch | null $oResultFilter */
|
||||
if (!isset($oResultFilter))
|
||||
{
|
||||
$oKPI = new ExecutionKPI();
|
||||
@@ -373,11 +441,20 @@ abstract class DBSearch
|
||||
return $oResultFilter;
|
||||
}
|
||||
|
||||
// Alternative to object mapping: the data are transfered directly into an array
|
||||
// This is 10 times faster than creating a set of objects, and makes sense when optimization is required
|
||||
/**
|
||||
* @param hash $aOrderBy Array of '[<classalias>.]attcode' => bAscending
|
||||
*/
|
||||
* Alternative to object mapping: the data are transfered directly into an array
|
||||
* This is 10 times faster than creating a set of objects, and makes sense when optimization is required
|
||||
*
|
||||
* @param array $aColumns
|
||||
* @param array $aOrderBy Array of '[<classalias>.]attcode' => bAscending
|
||||
* @param array $aArgs
|
||||
*
|
||||
* @return array|void
|
||||
* @throws \CoreException
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
*/
|
||||
public function ToDataArray($aColumns = array(), $aOrderBy = array(), $aArgs = array())
|
||||
{
|
||||
$sSQL = $this->MakeSelectQuery($aOrderBy, $aArgs);
|
||||
|
||||
@@ -116,6 +116,22 @@ class Dict
|
||||
self::$m_iErrorMode = $iErrorMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a dictionary entry exists or not
|
||||
* @param $sStringCode
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function Exists($sStringCode)
|
||||
{
|
||||
$sImpossibleString = 'aVlHYKEI3TZuDV5o0pghv7fvhYNYuzYkTk7WL0Zoqw8rggE7aq';
|
||||
if (static::S($sStringCode, $sImpossibleString) === $sImpossibleString)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a localised string from the dictonary
|
||||
*
|
||||
@@ -197,7 +213,7 @@ class Dict
|
||||
/**
|
||||
* Initialize a the entries for a given language (replaces the former Add() method)
|
||||
* @param string $sLanguageCode Code identifying the language i.e. 'FR-FR', 'EN-US'
|
||||
* @param hash $aEntries Hash array of dictionnary entries
|
||||
* @param array $aEntries Hash array of dictionnary entries
|
||||
*/
|
||||
public static function SetEntries($sLanguageCode, $aEntries)
|
||||
{
|
||||
|
||||
@@ -1270,7 +1270,6 @@ class DisplayableGraph extends SimpleGraph
|
||||
|
||||
$oPdf->SetAutoPageBreak(true, $fBreakMargin);
|
||||
$oPdf->SetAlpha(1);
|
||||
$oPdf->SetTextColor(0, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -234,28 +234,19 @@ class EMail
|
||||
$oDOMDoc = new DOMDocument();
|
||||
$oDOMDoc->preserveWhitespace = true;
|
||||
@$oDOMDoc->loadHTML('<?xml encoding="UTF-8"?>'.$this->m_aData['body']['body']); // For loading HTML chunks where the character set is not specified
|
||||
|
||||
|
||||
$oXPath = new DOMXPath($oDOMDoc);
|
||||
$sXPath = '//img[@'.InlineImage::DOM_ATTR_ID.']';
|
||||
$sXPath = "//img[@data-img-id]";
|
||||
$oImagesList = $oXPath->query($sXPath);
|
||||
|
||||
|
||||
if ($oImagesList->length != 0)
|
||||
{
|
||||
foreach($oImagesList as $oImg)
|
||||
{
|
||||
$iAttId = $oImg->getAttribute(InlineImage::DOM_ATTR_ID);
|
||||
$iAttId = $oImg->getAttribute('data-img-id');
|
||||
$oAttachment = MetaModel::GetObject('InlineImage', $iAttId, false, true /* Allow All Data */);
|
||||
if ($oAttachment)
|
||||
{
|
||||
$sImageSecret = $oImg->getAttribute('data-img-secret');
|
||||
$sAttachmentSecret = $oAttachment->Get('secret');
|
||||
if ($sImageSecret !== $sAttachmentSecret)
|
||||
{
|
||||
// @see N°1921
|
||||
// If copying from another iTop we could get an IMG pointing to an InlineImage with wrong secret
|
||||
continue;
|
||||
}
|
||||
|
||||
$oDoc = $oAttachment->Get('contents');
|
||||
$oSwiftImage = new Swift_Image($oDoc->GetData(), $oDoc->GetFileName(), $oDoc->GetMimeType());
|
||||
$sCid = $this->m_oMessage->embed($oSwiftImage);
|
||||
|
||||
@@ -188,11 +188,16 @@ EOF
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
|
||||
$sRet = $oAttDef->GetAsCSV($value, '', '', $oObj);
|
||||
}
|
||||
else if ($value instanceOf ormDocument)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
|
||||
$sRet = $oAttDef->GetAsCSV($value, '', '', $oObj);
|
||||
}
|
||||
else if ($value instanceOf ormDocument)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
|
||||
$sRet = $oAttDef->GetAsCSV($value, '', '', $oObj);
|
||||
}
|
||||
else if ($value instanceOf ormTagSet)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
|
||||
$sRet = $oAttDef->GetAsCSV($value, '', '', $oObj);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
|
||||
|
||||
@@ -78,7 +78,6 @@ EOF;
|
||||
}
|
||||
EOF;
|
||||
|
||||
SetupUtils::builddir(dirname($sFilePath));
|
||||
file_put_contents($sFilePath, $content);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ class HTMLDOMSanitizer extends HTMLSanitizer
|
||||
|
||||
/**
|
||||
* @var array
|
||||
* @see https://www.itophub.io/wiki/page?id=2_5_0%3Aadmin%3Arich_text_limitations
|
||||
* @see https://www.itophub.io/wiki/page?id=2_6_0%3Aadmin%3Arich_text_limitations
|
||||
*/
|
||||
protected static $aTagsWhiteList = array(
|
||||
'html' => array(),
|
||||
@@ -211,7 +211,7 @@ class HTMLDOMSanitizer extends HTMLSanitizer
|
||||
|
||||
/**
|
||||
* @var array
|
||||
* @see https://www.itophub.io/wiki/page?id=2_5_0%3Aadmin%3Arich_text_limitations
|
||||
* @see https://www.itophub.io/wiki/page?id=2_6_0%3Aadmin%3Arich_text_limitations
|
||||
*/
|
||||
protected static $aStylesWhiteList = array(
|
||||
'background-color',
|
||||
@@ -230,7 +230,9 @@ class HTMLDOMSanitizer extends HTMLSanitizer
|
||||
'margin',
|
||||
'padding',
|
||||
'text-align',
|
||||
'vertical-align',
|
||||
'width',
|
||||
'white-space',
|
||||
);
|
||||
|
||||
public function __construct()
|
||||
@@ -346,7 +348,7 @@ class HTMLDOMSanitizer extends HTMLSanitizer
|
||||
$this->CleanNode($oNode);
|
||||
if (($oNode instanceof DOMElement) && (strtolower($oNode->tagName) == 'img'))
|
||||
{
|
||||
InlineImage::ProcessImageTag($oNode);
|
||||
$this->ProcessImage($oNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -357,7 +359,24 @@ class HTMLDOMSanitizer extends HTMLSanitizer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add an extra attribute data-img-id for images which are based on an actual InlineImage
|
||||
* so that we can later reconstruct the full "src" URL when needed
|
||||
* @param DOMNode $oElement
|
||||
*/
|
||||
protected function ProcessImage(DOMNode $oElement)
|
||||
{
|
||||
$sSrc = $oElement->getAttribute('src');
|
||||
$sDownloadUrl = str_replace(array('.', '?'), array('\.', '\?'), INLINEIMAGE_DOWNLOAD_URL); // Escape . and ?
|
||||
$sUrlPattern = '|'.$sDownloadUrl.'([0-9]+)&s=([0-9a-f]+)|';
|
||||
if (preg_match($sUrlPattern, $sSrc, $aMatches))
|
||||
{
|
||||
$oElement->setAttribute('data-img-id', $aMatches[1]);
|
||||
$oElement->setAttribute('data-img-secret', $aMatches[2]);
|
||||
}
|
||||
}
|
||||
|
||||
protected function CleanStyle($sStyle)
|
||||
{
|
||||
$aAllowedStyles = array();
|
||||
|
||||
@@ -27,11 +27,6 @@ define('INLINEIMAGE_DOWNLOAD_URL', 'pages/ajax.document.php?operation=download_i
|
||||
|
||||
class InlineImage extends DBObject
|
||||
{
|
||||
/** @var string attribute to be added to IMG tags to contain ID */
|
||||
const DOM_ATTR_ID = 'data-img-id';
|
||||
/** @var string attribute to be added to IMG tags to contain secret */
|
||||
const DOM_ATTR_SECRET = 'data-img-secret';
|
||||
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
@@ -166,7 +161,7 @@ class InlineImage extends DBObject
|
||||
*/
|
||||
public static function FinalizeInlineImages(DBObject $oObject)
|
||||
{
|
||||
$iTransactionId = utils::ReadParam('transaction_id', null);
|
||||
$iTransactionId = utils::ReadParam('transaction_id', null, false, 'transaction_id');
|
||||
if (!is_null($iTransactionId))
|
||||
{
|
||||
// Attach new (temporary) inline images
|
||||
@@ -183,6 +178,18 @@ class InlineImage extends DBObject
|
||||
$oInlineImage->DBUpdate();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
IssueLog::Error('InlineImage: Error during FinalizeInlineImages(), no transaction ID for object '.get_class($oObject).'#'.$oObject->GetKey().'.');
|
||||
|
||||
IssueLog::Error('|- Call stack:');
|
||||
$oException = new Exception();
|
||||
$sStackTrace = $oException->getTraceAsString();
|
||||
IssueLog::Error($sStackTrace);
|
||||
|
||||
IssueLog::Error('|- POST vars:');
|
||||
IssueLog::Error(print_r($_POST, true));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -213,8 +220,7 @@ class InlineImage extends DBObject
|
||||
$aNeedles = array();
|
||||
$aReplacements = array();
|
||||
// Find img tags with an attribute data-img-id
|
||||
if (preg_match_all('/<img ([^>]*)'.self::DOM_ATTR_ID.'="([0-9]+)"([^>]*)>/i',
|
||||
$sHtml, $aMatches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE))
|
||||
if (preg_match_all('/<img ([^>]*)data-img-id="([0-9]+)"([^>]*)>/i', $sHtml, $aMatches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE))
|
||||
{
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot().INLINEIMAGE_DOWNLOAD_URL;
|
||||
foreach($aMatches as $aImgInfo)
|
||||
@@ -236,42 +242,6 @@ class InlineImage extends DBObject
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an extra attribute data-img-id for images which are based on an actual InlineImage
|
||||
* so that we can later reconstruct the full "src" URL when needed
|
||||
*
|
||||
* @param \DOMElement $oElement
|
||||
*/
|
||||
public static function ProcessImageTag(DOMElement $oElement)
|
||||
{
|
||||
$sSrc = $oElement->getAttribute('src');
|
||||
$sDownloadUrl = str_replace(array('.', '?'), array('\.', '\?'), INLINEIMAGE_DOWNLOAD_URL); // Escape . and ?
|
||||
$sUrlPattern = '|'.$sDownloadUrl.'([0-9]+)&s=([0-9a-f]+)|';
|
||||
$bIsInlineImage = preg_match($sUrlPattern, $sSrc, $aMatches);
|
||||
if (!$bIsInlineImage)
|
||||
{
|
||||
return;
|
||||
}
|
||||
$iInlineImageId = $aMatches[1];
|
||||
$sInlineIMageSecret = $aMatches[2];
|
||||
|
||||
$sAppRoot = utils::GetAbsoluteUrlAppRoot();
|
||||
$sAppRootPattern = '/^'.preg_quote($sAppRoot, '/').'/';
|
||||
$bIsSameItop = preg_match($sAppRootPattern, $sSrc);
|
||||
if (!$bIsSameItop)
|
||||
{
|
||||
// @see N°1921
|
||||
// image from another iTop should be treated as external images
|
||||
$oElement->removeAttribute(self::DOM_ATTR_ID);
|
||||
$oElement->removeAttribute(self::DOM_ATTR_SECRET);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$oElement->setAttribute(self::DOM_ATTR_ID, $iInlineImageId);
|
||||
$oElement->setAttribute(self::DOM_ATTR_SECRET, $sInlineIMageSecret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the javascript fragment - to be added to "on document ready" - to adjust (on the fly) the width on Inline Images
|
||||
*/
|
||||
@@ -299,8 +269,12 @@ EOF
|
||||
|
||||
/**
|
||||
* Check if an the given mimeType is an image that can be processed by the system
|
||||
*
|
||||
* @param string $sMimeType
|
||||
* @return boolean
|
||||
*
|
||||
* @return boolean always false if php-gd not installed
|
||||
* otherwise true if file is one of those type : image/gif, image/jpeg, image/png
|
||||
* @uses php-gd extension
|
||||
*/
|
||||
public static function IsImage($sMimeType)
|
||||
{
|
||||
@@ -446,9 +420,11 @@ EOF
|
||||
* Get the fragment of javascript needed to complete the initialization of
|
||||
* CKEditor when creating/modifying an object
|
||||
*
|
||||
* @param DBObject $oObject The object being edited
|
||||
* @param string $sTempId The concatenation of session_id().'_'.$iTransactionId.
|
||||
* @param \DBObject $oObject The object being edited
|
||||
* @param string $sTempId Generated through utils::GetUploadTempId($iTransactionId)
|
||||
*
|
||||
* @return string The JS fragment to insert in "on document ready"
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function EnableCKEditorImageUpload(DBObject $oObject, $sTempId)
|
||||
{
|
||||
@@ -541,49 +517,67 @@ class InlineImageGC implements iBackgroundProcess
|
||||
{
|
||||
public function GetPeriodicity()
|
||||
{
|
||||
return 3600; // Runs every hour
|
||||
return 1; // Runs every 8 hours
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $iTimeLimit
|
||||
*
|
||||
* @return string
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \DeleteException
|
||||
* @throws \MySQLException
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public function Process($iTimeLimit)
|
||||
{
|
||||
$sDateLimit = date(AttributeDateTime::GetSQLFormat(), time()); // Every temporary InlineImage/Attachment expired will be deleted
|
||||
|
||||
$iProcessed = 0;
|
||||
$sOQL = "SELECT InlineImage WHERE (item_id = 0) AND (expire < '$sDateLimit')";
|
||||
while (time() < $iTimeLimit)
|
||||
$aResults = array();
|
||||
$aClasses = array('InlineImage', 'Attachment');
|
||||
foreach($aClasses as $sClass)
|
||||
{
|
||||
// Next one ?
|
||||
$oSet = new CMDBObjectSet(DBObjectSearch::FromOQL($sOQL), array('expire' => true) /* order by*/, array(), null, 1 /* limit count */);
|
||||
$oSet->OptimizeColumnLoad(array());
|
||||
$oResult = $oSet->Fetch();
|
||||
if (is_null($oResult))
|
||||
$iProcessed = 0;
|
||||
if(class_exists($sClass))
|
||||
{
|
||||
// Nothing to be done
|
||||
break;
|
||||
$iProcessed = $this->DeleteExpiredDocuments($sClass, $iTimeLimit, $sDateLimit);
|
||||
}
|
||||
$iProcessed++;
|
||||
$oResult->DBDelete();
|
||||
$aResults[] = "$iProcessed old temporary $sClass(s)";
|
||||
}
|
||||
|
||||
$iProcessed2 = 0;
|
||||
if (class_exists('Attachment'))
|
||||
{
|
||||
$sOQL = "SELECT Attachment WHERE (item_id = 0) AND (expire < '$sDateLimit')";
|
||||
while (time() < $iTimeLimit)
|
||||
{
|
||||
// Next one ?
|
||||
$oSet = new CMDBObjectSet(DBObjectSearch::FromOQL($sOQL), array('expire' => true) /* order by*/, array(), null, 1 /* limit count */);
|
||||
$oSet->OptimizeColumnLoad(array());
|
||||
$oResult = $oSet->Fetch();
|
||||
if (is_null($oResult))
|
||||
{
|
||||
// Nothing to be done
|
||||
break;
|
||||
}
|
||||
$iProcessed2++;
|
||||
$oResult->DBDelete();
|
||||
}
|
||||
}
|
||||
return "Cleaned $iProcessed old temporary InlineImage(s) and $iProcessed2 old temporary Attachment(s).";
|
||||
|
||||
return "Cleaned ".implode(' and ', $aResults).".";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sClass
|
||||
* @param int $iTimeLimit
|
||||
* @param string $sDateLimit
|
||||
*
|
||||
* @return int
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \DeleteException
|
||||
* @throws \MySQLException
|
||||
* @throws \OQLException
|
||||
*/
|
||||
protected function DeleteExpiredDocuments($sClass, $iTimeLimit, $sDateLimit)
|
||||
{
|
||||
$iProcessed = 0;
|
||||
$sOQL = "SELECT $sClass WHERE (item_id = 0) AND (expire < '$sDateLimit')";
|
||||
// Next one ?
|
||||
$oSet = new CMDBObjectSet(DBObjectSearch::FromOQL($sOQL), array('expire' => true) /* order by*/, array(), null,
|
||||
1 /* limit count */);
|
||||
$oSet->OptimizeColumnLoad(array());
|
||||
while ((time() < $iTimeLimit) && ($oResult = $oSet->Fetch()))
|
||||
{
|
||||
/** @var \ormDocument $oDocument */
|
||||
$oDocument = $oResult->Get('contents');
|
||||
IssueLog::Info($sClass.' GC: Removed temp. file '.$oDocument->GetFileName().' on "'.$oResult->Get('item_class').'" #'.$oResult->Get('item_id').' as it has expired.');
|
||||
$oResult->DBDelete();
|
||||
$iProcessed++;
|
||||
}
|
||||
|
||||
return $iProcessed;
|
||||
}
|
||||
}
|
||||
@@ -116,13 +116,14 @@ class ExecutionKPI
|
||||
foreach (self::$m_aExecData as $aOpStats)
|
||||
{
|
||||
$sOperation = $aOpStats['op'];
|
||||
$sBegin = $sEnd = $sDuration = $sMemBegin = $sMemEnd = $sMemPeak = '?';
|
||||
|
||||
$sBegin = round($aOpStats['time_begin'], 3);
|
||||
$sEnd = round($aOpStats['time_end'], 3);
|
||||
$fDuration = $aOpStats['time_end'] - $aOpStats['time_begin'];
|
||||
$sDuration = round($fDuration, 3);
|
||||
|
||||
$sMemBegin = 'n/a';
|
||||
$sMemEnd = 'n/a';
|
||||
$sMemPeak = 'n/a';
|
||||
if (isset($aOpStats['mem_begin']))
|
||||
{
|
||||
$sMemBegin = self::MemStr($aOpStats['mem_begin']);
|
||||
@@ -197,12 +198,13 @@ class ExecutionKPI
|
||||
|
||||
self::Report("</div>");
|
||||
|
||||
self::Report("<p><a href=\"#end-".md5($sExecId)."\">Next page stats</a></p>");
|
||||
|
||||
// Report operation details
|
||||
foreach (self::$m_aStats as $sOperation => $aOpStats)
|
||||
{
|
||||
$sOperationHtml = '<a name="'.md5($sExecId.$sOperation).'">'.$sOperation.'</a>';
|
||||
self::Report("<h4>$sOperationHtml</h4>");
|
||||
self::Report("<p><a href=\"#".md5($sExecId)."\">Back to page stats</a></p>");
|
||||
self::Report("<table border=\"1\" style=\"$sTableStyle\">");
|
||||
self::Report("<thead>");
|
||||
self::Report(" <th>Operation details (+ blame caller if log_kpi_duration = 2)</th><th>Count</th><th>Duration</th><th>Min</th><th>Max</th>");
|
||||
@@ -251,7 +253,9 @@ class ExecutionKPI
|
||||
self::Report("</tr>");
|
||||
}
|
||||
self::Report("</table>");
|
||||
self::Report("<p><a href=\"#".md5($sExecId)."\">Back to page stats</a></p>");
|
||||
}
|
||||
self::Report('<a name="end-'.md5($sExecId).'"> </a>');
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@ abstract class MetaModel
|
||||
// $aBacktrace[1] is the info we want
|
||||
if (!empty($sExpectedFunctionName))
|
||||
{
|
||||
assert('$aBacktrace[2]["function"] == $sExpectedFunctionName');
|
||||
assert($aBacktrace[2]['function'] == $sExpectedFunctionName);
|
||||
}
|
||||
if ($bRecordSourceFile)
|
||||
{
|
||||
@@ -320,8 +320,7 @@ abstract class MetaModel
|
||||
final static public function GetName($sClass)
|
||||
{
|
||||
self::_check_subclass($sClass);
|
||||
$sStringCode = 'Class:'.$sClass;
|
||||
return Dict::S($sStringCode, str_replace('_', ' ', $sClass));
|
||||
return $sClass::GetClassName($sClass);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -411,8 +410,7 @@ abstract class MetaModel
|
||||
final static public function GetClassDescription($sClass)
|
||||
{
|
||||
self::_check_subclass($sClass);
|
||||
$sStringCode = 'Class:'.$sClass.'+';
|
||||
return Dict::S($sStringCode, '');
|
||||
return $sClass::GetClassDescription($sClass);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -528,6 +526,104 @@ abstract class MetaModel
|
||||
return $oRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sClass
|
||||
*
|
||||
* @return array
|
||||
* @throws \CoreException
|
||||
*
|
||||
* @since 2.6 N°659 uniqueness constraint
|
||||
*/
|
||||
final public static function GetUniquenessRules($sClass)
|
||||
{
|
||||
if (!isset(self::$m_aClassParams[$sClass]))
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
$aCurrentUniquenessRules = array();
|
||||
|
||||
if (array_key_exists('uniqueness_rules', self::$m_aClassParams[$sClass]))
|
||||
{
|
||||
$aCurrentUniquenessRules = self::$m_aClassParams[$sClass]['uniqueness_rules'];
|
||||
}
|
||||
|
||||
$sParentClass = self::GetParentClass($sClass);
|
||||
if ($sParentClass)
|
||||
{
|
||||
$aParentUniquenessRules = self::GetUniquenessRules($sParentClass);
|
||||
foreach ($aParentUniquenessRules as $sUniquenessRuleId => $aParentUniquenessRuleProperties)
|
||||
{
|
||||
$bCopyDisabledKey = true;
|
||||
$bCurrentDisabledValue = null;
|
||||
|
||||
if (array_key_exists($sUniquenessRuleId, $aCurrentUniquenessRules))
|
||||
{
|
||||
if (self::IsUniquenessRuleContainingOnlyDisabledKey($aCurrentUniquenessRules[$sUniquenessRuleId]))
|
||||
{
|
||||
$bCopyDisabledKey = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$aMergedUniquenessProperties = $aParentUniquenessRuleProperties;
|
||||
if (!$bCopyDisabledKey)
|
||||
{
|
||||
$aMergedUniquenessProperties['disabled'] = $aCurrentUniquenessRules[$sUniquenessRuleId]['disabled'];
|
||||
}
|
||||
$aCurrentUniquenessRules[$sUniquenessRuleId] = $aMergedUniquenessProperties;
|
||||
}
|
||||
}
|
||||
|
||||
return $aCurrentUniquenessRules;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sRuleId
|
||||
* @param string $sLeafClassName
|
||||
*
|
||||
* @return string name of the class, null if not present
|
||||
*/
|
||||
final public static function GetRootClassForUniquenessRule($sRuleId, $sLeafClassName)
|
||||
{
|
||||
$sFirstClassWithRuleId = null;
|
||||
if (isset(self::$m_aClassParams[$sLeafClassName]['uniqueness_rules'][$sRuleId]))
|
||||
{
|
||||
$sFirstClassWithRuleId = $sLeafClassName;
|
||||
}
|
||||
|
||||
$sParentClass = self::GetParentClass($sLeafClassName);
|
||||
if ($sParentClass)
|
||||
{
|
||||
$sParentClassWithRuleId = self::GetRootClassForUniquenessRule($sRuleId, $sParentClass);
|
||||
if (!is_null($sParentClassWithRuleId))
|
||||
{
|
||||
$sFirstClassWithRuleId = $sParentClassWithRuleId;
|
||||
}
|
||||
}
|
||||
|
||||
return $sFirstClassWithRuleId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $aRuleProperties
|
||||
*
|
||||
* @return bool
|
||||
* @since 2.6 N°659 uniqueness constraint
|
||||
*/
|
||||
private static function IsUniquenessRuleContainingOnlyDisabledKey($aRuleProperties)
|
||||
{
|
||||
$aNonNullRuleProperties = array_filter($aRuleProperties, function ($v) {
|
||||
return (!is_null($v));
|
||||
});
|
||||
|
||||
return ((count($aNonNullRuleProperties) == 1) && (array_key_exists('disabled', $aNonNullRuleProperties)));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $sClass
|
||||
*
|
||||
@@ -1024,7 +1120,7 @@ abstract class MetaModel
|
||||
/**
|
||||
* array of ("classname" => array of attributes)
|
||||
*
|
||||
* @var array
|
||||
* @var \AttributeDefinition[]
|
||||
*/
|
||||
private static $m_aAttribDefs = array();
|
||||
/**
|
||||
@@ -2599,7 +2695,7 @@ abstract class MetaModel
|
||||
|
||||
// Build the list of available extensions
|
||||
//
|
||||
$aInterfaces = array('iApplicationUIExtension', 'iApplicationObjectExtension', 'iQueryModifier', 'iOnClassInitialization', 'iPopupMenuExtension', 'iPageUIExtension', 'iPortalUIExtension');
|
||||
$aInterfaces = array('iApplicationUIExtension', 'iApplicationObjectExtension', 'iQueryModifier', 'iOnClassInitialization', 'iPopupMenuExtension', 'iPageUIExtension', 'iPortalUIExtension', 'ModuleHandlerApiInterface', 'iNewsroomProvider');
|
||||
foreach($aInterfaces as $sInterface)
|
||||
{
|
||||
self::$m_aExtensionClasses[$sInterface] = array();
|
||||
@@ -2682,6 +2778,25 @@ abstract class MetaModel
|
||||
$oClassInit->OnAfterClassInitialization($sPHPClass);
|
||||
}
|
||||
}
|
||||
|
||||
$aCurrentClassUniquenessRules = MetaModel::GetUniquenessRules($sPHPClass);
|
||||
if (!empty($aCurrentClassUniquenessRules))
|
||||
{
|
||||
$aClassFields = self::GetAttributesList($sPHPClass);
|
||||
foreach ($aCurrentClassUniquenessRules as $sUniquenessRuleId => $aUniquenessRuleProperties)
|
||||
{
|
||||
$bHasSameRuleInParent = self::HasSameUniquenessRuleInParent($sPHPClass, $sUniquenessRuleId);
|
||||
try
|
||||
{
|
||||
self::CheckUniquenessRuleValidity($aUniquenessRuleProperties, $bHasSameRuleInParent, $aClassFields);
|
||||
}
|
||||
catch (CoreUnexpectedValue $e)
|
||||
{
|
||||
throw new Exception("Invalid uniqueness rule declaration : class={$sPHPClass}, rule=$sUniquenessRuleId, reason={$e->getMessage()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch (ReflectionException $e)
|
||||
{
|
||||
@@ -2950,6 +3065,101 @@ abstract class MetaModel
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sClassName
|
||||
* @param string $sUniquenessRuleId
|
||||
*
|
||||
* @return bool true if one of the parent class (recursive) has the same rule defined
|
||||
* @throws \CoreException
|
||||
*/
|
||||
private static function HasSameUniquenessRuleInParent($sClassName, $sUniquenessRuleId)
|
||||
{
|
||||
$sParentClass = self::GetParentClass($sClassName);
|
||||
if (empty($sParentClass))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$aParentClassUniquenessRules = self::GetUniquenessRules($sParentClass);
|
||||
if (array_key_exists($sUniquenessRuleId, $aParentClassUniquenessRules))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return self::HasSameUniquenessRuleInParent($sParentClass, $sUniquenessRuleId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $aUniquenessRuleProperties
|
||||
* @param bool $bRuleOverride if false then control an original declaration validity,
|
||||
* otherwise an override validity (can have only the disabled key)
|
||||
* @param string[] $aExistingClassFields if non empty, will check that all fields declared in the rules exists in the class
|
||||
*
|
||||
* @throws \CoreUnexpectedValue if the rule is invalid
|
||||
*
|
||||
* @since 2.6 N°659 uniqueness constraint
|
||||
*/
|
||||
public static function CheckUniquenessRuleValidity($aUniquenessRuleProperties, $bRuleOverride = true, $aExistingClassFields = array())
|
||||
{
|
||||
$MANDATORY_ATTRIBUTES = array('attributes');
|
||||
$UNIQUENESS_MANDATORY_KEYS_NB = count($MANDATORY_ATTRIBUTES);
|
||||
$bHasDisabledKey = false;
|
||||
$bHasMissingMandatoryKey = true;
|
||||
$iMissingMandatoryKeysNb = $UNIQUENESS_MANDATORY_KEYS_NB;
|
||||
$bHasAllMandatoryKeysMissing = false;
|
||||
$bHasNonDisabledKeys = false;
|
||||
|
||||
foreach ($aUniquenessRuleProperties as $sUniquenessRuleKey => $aUniquenessRuleProperty)
|
||||
{
|
||||
if (($sUniquenessRuleKey === 'disabled') && (!is_null($aUniquenessRuleProperty)))
|
||||
{
|
||||
$bHasDisabledKey = true;
|
||||
continue;
|
||||
}
|
||||
$bHasNonDisabledKeys = true;
|
||||
|
||||
if (in_array($sUniquenessRuleKey, $MANDATORY_ATTRIBUTES, true)) {
|
||||
$bHasMissingMandatoryKey = false;
|
||||
$iMissingMandatoryKeysNb--;
|
||||
}
|
||||
|
||||
if (($sUniquenessRuleKey === 'attributes') && (!empty($aExistingClassFields)))
|
||||
{
|
||||
foreach ($aUniquenessRuleProperties[$sUniquenessRuleKey] as $sRuleAttribute)
|
||||
{
|
||||
if (!in_array($sRuleAttribute, $aExistingClassFields, true))
|
||||
{
|
||||
throw new CoreUnexpectedValue("Uniqueness rule : non existing field '$sRuleAttribute'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($iMissingMandatoryKeysNb == $UNIQUENESS_MANDATORY_KEYS_NB)
|
||||
{
|
||||
$bHasAllMandatoryKeysMissing = true;
|
||||
}
|
||||
|
||||
if ($bHasDisabledKey)
|
||||
{
|
||||
if ($bRuleOverride && $bHasAllMandatoryKeysMissing && !$bHasNonDisabledKeys)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if ($bHasMissingMandatoryKey)
|
||||
{
|
||||
throw new CoreUnexpectedValue('Uniqueness rule : missing mandatory properties');
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($bHasMissingMandatoryKey)
|
||||
{
|
||||
throw new CoreUnexpectedValue('Uniqueness rule : missing mandatory properties');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* To be overriden, must be called for any object class (optimization)
|
||||
*/
|
||||
@@ -4505,7 +4715,7 @@ abstract class MetaModel
|
||||
}
|
||||
}
|
||||
|
||||
// Check unicity of the SQL columns
|
||||
// Check SQL columns uniqueness
|
||||
//
|
||||
if (self::HasTable($sClass))
|
||||
{
|
||||
@@ -4971,6 +5181,7 @@ abstract class MetaModel
|
||||
$aCreateTableItems = array(); // array of <table> => array of <create definition>
|
||||
$aAlterTableMetaData = array();
|
||||
$aAlterTableItems = array(); // array of <table> => <alter specification>
|
||||
$aPostTableAlteration = array(); // array of <table> => post alteration queries
|
||||
|
||||
foreach(self::GetClasses() as $sClass)
|
||||
{
|
||||
@@ -5065,6 +5276,7 @@ abstract class MetaModel
|
||||
$aTableInfo['Fields'][$sField]['used'] = true;
|
||||
|
||||
$bIndexNeeded = $oAttDef->RequiresIndex();
|
||||
$bFullTextIndexNeeded = false;
|
||||
if (!$bIndexNeeded)
|
||||
{
|
||||
// Add an index on the columns of the friendlyname
|
||||
@@ -5073,6 +5285,13 @@ abstract class MetaModel
|
||||
$bIndexNeeded = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($oAttDef->RequiresFullTextIndex())
|
||||
{
|
||||
$bFullTextIndexNeeded = true;
|
||||
}
|
||||
}
|
||||
|
||||
$sFieldDefinition = "`$sField` $sDBFieldSpec";
|
||||
if (!CMDBSource::IsField($sTable, $sField))
|
||||
@@ -5092,21 +5311,37 @@ abstract class MetaModel
|
||||
if ($bIndexNeeded)
|
||||
{
|
||||
$aTableInfo['Indexes'][$sField]['used'] = true;
|
||||
$aColumns = array($sField);
|
||||
$aLength = self::DBGetIndexesLength($sClass, $aColumns, $aTableInfo);
|
||||
$sIndexName = $sField;
|
||||
$sColumns = '`'.$sField.'`';
|
||||
if (!is_null($aLength[0]))
|
||||
|
||||
if ($bFullTextIndexNeeded)
|
||||
{
|
||||
$sColumns .= ' ('.$aLength[0].')';
|
||||
}
|
||||
$aSugFix[$sClass][$sAttCode][] = "ALTER TABLE `$sTable` ADD INDEX `$sField` ($sColumns)";
|
||||
if ($bTableToCreate)
|
||||
{
|
||||
$aCreateTableItems[$sTable][] = "INDEX `$sField` ($sColumns)";
|
||||
$sIndexType = 'FULLTEXT INDEX';
|
||||
}
|
||||
else
|
||||
{
|
||||
$aAlterTableItems[$sTable][] = "ADD INDEX `$sField` ($sColumns)";
|
||||
$sIndexType = 'INDEX';
|
||||
$aColumns = array($sField);
|
||||
$aLength = self::DBGetIndexesLength($sClass, $aColumns, $aTableInfo);
|
||||
if (!is_null($aLength[0]))
|
||||
{
|
||||
$sColumns .= ' ('.$aLength[0].')';
|
||||
}
|
||||
}
|
||||
$sSugFix = "ALTER TABLE `$sTable` ADD $sIndexType `$sIndexName` ($sColumns)";
|
||||
$aSugFix[$sClass][$sAttCode][] = $sSugFix;
|
||||
if ($bFullTextIndexNeeded)
|
||||
{
|
||||
// MySQL does not support multi fulltext index creation in a single query (mysql_errno = 1795)
|
||||
$aPostTableAlteration[$sTable][] = $sSugFix;
|
||||
}
|
||||
elseif ($bTableToCreate)
|
||||
{
|
||||
$aCreateTableItems[$sTable][] = "$sIndexType `$sIndexName` ($sColumns)";
|
||||
}
|
||||
else
|
||||
{
|
||||
$aAlterTableItems[$sTable][] = "ADD $sIndexType `$sIndexName` ($sColumns)";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5119,12 +5354,24 @@ abstract class MetaModel
|
||||
$sAlterTableItemsAfterChange = '';
|
||||
if ($bIndexNeeded)
|
||||
{
|
||||
$aColumns = array($sField);
|
||||
$aLength = self::DBGetIndexesLength($sClass, $aColumns, $aTableInfo);
|
||||
$aTableInfo['Indexes'][$sField]['used'] = true;
|
||||
|
||||
if ($bFullTextIndexNeeded)
|
||||
{
|
||||
$sIndexType = 'FULLTEXT INDEX';
|
||||
$aColumns = null;
|
||||
$aLength = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sIndexType = 'INDEX';
|
||||
$aColumns = array($sField);
|
||||
$aLength = self::DBGetIndexesLength($sClass, $aColumns, $aTableInfo);
|
||||
}
|
||||
|
||||
if (!CMDBSource::HasIndex($sTable, $sField, $aColumns, $aLength))
|
||||
{
|
||||
$sIndexName = $sField;
|
||||
$sColumns = '`'.$sField.'`';
|
||||
if (!is_null($aLength[0]))
|
||||
{
|
||||
@@ -5134,16 +5381,11 @@ abstract class MetaModel
|
||||
$aErrors[$sClass][$sAttCode][] = "Foreign key '$sField' in table '$sTable' should have an index";
|
||||
if (CMDBSource::HasIndex($sTable, $sField))
|
||||
{
|
||||
$aSugFix[$sClass][$sAttCode][] = "ALTER TABLE `$sTable` DROP INDEX `$sField`";
|
||||
$sSugFixAfterChange = "ALTER TABLE `$sTable` ADD INDEX `$sField` ($sColumns)";
|
||||
$aAlterTableItems[$sTable][] = "DROP INDEX `$sField`";
|
||||
$sAlterTableItemsAfterChange = "ADD INDEX `$sField` ($sColumns)";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sSugFixAfterChange = "ALTER TABLE `$sTable` ADD INDEX `$sField` ($sColumns)";
|
||||
$sAlterTableItemsAfterChange = "ADD INDEX `$sField` ($sColumns)";
|
||||
$aSugFix[$sClass][$sAttCode][] = "ALTER TABLE `$sTable` DROP INDEX `$sIndexName`";
|
||||
$aAlterTableItems[$sTable][] = "DROP INDEX `$sIndexName`";
|
||||
}
|
||||
$sSugFixAfterChange = "ALTER TABLE `$sTable` ADD $sIndexType `$sIndexName` ($sColumns)";
|
||||
$sAlterTableItemsAfterChange = "ADD $sIndexType `$sIndexName` ($sColumns)";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5167,7 +5409,15 @@ abstract class MetaModel
|
||||
if (!empty($sSugFixAfterChange))
|
||||
{
|
||||
$aSugFix[$sClass][$sAttCode][] = $sSugFixAfterChange;
|
||||
$aAlterTableItems[$sTable][] = $sAlterTableItemsAfterChange;
|
||||
if ($bFullTextIndexNeeded)
|
||||
{
|
||||
// MySQL does not support multi fulltext index creation in a single query (mysql_errno = 1795)
|
||||
$aPostTableAlteration[$sTable][] = $sSugFixAfterChange;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aAlterTableItems[$sTable][] = $sAlterTableItemsAfterChange;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5291,6 +5541,10 @@ abstract class MetaModel
|
||||
$sChangeList = implode(', ', $aChangeList);
|
||||
$aCondensedQueries[] = "ALTER TABLE `$sTable` $sChangeList";
|
||||
}
|
||||
foreach($aPostTableAlteration as $sTable => $aChangeList)
|
||||
{
|
||||
$aCondensedQueries = array_merge($aCondensedQueries, $aChangeList);
|
||||
}
|
||||
|
||||
return array($aErrors, $aSugFix, $aCondensedQueries);
|
||||
}
|
||||
@@ -5858,20 +6112,10 @@ abstract class MetaModel
|
||||
|
||||
CMDBSource::SelectDB(self::$m_sDBName);
|
||||
|
||||
foreach(get_declared_classes() as $sPHPClass)
|
||||
{
|
||||
if (is_subclass_of($sPHPClass, 'ModuleHandlerAPI'))
|
||||
{
|
||||
$aCallSpec = array($sPHPClass, 'OnMetaModelStarted');
|
||||
call_user_func_array($aCallSpec, array());
|
||||
}
|
||||
}
|
||||
|
||||
// if (false)
|
||||
// {
|
||||
// echo "Debug<br/>\n";
|
||||
// self::static_var_dump();
|
||||
// }
|
||||
foreach(MetaModel::EnumPlugins('ModuleHandlerApiInterface') as $oPHPClass)
|
||||
{
|
||||
$oPHPClass::OnMetaModelStarted();
|
||||
}
|
||||
|
||||
ExpressionCache::Warmup();
|
||||
}
|
||||
@@ -6326,7 +6570,7 @@ abstract class MetaModel
|
||||
*
|
||||
* @param string $sClass
|
||||
* @param int $iKey id value of the object to retrieve
|
||||
* @param bool $bMustBeFound
|
||||
* @param bool $bMustBeFound see throws ArchivedObjectException
|
||||
* @param bool $bAllowAllData if true then no rights filtering
|
||||
* @param null $aModifierProperties
|
||||
*
|
||||
@@ -6393,12 +6637,14 @@ abstract class MetaModel
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
// We need to pop the pushed archived mode before the exception is thrown, otherwise the application stays in ArchiveMode true which has caused hazardious behavior!
|
||||
// Note: When switching to PHP 5.6, we can use a finally block instead of duplicating this line.
|
||||
utils::PopArchiveMode();
|
||||
// In the finally block we will pop the pushed archived mode
|
||||
// otherwise the application stays in ArchiveMode true which has caused hazardious behavior!
|
||||
throw $e;
|
||||
}
|
||||
utils::PopArchiveMode();
|
||||
finally
|
||||
{
|
||||
utils::PopArchiveMode();
|
||||
}
|
||||
|
||||
if (empty($aRow))
|
||||
{
|
||||
|
||||
@@ -56,6 +56,11 @@ abstract class ModelReflection
|
||||
abstract public function GetFiltersList($sClass);
|
||||
abstract public function IsValidFilterCode($sClass, $sFilterCode);
|
||||
|
||||
/**
|
||||
* @param string $sOQL
|
||||
*
|
||||
* @return \DBObjectSearch
|
||||
*/
|
||||
abstract public function GetQuery($sOQL);
|
||||
|
||||
abstract public function DictString($sStringCode, $sDefault = null, $bUserLanguageOnly = false);
|
||||
@@ -75,6 +80,13 @@ abstract class ModelReflection
|
||||
return vsprintf($sLocalizedFormat, $aArguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sCode
|
||||
* @param string $sLabel
|
||||
* @param string $defaultValue
|
||||
*
|
||||
* @return \RunTimeIconSelectionField
|
||||
*/
|
||||
abstract public function GetIconSelectionField($sCode, $sLabel = '', $defaultValue = '');
|
||||
|
||||
abstract public function GetRootClass($sClass);
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
abstract class ModuleHandlerAPI
|
||||
abstract class ModuleHandlerAPI implements ModuleHandlerApiInterface
|
||||
{
|
||||
public static function OnMetaModelStarted()
|
||||
{
|
||||
@@ -34,5 +34,19 @@ abstract class ModuleHandlerAPI
|
||||
public static function OnMenuCreation()
|
||||
{
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
|
||||
|
||||
interface ModuleHandlerApiInterface
|
||||
{
|
||||
public static function OnMetaModelStarted();
|
||||
|
||||
public static function OnMenuCreation();
|
||||
|
||||
public function __construct(); //empty params is required in order to be instantiable by MetaModel::InitClasses()
|
||||
}
|
||||
@@ -45,7 +45,7 @@ class iTopMutex
|
||||
static protected $aAcquiredLocks = array(); // Number of instances of the Mutex, having the lock, in this page
|
||||
|
||||
public function __construct(
|
||||
$sName, $sDBHost = null, $sDBUser = null, $sDBPwd = null, $bDBTlsEnabled = null, $sDBTlsCA = null
|
||||
$sName, $sDBHost = null, $sDBUser = null, $sDBPwd = null, $bDBTlsEnabled = false, $sDBTlsCA = null
|
||||
)
|
||||
{
|
||||
// Compute the name of a lock for mysql
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
* @copyright 2006 Gregory Beaver
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
|
||||
*/
|
||||
require_once 'PEAR/Exception.php';
|
||||
//require_once 'PEAR/Exception.php';
|
||||
/**
|
||||
* @package PHP_LexerGenerator
|
||||
* @author Gregory Beaver <cellog@php.net>
|
||||
@@ -51,5 +51,5 @@ require_once 'PEAR/Exception.php';
|
||||
* @version @package_version@
|
||||
* @since File available since Release 0.1.0
|
||||
*/
|
||||
class PHP_LexerGenerator_Exception extends PEAR_Exception {}
|
||||
class PHP_LexerGenerator_Exception extends Exception {}
|
||||
?>
|
||||
@@ -22,6 +22,9 @@ class MissingQueryArgument extends CoreException
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @method Check($oModelReflection, array $aAliases, $sSourceQuery)
|
||||
*/
|
||||
abstract class Expression
|
||||
{
|
||||
/**
|
||||
@@ -47,13 +50,30 @@ abstract class Expression
|
||||
/**
|
||||
* recursive rendering
|
||||
*
|
||||
* @deprecated use RenderExpression
|
||||
*
|
||||
* @param array $aArgs used as input by default, or used as output if bRetrofitParams set to True
|
||||
* @param bool $bRetrofitParams
|
||||
*
|
||||
* @return array|string
|
||||
* @throws \MissingQueryArgument
|
||||
*/
|
||||
abstract public function Render(&$aArgs = null, $bRetrofitParams = false);
|
||||
public function Render(&$aArgs = null, $bRetrofitParams = false)
|
||||
{
|
||||
return $this->RenderExpression(false, $aArgs, $bRetrofitParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* recursive rendering
|
||||
*
|
||||
* @param bool $bForSQL generates code for OQL if false, for SQL otherwise
|
||||
* @param array $aArgs used as input by default, or used as output if bRetrofitParams set to True
|
||||
* @param bool $bRetrofitParams
|
||||
*
|
||||
* @return array|string
|
||||
* @throws \MissingQueryArgument
|
||||
*/
|
||||
abstract public function RenderExpression($bForSQL = false, &$aArgs = null, $bRetrofitParams = false);
|
||||
|
||||
/**
|
||||
* @param DBObjectSearch $oSearch
|
||||
@@ -66,7 +86,7 @@ abstract class Expression
|
||||
*/
|
||||
public function Display($oSearch, &$aArgs = null, $oAttDef = null, &$aCtx = array())
|
||||
{
|
||||
return $this->Render($aArgs);
|
||||
return $this->RenderExpression(false, $aArgs);
|
||||
}
|
||||
|
||||
public function GetAttDef($aClasses = array())
|
||||
@@ -104,7 +124,7 @@ abstract class Expression
|
||||
|
||||
public function serialize()
|
||||
{
|
||||
return base64_encode($this->Render());
|
||||
return base64_encode($this->RenderExpression(false));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -172,7 +192,7 @@ abstract class Expression
|
||||
* @param string sValue The value returned by the query, for this expression
|
||||
* @param string sDefault The default value if no relevant label could be computed
|
||||
*
|
||||
* @return The label
|
||||
* @return string label
|
||||
*/
|
||||
public function MakeValueLabel($oFilter, $sValue, $sDefault)
|
||||
{
|
||||
@@ -183,7 +203,7 @@ abstract class Expression
|
||||
{
|
||||
return array(
|
||||
'widget' => AttributeDefinition::SEARCH_WIDGET_TYPE_RAW,
|
||||
'oql' => $this->Render($aArgs, $bRetrofitParams),
|
||||
'oql' => $this->RenderExpression(false, $aArgs, $bRetrofitParams),
|
||||
'label' => $this->Display($oSearch, $aArgs, $oAttDef),
|
||||
'source' => get_class($this),
|
||||
);
|
||||
@@ -229,7 +249,7 @@ class SQLExpression extends Expression
|
||||
}
|
||||
|
||||
// recursive rendering
|
||||
public function Render(&$aArgs = null, $bRetrofitParams = false)
|
||||
public function RenderExpression($bForSql = false, &$aArgs = null, $bRetrofitParams = false)
|
||||
{
|
||||
return $this->m_sSQL;
|
||||
}
|
||||
@@ -285,7 +305,30 @@ class BinaryExpression extends Expression
|
||||
protected $m_oRightExpr;
|
||||
protected $m_sOperator;
|
||||
|
||||
/**
|
||||
* @param \Expression $oLeftExpr
|
||||
* @param string $sOperator
|
||||
* @param \Expression $oRightExpr
|
||||
*
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function __construct($oLeftExpr, $sOperator, $oRightExpr)
|
||||
{
|
||||
$this->ValidateConstructorParams($oLeftExpr, $sOperator, $oRightExpr);
|
||||
|
||||
$this->m_oLeftExpr = $oLeftExpr;
|
||||
$this->m_oRightExpr = $oRightExpr;
|
||||
$this->m_sOperator = $sOperator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $oLeftExpr
|
||||
* @param $sOperator
|
||||
* @param $oRightExpr
|
||||
*
|
||||
* @throws \CoreException if one of the parameter is invalid
|
||||
*/
|
||||
protected function ValidateConstructorParams($oLeftExpr, $sOperator, $oRightExpr)
|
||||
{
|
||||
if (!is_object($oLeftExpr))
|
||||
{
|
||||
@@ -303,13 +346,11 @@ class BinaryExpression extends Expression
|
||||
{
|
||||
throw new CoreException('Expecting an Expression object on the right hand', array('found_class' => get_class($oRightExpr)));
|
||||
}
|
||||
if ( (($sOperator == "IN") || ($sOperator == "NOT IN")) && !$oRightExpr instanceof ListExpression)
|
||||
if ((($sOperator == "IN") || ($sOperator == "NOT IN")) && !($oRightExpr instanceof ListExpression))
|
||||
{
|
||||
throw new CoreException("Expecting a List Expression object on the right hand for operator $sOperator", array('found_class' => get_class($oRightExpr)));
|
||||
throw new CoreException("Expecting a List Expression object on the right hand for operator $sOperator",
|
||||
array('found_class' => get_class($oRightExpr)));
|
||||
}
|
||||
$this->m_oLeftExpr = $oLeftExpr;
|
||||
$this->m_oRightExpr = $oRightExpr;
|
||||
$this->m_sOperator = $sOperator;
|
||||
}
|
||||
|
||||
public function IsTrue()
|
||||
@@ -341,12 +382,11 @@ class BinaryExpression extends Expression
|
||||
return $this->m_sOperator;
|
||||
}
|
||||
|
||||
// recursive rendering
|
||||
public function Render(&$aArgs = null, $bRetrofitParams = false)
|
||||
public function RenderExpression($bForSQL = false, &$aArgs = null, $bRetrofitParams = false)
|
||||
{
|
||||
$sOperator = $this->GetOperator();
|
||||
$sLeft = $this->GetLeftExpr()->Render($aArgs, $bRetrofitParams);
|
||||
$sRight = $this->GetRightExpr()->Render($aArgs, $bRetrofitParams);
|
||||
$sLeft = $this->GetLeftExpr()->RenderExpression($bForSQL, $aArgs, $bRetrofitParams);
|
||||
$sRight = $this->GetRightExpr()->RenderExpression($bForSQL, $aArgs, $bRetrofitParams);
|
||||
return "($sLeft $sOperator $sRight)";
|
||||
}
|
||||
|
||||
@@ -538,6 +578,7 @@ class BinaryExpression extends Expression
|
||||
* @param null $oAttDef
|
||||
*
|
||||
* @return array
|
||||
* @throws \MissingQueryArgument
|
||||
*/
|
||||
public function GetCriterion($oSearch, &$aArgs = null, $bRetrofitParams = false, $oAttDef = null)
|
||||
{
|
||||
@@ -605,7 +646,7 @@ class BinaryExpression extends Expression
|
||||
|
||||
$aCriteria = self::MergeCriteria($aCriteriaLeft, $aCriteriaRight, $this->GetOperator());
|
||||
}
|
||||
$aCriteria['oql'] = $this->Render($aArgs, $bRetrofitParams);
|
||||
$aCriteria['oql'] = $this->RenderExpression(false, $aArgs, $bRetrofitParams);
|
||||
$aCriteria['label'] = $this->Display($oSearch, $aArgs, $oAttDef);
|
||||
|
||||
if (isset($aCriteriaLeft['ref']) && isset($aCriteriaRight['ref']) && ($aCriteriaLeft['ref'] != $aCriteriaRight['ref']))
|
||||
@@ -636,6 +677,10 @@ class BinaryExpression extends Expression
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isset($aCriteriaLeft['widget']) && isset($aCriteriaRight['widget']) && ($aCriteriaLeft['widget'] == AttributeDefinition::SEARCH_WIDGET_TYPE_TAG_SET) && ($aCriteriaRight['widget'] == AttributeDefinition::SEARCH_WIDGET_TYPE_TAG_SET))
|
||||
{
|
||||
$aCriteriaOverride['operator'] = 'MATCHES';
|
||||
}
|
||||
}
|
||||
|
||||
return array_merge($aCriteriaLeft, $aCriteriaRight, $aCriteriaOverride);
|
||||
@@ -643,6 +688,58 @@ class BinaryExpression extends Expression
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @since 2.6 N°931 tag fields
|
||||
*/
|
||||
class MatchExpression extends BinaryExpression
|
||||
{
|
||||
/** @var \FieldExpression */
|
||||
protected $m_oLeftExpr;
|
||||
/** @var \ScalarExpression */
|
||||
protected $m_oRightExpr;
|
||||
|
||||
/**
|
||||
* MatchExpression constructor.
|
||||
*
|
||||
* @param \FieldExpression $oLeftExpr
|
||||
* @param \ScalarExpression $oRightExpr
|
||||
*
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function __construct(FieldExpression $oLeftExpr, ScalarExpression $oRightExpr)
|
||||
{
|
||||
parent::__construct($oLeftExpr, 'MATCHES', $oRightExpr);
|
||||
}
|
||||
|
||||
public function RenderExpression($bForSQL = false, &$aArgs = null, $bRetrofitParams = false)
|
||||
{
|
||||
$sLeft = $this->GetLeftExpr()->RenderExpression($bForSQL, $aArgs, $bRetrofitParams);
|
||||
$sRight = $this->GetRightExpr()->RenderExpression($bForSQL, $aArgs, $bRetrofitParams);
|
||||
|
||||
if ($bForSQL)
|
||||
{
|
||||
$sRet = "MATCH ($sLeft) AGAINST ($sRight IN BOOLEAN MODE)";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sRet = "$sLeft MATCHES $sRight";
|
||||
}
|
||||
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
public function Translate($aTranslationData, $bMatchAll = true, $bMarkFieldsAsResolved = true)
|
||||
{
|
||||
/** @var \FieldExpression $oLeft */
|
||||
$oLeft = $this->GetLeftExpr()->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
|
||||
/** @var \ScalarExpression $oRight */
|
||||
$oRight = $this->GetRightExpr()->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
|
||||
|
||||
return new static($oLeft, $oRight);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class UnaryExpression extends Expression
|
||||
{
|
||||
protected $m_value;
|
||||
@@ -664,7 +761,7 @@ class UnaryExpression extends Expression
|
||||
}
|
||||
|
||||
// recursive rendering
|
||||
public function Render(&$aArgs = null, $bRetrofitParams = false)
|
||||
public function RenderExpression($bForSQL = false, &$aArgs = null, $bRetrofitParams = false)
|
||||
{
|
||||
return CMDBSource::Quote($this->m_value);
|
||||
}
|
||||
@@ -772,11 +869,11 @@ class ScalarExpression extends UnaryExpression
|
||||
return $aCtx['date_display']->MakeValueLabel($oSearch, $this->m_value, $this->m_value);
|
||||
}
|
||||
|
||||
return $this->Render($aArgs);
|
||||
return $this->RenderExpression(false, $aArgs);
|
||||
}
|
||||
|
||||
// recursive rendering
|
||||
public function Render(&$aArgs = null, $bRetrofitParams = false)
|
||||
public function RenderExpression($bForSQL = false, &$aArgs = null, $bRetrofitParams = false)
|
||||
{
|
||||
if (is_null($this->m_value))
|
||||
{
|
||||
@@ -796,23 +893,23 @@ class ScalarExpression extends UnaryExpression
|
||||
|
||||
public function GetCriterion($oSearch, &$aArgs = null, $bRetrofitParams = false, $oAttDef = null)
|
||||
{
|
||||
$aCriteria = array();
|
||||
$aCriterion = array();
|
||||
switch ((string)($this->m_value))
|
||||
{
|
||||
case '%Y-%m-%d':
|
||||
$aCriteria['unit'] = 'DAY';
|
||||
$aCriterion['unit'] = 'DAY';
|
||||
break;
|
||||
case '%Y-%m':
|
||||
$aCriteria['unit'] = 'MONTH';
|
||||
$aCriterion['unit'] = 'MONTH';
|
||||
break;
|
||||
case '%w':
|
||||
$aCriteria['unit'] = 'WEEKDAY';
|
||||
$aCriterion['unit'] = 'WEEKDAY';
|
||||
break;
|
||||
case '%H':
|
||||
$aCriteria['unit'] = 'HOUR';
|
||||
$aCriterion['unit'] = 'HOUR';
|
||||
break;
|
||||
default:
|
||||
$aValue = array('value' => $this->GetValue());
|
||||
$aValue = array();
|
||||
if (!is_null($oAttDef))
|
||||
{
|
||||
switch (true)
|
||||
@@ -822,7 +919,7 @@ class ScalarExpression extends UnaryExpression
|
||||
{
|
||||
$oFinalAttDef = $oAttDef->GetFinalAttDef();
|
||||
if($oFinalAttDef instanceof AttributeExternalKey)
|
||||
{
|
||||
{
|
||||
if ($this->GetValue() !== 0)
|
||||
{
|
||||
/** @var AttributeExternalKey $oFinalAttDef */
|
||||
@@ -849,42 +946,79 @@ class ScalarExpression extends UnaryExpression
|
||||
IssueLog::Error($e->getMessage());
|
||||
}
|
||||
break;
|
||||
case ($oAttDef instanceof AttributeTagSet):
|
||||
try
|
||||
{
|
||||
if (!empty($this->GetValue()))
|
||||
{
|
||||
$aValues = array();
|
||||
$oValue = $this->GetValue();
|
||||
if (is_string($oValue))
|
||||
{
|
||||
$oValue = $oAttDef->GetExistingTagsFromString($oValue, true);
|
||||
}
|
||||
/** @var \ormTagSet $oValue */
|
||||
$aTags = $oValue->GetTags();
|
||||
foreach($aTags as $oTag)
|
||||
{
|
||||
$aValue['label'] = $oTag->Get('label');
|
||||
$aValue['value'] = $oTag->Get('code');
|
||||
$aValues[] = $aValue;
|
||||
}
|
||||
$aCriterion['values'] = $aValues;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aCriterion['has_undefined'] = true;
|
||||
}
|
||||
} catch (Exception $e)
|
||||
{
|
||||
IssueLog::Error($e->getMessage());
|
||||
}
|
||||
break;
|
||||
case $oAttDef->IsExternalKey():
|
||||
try
|
||||
{
|
||||
if ($this->GetValue() == 0)
|
||||
{
|
||||
$aValue['label'] = Dict::S('UI:UndefinedObject');
|
||||
}
|
||||
else
|
||||
if ($this->GetValue() != 0)
|
||||
{
|
||||
/** @var AttributeExternalKey $oAttDef */
|
||||
$sTarget = $oAttDef->GetTargetClass();
|
||||
$oObj = MetaModel::GetObject($sTarget, $this->GetValue(), true, true);
|
||||
$aValue['label'] = $oObj->Get("friendlyname");
|
||||
$aValue['value'] = $this->GetValue();
|
||||
$aCriterion['values'] = array($aValue);
|
||||
}
|
||||
}
|
||||
catch (Exception $e)
|
||||
else
|
||||
{
|
||||
$aValue['label'] = Dict::S('Enum:Undefined');
|
||||
$aValue['value'] = $this->GetValue();
|
||||
$aCriterion['values'] = array($aValue);
|
||||
}
|
||||
} catch (Exception $e)
|
||||
{
|
||||
IssueLog::Error($e->getMessage());
|
||||
// This object cannot be seen... ignore
|
||||
}
|
||||
break;
|
||||
default:
|
||||
try
|
||||
{
|
||||
$aValue['label'] = $oAttDef->GetAsPlainText($this->GetValue());
|
||||
$aValue['value'] = $this->GetValue();
|
||||
$aCriterion['values'] = array($aValue);
|
||||
} catch (Exception $e)
|
||||
{
|
||||
$aValue['label'] = $this->GetValue();
|
||||
$aValue['value'] = $this->GetValue();
|
||||
$aCriterion['values'] = array($aValue);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
$aCriteria['values'] = array($aValue);
|
||||
break;
|
||||
}
|
||||
$aCriteria['oql'] = $this->Render($aArgs, $bRetrofitParams);
|
||||
return $aCriteria;
|
||||
$aCriterion['oql'] = $this->RenderExpression(false, $aArgs, $bRetrofitParams);
|
||||
|
||||
return $aCriterion;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -991,7 +1125,7 @@ class FieldExpression extends UnaryExpression
|
||||
}
|
||||
|
||||
// recursive rendering
|
||||
public function Render(&$aArgs = null, $bRetrofitParams = false)
|
||||
public function RenderExpression($bForSQL = false, &$aArgs = null, $bRetrofitParams = false)
|
||||
{
|
||||
if (empty($this->m_sParent))
|
||||
{
|
||||
@@ -1097,7 +1231,7 @@ class FieldExpression extends UnaryExpression
|
||||
* @param string sValue The value returned by the query, for this expression
|
||||
* @param string sDefault The default value if no relevant label could be computed
|
||||
*
|
||||
* @return The label
|
||||
* @return string label
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function MakeValueLabel($oFilter, $sValue, $sDefault)
|
||||
@@ -1337,19 +1471,18 @@ class VariableExpression extends UnaryExpression
|
||||
return $oAttDef->GetAsPlainText($sValue);
|
||||
}
|
||||
|
||||
return $this->Render($aArgs);
|
||||
return $this->RenderExpression(false, $aArgs);
|
||||
}
|
||||
|
||||
// recursive rendering
|
||||
|
||||
/**
|
||||
* @param null $aArgs
|
||||
* @param bool $bForSQL
|
||||
* @param array $aArgs
|
||||
* @param bool $bRetrofitParams
|
||||
*
|
||||
* @return array|string
|
||||
* @throws \MissingQueryArgument
|
||||
*/
|
||||
public function Render(&$aArgs = null, $bRetrofitParams = false)
|
||||
public function RenderExpression($bForSQL = false, &$aArgs = null, $bRetrofitParams = false)
|
||||
{
|
||||
if (is_null($aArgs))
|
||||
{
|
||||
@@ -1467,12 +1600,12 @@ class ListExpression extends Expression
|
||||
}
|
||||
|
||||
// recursive rendering
|
||||
public function Render(&$aArgs = null, $bRetrofitParams = false)
|
||||
public function RenderExpression($bForSQL = false, &$aArgs = null, $bRetrofitParams = false)
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($this->m_aExpressions as $oExpr)
|
||||
{
|
||||
$aRes[] = $oExpr->Render($aArgs, $bRetrofitParams);
|
||||
$aRes[] = $oExpr->RenderExpression($bForSQL, $aArgs, $bRetrofitParams);
|
||||
}
|
||||
return '('.implode(', ', $aRes).')';
|
||||
}
|
||||
@@ -1488,12 +1621,11 @@ class ListExpression extends Expression
|
||||
|
||||
public function ApplyParameters($aArgs)
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($this->m_aExpressions as $idx => $oExpr)
|
||||
{
|
||||
if ($oExpr instanceof VariableExpression)
|
||||
{
|
||||
$this->m_aExpressions[$idx] = $oExpr->GetAsScalar();
|
||||
$this->m_aExpressions[$idx] = $oExpr->GetAsScalar($aArgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1550,7 +1682,6 @@ class ListExpression extends Expression
|
||||
|
||||
public function RenameParam($sOldName, $sNewName)
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($this->m_aExpressions as $key => $oExpr)
|
||||
{
|
||||
$this->m_aExpressions[$key] = $oExpr->RenameParam($sOldName, $sNewName);
|
||||
@@ -1559,7 +1690,6 @@ class ListExpression extends Expression
|
||||
|
||||
public function RenameAlias($sOldName, $sNewName)
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($this->m_aExpressions as $key => $oExpr)
|
||||
{
|
||||
$oExpr->RenameAlias($sOldName, $sNewName);
|
||||
@@ -1623,12 +1753,12 @@ class FunctionExpression extends Expression
|
||||
}
|
||||
|
||||
// recursive rendering
|
||||
public function Render(&$aArgs = null, $bRetrofitParams = false)
|
||||
public function RenderExpression($bForSQL = false, &$aArgs = null, $bRetrofitParams = false)
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($this->m_aArgs as $iPos => $oExpr)
|
||||
{
|
||||
$aRes[] = $oExpr->Render($aArgs, $bRetrofitParams);
|
||||
$aRes[] = $oExpr->RenderExpression($bForSQL, $aArgs, $bRetrofitParams);
|
||||
}
|
||||
return $this->m_sVerb.'('.implode(', ', $aRes).')';
|
||||
}
|
||||
@@ -1644,7 +1774,6 @@ class FunctionExpression extends Expression
|
||||
|
||||
public function ApplyParameters($aArgs)
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($this->m_aArgs as $idx => $oExpr)
|
||||
{
|
||||
if ($oExpr instanceof VariableExpression)
|
||||
@@ -1738,7 +1867,7 @@ class FunctionExpression extends Expression
|
||||
* @param string sValue The value returned by the query, for this expression
|
||||
* @param string sDefault The default value if no relevant label could be computed
|
||||
*
|
||||
* @return The label
|
||||
* @return string label
|
||||
*/
|
||||
public function MakeValueLabel($oFilter, $sValue, $sDefault)
|
||||
{
|
||||
@@ -1814,6 +1943,7 @@ class FunctionExpression extends Expression
|
||||
$sVerb = '';
|
||||
switch ($this->m_sVerb)
|
||||
{
|
||||
case 'ISNULL':
|
||||
case 'NOW':
|
||||
$sVerb = $this->VerbToNaturalLanguage();
|
||||
break;
|
||||
@@ -1827,7 +1957,7 @@ class FunctionExpression extends Expression
|
||||
$aCtx['date_display'] = $this;
|
||||
break;
|
||||
default:
|
||||
return $this->Render($aArgs);
|
||||
return $this->RenderExpression(false, $aArgs);
|
||||
}
|
||||
|
||||
foreach($this->m_aArgs as $oExpression)
|
||||
@@ -1844,7 +1974,7 @@ class FunctionExpression extends Expression
|
||||
{
|
||||
$sOperation .= $sVerb;
|
||||
}
|
||||
return $sOperation;
|
||||
return '('.$sOperation.')';
|
||||
}
|
||||
|
||||
private function VerbToNaturalLanguage()
|
||||
@@ -1864,7 +1994,7 @@ class FunctionExpression extends Expression
|
||||
$aCriteria = array_merge($oExpression->GetCriterion($oSearch, $aArgs, $bRetrofitParams, $oAttDef), $aCriteria);
|
||||
}
|
||||
$aCriteria['has_undefined'] = true;
|
||||
$aCriteria['oql'] = $this->Render($aArgs, $bRetrofitParams);
|
||||
$aCriteria['oql'] = $this->RenderExpression(false, $aArgs, $bRetrofitParams);
|
||||
break;
|
||||
|
||||
case 'NOW':
|
||||
@@ -1892,6 +2022,9 @@ class FunctionExpression extends Expression
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
|
||||
*/
|
||||
class IntervalExpression extends Expression
|
||||
{
|
||||
protected $m_oValue; // expression
|
||||
@@ -1920,9 +2053,9 @@ class IntervalExpression extends Expression
|
||||
}
|
||||
|
||||
// recursive rendering
|
||||
public function Render(&$aArgs = null, $bRetrofitParams = false)
|
||||
public function RenderExpression($bForSQL = false, &$aArgs = null, $bRetrofitParams = false)
|
||||
{
|
||||
return 'INTERVAL '.$this->m_oValue->Render($aArgs, $bRetrofitParams).' '.$this->m_sUnit;
|
||||
return 'INTERVAL '.$this->m_oValue->RenderExpression($bForSQL, $aArgs, $bRetrofitParams).' '.$this->m_sUnit;
|
||||
}
|
||||
|
||||
public function Browse(Closure $callback)
|
||||
@@ -1987,7 +2120,7 @@ class IntervalExpression extends Expression
|
||||
|
||||
public function Display($oSearch, &$aArgs = null, $oAttDef = null, &$aCtx = array())
|
||||
{
|
||||
return $this->m_oValue->Render($aArgs).' '.Dict::S('Expression:Unit:Long:'.$this->m_sUnit, $this->m_sUnit);
|
||||
return $this->m_oValue->RenderExpression(false, $aArgs).' '.Dict::S('Expression:Unit:Long:'.$this->m_sUnit, $this->m_sUnit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2012,12 +2145,12 @@ class CharConcatExpression extends Expression
|
||||
}
|
||||
|
||||
// recursive rendering
|
||||
public function Render(&$aArgs = null, $bRetrofitParams = false)
|
||||
public function RenderExpression($bForSQL = false, &$aArgs = null, $bRetrofitParams = false)
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($this->m_aExpressions as $oExpr)
|
||||
{
|
||||
$sCol = $oExpr->Render($aArgs, $bRetrofitParams);
|
||||
$sCol = $oExpr->RenderExpression($bForSQL, $aArgs, $bRetrofitParams);
|
||||
// Concat will be globally NULL if one single argument is null !
|
||||
$aRes[] = "COALESCE($sCol, '')";
|
||||
}
|
||||
@@ -2035,12 +2168,11 @@ class CharConcatExpression extends Expression
|
||||
|
||||
public function ApplyParameters($aArgs)
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($this->m_aExpressions as $idx => $oExpr)
|
||||
{
|
||||
if ($oExpr instanceof VariableExpression)
|
||||
{
|
||||
$this->m_aExpressions[$idx] = $oExpr->GetAsScalar();
|
||||
$this->m_aExpressions[$idx] = $oExpr->GetAsScalar($aArgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2124,12 +2256,12 @@ class CharConcatWSExpression extends CharConcatExpression
|
||||
}
|
||||
|
||||
// recursive rendering
|
||||
public function Render(&$aArgs = null, $bRetrofitParams = false)
|
||||
public function RenderExpression($bForSQL = false, &$aArgs = null, $bRetrofitParams = false)
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($this->m_aExpressions as $oExpr)
|
||||
{
|
||||
$sCol = $oExpr->Render($aArgs, $bRetrofitParams);
|
||||
$sCol = $oExpr->RenderExpression($bForSQL, $aArgs, $bRetrofitParams);
|
||||
// Concat will be globally NULL if one single argument is null !
|
||||
$aRes[] = "COALESCE($sCol, '')";
|
||||
}
|
||||
|
||||
@@ -140,6 +140,7 @@ class OQLLexerRaw
|
||||
'/\GNOT LIKE/ ',
|
||||
'/\GIN/ ',
|
||||
'/\GNOT IN/ ',
|
||||
'/\GMATCHES/ ',
|
||||
'/\GINTERVAL/ ',
|
||||
'/\GIF/ ',
|
||||
'/\GELT/ ',
|
||||
@@ -441,204 +442,209 @@ class OQLLexerRaw
|
||||
function yy_r1_33($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::INTERVAL;
|
||||
$this->token = OQLParser::MATCHES;
|
||||
}
|
||||
function yy_r1_34($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_IF;
|
||||
$this->token = OQLParser::INTERVAL;
|
||||
}
|
||||
function yy_r1_35($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_ELT;
|
||||
$this->token = OQLParser::F_IF;
|
||||
}
|
||||
function yy_r1_36($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_COALESCE;
|
||||
$this->token = OQLParser::F_ELT;
|
||||
}
|
||||
function yy_r1_37($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_ISNULL;
|
||||
$this->token = OQLParser::F_COALESCE;
|
||||
}
|
||||
function yy_r1_38($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_CONCAT;
|
||||
$this->token = OQLParser::F_ISNULL;
|
||||
}
|
||||
function yy_r1_39($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_SUBSTR;
|
||||
$this->token = OQLParser::F_CONCAT;
|
||||
}
|
||||
function yy_r1_40($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_TRIM;
|
||||
$this->token = OQLParser::F_SUBSTR;
|
||||
}
|
||||
function yy_r1_41($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_DATE;
|
||||
$this->token = OQLParser::F_TRIM;
|
||||
}
|
||||
function yy_r1_42($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_DATE_FORMAT;
|
||||
$this->token = OQLParser::F_DATE;
|
||||
}
|
||||
function yy_r1_43($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_CURRENT_DATE;
|
||||
$this->token = OQLParser::F_DATE_FORMAT;
|
||||
}
|
||||
function yy_r1_44($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_NOW;
|
||||
$this->token = OQLParser::F_CURRENT_DATE;
|
||||
}
|
||||
function yy_r1_45($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_TIME;
|
||||
$this->token = OQLParser::F_NOW;
|
||||
}
|
||||
function yy_r1_46($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_TO_DAYS;
|
||||
$this->token = OQLParser::F_TIME;
|
||||
}
|
||||
function yy_r1_47($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_FROM_DAYS;
|
||||
$this->token = OQLParser::F_TO_DAYS;
|
||||
}
|
||||
function yy_r1_48($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_YEAR;
|
||||
$this->token = OQLParser::F_FROM_DAYS;
|
||||
}
|
||||
function yy_r1_49($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_MONTH;
|
||||
$this->token = OQLParser::F_YEAR;
|
||||
}
|
||||
function yy_r1_50($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_DAY;
|
||||
$this->token = OQLParser::F_MONTH;
|
||||
}
|
||||
function yy_r1_51($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_HOUR;
|
||||
$this->token = OQLParser::F_DAY;
|
||||
}
|
||||
function yy_r1_52($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_MINUTE;
|
||||
$this->token = OQLParser::F_HOUR;
|
||||
}
|
||||
function yy_r1_53($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_SECOND;
|
||||
$this->token = OQLParser::F_MINUTE;
|
||||
}
|
||||
function yy_r1_54($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_DATE_ADD;
|
||||
$this->token = OQLParser::F_SECOND;
|
||||
}
|
||||
function yy_r1_55($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_DATE_SUB;
|
||||
$this->token = OQLParser::F_DATE_ADD;
|
||||
}
|
||||
function yy_r1_56($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_ROUND;
|
||||
$this->token = OQLParser::F_DATE_SUB;
|
||||
}
|
||||
function yy_r1_57($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_FLOOR;
|
||||
$this->token = OQLParser::F_ROUND;
|
||||
}
|
||||
function yy_r1_58($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_INET_ATON;
|
||||
$this->token = OQLParser::F_FLOOR;
|
||||
}
|
||||
function yy_r1_59($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_INET_NTOA;
|
||||
$this->token = OQLParser::F_INET_ATON;
|
||||
}
|
||||
function yy_r1_60($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::BELOW;
|
||||
$this->token = OQLParser::F_INET_NTOA;
|
||||
}
|
||||
function yy_r1_61($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::BELOW_STRICT;
|
||||
$this->token = OQLParser::BELOW;
|
||||
}
|
||||
function yy_r1_62($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NOT_BELOW;
|
||||
$this->token = OQLParser::BELOW_STRICT;
|
||||
}
|
||||
function yy_r1_63($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NOT_BELOW_STRICT;
|
||||
$this->token = OQLParser::NOT_BELOW;
|
||||
}
|
||||
function yy_r1_64($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::ABOVE;
|
||||
$this->token = OQLParser::NOT_BELOW_STRICT;
|
||||
}
|
||||
function yy_r1_65($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::ABOVE_STRICT;
|
||||
$this->token = OQLParser::ABOVE;
|
||||
}
|
||||
function yy_r1_66($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NOT_ABOVE;
|
||||
$this->token = OQLParser::ABOVE_STRICT;
|
||||
}
|
||||
function yy_r1_67($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NOT_ABOVE_STRICT;
|
||||
$this->token = OQLParser::NOT_ABOVE;
|
||||
}
|
||||
function yy_r1_68($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::HEXVAL;
|
||||
$this->token = OQLParser::NOT_ABOVE_STRICT;
|
||||
}
|
||||
function yy_r1_69($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NUMVAL;
|
||||
$this->token = OQLParser::HEXVAL;
|
||||
}
|
||||
function yy_r1_70($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::STRVAL;
|
||||
$this->token = OQLParser::NUMVAL;
|
||||
}
|
||||
function yy_r1_71($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NAME;
|
||||
$this->token = OQLParser::STRVAL;
|
||||
}
|
||||
function yy_r1_72($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::VARNAME;
|
||||
$this->token = OQLParser::NAME;
|
||||
}
|
||||
function yy_r1_73($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::VARNAME;
|
||||
}
|
||||
function yy_r1_74($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::DOT;
|
||||
@@ -666,25 +672,25 @@ class OQLLexer extends OQLLexerRaw
|
||||
|
||||
function yylex()
|
||||
{
|
||||
try
|
||||
{
|
||||
return parent::yylex();
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$sMessage = $e->getMessage();
|
||||
if (substr($sMessage, 0, strlen(UNEXPECTED_INPUT_AT_LINE)) == UNEXPECTED_INPUT_AT_LINE)
|
||||
{
|
||||
$sLineAndChar = substr($sMessage, strlen(UNEXPECTED_INPUT_AT_LINE));
|
||||
if (preg_match('#^([0-9]+): (.+)$#', $sLineAndChar, $aMatches))
|
||||
{
|
||||
$iLine = $aMatches[1];
|
||||
$sUnexpected = $aMatches[2];
|
||||
throw new OQLLexerException($this->data, $iLine, $this->count, $sUnexpected);
|
||||
}
|
||||
}
|
||||
// Default: forward the exception
|
||||
throw $e;
|
||||
try
|
||||
{
|
||||
return parent::yylex();
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$sMessage = $e->getMessage();
|
||||
if (substr($sMessage, 0, strlen(UNEXPECTED_INPUT_AT_LINE)) == UNEXPECTED_INPUT_AT_LINE)
|
||||
{
|
||||
$sLineAndChar = substr($sMessage, strlen(UNEXPECTED_INPUT_AT_LINE));
|
||||
if (preg_match('#^([0-9]+): (.+)$#', $sLineAndChar, $aMatches))
|
||||
{
|
||||
$iLine = $aMatches[1];
|
||||
$sUnexpected = $aMatches[2];
|
||||
throw new OQLLexerException($this->data, $iLine, $this->count, $sUnexpected);
|
||||
}
|
||||
}
|
||||
// Default: forward the exception
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,6 +139,7 @@ f_round = "ROUND"
|
||||
f_floor = "FLOOR"
|
||||
f_inet_aton = "INET_ATON"
|
||||
f_inet_ntoa = "INET_NTOA"
|
||||
matches = "MATCHES"
|
||||
below = "BELOW"
|
||||
below_strict = "BELOW STRICT"
|
||||
not_below = "NOT BELOW"
|
||||
@@ -273,6 +274,9 @@ in {
|
||||
not_in {
|
||||
$this->token = OQLParser::NOT_IN;
|
||||
}
|
||||
matches {
|
||||
$this->token = OQLParser::MATCHES;
|
||||
}
|
||||
interval {
|
||||
$this->token = OQLParser::INTERVAL;
|
||||
}
|
||||
@@ -419,25 +423,25 @@ class OQLLexer extends OQLLexerRaw
|
||||
|
||||
function yylex()
|
||||
{
|
||||
try
|
||||
{
|
||||
return parent::yylex();
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$sMessage = $e->getMessage();
|
||||
if (substr($sMessage, 0, strlen(UNEXPECTED_INPUT_AT_LINE)) == UNEXPECTED_INPUT_AT_LINE)
|
||||
{
|
||||
$sLineAndChar = substr($sMessage, strlen(UNEXPECTED_INPUT_AT_LINE));
|
||||
if (preg_match('#^([0-9]+): (.+)$#', $sLineAndChar, $aMatches))
|
||||
{
|
||||
$iLine = $aMatches[1];
|
||||
$sUnexpected = $aMatches[2];
|
||||
throw new OQLLexerException($this->data, $iLine, $this->count, $sUnexpected);
|
||||
}
|
||||
}
|
||||
// Default: forward the exception
|
||||
throw $e;
|
||||
try
|
||||
{
|
||||
return parent::yylex();
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$sMessage = $e->getMessage();
|
||||
if (substr($sMessage, 0, strlen(UNEXPECTED_INPUT_AT_LINE)) == UNEXPECTED_INPUT_AT_LINE)
|
||||
{
|
||||
$sLineAndChar = substr($sMessage, strlen(UNEXPECTED_INPUT_AT_LINE));
|
||||
if (preg_match('#^([0-9]+): (.+)$#', $sLineAndChar, $aMatches))
|
||||
{
|
||||
$iLine = $aMatches[1];
|
||||
$sUnexpected = $aMatches[2];
|
||||
throw new OQLLexerException($this->data, $iLine, $this->count, $sUnexpected);
|
||||
}
|
||||
}
|
||||
// Default: forward the exception
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,7 +16,7 @@ Example:
|
||||
%left TIMES DIVIDE MOD.
|
||||
%right EXP NOT.
|
||||
|
||||
TODO : solve the 2 remaining shift-reduce conflicts (JOIN)
|
||||
later : solve the 2 remaining shift-reduce conflicts (JOIN)
|
||||
|
||||
*/
|
||||
|
||||
@@ -108,7 +108,16 @@ expression_prio1(A) ::= expression_basic(X). { A = X; }
|
||||
expression_prio1(A) ::= expression_prio1(X) operator1(Y) expression_basic(Z). { A = new BinaryOqlExpression(X, Y, Z); }
|
||||
|
||||
expression_prio2(A) ::= expression_prio1(X). { A = X; }
|
||||
expression_prio2(A) ::= expression_prio2(X) operator2(Y) expression_prio1(Z). { A = new BinaryOqlExpression(X, Y, Z); }
|
||||
expression_prio2(A) ::= expression_prio2(X) operator2(Y) expression_prio1(Z).{
|
||||
if (Y == 'MATCHES')
|
||||
{
|
||||
A = new MatchOqlExpression(X, Z);
|
||||
}
|
||||
else
|
||||
{
|
||||
A = new BinaryOqlExpression(X, Y, Z);
|
||||
}
|
||||
}
|
||||
|
||||
expression_prio3(A) ::= expression_prio2(X). { A = X; }
|
||||
expression_prio3(A) ::= expression_prio3(X) operator3(Y) expression_prio2(Z). { A = new BinaryOqlExpression(X, Y, Z); }
|
||||
@@ -180,18 +189,22 @@ str_value(A) ::= STRVAL(X). {A=stripslashes(substr(X, 1, strlen(X) - 2));}
|
||||
|
||||
operator1(A) ::= num_operator1(X). {A=X;}
|
||||
operator1(A) ::= bitwise_operator1(X). {A=X;}
|
||||
|
||||
operator2(A) ::= num_operator2(X). {A=X;}
|
||||
operator2(A) ::= str_operator(X). {A=X;}
|
||||
operator2(A) ::= REGEXP(X). {A=X;}
|
||||
operator2(A) ::= EQ(X). {A=X;}
|
||||
operator2(A) ::= NOT_EQ(X). {A=X;}
|
||||
|
||||
operator3(A) ::= LOG_AND(X). {A=X;}
|
||||
operator3(A) ::= bitwise_operator3(X). {A=X;}
|
||||
|
||||
operator4(A) ::= LOG_OR(X). {A=X;}
|
||||
operator4(A) ::= bitwise_operator4(X). {A=X;}
|
||||
|
||||
num_operator1(A) ::= MATH_DIV(X). {A=X;}
|
||||
num_operator1(A) ::= MATH_MULT(X). {A=X;}
|
||||
|
||||
num_operator2(A) ::= MATH_PLUS(X). {A=X;}
|
||||
num_operator2(A) ::= MATH_MINUS(X). {A=X;}
|
||||
num_operator2(A) ::= GT(X). {A=X;}
|
||||
@@ -201,10 +214,13 @@ num_operator2(A) ::= LE(X). {A=X;}
|
||||
|
||||
str_operator(A) ::= LIKE(X). {A=X;}
|
||||
str_operator(A) ::= NOT_LIKE(X). {A=X;}
|
||||
str_operator(A) ::= MATCHES(X). {A=X;}
|
||||
|
||||
bitwise_operator1(A) ::= BITWISE_LEFT_SHIFT(X). {A=X;}
|
||||
bitwise_operator1(A) ::= BITWISE_RIGHT_SHIFT(X). {A=X;}
|
||||
|
||||
bitwise_operator3(A) ::= BITWISE_AND(X). {A=X;}
|
||||
|
||||
bitwise_operator4(A) ::= BITWISE_OR(X). {A=X;}
|
||||
bitwise_operator4(A) ::= BITWISE_XOR(X). {A=X;}
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ class OqlInterpreter
|
||||
}
|
||||
|
||||
/**
|
||||
* @return OqlQuery
|
||||
* @return \OqlObjectQuery|\OqlUnionQuery
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public function ParseQuery()
|
||||
|
||||
@@ -160,6 +160,26 @@ class BinaryOqlExpression extends BinaryExpression implements CheckableExpressio
|
||||
}
|
||||
}
|
||||
|
||||
class MatchOqlExpression extends MatchExpression implements CheckableExpression
|
||||
{
|
||||
public function Check(ModelReflection $oModelReflection, $aAliases, $sSourceQuery)
|
||||
{
|
||||
$this->m_oLeftExpr->Check($oModelReflection, $aAliases, $sSourceQuery);
|
||||
$this->m_oRightExpr->Check($oModelReflection, $aAliases, $sSourceQuery);
|
||||
|
||||
// Only field MATCHES scalar is allowed
|
||||
if (!$this->m_oLeftExpr instanceof FieldExpression)
|
||||
{
|
||||
throw new OqlNormalizeException('Only "field MATCHES string" syntax is allowed', $sSourceQuery, new OqlName($this->m_oLeftExpr->RenderExpression(true), 0));
|
||||
}
|
||||
// Only field MATCHES scalar is allowed
|
||||
if (!$this->m_oRightExpr instanceof ScalarExpression)
|
||||
{
|
||||
throw new OqlNormalizeException('Only "field MATCHES string" syntax is allowed', $sSourceQuery, new OqlName($this->m_oRightExpr->RenderExpression(true), 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ScalarOqlExpression extends ScalarExpression implements CheckableExpression
|
||||
{
|
||||
public function Check(ModelReflection $oModelReflection, $aAliases, $sSourceQuery)
|
||||
|
||||
@@ -1 +1 @@
|
||||
2015-08-31
|
||||
2018-08-31
|
||||
@@ -34,7 +34,7 @@ class ormCaseLog {
|
||||
/**
|
||||
* Initializes the log with the first (initial) entry
|
||||
* @param $sLog string The text of the whole case log
|
||||
* @param $aIndex hash The case log index
|
||||
* @param $aIndex array The case log index
|
||||
*/
|
||||
public function __construct($sLog = '', $aIndex = array())
|
||||
{
|
||||
@@ -159,6 +159,7 @@ class ormCaseLog {
|
||||
$aEntries[] = array(
|
||||
'date' => '',
|
||||
'user_login' => '',
|
||||
'user_id' => 0,
|
||||
'message' => $sTextEntry,
|
||||
'message_html' => utils::TextToHtml($sTextEntry),
|
||||
);
|
||||
@@ -295,7 +296,6 @@ class ormCaseLog {
|
||||
*/
|
||||
public function GetAsSimpleHtml($aTransfoHandler = null)
|
||||
{
|
||||
$sStyleCaseLogHeader = '';
|
||||
$sStyleCaseLogEntry = '';
|
||||
|
||||
$sHtml = '<ul class="case_log_simple_html">';
|
||||
@@ -513,7 +513,6 @@ class ormCaseLog {
|
||||
public function AddLogEntry($sText, $sOnBehalfOf = '')
|
||||
{
|
||||
$sText = HTMLSanitizer::Sanitize($sText);
|
||||
$bMergeEntries = false;
|
||||
$sDate = date(AttributeDateTime::GetInternalFormat());
|
||||
if ($sOnBehalfOf == '')
|
||||
{
|
||||
@@ -699,7 +698,6 @@ class ormCaseLog {
|
||||
{
|
||||
$iPos = 0;
|
||||
$index = count($this->m_aIndex) - 1;
|
||||
$aIndex = $this->m_aIndex;
|
||||
while($index > $iIndex)
|
||||
{
|
||||
$iPos += $this->m_aIndex[$index]['separator_length'];
|
||||
|
||||
@@ -126,7 +126,6 @@ class ormDocument
|
||||
*/
|
||||
public function GetDisplayURL($sClass, $Id, $sAttCode)
|
||||
{
|
||||
// TODO: When refactoring this with the URLMaker system, mind to also change calls in the portal (look for the "p_object_document_display" route)
|
||||
return utils::GetAbsoluteUrlAppRoot() . "pages/ajax.render.php?operation=display_document&class=$sClass&id=$Id&field=$sAttCode";
|
||||
}
|
||||
|
||||
@@ -138,7 +137,6 @@ class ormDocument
|
||||
{
|
||||
// Compute a signature to reset the cache anytime the data changes (this is acceptable if used only with icon files)
|
||||
$sSignature = md5($this->GetData());
|
||||
// TODO: When refactoring this with the URLMaker system, mind to also change calls in the portal (look for the "p_object_document_display" route)
|
||||
return utils::GetAbsoluteUrlAppRoot() . "pages/ajax.document.php?operation=download_document&class=$sClass&id=$Id&field=$sAttCode&s=$sSignature&cache=86400";
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ class ormPassword
|
||||
public function __construct($sHash = '', $sSalt = '')
|
||||
{
|
||||
$this->m_sHashed = $sHash;
|
||||
//only used for <= 2.5 hashed password
|
||||
$this->m_sSalt = $sSalt;
|
||||
}
|
||||
|
||||
@@ -50,8 +51,7 @@ class ormPassword
|
||||
*/
|
||||
public function SetPassword($sClearTextPassword)
|
||||
{
|
||||
$this->m_sSalt = SimpleCrypt::GetNewSalt();
|
||||
$this->m_sHashed = $this->ComputeHash($sClearTextPassword);
|
||||
$this->m_sHashed = password_hash($sClearTextPassword, PASSWORD_DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,10 +95,19 @@ class ormPassword
|
||||
public function CheckPassword($sClearTextPassword)
|
||||
{
|
||||
$bResult = false;
|
||||
$sHashedPwd = $this->ComputeHash($sClearTextPassword);
|
||||
if ($this->m_sHashed == $sHashedPwd)
|
||||
$aInfo = password_get_info($this->m_sHashed);
|
||||
switch ($aInfo["algo"])
|
||||
{
|
||||
$bResult = true;
|
||||
case 0:
|
||||
//unknown, assume it's a legacy password
|
||||
$sHashedPwd = $this->ComputeHash($sClearTextPassword);
|
||||
if ($this->m_sHashed == $sHashedPwd)
|
||||
{
|
||||
$bResult = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$bResult = password_verify($sClearTextPassword, $this->m_sHashed);
|
||||
}
|
||||
return $bResult;
|
||||
}
|
||||
|
||||
380
core/ormset.class.inc.php
Normal file
380
core/ormset.class.inc.php
Normal file
@@ -0,0 +1,380 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) 2010-2018 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/>
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* Date: 24/08/2018
|
||||
* Time: 14:35
|
||||
*/
|
||||
class ormSet
|
||||
{
|
||||
protected $sClass; // class of the field
|
||||
protected $sAttCode; // attcode of the field
|
||||
protected $aOriginalObjects = null;
|
||||
|
||||
/**
|
||||
* Object from the original set, minus the removed objects
|
||||
*/
|
||||
protected $aPreserved = array();
|
||||
|
||||
/**
|
||||
* New items
|
||||
*/
|
||||
protected $aAdded = array();
|
||||
|
||||
/**
|
||||
* Removed items
|
||||
*/
|
||||
protected $aRemoved = array();
|
||||
|
||||
/**
|
||||
* Modified items (mass edit)
|
||||
*/
|
||||
protected $aModified = array();
|
||||
|
||||
/**
|
||||
* @var int Max number of tags in collection
|
||||
*/
|
||||
protected $iLimit;
|
||||
|
||||
/**
|
||||
* __toString magical function overload.
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
$aValue = $this->GetValues();
|
||||
if (!empty($aValue))
|
||||
{
|
||||
return implode(', ', $aValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
return ' ';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ormSet constructor.
|
||||
*
|
||||
* @param string $sClass
|
||||
* @param string $sAttCode
|
||||
* @param int $iLimit
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct($sClass, $sAttCode, $iLimit = 12)
|
||||
{
|
||||
$this->sAttCode = $sAttCode;
|
||||
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
if (!$oAttDef instanceof AttributeSet)
|
||||
{
|
||||
throw new Exception("ormSet: field {$sClass}:{$sAttCode} is not a set");
|
||||
}
|
||||
$this->sClass = $sClass;
|
||||
$this->iLimit = $iLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function GetClass()
|
||||
{
|
||||
return $this->sClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function GetAttCode()
|
||||
{
|
||||
return $this->sAttCode;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param array $aItems
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue when a code is invalid
|
||||
*/
|
||||
public function SetValues($aItems)
|
||||
{
|
||||
if (!is_array($aItems))
|
||||
{
|
||||
throw new CoreUnexpectedValue("Wrong value {$aItems} for {$this->sClass}:{$this->sAttCode}");
|
||||
}
|
||||
|
||||
$aValues = array();
|
||||
$iCount = 0;
|
||||
$bError = false;
|
||||
foreach($aItems as $oItem)
|
||||
{
|
||||
$iCount++;
|
||||
if (($this->iLimit != 0) && ($iCount > $this->iLimit))
|
||||
{
|
||||
$bError = true;
|
||||
continue;
|
||||
}
|
||||
$aValues[] = $oItem;
|
||||
}
|
||||
|
||||
$this->aPreserved = &$aValues;
|
||||
$this->aRemoved = array();
|
||||
$this->aAdded = array();
|
||||
$this->aModified = array();
|
||||
$this->aOriginalObjects = $aValues;
|
||||
|
||||
if ($bError)
|
||||
{
|
||||
throw new CoreException("Maximum number of items ({$this->iLimit}) reached for {$this->sClass}:{$this->sAttCode}");
|
||||
}
|
||||
}
|
||||
|
||||
public function Count()
|
||||
{
|
||||
return count($this->aPreserved) + count($this->aAdded) - count($this->aRemoved);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array of codes
|
||||
*/
|
||||
public function GetValues()
|
||||
{
|
||||
$aValues = array_merge($this->aPreserved, $this->aAdded);
|
||||
|
||||
return $aValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array of tag labels indexed by code for only the added tags
|
||||
*/
|
||||
private function GetAdded()
|
||||
{
|
||||
return $this->aAdded;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array of tag labels indexed by code for only the removed tags
|
||||
*/
|
||||
private function GetRemoved()
|
||||
{
|
||||
return $this->aRemoved;
|
||||
}
|
||||
|
||||
/** Get the delta with another ItemSet
|
||||
*
|
||||
* $aDelta['added] = array of tag codes for only the added tags
|
||||
* $aDelta['removed'] = array of tag codes for only the removed tags
|
||||
*
|
||||
* @param \ormSet $oOtherSet
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function GetDelta(ormSet $oOtherSet)
|
||||
{
|
||||
$oSet = new ormSet($this->sClass, $this->sAttCode, $this->iLimit);
|
||||
// Set the initial value
|
||||
$aOrigItems = $this->GetValues();
|
||||
$oSet->SetValues($aOrigItems);
|
||||
|
||||
// now remove everything
|
||||
foreach($aOrigItems as $oItem)
|
||||
{
|
||||
$oSet->Remove($oItem);
|
||||
}
|
||||
|
||||
// now add the tags of the other ItemSet
|
||||
foreach($oOtherSet->GetValues() as $oItem)
|
||||
{
|
||||
$oSet->Add($oItem);
|
||||
}
|
||||
|
||||
$aDelta = array();
|
||||
$aDelta['added'] = $oSet->GetAdded();
|
||||
$aDelta['removed'] = $oSet->GetRemoved();
|
||||
|
||||
return $aDelta;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[] list of codes for partial entries
|
||||
*/
|
||||
public function GetModified()
|
||||
{
|
||||
return $this->aModified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a delta to the current ItemSet
|
||||
* $aDelta['added] = array of added items
|
||||
* $aDelta['removed'] = array of removed items
|
||||
*
|
||||
* @param $aDelta
|
||||
*
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function ApplyDelta($aDelta)
|
||||
{
|
||||
if (isset($aDelta['removed']))
|
||||
{
|
||||
foreach($aDelta['removed'] as $oItem)
|
||||
{
|
||||
$this->Remove($oItem);
|
||||
}
|
||||
}
|
||||
if (isset($aDelta['added']))
|
||||
{
|
||||
foreach($aDelta['added'] as $oItem)
|
||||
{
|
||||
$this->Add($oItem);
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the object
|
||||
$this->SetValues($this->GetValues());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $oItem
|
||||
*
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function Add($oItem)
|
||||
{
|
||||
if (($this->iLimit != 0) && ($this->Count() > $this->iLimit))
|
||||
{
|
||||
throw new CoreException("Maximum number of items ({$this->iLimit}) reached for {$this->sClass}:{$this->sAttCode}");
|
||||
}
|
||||
if ($this->IsItemInList($this->aPreserved, $oItem) || $this->IsItemInList($this->aAdded, $oItem))
|
||||
{
|
||||
// nothing to do, already existing tag
|
||||
return;
|
||||
}
|
||||
// if removed and added again
|
||||
if (($this->RemoveItemFromList($this->aRemoved, $oItem)) !== false)
|
||||
{
|
||||
// put it back into preserved
|
||||
$this->aPreserved[] = $oItem;
|
||||
// no need to add it to aModified : was already done when calling RemoveItem method
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->aAdded[] = $oItem;
|
||||
$this->aModified[] = $oItem;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $oItem
|
||||
*/
|
||||
public function Remove($oItem)
|
||||
{
|
||||
if ($this->IsItemInList($this->aRemoved, $oItem))
|
||||
{
|
||||
// nothing to do, already removed tag
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->RemoveItemFromList($this->aAdded, $oItem) !== false)
|
||||
{
|
||||
$this->aModified[] = $oItem;
|
||||
|
||||
return; // if present in added, can't be in preserved !
|
||||
}
|
||||
|
||||
if ($this->RemoveItemFromList($this->aPreserved, $oItem) !== false)
|
||||
{
|
||||
$this->aModified[] = $oItem;
|
||||
$this->aRemoved[] = $oItem;
|
||||
}
|
||||
}
|
||||
|
||||
private function IsItemInList($aItemList, $oItem)
|
||||
{
|
||||
return in_array($oItem, $aItemList);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DBObject[] $aItemList
|
||||
* @param $oItem
|
||||
*
|
||||
* @return bool|\DBObject false if not found, else the removed element
|
||||
*/
|
||||
private function RemoveItemFromList(&$aItemList, $oItem)
|
||||
{
|
||||
if (!($this->IsItemInList($aItemList, $oItem)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
foreach ($aItemList as $index => $value)
|
||||
{
|
||||
if ($value === $oItem)
|
||||
{
|
||||
unset($aItemList[$index]);
|
||||
return $oItem;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the added and removed arrays for bulk edit
|
||||
*
|
||||
* @param string[] $aItems
|
||||
*
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function GenerateDiffFromArray($aItems)
|
||||
{
|
||||
foreach($this->GetValues() as $oCurrentItem)
|
||||
{
|
||||
if (!in_array($oCurrentItem, $aItems))
|
||||
{
|
||||
$this->Remove($oCurrentItem);
|
||||
}
|
||||
}
|
||||
|
||||
foreach($aItems as $oNewItem)
|
||||
{
|
||||
$this->Add($oNewItem);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare Item Set
|
||||
*
|
||||
* @param \ormSet $other
|
||||
*
|
||||
* @return bool true if same tag set
|
||||
*/
|
||||
public function Equals(ormSet $other)
|
||||
{
|
||||
return implode(', ', $this->GetValue()) === implode(', ', $other->GetValue());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
// Copyright (C) 2010-2018 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -85,8 +85,12 @@ class ormStopWatch
|
||||
/**
|
||||
* Get the working elapsed time since the start of the stop watch
|
||||
* even if it is currently running
|
||||
* @param oAttDef AttributeDefinition Attribute hosting the stop watch
|
||||
* @param oObject Hosting object (used for query parameters)
|
||||
*
|
||||
* @param AttributeDefinition oAttDef Attribute hosting the stop watch
|
||||
* @param Object oObject Hosting object (used for query parameters)
|
||||
*
|
||||
* @return int|mixed
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function GetElapsedTime($oAttDef, $oObject)
|
||||
{
|
||||
@@ -260,6 +264,16 @@ class ormStopWatch
|
||||
return $iRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $oObject
|
||||
* @param $oAttDef
|
||||
* @param $iPercent
|
||||
* @param $iStartTime
|
||||
* @param $iDurationSec
|
||||
*
|
||||
* @return mixed
|
||||
* @throws \CoreException
|
||||
*/
|
||||
protected function ComputeDeadline($oObject, $oAttDef, $iPercent, $iStartTime, $iDurationSec)
|
||||
{
|
||||
$sWorkingTimeComputer = $oAttDef->Get('working_time_computing');
|
||||
@@ -267,11 +281,6 @@ class ormStopWatch
|
||||
{
|
||||
$sWorkingTimeComputer = class_exists('SLAComputation') ? 'SLAComputation' : 'DefaultWorkingTimeComputer';
|
||||
}
|
||||
$aCallSpec = array($sWorkingTimeComputer, '__construct');
|
||||
if (!is_callable($aCallSpec))
|
||||
{
|
||||
//throw new CoreException("Pas de constructeur pour $sWorkingTimeComputer!");
|
||||
}
|
||||
$oComputer = new $sWorkingTimeComputer();
|
||||
$aCallSpec = array($oComputer, 'GetDeadline');
|
||||
if (!is_callable($aCallSpec))
|
||||
@@ -285,6 +294,15 @@ class ormStopWatch
|
||||
return $iRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $oObject
|
||||
* @param $oAttDef
|
||||
* @param $iStartTime
|
||||
* @param $iEndTime
|
||||
*
|
||||
* @return mixed
|
||||
* @throws \CoreException
|
||||
*/
|
||||
protected function ComputeDuration($oObject, $oAttDef, $iStartTime, $iEndTime)
|
||||
{
|
||||
$sWorkingTimeComputer = $oAttDef->Get('working_time_computing');
|
||||
@@ -384,7 +402,20 @@ class ormStopWatch
|
||||
$sAttCode = $oAttDef->GetCode();
|
||||
WorkingTimeRecorder::Start($oObject, $iComputationRefTime, "ormStopWatch-Deadline-$iPercent-$sAttCode", 'Core:ExplainWTC:StopWatch-Deadline', array("Class:$sClass/Attribute:$sAttCode", $iPercent));
|
||||
}
|
||||
$aThresholdData['deadline'] = $this->ComputeDeadline($oObject, $oAttDef, $iPercent, $this->iLastStart, $iThresholdDuration - $this->iTimeSpent);
|
||||
$iRemaining = $iThresholdDuration - $this->iTimeSpent;
|
||||
if ($iRemaining < 0)
|
||||
{
|
||||
if (class_exists('WorkingTimeRecorder'))
|
||||
{
|
||||
$sClass = get_class($oObject);
|
||||
$sKey = $oObject->GetKey();
|
||||
$sAttCode = $oAttDef->GetCode();
|
||||
$sDate = date('Y-m-d H:i:s', $aThresholdData['deadline']);
|
||||
WorkingTimeRecorder::Log(WorkingTimeRecorder::TRACE_INFO, "$sClass($sKey) ormStopWatch-Deadline-$iPercent-$sAttCode ($sDate) already reached, not changed.");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
$aThresholdData['deadline'] = $this->ComputeDeadline($oObject, $oAttDef, $iPercent, $this->iLastStart, $iRemaining);
|
||||
// OR $aThresholdData['deadline'] = $this->ComputeDeadline($oObject, $oAttDef, $iPercent, $this->iStarted, $iThresholdDuration);
|
||||
|
||||
if (class_exists('WorkingTimeRecorder'))
|
||||
@@ -398,7 +429,7 @@ class ormStopWatch
|
||||
$aThresholdData['triggered'] = false;
|
||||
$aThresholdData['overrun'] = null;
|
||||
}
|
||||
else
|
||||
// else
|
||||
{
|
||||
// The new threshold is in the past
|
||||
// Note: the overrun can be wrong, but the correct algorithm to compute
|
||||
@@ -454,7 +485,10 @@ class ormStopWatch
|
||||
$aThresholdData['overrun'] = $iOverrun;
|
||||
}
|
||||
}
|
||||
$aThresholdData['deadline'] = null;
|
||||
if ($aThresholdData['overrun'] == 0)
|
||||
{
|
||||
$aThresholdData['deadline'] = null;
|
||||
}
|
||||
}
|
||||
|
||||
$this->iLastStart = null;
|
||||
@@ -558,7 +592,7 @@ class CheckStopWatchThresholds implements iBackgroundProcess
|
||||
CMDBObject::SetTrackInfo("Automatic - threshold triggered");
|
||||
|
||||
$oMyChange = CMDBObject::GetCurrentChange();
|
||||
$oObj->DBUpdateTracked($oMyChange, true /*skip security*/);
|
||||
$oObj->DBUpdateTracked($oMyChange);
|
||||
}
|
||||
|
||||
// Activate any existing trigger
|
||||
|
||||
482
core/ormtagset.class.inc.php
Normal file
482
core/ormtagset.class.inc.php
Normal file
@@ -0,0 +1,482 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) 2010-2018 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/>
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* Date: 24/08/2018
|
||||
* Time: 14:35
|
||||
*/
|
||||
final class ormTagSet extends ormSet
|
||||
{
|
||||
/**
|
||||
* ormTagSet constructor.
|
||||
*
|
||||
* @param string $sClass
|
||||
* @param string $sAttCode
|
||||
* @param int $iLimit
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct($sClass, $sAttCode, $iLimit = 12)
|
||||
{
|
||||
parent::__construct($sClass, $sAttCode, $iLimit);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param array $aTagCodes
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue when a code is invalid
|
||||
*/
|
||||
public function SetValues($aTagCodes)
|
||||
{
|
||||
if (is_null($aTagCodes))
|
||||
{
|
||||
$aTagCodes = array();
|
||||
}
|
||||
if (!is_array($aTagCodes))
|
||||
{
|
||||
throw new CoreUnexpectedValue("Wrong value {$aTagCodes} for {$this->sClass}:{$this->sAttCode}");
|
||||
}
|
||||
|
||||
$oTags = array();
|
||||
$iCount = 0;
|
||||
$bError = false;
|
||||
foreach($aTagCodes as $sTagCode)
|
||||
{
|
||||
$iCount++;
|
||||
if (($this->iLimit != 0) && ($iCount > $this->iLimit))
|
||||
{
|
||||
$bError = true;
|
||||
continue;
|
||||
}
|
||||
$oTag = $this->GetTagFromCode($sTagCode);
|
||||
$oTags[$sTagCode] = $oTag;
|
||||
}
|
||||
|
||||
$this->aPreserved = &$oTags;
|
||||
$this->aRemoved = array();
|
||||
$this->aAdded = array();
|
||||
$this->aModified = array();
|
||||
$this->aOriginalObjects = $oTags;
|
||||
|
||||
if ($bError)
|
||||
{
|
||||
throw new CoreException("Maximum number of tags ({$this->iLimit}) reached for {$this->sClass}:{$this->sAttCode}");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array of tag labels indexed by code
|
||||
*/
|
||||
public function GetLabels()
|
||||
{
|
||||
$aTags = array();
|
||||
/** @var \TagSetFieldData $oTag */
|
||||
foreach($this->aPreserved as $sTagCode => $oTag)
|
||||
{
|
||||
try
|
||||
{
|
||||
$aTags[$sTagCode] = $oTag->Get('label');
|
||||
} catch (CoreException $e)
|
||||
{
|
||||
IssueLog::Error($e->getMessage());
|
||||
}
|
||||
}
|
||||
foreach($this->aAdded as $sTagCode => $oTag)
|
||||
{
|
||||
try
|
||||
{
|
||||
$aTags[$sTagCode] = $oTag->Get('label');
|
||||
} catch (CoreException $e)
|
||||
{
|
||||
IssueLog::Error($e->getMessage());
|
||||
}
|
||||
}
|
||||
ksort($aTags);
|
||||
|
||||
return $aTags;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array of tags indexed by code
|
||||
*/
|
||||
public function GetTags()
|
||||
{
|
||||
$aTags = array();
|
||||
foreach($this->aPreserved as $sTagCode => $oTag)
|
||||
{
|
||||
$aTags[$sTagCode] = $oTag;
|
||||
}
|
||||
foreach($this->aAdded as $sTagCode => $oTag)
|
||||
{
|
||||
$aTags[$sTagCode] = $oTag;
|
||||
}
|
||||
ksort($aTags);
|
||||
|
||||
return $aTags;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array of tag labels indexed by code for only the added tags
|
||||
*/
|
||||
private function GetAddedCodes()
|
||||
{
|
||||
$aTags = array();
|
||||
foreach($this->aAdded as $sTagCode => $oTag)
|
||||
{
|
||||
$aTags[] = $sTagCode;
|
||||
}
|
||||
ksort($aTags);
|
||||
|
||||
return $aTags;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array of tag labels indexed by code for only the removed tags
|
||||
*/
|
||||
private function GetRemovedCodes()
|
||||
{
|
||||
$aTags = array();
|
||||
foreach($this->aRemoved as $sTagCode => $oTag)
|
||||
{
|
||||
$aTags[] = $sTagCode;
|
||||
}
|
||||
ksort($aTags);
|
||||
|
||||
return $aTags;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array of tag labels indexed by code for only the added tags
|
||||
*/
|
||||
private function GetAddedTags()
|
||||
{
|
||||
$aTags = array();
|
||||
foreach($this->aAdded as $sTagCode => $oTag)
|
||||
{
|
||||
$aTags[$sTagCode] = $oTag;
|
||||
}
|
||||
ksort($aTags);
|
||||
|
||||
return $aTags;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array of tag labels indexed by code for only the removed tags
|
||||
*/
|
||||
private function GetRemovedTags()
|
||||
{
|
||||
$aTags = array();
|
||||
foreach($this->aRemoved as $sTagCode => $oTag)
|
||||
{
|
||||
$aTags[$sTagCode] = $oTag;
|
||||
}
|
||||
ksort($aTags);
|
||||
|
||||
return $aTags;
|
||||
}
|
||||
|
||||
/** Get the delta with another TagSet
|
||||
*
|
||||
* $aDelta['added] = array of tag codes for only the added tags
|
||||
* $aDelta['removed'] = array of tag codes for only the removed tags
|
||||
*
|
||||
* @param \ormTagSet $oOtherTagSet
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function GetDelta(ormSet $oOtherTagSet)
|
||||
{
|
||||
$oTag = new ormTagSet($this->sClass, $this->sAttCode, 0);
|
||||
// Set the initial value
|
||||
$aOrigTagCodes = $this->GetValues();
|
||||
$oTag->SetValues($aOrigTagCodes);
|
||||
// now remove everything
|
||||
foreach($aOrigTagCodes as $sTagCode)
|
||||
{
|
||||
$oTag->Remove($sTagCode);
|
||||
}
|
||||
// now add the tags of the other TagSet
|
||||
foreach($oOtherTagSet->GetValues() as $sTagCode)
|
||||
{
|
||||
$oTag->Add($sTagCode);
|
||||
}
|
||||
$aDelta = array();
|
||||
$aDelta['added'] = $oTag->GetAddedCodes();
|
||||
$aDelta['removed'] = $oTag->GetRemovedCodes();
|
||||
|
||||
return $aDelta;
|
||||
}
|
||||
|
||||
/** Get the delta with another TagSet
|
||||
*
|
||||
* $aDelta['added] = array of tag labels indexed by code for only the added tags
|
||||
* $aDelta['removed'] = array of tag labels indexed by code for only the removed tags
|
||||
*
|
||||
* @param \ormTagSet $oOtherTagSet
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function GetDeltaTags(ormTagSet $oOtherTagSet)
|
||||
{
|
||||
$oTag = new ormTagSet($this->sClass, $this->sAttCode, 0);
|
||||
// Set the initial value
|
||||
$aOrigTagCodes = $this->GetValues();
|
||||
$oTag->SetValues($aOrigTagCodes);
|
||||
// now remove everything
|
||||
foreach($aOrigTagCodes as $sTagCode)
|
||||
{
|
||||
$oTag->Remove($sTagCode);
|
||||
}
|
||||
// now add the tags of the other TagSet
|
||||
foreach($oOtherTagSet->GetValues() as $sTagCode)
|
||||
{
|
||||
$oTag->Add($sTagCode);
|
||||
}
|
||||
$aDelta = array();
|
||||
$aDelta['added'] = $oTag->GetAddedTags();
|
||||
$aDelta['removed'] = $oTag->GetRemovedTags();
|
||||
|
||||
return $aDelta;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[] list of codes for partial entries
|
||||
*/
|
||||
public function GetModified()
|
||||
{
|
||||
$aModifiedTagCodes = array_keys($this->aModified);
|
||||
sort($aModifiedTagCodes);
|
||||
|
||||
return $aModifiedTagCodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a tag code is valid or not for this TagSet
|
||||
*
|
||||
* @param string $sTagCode
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function IsValidTag($sTagCode)
|
||||
{
|
||||
try
|
||||
{
|
||||
$this->GetTagFromCode($sTagCode);
|
||||
|
||||
return true;
|
||||
} catch (Exception $e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sTagCode
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
*/
|
||||
public function Add($sTagCode)
|
||||
{
|
||||
if (($this->iLimit != 0) && ($this->Count() == $this->iLimit))
|
||||
{
|
||||
throw new CoreException("Maximum number of tags ({$this->iLimit}) reached for {$this->sClass}:{$this->sAttCode}");
|
||||
}
|
||||
if ($this->IsTagInList($this->aPreserved, $sTagCode) || $this->IsTagInList($this->aAdded, $sTagCode))
|
||||
{
|
||||
// nothing to do, already existing tag
|
||||
return;
|
||||
}
|
||||
// if removed then added again
|
||||
if (($oTag = $this->RemoveTagFromList($this->aRemoved, $sTagCode)) !== false)
|
||||
{
|
||||
// put it back into preserved
|
||||
$this->aPreserved[$sTagCode] = $oTag;
|
||||
// no need to add it to aModified : was already done when calling Remove method
|
||||
}
|
||||
else
|
||||
{
|
||||
$oTag = $this->GetTagFromCode($sTagCode);
|
||||
$this->aAdded[$sTagCode] = $oTag;
|
||||
$this->aModified[$sTagCode] = $oTag;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sTagCode
|
||||
*/
|
||||
public function Remove($sTagCode)
|
||||
{
|
||||
if ($this->IsTagInList($this->aRemoved, $sTagCode))
|
||||
{
|
||||
// nothing to do, already removed tag
|
||||
return;
|
||||
}
|
||||
|
||||
$oTag = $this->RemoveTagFromList($this->aAdded, $sTagCode);
|
||||
if ($oTag !== false)
|
||||
{
|
||||
$this->aModified[$sTagCode] = $oTag;
|
||||
|
||||
return; // if present in added, can't be in preserved !
|
||||
}
|
||||
|
||||
$oTag = $this->RemoveTagFromList($this->aPreserved, $sTagCode);
|
||||
if ($oTag !== false)
|
||||
{
|
||||
$this->aModified[$sTagCode] = $oTag;
|
||||
$this->aRemoved[$sTagCode] = $oTag;
|
||||
}
|
||||
}
|
||||
|
||||
private function IsTagInList($aTagList, $sTagCode)
|
||||
{
|
||||
return isset($aTagList[$sTagCode]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DBObject[] $aTagList
|
||||
* @param string $sTagCode
|
||||
*
|
||||
* @return bool|\DBObject false if not found, else the removed element
|
||||
*/
|
||||
private function RemoveTagFromList(&$aTagList, $sTagCode)
|
||||
{
|
||||
if (!($this->IsTagInList($aTagList, $sTagCode)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$oTag = $aTagList[$sTagCode];
|
||||
unset($aTagList[$sTagCode]);
|
||||
|
||||
return $oTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sTagCode
|
||||
*
|
||||
* @return DBObject tag
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \CoreException
|
||||
*/
|
||||
private function GetTagFromCode($sTagCode)
|
||||
{
|
||||
$aAllowedTags = $this->GetAllowedTags();
|
||||
foreach($aAllowedTags as $oAllowedTag)
|
||||
{
|
||||
if ($oAllowedTag->Get('code') === $sTagCode)
|
||||
{
|
||||
return $oAllowedTag;
|
||||
}
|
||||
}
|
||||
throw new CoreUnexpectedValue("{$sTagCode} is not defined as a valid tag for {$this->sClass}:{$this->sAttCode}");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sTagCode
|
||||
*
|
||||
* @return DBObject tag
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function GetTagFromLabel($sTagLabel)
|
||||
{
|
||||
$aAllowedTags = $this->GetAllowedTags();
|
||||
foreach($aAllowedTags as $oAllowedTag)
|
||||
{
|
||||
if ($oAllowedTag->Get('label') === $sTagLabel)
|
||||
{
|
||||
return $oAllowedTag->Get('code');
|
||||
}
|
||||
}
|
||||
throw new CoreUnexpectedValue("{$sTagLabel} is not defined as a valid tag for {$this->sClass}:{$this->sAttCode}");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \TagSetFieldData[]
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \MySQLException
|
||||
*/
|
||||
private function GetAllowedTags()
|
||||
{
|
||||
return TagSetFieldData::GetAllowedValues($this->sClass, $this->sAttCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare Tag Set
|
||||
*
|
||||
* @param \ormTagSet $other
|
||||
*
|
||||
* @return bool true if same tag set
|
||||
*/
|
||||
public function Equals(ormSet $other)
|
||||
{
|
||||
if (!($other instanceof ormTagSet))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if ($this->GetTagDataClass() !== $other->GetTagDataClass())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return implode(' ', $this->GetValues()) === implode(' ', $other->GetValues());
|
||||
}
|
||||
|
||||
public function GetTagDataClass()
|
||||
{
|
||||
return TagSetFieldData::GetTagDataClassName($this->sClass, $this->sAttCode);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -257,14 +257,13 @@ class CoreServices implements iRestServiceProvider
|
||||
*/
|
||||
public function ListOperations($sVersion)
|
||||
{
|
||||
// 1.4 - iTop 2.5.2, 2.6.1, 2.7.0, Verb 'core/get': added pagination parameters limit and page
|
||||
// 1.3 - iTop 2.2.0, Verb 'get_related': added the options 'redundancy' and 'direction' to take into account the redundancy in the impact analysis
|
||||
// 1.2 - was documented in the wiki but never released ! Same as 1.3
|
||||
// 1.1 - In the reply, objects have a 'key' entry so that it is no more necessary to split class::key programmaticaly
|
||||
// 1.0 - Initial implementation in iTop 2.0.1
|
||||
//
|
||||
$aOps = array();
|
||||
if (in_array($sVersion, array('1.0', '1.1', '1.2', '1.3', '1.4')))
|
||||
if (in_array($sVersion, array('1.0', '1.1', '1.2', '1.3')))
|
||||
{
|
||||
$aOps[] = array(
|
||||
'verb' => 'core/create',
|
||||
@@ -300,16 +299,9 @@ class CoreServices implements iRestServiceProvider
|
||||
|
||||
/**
|
||||
* Enumerate services delivered by this class
|
||||
*
|
||||
* @param string $sVersion The version (e.g. 1.0) supported by the services
|
||||
* @param string $sVerb
|
||||
* @param $aParams
|
||||
*
|
||||
* @return RestResult The standardized result structure (at least a message)
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \SimpleGraphException
|
||||
* @throws \Exception
|
||||
* @throws Exception in case of internal failure.
|
||||
*/
|
||||
public function ExecOperation($sVersion, $sVerb, $aParams)
|
||||
{
|
||||
@@ -444,10 +436,8 @@ class CoreServices implements iRestServiceProvider
|
||||
$key = RestUtils::GetMandatoryParam($aParams, 'key');
|
||||
$aShowFields = RestUtils::GetFieldList($sClass, $aParams, 'output_fields');
|
||||
$bExtendedOutput = (RestUtils::GetOptionalParam($aParams, 'output_fields', '*') == '*+');
|
||||
$iLimit = (int)RestUtils::GetOptionalParam($aParams, 'limit', 0);
|
||||
$iPage = (int)RestUtils::GetOptionalParam($aParams, 'page', 1);
|
||||
|
||||
$oObjectSet = RestUtils::GetObjectSetFromKey($sClass, $key, $iLimit, self::getOffsetFromLimitAndPage($iLimit, $iPage));
|
||||
$oObjectSet = RestUtils::GetObjectSetFromKey($sClass, $key);
|
||||
$sTargetClass = $oObjectSet->GetFilter()->GetClass();
|
||||
|
||||
if (UserRights::IsActionAllowed($sTargetClass, UR_ACTION_READ) != UR_ALLOWED_YES)
|
||||
@@ -460,11 +450,6 @@ class CoreServices implements iRestServiceProvider
|
||||
$oResult->code = RestResult::UNAUTHORIZED;
|
||||
$oResult->message = "The current user does not have enough permissions for exporting data of class $sTargetClass";
|
||||
}
|
||||
elseif ($iPage < 1)
|
||||
{
|
||||
$oResult->code = RestResult::INVALID_PAGE;
|
||||
$oResult->message = "The request page number is not valid. It must be an integer greater than 0";
|
||||
}
|
||||
else
|
||||
{
|
||||
while ($oObject = $oObjectSet->Fetch())
|
||||
@@ -789,15 +774,4 @@ class CoreServices implements iRestServiceProvider
|
||||
$oResult->message = $sRes;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $iLimit
|
||||
* @param int $iPage
|
||||
*
|
||||
* @return int Offset for a given page number
|
||||
*/
|
||||
protected static function getOffsetFromLimitAndPage($iLimit, $iPage)
|
||||
{
|
||||
return $iLimit * max(0, $iPage - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,17 +43,61 @@
|
||||
|
||||
class SimpleCrypt
|
||||
{
|
||||
public static function GetNewDefaultParams()
|
||||
{
|
||||
if(function_exists('sodium_crypto_secretbox_open') && function_exists('random_bytes')){
|
||||
$sEngineName = 'Sodium';
|
||||
}
|
||||
else if (function_exists('openssl_decrypt'))
|
||||
{
|
||||
$sEngineName = 'OpenSSL';
|
||||
}
|
||||
else if(function_exists('mcrypt_module_open')){
|
||||
$sEngineName = 'Mcrypt';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sEngineName = 'Simple';
|
||||
}
|
||||
$sEngineName = 'SimpleCrypt' . $sEngineName . 'Engine';
|
||||
return $sEngineName::GetNewDefaultParams();
|
||||
}
|
||||
/**
|
||||
* Constructor
|
||||
* @param string $sEngineName Engine for encryption. Values: Simple, Mcrypt
|
||||
* @param string $sEngineName Engine for encryption. Values: Simple, Mcrypt, Sodium or OpenSSL
|
||||
* @throws Exception This library is unkown
|
||||
*/
|
||||
function __construct($sEngineName = 'Mcrypt')
|
||||
{
|
||||
if (($sEngineName == 'Mcrypt') && (!function_exists('mcrypt_module_open')))
|
||||
{
|
||||
// Defaults to Simple encryption if the mcrypt module is not present
|
||||
$sEngineName = 'Simple';
|
||||
}
|
||||
switch($sEngineName){
|
||||
case 'Sodium':
|
||||
if(!function_exists('sodium_crypto_secretbox_open')){
|
||||
$sEngineName = 'Simple';
|
||||
}
|
||||
break;
|
||||
case 'Mcrypt':
|
||||
if(!function_exists('mcrypt_module_open')){
|
||||
if (function_exists('openssl_decrypt'))
|
||||
{
|
||||
$sEngineName = 'OpenSSLMcryptCompatibility';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sEngineName = 'Simple';
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'OpenSSL':
|
||||
if(!function_exists('openssl_decrypt')){
|
||||
$sEngineName = 'Simple';
|
||||
}
|
||||
break;
|
||||
case 'Simple':
|
||||
break;
|
||||
default:
|
||||
throw new Exception(Dict::Format("Core:AttributeEncryptUnknownLibrary", $sEngineName));
|
||||
}
|
||||
|
||||
$sEngineName = 'SimpleCrypt' . $sEngineName . 'Engine';
|
||||
$this->oEngine = new $sEngineName;
|
||||
}
|
||||
@@ -150,6 +194,7 @@ class SimpleCrypt
|
||||
*/
|
||||
interface CryptEngine
|
||||
{
|
||||
public static function GetNewDefaultParams();
|
||||
function Encrypt($key, $sString);
|
||||
function Decrypt($key, $encrypted_data);
|
||||
}
|
||||
@@ -161,7 +206,12 @@ interface CryptEngine
|
||||
*/
|
||||
class SimpleCryptSimpleEngine implements CryptEngine
|
||||
{
|
||||
public function Encrypt($key, $sString)
|
||||
public static function GetNewDefaultParams()
|
||||
{
|
||||
return array( 'lib' => 'Simple', 'key' => null);
|
||||
}
|
||||
|
||||
public function Encrypt($key, $sString)
|
||||
{
|
||||
$result = '';
|
||||
for($i=1; $i<=strlen($sString); $i++)
|
||||
@@ -197,7 +247,13 @@ class SimpleCryptMcryptEngine implements CryptEngine
|
||||
{
|
||||
var $alg = MCRYPT_BLOWFISH;
|
||||
var $td = null;
|
||||
|
||||
|
||||
public static function GetNewDefaultParams()
|
||||
{
|
||||
return array('lib' => 'Mcrypt', 'key' => null);
|
||||
}
|
||||
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->td = mcrypt_module_open($this->alg,'','cbc','');
|
||||
@@ -205,7 +261,7 @@ class SimpleCryptMcryptEngine implements CryptEngine
|
||||
|
||||
public function Encrypt($key, $sString)
|
||||
{
|
||||
$iv = mcrypt_create_iv (mcrypt_enc_get_iv_size($this->td), MCRYPT_RAND); // MCRYPT_RAND is the only choice on Windows prior to PHP 5.3
|
||||
$iv = mcrypt_create_iv (mcrypt_enc_get_iv_size($this->td), MCRYPT_RAND_URANDOM); // MCRYPT_RAND_URANDOM is now useable since itop requires php >= 5.6
|
||||
mcrypt_generic_init($this->td, $key, $iv);
|
||||
if (empty($sString))
|
||||
{
|
||||
@@ -223,7 +279,7 @@ class SimpleCryptMcryptEngine implements CryptEngine
|
||||
$r = mcrypt_generic_init($this->td, $key, $iv);
|
||||
if (($r < 0) || ($r === false))
|
||||
{
|
||||
$decrypted_data = '** decryption error **';
|
||||
$decrypted_data = Dict::S("Core:AttributeEncryptFailedToDecrypt");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -238,4 +294,121 @@ class SimpleCryptMcryptEngine implements CryptEngine
|
||||
mcrypt_module_close($this->td);
|
||||
}
|
||||
}
|
||||
?>
|
||||
/**
|
||||
* SodiumEngine requires Sodium extension
|
||||
* Every encryption of the same string with the same key
|
||||
* will return a different encrypted string.
|
||||
* The key has to be SODIUM_CRYPTO_SECRETBOX_KEYBYTES bytes long.
|
||||
*/
|
||||
class SimpleCryptSodiumEngine implements CryptEngine
|
||||
{
|
||||
public static function GetNewDefaultParams()
|
||||
{
|
||||
return array('lib' => 'Sodium', 'key' => bin2hex(sodium_crypto_secretbox_keygen()));
|
||||
}
|
||||
|
||||
public function Encrypt($key, $sString)
|
||||
{
|
||||
$key = hex2bin($key);
|
||||
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
|
||||
$encrypted_string = sodium_crypto_secretbox($sString, $nonce, $key);
|
||||
sodium_memzero($sString);
|
||||
sodium_memzero($key);
|
||||
return base64_encode($nonce.$encrypted_string);
|
||||
}
|
||||
|
||||
public function Decrypt($key, $encrypted_data)
|
||||
{
|
||||
$key = hex2bin($key);
|
||||
$encrypted_data = base64_decode($encrypted_data);
|
||||
$nonce = mb_substr($encrypted_data, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
|
||||
$encrypted_data = mb_substr($encrypted_data, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');
|
||||
$plaintext = sodium_crypto_secretbox_open($encrypted_data, $nonce, $key);
|
||||
if ($plaintext === false)
|
||||
{
|
||||
$plaintext = Dict::S("Core:AttributeEncryptFailedToDecrypt");
|
||||
}
|
||||
sodium_memzero($encrypted_data);
|
||||
sodium_memzero($key);
|
||||
return $plaintext;
|
||||
}
|
||||
|
||||
}
|
||||
class SimpleCryptOpenSSLEngine implements CryptEngine
|
||||
{
|
||||
public static function GetNewDefaultParams()
|
||||
{
|
||||
return array('lib' => 'OpenSSL', 'key' => bin2hex(openssl_random_pseudo_bytes(32)));
|
||||
}
|
||||
|
||||
public function Encrypt($key, $sString)
|
||||
{
|
||||
$key = hex2bin($key);
|
||||
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length("AES-256-CBC"));
|
||||
$encrypted_string = openssl_encrypt($sString, "AES-256-CBC", $key, 0 , $iv);
|
||||
return $iv.$encrypted_string;
|
||||
}
|
||||
|
||||
public function Decrypt($key, $encrypted_data)
|
||||
{
|
||||
$key = hex2bin($key);
|
||||
$iv = mb_substr($encrypted_data, 0, openssl_cipher_iv_length("AES-256-CBC"), '8bit');
|
||||
$encrypted_data = mb_substr($encrypted_data, openssl_cipher_iv_length("AES-256-CBC"), null, '8bit');
|
||||
$plaintext = openssl_decrypt($encrypted_data,"AES-256-CBC", $key, 0 , $iv);
|
||||
if ($plaintext === false)
|
||||
{
|
||||
$plaintext = Dict::S("Core:AttributeEncryptFailedToDecrypt");
|
||||
}
|
||||
return trim($plaintext);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SimpleCryptOpenSSLMcryptCompatibilityEngine implements CryptEngine
|
||||
{
|
||||
public static function GetNewDefaultParams()
|
||||
{
|
||||
return array('lib' => 'OpenSSLMcryptCompatibility', 'key' => null);
|
||||
}
|
||||
//fix for php < 7.1.8 (keys are Zero padded instead of cycle padded)
|
||||
static private function MakeOpenSSLBlowfishKey($key)
|
||||
{
|
||||
if("$key" === '')
|
||||
{
|
||||
return $key;
|
||||
}
|
||||
$len = (16+2)*4;
|
||||
while(strlen($key) < $len)
|
||||
{
|
||||
$key .= $key;
|
||||
}
|
||||
$key = substr($key, 0, $len);
|
||||
return $key;
|
||||
}
|
||||
public function Encrypt($key, $sString)
|
||||
{
|
||||
$key = SimpleCryptOpenSSLMcryptCompatibilityEngine::MakeOpenSSLBlowfishKey($key);
|
||||
$blockSize = 8;
|
||||
$len = strlen($sString);
|
||||
$paddingLen = intval (($len + $blockSize -1) / $blockSize) * $blockSize - $len;
|
||||
$padding = str_repeat("\0", $paddingLen);
|
||||
$sData = $sString . $padding;
|
||||
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length("BF-CBC"));
|
||||
$encrypted_string = openssl_encrypt($sData, "BF-CBC", $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
|
||||
return $iv.$encrypted_string;
|
||||
}
|
||||
|
||||
public function Decrypt($key, $encrypted_data)
|
||||
{
|
||||
$key = SimpleCryptOpenSSLMcryptCompatibilityEngine::MakeOpenSSLBlowfishKey($key);
|
||||
$iv = mb_substr($encrypted_data, 0, openssl_cipher_iv_length("BF-CBC"), '8bit');
|
||||
$encrypted_data = mb_substr($encrypted_data, openssl_cipher_iv_length("BF-CBC"), null, '8bit');
|
||||
$plaintext = openssl_decrypt($encrypted_data,"BF-CBC", $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
|
||||
if ($plaintext === false)
|
||||
{
|
||||
$plaintext = Dict::S("Core:AttributeEncryptFailedToDecrypt");
|
||||
}
|
||||
return trim($plaintext);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -275,6 +275,7 @@ EOF
|
||||
$sAttCode = $aFieldSpec['sAttCode'];
|
||||
|
||||
$sField = '';
|
||||
/** @var \DBObject $oObj */
|
||||
$oObj = $aRow[$sAlias];
|
||||
if ($oObj == null)
|
||||
{
|
||||
@@ -332,11 +333,16 @@ EOF
|
||||
$sField = utils::TextToHtml($oObj->GetEditValue($sAttCode));
|
||||
$sData .= "<td x:str>$sField</td>";
|
||||
}
|
||||
else if($oAttDef instanceof AttributeString)
|
||||
else if ($oAttDef instanceof AttributeString)
|
||||
{
|
||||
$sField = $oObj->GetAsHTML($sAttCode, $this->bLocalizeOutput);
|
||||
$sData .= "<td x:str>$sField</td>";
|
||||
}
|
||||
else if ($oAttDef instanceof AttributeTagSet)
|
||||
{
|
||||
$sField = $oObj->GetAsCSV($sAttCode, $this->bLocalizeOutput, '');
|
||||
$sData .= "<td x:str>$sField</td>";
|
||||
}
|
||||
else
|
||||
{
|
||||
$rawValue = $oObj->Get($sAttCode);
|
||||
|
||||
@@ -95,7 +95,7 @@ class SQLObjectQuery extends SQLQuery
|
||||
$aFieldDesc = array();
|
||||
foreach ($this->m_aFields as $sAlias => $oExpression)
|
||||
{
|
||||
$aFieldDesc[] = $oExpression->Render()." as <em>$sAlias</em>";
|
||||
$aFieldDesc[] = $oExpression->RenderExpression(false)." as <em>$sAlias</em>";
|
||||
}
|
||||
$sFields = " => ".implode(', ', $aFieldDesc);
|
||||
}
|
||||
@@ -112,7 +112,7 @@ class SQLObjectQuery extends SQLQuery
|
||||
$oSQLQuery = $aJoinInfo["select"];
|
||||
if (isset($aJoinInfo["on_expression"]))
|
||||
{
|
||||
$sOnCondition = $aJoinInfo["on_expression"]->Render();
|
||||
$sOnCondition = $aJoinInfo["on_expression"]->RenderExpression(false);
|
||||
|
||||
echo "<li>Join '$sJoinType', ON ($sOnCondition)".$oSQLQuery->DisplayHtml()."</li>\n";
|
||||
}
|
||||
@@ -339,8 +339,12 @@ class SQLObjectQuery extends SQLQuery
|
||||
$this->PrepareRendering();
|
||||
$sFrom = self::ClauseFrom($this->__aFrom, $sIndent);
|
||||
$sWhere = self::ClauseWhere($this->m_oConditionExpr, $aArgs);
|
||||
// Sanity
|
||||
$iLimitCount = (int)$iLimitCount;
|
||||
if ($iLimitCount > 0)
|
||||
{
|
||||
// Sanity
|
||||
$iLimitStart = (int)$iLimitStart;
|
||||
$sLimit = 'LIMIT '.$iLimitStart.', '.$iLimitCount;
|
||||
}
|
||||
else
|
||||
@@ -464,7 +468,7 @@ class SQLObjectQuery extends SQLQuery
|
||||
case "left":
|
||||
if (isset($aJoinData["on_expression"]))
|
||||
{
|
||||
$sJoinCond = $aJoinData["on_expression"]->Render();
|
||||
$sJoinCond = $aJoinData["on_expression"]->RenderExpression(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -531,13 +535,13 @@ class SQLObjectQuery extends SQLQuery
|
||||
//
|
||||
foreach($this->m_aFields as $sAlias => $oExpression)
|
||||
{
|
||||
$oRootQuery->__aFields["`$sAlias`"] = $oExpression->Render();
|
||||
$oRootQuery->__aFields["`$sAlias`"] = $oExpression->RenderExpression(true);
|
||||
}
|
||||
if ($this->m_aGroupBy)
|
||||
{
|
||||
foreach($this->m_aGroupBy as $sAlias => $oExpression)
|
||||
{
|
||||
$oRootQuery->__aGroupBy["`$sAlias`"] = $oExpression->Render();
|
||||
$oRootQuery->__aGroupBy["`$sAlias`"] = $oExpression->RenderExpression(true);
|
||||
}
|
||||
}
|
||||
if ($this->m_bToDelete)
|
||||
@@ -551,7 +555,7 @@ class SQLObjectQuery extends SQLQuery
|
||||
|
||||
if (!is_null($this->m_oSelectedIdField))
|
||||
{
|
||||
$oRootQuery->__aSelectedIdFields[] = $this->m_oSelectedIdField->Render();
|
||||
$oRootQuery->__aSelectedIdFields[] = $this->m_oSelectedIdField->RenderExpression(true);
|
||||
}
|
||||
|
||||
// loop on joins, to complete the list of tables/fields/conditions
|
||||
|
||||
@@ -177,7 +177,7 @@ abstract class SQLQuery
|
||||
}
|
||||
else
|
||||
{
|
||||
return $oConditionExpr->Render($aArgs);
|
||||
return $oConditionExpr->RenderExpression(true, $aArgs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -168,7 +168,7 @@ class SQLUnionQuery extends SQLQuery
|
||||
}
|
||||
foreach($this->aSelectExpr as $sSelectAlias => $oExpr)
|
||||
{
|
||||
$aSelectAliases[$sSelectAlias] = $oExpr->Render()." AS `$sSelectAlias`";
|
||||
$aSelectAliases[$sSelectAlias] = $oExpr->RenderExpression(true)." AS `$sSelectAlias`";
|
||||
}
|
||||
|
||||
$sSelect = implode(",$sLineSep ", $aSelectAliases);
|
||||
|
||||
@@ -150,15 +150,18 @@ abstract class TabularBulkExport extends BulkExport
|
||||
// Add the reconciliation keys
|
||||
foreach(MetaModel::GetReconcKeys($sRemoteClass) as $sRemoteAttCode)
|
||||
{
|
||||
$sAttCodeEx = $sAttCode.'->'.$sRemoteAttCode;
|
||||
if (!array_key_exists($sAttCodeEx, $aResult))
|
||||
{
|
||||
$oRemoteAttDef = MetaModel::GetAttributeDef($sRemoteClass, $sRemoteAttCode);
|
||||
if ($this->IsExportableField($sRemoteClass, $sRemoteAttCode, $oRemoteAttDef))
|
||||
{
|
||||
$aResult[$sAttCodeEx] = array('code' => $sAttCodeEx, 'unique_label' => $oAttDef->GetLabel().'->'.$oRemoteAttDef->GetLabel(), 'label' => MetaModel::GetLabel($sRemoteClass, $sRemoteAttCode), 'attdef' => $oRemoteAttDef);
|
||||
}
|
||||
}
|
||||
if (!empty($sRemoteAttCode))
|
||||
{
|
||||
$sAttCodeEx = $sAttCode.'->'.$sRemoteAttCode;
|
||||
if (!array_key_exists($sAttCodeEx, $aResult))
|
||||
{
|
||||
$oRemoteAttDef = MetaModel::GetAttributeDef($sRemoteClass, $sRemoteAttCode);
|
||||
if ($this->IsExportableField($sRemoteClass, $sRemoteAttCode, $oRemoteAttDef))
|
||||
{
|
||||
$aResult[$sAttCodeEx] = array('code' => $sAttCodeEx, 'unique_label' => $oAttDef->GetLabel().'->'.$oRemoteAttDef->GetLabel(), 'label' => MetaModel::GetLabel($sRemoteClass, $sRemoteAttCode), 'attdef' => $oRemoteAttDef);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
394
core/tagsetfield.class.inc.php
Normal file
394
core/tagsetfield.class.inc.php
Normal file
@@ -0,0 +1,394 @@
|
||||
<?php
|
||||
// Copyright (C) 2018 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/>
|
||||
|
||||
|
||||
/**
|
||||
* <p>Stores data for {@link AttributeTagSet} fields
|
||||
*
|
||||
* <p>We will have an implementation for each class/field to be able to customize rights (generated in
|
||||
* \MFCompiler::CompileClass).<br> Only this abstract class will exists in the DB : the implementations won't had any
|
||||
* new field.
|
||||
*
|
||||
* @since 2.6 N°931 tag fields
|
||||
*/
|
||||
abstract class TagSetFieldData extends cmdbAbstractObject
|
||||
{
|
||||
private static $m_aAllowedValues = array();
|
||||
|
||||
/**
|
||||
* @throws \CoreException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
'category' => 'bizmodel',
|
||||
'key_type' => 'autoincrement',
|
||||
'name_attcode' => array('label'),
|
||||
'state_attcode' => '',
|
||||
'reconc_keys' => array('code'),
|
||||
'db_table' => 'priv_tagfielddata',
|
||||
'db_key_field' => 'id',
|
||||
'db_finalclass_field' => 'finalclass',
|
||||
);
|
||||
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeString("code", array(
|
||||
"allowed_values" => null,
|
||||
"sql" => 'code',
|
||||
"default_value" => '',
|
||||
"is_null_allowed" => false,
|
||||
"depends_on" => array(),
|
||||
"validation_pattern" => '^[a-zA-Z][a-zA-Z0-9]{3,}$',
|
||||
)));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("label", array(
|
||||
"allowed_values" => null,
|
||||
"sql" => 'label',
|
||||
"default_value" => '',
|
||||
"is_null_allowed" => false,
|
||||
"depends_on" => array()
|
||||
)));
|
||||
MetaModel::Init_AddAttribute(new AttributeHTML("description", array(
|
||||
"allowed_values" => null,
|
||||
"sql" => 'description',
|
||||
"default_value" => '',
|
||||
"is_null_allowed" => true,
|
||||
"depends_on" => array()
|
||||
)));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("obj_class", array(
|
||||
"allowed_values" => null,
|
||||
"sql" => 'obj_class',
|
||||
"default_value" => '',
|
||||
"is_null_allowed" => false,
|
||||
"depends_on" => array()
|
||||
)));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("obj_attcode", array(
|
||||
"allowed_values" => null,
|
||||
"sql" => 'obj_attcode',
|
||||
"default_value" => '',
|
||||
"is_null_allowed" => false,
|
||||
"depends_on" => array()
|
||||
)));
|
||||
|
||||
|
||||
MetaModel::Init_SetZListItems('details', array('code', 'label', 'description'));
|
||||
MetaModel::Init_SetZListItems('standard_search', array('code', 'label', 'description'));
|
||||
MetaModel::Init_SetZListItems('list', array('code', 'label', 'description'));
|
||||
}
|
||||
|
||||
public function ComputeValues()
|
||||
{
|
||||
$sClassName = get_class($this);
|
||||
$aRes = static::ExtractTagFieldName($sClassName);
|
||||
$this->_Set('obj_class', $aRes['obj_class']);
|
||||
$this->_Set('obj_attcode', $aRes['obj_attcode']);
|
||||
}
|
||||
|
||||
public static function GetTagDataClassName($sClass, $sAttCode)
|
||||
{
|
||||
$sTagSuffix = $sClass.'__'.$sAttCode;
|
||||
|
||||
return 'TagSetFieldDataFor_'.$sTagSuffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract Tag class and attcode from the TagFieldData class name
|
||||
*
|
||||
* @param $sClassName
|
||||
*
|
||||
* @return string[]
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public static function ExtractTagFieldName($sClassName)
|
||||
{
|
||||
$aRes = array();
|
||||
// Extract class and attcode from class name using pattern TagSetFieldDataFor_<class>_<attcode>>;
|
||||
if (preg_match('@^TagSetFieldDataFor_(?<class>\w+)__(?<attcode>\w+)$@', $sClassName, $aMatches))
|
||||
{
|
||||
$aRes['obj_class'] = $aMatches['class'];
|
||||
$aRes['obj_attcode'] = $aMatches['attcode'];
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new CoreException("Bad Class name format: $sClassName");
|
||||
}
|
||||
return $aRes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DeletionPlan $oDeletionPlan
|
||||
*
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function DoCheckToDelete(&$oDeletionPlan)
|
||||
{
|
||||
parent::DoCheckToDelete($oDeletionPlan);
|
||||
|
||||
$sTagCode = $this->Get('code');
|
||||
if ($this->IsCodeUsed($sTagCode))
|
||||
{
|
||||
$this->m_aDeleteIssues[] = Dict::S('Core:TagSetFieldData:ErrorDeleteUsedTag');
|
||||
}
|
||||
// Clear cache
|
||||
$sClass = $this->Get('obj_class');
|
||||
$sAttCode = $this->Get('obj_attcode');
|
||||
$sTagDataClass = self::GetTagDataClassName($sClass, $sAttCode);
|
||||
unset(self::$m_aAllowedValues[$sTagDataClass]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \CoreException
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
* @throws \OQLException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function DoCheckToWrite()
|
||||
{
|
||||
$this->ComputeValues();
|
||||
$sClass = $this->Get('obj_class');
|
||||
$sAttCode = $this->Get('obj_attcode');
|
||||
$iMaxLen = 20;
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
if ($oAttDef instanceof AttributeTagSet)
|
||||
{
|
||||
$iMaxLen = $oAttDef->GetTagCodeMaxLength();
|
||||
}
|
||||
|
||||
$sTagCode = $this->Get('code');
|
||||
// Check code syntax
|
||||
$iMax = $iMaxLen - 1;
|
||||
if (!preg_match("@^[a-zA-Z][a-zA-Z0-9]{2,$iMax}$@", $sTagCode))
|
||||
{
|
||||
$this->m_aCheckIssues[] = Dict::Format('Core:TagSetFieldData:ErrorTagCodeSyntax', $iMaxLen);
|
||||
}
|
||||
|
||||
// Check that the code is not a MySQL stop word
|
||||
$sSQL = "SELECT * FROM INFORMATION_SCHEMA.INNODB_FT_DEFAULT_STOPWORD";
|
||||
try
|
||||
{
|
||||
$aResults = CMDBSource::QueryToArray($sSQL);
|
||||
} catch (MySQLException $e)
|
||||
{
|
||||
IssueLog::Warning($e->getMessage());
|
||||
$aResults = array();
|
||||
}
|
||||
|
||||
foreach($aResults as $aResult)
|
||||
{
|
||||
if ($aResult['value'] == $sTagCode)
|
||||
{
|
||||
$this->m_aCheckIssues[] = Dict::S('Core:TagSetFieldData:ErrorTagCodeReservedWord');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$sTagLabel = $this->Get('label');
|
||||
$sSepItem = MetaModel::GetConfig()->Get('tag_set_item_separator');
|
||||
if (empty($sTagLabel) || (strpos($sTagLabel, $sSepItem) !== false))
|
||||
{
|
||||
// Label must not contain | character
|
||||
$this->m_aCheckIssues[] = Dict::Format('Core:TagSetFieldData:ErrorTagLabelSyntax', $sSepItem);
|
||||
}
|
||||
|
||||
// Check that code and labels are uniques
|
||||
$id = $this->GetKey();
|
||||
$sClassName = get_class($this);
|
||||
if (empty($id))
|
||||
{
|
||||
$oSearch = DBSearch::FromOQL("SELECT $sClassName WHERE (code = :tag_code OR label = :tag_label)");
|
||||
}
|
||||
else
|
||||
{
|
||||
$oSearch = DBSearch::FromOQL("SELECT $sClassName WHERE id != :id AND (code = :tag_code OR label = :tag_label)");
|
||||
}
|
||||
$aArgs = array('id' => $id, 'tag_code' => $sTagCode, 'tag_label' => $sTagLabel);
|
||||
$oSet = new DBObjectSet($oSearch, array(), $aArgs);
|
||||
if ($oSet->CountExceeds(0))
|
||||
{
|
||||
$this->m_aCheckIssues[] = Dict::S('Core:TagSetFieldData:ErrorDuplicateTagCodeOrLabel');
|
||||
}
|
||||
// Clear cache
|
||||
$sTagDataClass = self::GetTagDataClassName($sClass, $sAttCode);
|
||||
unset(self::$m_aAllowedValues[$sTagDataClass]);
|
||||
|
||||
parent::DoCheckToWrite();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function OnUpdate()
|
||||
{
|
||||
parent::OnUpdate();
|
||||
$aChanges = $this->ListChanges();
|
||||
if (array_key_exists('code', $aChanges))
|
||||
{
|
||||
$sTagCode = $this->GetOriginal('code');
|
||||
if ($this->IsCodeUsed($sTagCode))
|
||||
{
|
||||
throw new CoreException(Dict::S('Core:TagSetFieldData:ErrorCodeUpdateNotAllowed'));
|
||||
}
|
||||
}
|
||||
if (array_key_exists('obj_class', $aChanges))
|
||||
{
|
||||
throw new CoreException(Dict::S('Core:TagSetFieldData:ErrorClassUpdateNotAllowed'));
|
||||
}
|
||||
if (array_key_exists('obj_attcode', $aChanges))
|
||||
{
|
||||
throw new CoreException(Dict::S('Core:TagSetFieldData:ErrorAttCodeUpdateNotAllowed'));
|
||||
}
|
||||
}
|
||||
|
||||
private function IsCodeUsed($sTagCode)
|
||||
{
|
||||
try
|
||||
{
|
||||
$sClass = $this->Get('obj_class');
|
||||
$sAttCode = $this->Get('obj_attcode');
|
||||
$oSearch = DBSearch::FromOQL("SELECT $sClass WHERE $sAttCode MATCHES '$sTagCode'");
|
||||
$oSet = new DBObjectSet($oSearch);
|
||||
if ($oSet->CountExceeds(0))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
IssueLog::Warning($e->getMessage());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function GetAttributeFlags($sAttCode, &$aReasons = array(), $sTargetState = '')
|
||||
{
|
||||
if ($sAttCode == 'code')
|
||||
{
|
||||
if ((!$this->IsNew()) && ($this->IsCodeUsed($this->Get('code'))))
|
||||
{
|
||||
return OPT_ATT_READONLY;
|
||||
}
|
||||
}
|
||||
|
||||
return parent::GetAttributeFlags($sAttCode, $aReasons, $sTargetState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display Tag Usage
|
||||
*
|
||||
* @param \WebPage $oPage
|
||||
* @param bool $bEditMode
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \DictExceptionMissingString
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
* @throws \OQLException
|
||||
*/
|
||||
function DisplayBareRelations(WebPage $oPage, $bEditMode = false)
|
||||
{
|
||||
parent::DisplayBareRelations($oPage, $bEditMode);
|
||||
if (!$bEditMode)
|
||||
{
|
||||
$sClass = $this->Get('obj_class');
|
||||
$sAttCode = $this->Get('obj_attcode');
|
||||
$sTagCode = $this->Get('code');
|
||||
|
||||
|
||||
$oFilter = DBSearch::FromOQL("SELECT $sClass WHERE $sAttCode MATCHES '$sTagCode'");
|
||||
$oSet = new DBObjectSet($oFilter);
|
||||
$iCount = $oSet->Count();
|
||||
$oPage->SetCurrentTab(Dict::Format('Core:TagSetFieldData:WhereIsThisTagTab', $iCount));
|
||||
|
||||
if ($iCount === 0)
|
||||
{
|
||||
$sNoEntries = Dict::S('Core:TagSetFieldData:NoEntryFound');
|
||||
$oPage->add("<p>$sNoEntries</p>");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$oFilter = DBSearch::FromOQL("SELECT $sClass WHERE $sAttCode MATCHES '$sTagCode'");
|
||||
$oSet = new DBObjectSet($oFilter);
|
||||
if ($oSet->CountExceeds(0))
|
||||
{
|
||||
$sClassLabel = MetaModel::GetName($sClass);
|
||||
$oPage->add("<h2>$sClassLabel</h2>");
|
||||
$oResultBlock = new DisplayBlock($oFilter, 'list', false);
|
||||
$oResultBlock->Display($oPage, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function GetClassName($sClass)
|
||||
{
|
||||
if ($sClass == 'TagSetFieldData')
|
||||
{
|
||||
$aWords = preg_split('/(?=[A-Z]+)/', $sClass);
|
||||
return trim(implode(' ', $aWords));
|
||||
}
|
||||
try
|
||||
{
|
||||
$aTagFieldInfo = self::ExtractTagFieldName($sClass);
|
||||
} catch (CoreException $e)
|
||||
{
|
||||
return $sClass;
|
||||
}
|
||||
$sClassDesc = MetaModel::GetName($aTagFieldInfo['obj_class']);
|
||||
$sAttDesc = MetaModel::GetAttributeDef($aTagFieldInfo['obj_class'], $aTagFieldInfo['obj_attcode'])->GetLabel();
|
||||
if (Dict::Exists("Class:$sClass"))
|
||||
{
|
||||
$sName = Dict::Format("Class:$sClass", $sClassDesc, $sAttDesc);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sName = Dict::Format('Class:TagSetFieldData', $sClassDesc, $sAttDesc);
|
||||
}
|
||||
return $sName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sClass
|
||||
* @param $sAttCode
|
||||
*
|
||||
* @return \TagSetFieldData[]
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \MySQLException
|
||||
*/
|
||||
public static function GetAllowedValues($sClass, $sAttCode)
|
||||
{
|
||||
$sClass = MetaModel::GetAttributeOrigin($sClass, $sAttCode);
|
||||
$sTagDataClass = self::GetTagDataClassName($sClass, $sAttCode);
|
||||
if (!isset(self::$m_aAllowedValues[$sTagDataClass]))
|
||||
{
|
||||
$oSearch = new DBObjectSearch($sTagDataClass);
|
||||
$oSearch->AddCondition('obj_class', $sClass);
|
||||
$oSearch->AddCondition('obj_attcode', $sAttCode);
|
||||
$oSet = new DBObjectSet($oSearch);
|
||||
self::$m_aAllowedValues[$sTagDataClass] = $oSet->ToArray();
|
||||
}
|
||||
|
||||
return self::$m_aAllowedValues[$sTagDataClass];
|
||||
}
|
||||
}
|
||||
@@ -56,18 +56,20 @@ class TemplateString
|
||||
{
|
||||
protected $m_sRaw;
|
||||
protected $m_aPlaceholders;
|
||||
|
||||
|
||||
public function __construct($sRaw)
|
||||
{
|
||||
$this->m_sRaw = $sRaw;
|
||||
$this->m_aPlaceholders = null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Split the string into placholders
|
||||
* @param Hash $aParamTypes Class of the expected parameters: hash array of '<param_id>' => '<class_name>'
|
||||
* @return void
|
||||
*/
|
||||
* Split the string into placholders
|
||||
*
|
||||
* @param array $aParamTypes Class of the expected parameters: hash array of '<param_id>' => '<class_name>'
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function Analyze($aParamTypes = array())
|
||||
{
|
||||
if (!is_null($this->m_aPlaceholders)) return;
|
||||
@@ -100,9 +102,10 @@ class TemplateString
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the placeholders (for reporting purposes)
|
||||
* @return void
|
||||
*/
|
||||
* Return the placeholders (for reporting purposes)
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function GetPlaceholders()
|
||||
{
|
||||
return $this->m_aPlaceholders;
|
||||
@@ -110,9 +113,11 @@ class TemplateString
|
||||
|
||||
/**
|
||||
* Check the format when possible
|
||||
* @param Hash $aParamTypes Class of the expected parameters: hash array of '<param_id>' => '<class_name>'
|
||||
* @return void
|
||||
*/
|
||||
*
|
||||
* @param array $aParamTypes Class of the expected parameters: hash array of '<param_id>' => '<class_name>'
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function IsValid($aParamTypes = array())
|
||||
{
|
||||
$this->Analyze($aParamTypes);
|
||||
@@ -135,10 +140,12 @@ class TemplateString
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the given parameters to replace the placeholders
|
||||
* @param Hash $aParamValues Value of the expected parameters: hash array of '<param_id>' => '<value>'
|
||||
* @return void
|
||||
*/
|
||||
* Apply the given parameters to replace the placeholders
|
||||
*
|
||||
* @param array $aParamValues Value of the expected parameters: hash array of '<param_id>' => '<value>'
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function Render($aParamValues = array())
|
||||
{
|
||||
$aParamTypes = array();
|
||||
@@ -174,5 +181,4 @@ class TemplateString
|
||||
}
|
||||
return str_replace($aSearch, $aReplace, $this->m_sRaw);
|
||||
}
|
||||
}
|
||||
?>
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user