mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-14 07:54:10 +01:00
Compare commits
625 Commits
support/2.
...
2.4.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab6cf18445 | ||
|
|
a0171ac9cf | ||
|
|
4b4bb6aa0b | ||
|
|
ff8397edf6 | ||
|
|
d46dc519e1 | ||
|
|
fe7eb22d32 | ||
|
|
bafb6ab212 | ||
|
|
6c0e986563 | ||
|
|
cf06f3e0d5 | ||
|
|
fe887f01c0 | ||
|
|
9109138127 | ||
|
|
51b19c03b1 | ||
|
|
8cb584abdd | ||
|
|
3480d478b0 | ||
|
|
3a34417a7d | ||
|
|
6ca3ca108b | ||
|
|
037036ce6a | ||
|
|
9d2dab5eba | ||
|
|
3169b39952 | ||
|
|
ba33b01d84 | ||
|
|
7db69ed158 | ||
|
|
a6f6d536e0 | ||
|
|
880faf2021 | ||
|
|
f96902c03e | ||
|
|
6e810322f7 | ||
|
|
dd9984be7f | ||
|
|
98f3f88ea2 | ||
|
|
8be0c36859 | ||
|
|
8c5a65d836 | ||
|
|
db47973063 | ||
|
|
028e815bfa | ||
|
|
caf07affa6 | ||
|
|
bdef9e59de | ||
|
|
4bf001d108 | ||
|
|
a095208e53 | ||
|
|
2429ec4eec | ||
|
|
55655e3fca | ||
|
|
7e98b04ed3 | ||
|
|
59c6c0250d | ||
|
|
18a5afb8a6 | ||
|
|
4b6258dfa1 | ||
|
|
44b9f23117 | ||
|
|
2ddefcd2fc | ||
|
|
39b15fec50 | ||
|
|
c180dd9863 | ||
|
|
1172130d7c | ||
|
|
8172e14345 | ||
|
|
6e4836f5e0 | ||
|
|
4ee6e6f915 | ||
|
|
e5b318b94f | ||
|
|
c081b89f03 | ||
|
|
f237a98c1d | ||
|
|
fd2a41aee6 | ||
|
|
25b1a0d8af | ||
|
|
985b366cc7 | ||
|
|
d1e2bc9b2b | ||
|
|
757b3d1cc3 | ||
|
|
8c0bbadbfe | ||
|
|
cbd3d2c165 | ||
|
|
b286bd48c7 | ||
|
|
c4efbe2891 | ||
|
|
58506441cf | ||
|
|
d575ee50ee | ||
|
|
997fa3a856 | ||
|
|
70abd8027e | ||
|
|
4c6e6ffc9c | ||
|
|
b268df7bb4 | ||
|
|
7aaa35f88e | ||
|
|
4bb91b819c | ||
|
|
656cae3e66 | ||
|
|
292735b4b2 | ||
|
|
dcc4061261 | ||
|
|
ff7ac731df | ||
|
|
c784afdb06 | ||
|
|
5e60e8d725 | ||
|
|
564bf04647 | ||
|
|
17d18eb75f | ||
|
|
a9a6460747 | ||
|
|
1fa1a053e4 | ||
|
|
01e151632f | ||
|
|
c17d0b1027 | ||
|
|
517d16e3d9 | ||
|
|
696a5d1a48 | ||
|
|
96288db97b | ||
|
|
e6072cd8b6 | ||
|
|
db8bb7d94a | ||
|
|
1b2dbf751c | ||
|
|
b6805af20e | ||
|
|
4cc6290c88 | ||
|
|
c79d17516d | ||
|
|
a2154d6418 | ||
|
|
f820d3bd81 | ||
|
|
0a914f527a | ||
|
|
a8c749a46a | ||
|
|
e58c8afeab | ||
|
|
005b0eab1d | ||
|
|
55b34b5d11 | ||
|
|
7b4f00ee45 | ||
|
|
cc99cec250 | ||
|
|
105dc46f46 | ||
|
|
770fbe157c | ||
|
|
21d44c664f | ||
|
|
b318cbe211 | ||
|
|
7106501812 | ||
|
|
567dd39270 | ||
|
|
b292621f82 | ||
|
|
46f0b48add | ||
|
|
9a5f16bd3e | ||
|
|
6224ec7a37 | ||
|
|
c460edcd5e | ||
|
|
b839b42cd4 | ||
|
|
fabc8b91d7 | ||
|
|
4c38cd570c | ||
|
|
c4fd15ae90 | ||
|
|
bbca491118 | ||
|
|
0d04bd8b0e | ||
|
|
3bcb3a2154 | ||
|
|
aa863c40d3 | ||
|
|
5d7ad9f6dd | ||
|
|
72cb41dd7e | ||
|
|
6ef39c9f5b | ||
|
|
614b948c82 | ||
|
|
7c0715ad59 | ||
|
|
31819497ae | ||
|
|
c86e2c6c79 | ||
|
|
1d54b66de9 | ||
|
|
f932765e19 | ||
|
|
11b4085566 | ||
|
|
12afcd7cce | ||
|
|
666c95a656 | ||
|
|
fde9c28263 | ||
|
|
52c3ff0406 | ||
|
|
9f4d07aaa5 | ||
|
|
70561d6331 | ||
|
|
ae8311e224 | ||
|
|
e09fa0ffc1 | ||
|
|
5b6a20048f | ||
|
|
d86c211b73 | ||
|
|
8499babcb3 | ||
|
|
2d44cbbb60 | ||
|
|
3d89ef9076 | ||
|
|
e896f8af3d | ||
|
|
23268e8969 | ||
|
|
421e12debd | ||
|
|
389848cef4 | ||
|
|
7da4423346 | ||
|
|
2a770b9dc4 | ||
|
|
8df12e64f2 | ||
|
|
341261c14e | ||
|
|
bfc7c73e18 | ||
|
|
9a6c4ba7bb | ||
|
|
dc47c34981 | ||
|
|
3ac5131a19 | ||
|
|
134a9aa684 | ||
|
|
a98c0d32ae | ||
|
|
38af2b85c4 | ||
|
|
c9c84735c4 | ||
|
|
646972838a | ||
|
|
ce92241593 | ||
|
|
715eeff627 | ||
|
|
651de821df | ||
|
|
2233ea5f54 | ||
|
|
39e6915e05 | ||
|
|
6279f4ac70 | ||
|
|
badf10e74e | ||
|
|
2e9cd6a342 | ||
|
|
cef70ee9a2 | ||
|
|
37e15706b2 | ||
|
|
4b210273d2 | ||
|
|
951e8e03b0 | ||
|
|
c016ca364d | ||
|
|
4611442665 | ||
|
|
c7e7976607 | ||
|
|
0ca9fac04b | ||
|
|
7edc79f398 | ||
|
|
c110206264 | ||
|
|
b3ad2030cb | ||
|
|
0938ba939c | ||
|
|
bca393def3 | ||
|
|
796ac55ff4 | ||
|
|
66ae89e91d | ||
|
|
361cca465a | ||
|
|
7462d66643 | ||
|
|
a177b9b1d4 | ||
|
|
48f6635917 | ||
|
|
d6707743a9 | ||
|
|
6f474686ad | ||
|
|
affd8ea8a6 | ||
|
|
abd2748a09 | ||
|
|
6a1c9b9f26 | ||
|
|
ac04aa3359 | ||
|
|
051d68c902 | ||
|
|
ce81f7abb6 | ||
|
|
03fbde3403 | ||
|
|
59ddab9f94 | ||
|
|
225cbb450e | ||
|
|
7cace73844 | ||
|
|
2562d33997 | ||
|
|
a1e5b32e49 | ||
|
|
0c5df83a95 | ||
|
|
cf79456ff3 | ||
|
|
a0a378a91a | ||
|
|
9cf42f6cc8 | ||
|
|
c022d12a0a | ||
|
|
3d72060bf5 | ||
|
|
43709576c0 | ||
|
|
8ca6610e75 | ||
|
|
b02d347541 | ||
|
|
a39789cf89 | ||
|
|
58534dca39 | ||
|
|
538e9c11e8 | ||
|
|
cba6e8d8a3 | ||
|
|
fd9fe85ef3 | ||
|
|
7ed3e10d0f | ||
|
|
fe98b850d1 | ||
|
|
b1494d0dd9 | ||
|
|
0655904683 | ||
|
|
bad5aff764 | ||
|
|
440f2639b5 | ||
|
|
5f2245595f | ||
|
|
03db4e7a14 | ||
|
|
04030e07e3 | ||
|
|
80832877eb | ||
|
|
aa63847502 | ||
|
|
4171c5c35f | ||
|
|
8e06a95502 | ||
|
|
1c84934777 | ||
|
|
0294f50339 | ||
|
|
33098943a1 | ||
|
|
b953a6ab3e | ||
|
|
7287cab6f4 | ||
|
|
df4cad3ff1 | ||
|
|
66b6206daf | ||
|
|
6f40459215 | ||
|
|
24430e630f | ||
|
|
3420db26a5 | ||
|
|
c27cafb02f | ||
|
|
09a2dbc185 | ||
|
|
015c5b7a96 | ||
|
|
4710ae6eb7 | ||
|
|
c6b98dd5c1 | ||
|
|
61d3a4e48f | ||
|
|
be4c1e2c1c | ||
|
|
6f499dd36e | ||
|
|
03661158f1 | ||
|
|
b9866008b2 | ||
|
|
2557d6e7d5 | ||
|
|
79582b55a4 | ||
|
|
46b5293867 | ||
|
|
54ca6ad3d9 | ||
|
|
2ab8161182 | ||
|
|
3c7f5db5e7 | ||
|
|
e72bc65df8 | ||
|
|
c039414adc | ||
|
|
d45cbb36f7 | ||
|
|
ba5dd94240 | ||
|
|
fe84cbd978 | ||
|
|
f8ddcc7b70 | ||
|
|
c0d7737872 | ||
|
|
97e6c53add | ||
|
|
68ac5d7300 | ||
|
|
e0eebc0b4e | ||
|
|
fb28de971c | ||
|
|
ae637c7c96 | ||
|
|
6bc24b6064 | ||
|
|
db60dfb707 | ||
|
|
ee2aa67959 | ||
|
|
eff42f5d3b | ||
|
|
3a1810bc42 | ||
|
|
1f1ab2b10c | ||
|
|
d0d9b1ce50 | ||
|
|
307145502c | ||
|
|
5c84703cf0 | ||
|
|
cb2745be24 | ||
|
|
b613b9b302 | ||
|
|
624f6bcfc5 | ||
|
|
ce2f1edaac | ||
|
|
140efb4240 | ||
|
|
e3847ac24b | ||
|
|
b583bd2edc | ||
|
|
c654a2067d | ||
|
|
3531d5c5b7 | ||
|
|
e31fa066fc | ||
|
|
bc476295cb | ||
|
|
98781fac6d | ||
|
|
efeee395d1 | ||
|
|
b17505f86e | ||
|
|
f89dd77dbc | ||
|
|
f8b6fb51c2 | ||
|
|
d80c2293dc | ||
|
|
3cecdeff1c | ||
|
|
3f819eaa19 | ||
|
|
01de060093 | ||
|
|
7c39a8baf1 | ||
|
|
ebcaaa089a | ||
|
|
121635d636 | ||
|
|
68885496dd | ||
|
|
9a56c3acfd | ||
|
|
a0259636b1 | ||
|
|
0844beca79 | ||
|
|
dbe3e94d5c | ||
|
|
9a43083b3b | ||
|
|
c6e472b98b | ||
|
|
0834e36b38 | ||
|
|
f7cf825975 | ||
|
|
189c03dfea | ||
|
|
990be7a105 | ||
|
|
813c80499c | ||
|
|
495cedc04f | ||
|
|
cbf2919dcd | ||
|
|
7cbdfaa5d4 | ||
|
|
374ce20d0c | ||
|
|
1dbc2051cc | ||
|
|
7e38d4be50 | ||
|
|
8451ffdfb4 | ||
|
|
c2b9716e28 | ||
|
|
b15621639b | ||
|
|
4ca998ce91 | ||
|
|
71ec3da4ac | ||
|
|
43b8522b85 | ||
|
|
8b820ce403 | ||
|
|
4301a5d39d | ||
|
|
51519a3659 | ||
|
|
2693e81bad | ||
|
|
eca746f558 | ||
|
|
01865ed7f9 | ||
|
|
14dfe04714 | ||
|
|
befa0b4429 | ||
|
|
68ff589f9c | ||
|
|
04e1f32860 | ||
|
|
636356f479 | ||
|
|
313ea72017 | ||
|
|
98b4a0178f | ||
|
|
602be73d0b | ||
|
|
9bfc9a0a76 | ||
|
|
7e6a040983 | ||
|
|
905d47cab5 | ||
|
|
2118a5da71 | ||
|
|
c2c0221535 | ||
|
|
ba0a9709f4 | ||
|
|
80121b89c3 | ||
|
|
3b48428897 | ||
|
|
018d6a98e9 | ||
|
|
522108dc69 | ||
|
|
5451ced894 | ||
|
|
944c24e18f | ||
|
|
248f8d6fd4 | ||
|
|
ebe467b77a | ||
|
|
e9b7ccd475 | ||
|
|
b213f2baea | ||
|
|
50970810d2 | ||
|
|
17fbc504e2 | ||
|
|
237980097d | ||
|
|
44b53e40a0 | ||
|
|
6c6ad0a45b | ||
|
|
623fa8ec63 | ||
|
|
02c79fd0a2 | ||
|
|
05bb797768 | ||
|
|
096cfdc529 | ||
|
|
5354b0b32b | ||
|
|
88ec528071 | ||
|
|
5584d42813 | ||
|
|
b696b140b2 | ||
|
|
1de6ac1765 | ||
|
|
d2d895fdf5 | ||
|
|
a8ad3004ea | ||
|
|
b707db9364 | ||
|
|
36c53249a0 | ||
|
|
f9ed88a084 | ||
|
|
294964b227 | ||
|
|
8a6fba1981 | ||
|
|
1fe0ce5640 | ||
|
|
7bbe907edc | ||
|
|
6f7d364826 | ||
|
|
ea2681e08c | ||
|
|
c66728e478 | ||
|
|
ba9c6bd8b3 | ||
|
|
b23b468c0f | ||
|
|
f5144c2bb1 | ||
|
|
ee95dd2480 | ||
|
|
c1805ce47f | ||
|
|
23556e3a43 | ||
|
|
e70e1de75e | ||
|
|
091b989715 | ||
|
|
b238283104 | ||
|
|
de2eed5187 | ||
|
|
8e046cafda | ||
|
|
f02df401ff | ||
|
|
28eba2512a | ||
|
|
e3931274ae | ||
|
|
a55245d203 | ||
|
|
7b0acbdad3 | ||
|
|
9cc583c47b | ||
|
|
fa2fd6dcdf | ||
|
|
9d242e1623 | ||
|
|
ec6a8537e9 | ||
|
|
402e95b9e2 | ||
|
|
2fcf50bb88 | ||
|
|
2e8c629195 | ||
|
|
9c5b3818eb | ||
|
|
c9bb27a3ff | ||
|
|
f96a2092e7 | ||
|
|
5dafb92dd3 | ||
|
|
63f0a0ef11 | ||
|
|
bb9236deae | ||
|
|
1a66a6491a | ||
|
|
1528d85f5f | ||
|
|
3773a88cc1 | ||
|
|
856a73fb87 | ||
|
|
d0bc9a0bb8 | ||
|
|
751b7c8560 | ||
|
|
4fccae157b | ||
|
|
85dc7c9452 | ||
|
|
1cf25e49ae | ||
|
|
ab6cd49fcb | ||
|
|
5411e60c81 | ||
|
|
15da430459 | ||
|
|
5adb0fd556 | ||
|
|
2876d7e08c | ||
|
|
59c3f565a3 | ||
|
|
86191b36d8 | ||
|
|
b737252076 | ||
|
|
e688d04511 | ||
|
|
76fd5c0b5f | ||
|
|
d944f386ce | ||
|
|
9f9fd8550f | ||
|
|
772954711a | ||
|
|
3644553886 | ||
|
|
c073611597 | ||
|
|
802d20d554 | ||
|
|
38c12104b3 | ||
|
|
399662ef99 | ||
|
|
3daf4c62db | ||
|
|
9937f62f9d | ||
|
|
cf17e197ce | ||
|
|
2f8b5e5eeb | ||
|
|
d22fb83443 | ||
|
|
af093ba86a | ||
|
|
020f47ab2c | ||
|
|
8f0095f751 | ||
|
|
b7fc2e4012 | ||
|
|
1dcf830141 | ||
|
|
a10cdaba74 | ||
|
|
435a177283 | ||
|
|
d22d657886 | ||
|
|
1da5618204 | ||
|
|
318c069f3d | ||
|
|
d0267f60ae | ||
|
|
ea3c7703c4 | ||
|
|
95e04178ea | ||
|
|
cf0792cd64 | ||
|
|
e41a8833e2 | ||
|
|
0b2ce4289d | ||
|
|
a13270ef91 | ||
|
|
64ca121f5b | ||
|
|
0a5708272d | ||
|
|
fec15ffe66 | ||
|
|
0b0340bf21 | ||
|
|
cf716ce2a3 | ||
|
|
5ebcb41224 | ||
|
|
2d9c0e16b9 | ||
|
|
20e0ab3d6e | ||
|
|
d94ccc441a | ||
|
|
fdff9e048b | ||
|
|
64e1d7e276 | ||
|
|
5f82c78dc7 | ||
|
|
b3a0c6119e | ||
|
|
342ac0444c | ||
|
|
d9469360fe | ||
|
|
0df420cd17 | ||
|
|
ab44522016 | ||
|
|
bc875653d2 | ||
|
|
ee41204d6c | ||
|
|
c4d1113bb8 | ||
|
|
6582569150 | ||
|
|
69d0ceaf5b | ||
|
|
60165d5216 | ||
|
|
76ca7dc9e8 | ||
|
|
fcc5342775 | ||
|
|
b4534f455a | ||
|
|
0d3203476c | ||
|
|
9fa6157c37 | ||
|
|
2ee674f11b | ||
|
|
820d7108e4 | ||
|
|
9a6a562ed2 | ||
|
|
f3f9e98aa4 | ||
|
|
fc375da128 | ||
|
|
172ceba464 | ||
|
|
ecae7ea983 | ||
|
|
a204a786c0 | ||
|
|
79a2ab8abd | ||
|
|
af13b42eab | ||
|
|
75721091f2 | ||
|
|
6e327e245b | ||
|
|
6bfead405d | ||
|
|
a4f5620076 | ||
|
|
134a427901 | ||
|
|
27c3facb2b | ||
|
|
fa9848d1be | ||
|
|
1afd7d2ae4 | ||
|
|
c622efe95c | ||
|
|
c0949421ad | ||
|
|
0e21edcc77 | ||
|
|
df85186407 | ||
|
|
403ecf7fba | ||
|
|
d143f0880b | ||
|
|
c185e1fc4f | ||
|
|
68c180f7eb | ||
|
|
c903fc2246 | ||
|
|
7d5898f302 | ||
|
|
a4bdd3aaf4 | ||
|
|
5bae9deecc | ||
|
|
ffbd666aca | ||
|
|
4ec3aeec92 | ||
|
|
c5a00a1bf1 | ||
|
|
1b1e91f0dc | ||
|
|
21fc272674 | ||
|
|
5716c11450 | ||
|
|
f7e77e9fa6 | ||
|
|
ad8c3db6b4 | ||
|
|
d5c3b8d8e2 | ||
|
|
6ac6aea29f | ||
|
|
7ce06c0797 | ||
|
|
53bf1f424a | ||
|
|
b793cded34 | ||
|
|
47ec6d4917 | ||
|
|
e586ba8d6e | ||
|
|
8a913a18cf | ||
|
|
a09e579451 | ||
|
|
79c5dc2ce2 | ||
|
|
95d7bc5319 | ||
|
|
d3f5d05063 | ||
|
|
b30a35ceb5 | ||
|
|
fac22c9729 | ||
|
|
44e329c38d | ||
|
|
e48ad8cb61 | ||
|
|
359dc73526 | ||
|
|
8169e81e9a | ||
|
|
f62f087bcc | ||
|
|
4eb0b3086d | ||
|
|
1c81650572 | ||
|
|
59e3367da8 | ||
|
|
2b4b0fed83 | ||
|
|
fe6ae6f2eb | ||
|
|
79d994acf7 | ||
|
|
9dbbb8e012 | ||
|
|
17fafbf85b | ||
|
|
f3a6455ed8 | ||
|
|
5336d9e965 | ||
|
|
97d11ba910 | ||
|
|
2fa68a3abc | ||
|
|
a88365ca49 | ||
|
|
f3053c39c2 | ||
|
|
c7ac39b86a | ||
|
|
88eece7188 | ||
|
|
b7d101cdfb | ||
|
|
56ade2b44c | ||
|
|
2a38eb757d | ||
|
|
954ba60611 | ||
|
|
f9a984f3e4 | ||
|
|
8fd9acb2ce | ||
|
|
915a88afd4 | ||
|
|
c755b66d8c | ||
|
|
2f8dc0fa0f | ||
|
|
a34747f893 | ||
|
|
4dd6c813b1 | ||
|
|
8e96094977 | ||
|
|
dd41ebf861 | ||
|
|
f732df751c | ||
|
|
acfab8fb63 | ||
|
|
23193153c6 | ||
|
|
a908dcd752 | ||
|
|
b8a80cb267 | ||
|
|
ad1412c7e2 | ||
|
|
4bf51515be | ||
|
|
05f97dd75f | ||
|
|
90cab29a3c | ||
|
|
445b488603 | ||
|
|
7141e0f4b0 | ||
|
|
ffc756e4a4 | ||
|
|
8bed267feb | ||
|
|
13933e488c | ||
|
|
d48f76e965 | ||
|
|
a025c95054 | ||
|
|
b2fa9c468f | ||
|
|
8feef7fd8a | ||
|
|
8330f3d498 | ||
|
|
88e7bd225c | ||
|
|
1b0f818c4b | ||
|
|
32dc1225ca | ||
|
|
2b35cf5047 | ||
|
|
df66e28545 | ||
|
|
0ab060c958 | ||
|
|
dfceef4ca6 | ||
|
|
49f82e6377 | ||
|
|
c5957f1146 | ||
|
|
b357e7d7d6 | ||
|
|
e7342b0eb8 | ||
|
|
f33f4e3406 | ||
|
|
3d57c720e0 | ||
|
|
ef5d156b98 | ||
|
|
8722447f2f | ||
|
|
e6047dcbf5 | ||
|
|
df1d10f1cb | ||
|
|
d1dd60f928 | ||
|
|
7bea59fea1 | ||
|
|
17703ce572 | ||
|
|
6d556249aa | ||
|
|
16b5db448b | ||
|
|
8ef7f073b3 | ||
|
|
22c647e3f8 | ||
|
|
bf94dfa894 | ||
|
|
43d3cfefb5 | ||
|
|
e0855093ea | ||
|
|
c147062aaa | ||
|
|
480c2fab04 | ||
|
|
53fb619da1 | ||
|
|
c0a7bbaa72 | ||
|
|
12b27778f5 | ||
|
|
babcd6a92a | ||
|
|
d80d24c348 | ||
|
|
2c78a91a00 | ||
|
|
109e5dfe2c | ||
|
|
93ff327b54 | ||
|
|
359c188089 |
122
.gitignore
vendored
Normal file
122
.gitignore
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
|
||||
/toolkit/
|
||||
/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
|
||||
|
||||
# iTop extensions
|
||||
extensions/*
|
||||
!extensions/readme.txt
|
||||
|
||||
# all logs but listing prevention
|
||||
log/*
|
||||
!log/.htaccess
|
||||
!log/index.php
|
||||
!log/web.config
|
||||
|
||||
|
||||
# Jetbrains
|
||||
.idea/**
|
||||
!.idea/encodings.xml
|
||||
!.idea/codeStyles
|
||||
!.idea/codeStyles/*
|
||||
!.idea/inspectionProfiles
|
||||
!.idea/inspectionProfiles/*
|
||||
|
||||
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
### Eclipse template
|
||||
|
||||
.metadata
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
local.properties
|
||||
.settings/
|
||||
.loadpath
|
||||
.recommenders
|
||||
.project
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
# Locally stored "Eclipse launch configurations"
|
||||
*.launch
|
||||
|
||||
# PyDev specific (Python IDE for Eclipse)
|
||||
*.pydevproject
|
||||
|
||||
# CDT-specific (C/C++ Development Tooling)
|
||||
.cproject
|
||||
|
||||
# CDT- autotools
|
||||
.autotools
|
||||
|
||||
# Java annotation processor (APT)
|
||||
.factorypath
|
||||
|
||||
# PDT-specific (PHP Development Tools)
|
||||
.buildpath
|
||||
|
||||
# sbteclipse plugin
|
||||
.target
|
||||
|
||||
# Tern plugin
|
||||
.tern-project
|
||||
|
||||
# TeXlipse plugin
|
||||
.texlipse
|
||||
|
||||
# STS (Spring Tool Suite)
|
||||
.springBeans
|
||||
|
||||
# Code Recommenders
|
||||
.recommenders/
|
||||
|
||||
# Annotation Processing
|
||||
.apt_generated/
|
||||
|
||||
# Scala IDE specific (Scala & Java development for Eclipse)
|
||||
.cache-main
|
||||
.scala_dependencies
|
||||
.worksheet
|
||||
@@ -79,7 +79,7 @@ class Html2Text {
|
||||
// replace with spaces
|
||||
|
||||
$html = str_replace(" ", " ", $html);
|
||||
$html = mb_str_replace("\xa0", " ", $html); // DO NOT USE str_replace since it breaks the "à" character which is \xc3 \xa0 in UTF-8
|
||||
$html = mb_str_replace("\xc2\xa0", " ", $html); // DO NOT USE str_replace since it breaks the "à" character which is \xc3 \xa0 in UTF-8
|
||||
|
||||
$html = static::fixNewlines($html);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
* Simple web page with no includes, header or fancy formatting, useful to
|
||||
* generate HTML fragments when called by an AJAX method
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -53,7 +53,9 @@ class ajax_page extends WebPage implements iTabbedPage
|
||||
$this->sContentType = 'text/html';
|
||||
$this->sContentDisposition = 'inline';
|
||||
$this->m_sMenu = "";
|
||||
}
|
||||
|
||||
utils::InitArchiveMode();
|
||||
}
|
||||
|
||||
public function AddTabContainer($sTabContainer, $sPrefix = '')
|
||||
{
|
||||
@@ -202,32 +204,16 @@ EOF
|
||||
// Render the tabs in the page (if any)
|
||||
$this->s_content = $this->m_oTabs->RenderIntoContent($this->s_content, $this);
|
||||
|
||||
// Additional UI widgets to be activated inside the ajax fragment ??
|
||||
if (($this->sContentType == 'text/html') && (preg_match('/class="date-pick"/', $this->s_content) || preg_match('/class="datetime-pick"/', $this->s_content)) )
|
||||
// Additional UI widgets to be activated inside the ajax fragment
|
||||
// Important: Testing the content type is not enough because some ajax handlers have not correctly positionned the flag (e.g json response corrupted by the script)
|
||||
if (($this->sContentType == 'text/html') && (preg_match('/class="date-pick"/', $this->s_content) || preg_match('/class="datetime-pick"/', $this->s_content)) )
|
||||
{
|
||||
$this->add_ready_script(
|
||||
<<<EOF
|
||||
$(".date-pick").datepicker({
|
||||
showOn: 'button',
|
||||
buttonImage: '../images/calendar.png',
|
||||
buttonImageOnly: true,
|
||||
dateFormat: 'yy-mm-dd',
|
||||
constrainInput: false,
|
||||
changeMonth: true,
|
||||
changeYear: true
|
||||
});
|
||||
$(".datetime-pick").datepicker({
|
||||
showOn: 'button',
|
||||
buttonImage: '../images/calendar.png',
|
||||
buttonImageOnly: true,
|
||||
dateFormat: 'yy-mm-dd 00:00:00',
|
||||
constrainInput: false,
|
||||
changeMonth: true,
|
||||
changeYear: true
|
||||
});
|
||||
PrepareWidgets();
|
||||
EOF
|
||||
);
|
||||
}
|
||||
}
|
||||
$s_captured_output = $this->ob_get_clean_safe();
|
||||
if (($this->sContentType == 'text/html') && ($this->sContentDisposition == 'inline'))
|
||||
{
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
require_once(APPROOT.'/application/applicationcontext.class.inc.php');
|
||||
require_once(APPROOT.'/application/cmdbabstract.class.inc.php');
|
||||
require_once(APPROOT.'/application/displayblock.class.inc.php');
|
||||
require_once(APPROOT.'/application/sqlblock.class.inc.php');
|
||||
require_once(APPROOT.'/application/audit.category.class.inc.php');
|
||||
require_once(APPROOT.'/application/audit.rule.class.inc.php');
|
||||
require_once(APPROOT.'/application/query.class.inc.php');
|
||||
|
||||
@@ -118,8 +118,7 @@ class ApplicationContext
|
||||
$oSearchFilter = new DBObjectSearch('Organization');
|
||||
$oSearchFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', true);
|
||||
$oSet = new CMDBObjectSet($oSearchFilter);
|
||||
$iCount = $oSet->Count();
|
||||
if ($iCount == 1)
|
||||
if ($oSet->Count(2) == 1)
|
||||
{
|
||||
// Only one possible value for org_id, set it in the context
|
||||
$oOrg = $oSet->Fetch();
|
||||
|
||||
@@ -308,6 +308,43 @@ interface iPopupMenuExtension
|
||||
* $param is null
|
||||
*/
|
||||
const MENU_USER_ACTIONS = 5;
|
||||
/**
|
||||
* Insert an item into the Action menu on an object item in an objects list in the portal
|
||||
*
|
||||
* $param is an array('portal_id' => $sPortalId, 'object' => $oObject) containing the portal id and a DBObject instance (the object on the current line)
|
||||
*/
|
||||
const PORTAL_OBJLISTITEM_ACTIONS = 7;
|
||||
/**
|
||||
* Insert an item into the Action menu on an object details page in the portal
|
||||
*
|
||||
* $param is an array('portal_id' => $sPortalId, 'object' => $oObject) containing the portal id and a DBObject instance (the object currently displayed)
|
||||
*/
|
||||
const PORTAL_OBJDETAILS_ACTIONS = 8;
|
||||
|
||||
/**
|
||||
* Insert an item into the Actions menu of a list in the portal
|
||||
* Note: This is not implemented yet !
|
||||
*
|
||||
* $param is an array('portal_id' => $sPortalId, 'object_set' => $oSet) containing DBObjectSet containing the list of objects
|
||||
* @todo
|
||||
*/
|
||||
const PORTAL_OBJLIST_ACTIONS = 6;
|
||||
/**
|
||||
* Insert an item into the user menu of the portal
|
||||
* Note: This is not implemented yet !
|
||||
*
|
||||
* $param is the portal id
|
||||
* @todo
|
||||
*/
|
||||
const PORTAL_USER_ACTIONS = 9;
|
||||
/**
|
||||
* Insert an item into the navigation menu of the portal
|
||||
* Note: This is not implemented yet !
|
||||
*
|
||||
* $param is the portal id
|
||||
* @todo
|
||||
*/
|
||||
const PORTAL_MENU_ACTIONS = 10;
|
||||
|
||||
/**
|
||||
* Get the list of items to be added to a menu.
|
||||
@@ -334,17 +371,21 @@ abstract class ApplicationPopupMenuItem
|
||||
protected $sUID;
|
||||
/** @ignore */
|
||||
protected $sLabel;
|
||||
/** @ignore */
|
||||
protected $aCssClasses;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $sUID The unique identifier of this menu in iTop... make sure you pass something unique enough
|
||||
* @param string $sLabel The display label of the menu (must be localized)
|
||||
*/
|
||||
* @param string $sLabel The display label of the menu (must be localized)
|
||||
* @param array $aCssClasses The CSS classes to add to the menu
|
||||
*/
|
||||
public function __construct($sUID, $sLabel)
|
||||
{
|
||||
$this->sUID = $sUID;
|
||||
$this->sLabel = $sLabel;
|
||||
$this->aCssClasses = array();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -368,6 +409,35 @@ abstract class ApplicationPopupMenuItem
|
||||
{
|
||||
return $this->sLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the CSS classes
|
||||
*
|
||||
* @return array
|
||||
* @ignore
|
||||
*/
|
||||
public function GetCssClasses()
|
||||
{
|
||||
return $this->aCssClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $aCssClasses
|
||||
*/
|
||||
public function SetCssClasses($aCssClasses)
|
||||
{
|
||||
$this->aCssClasses = $aCssClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a CSS class to the CSS classes that will be put on the menu item
|
||||
*
|
||||
* @param $sCssClass
|
||||
*/
|
||||
public function AddCssClass($sCssClass)
|
||||
{
|
||||
$this->aCssClasses[] = $sCssClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the components to create a popup menu item in HTML
|
||||
@@ -415,7 +485,7 @@ class URLPopupMenuItem extends ApplicationPopupMenuItem
|
||||
/** @ignore */
|
||||
public function GetMenuItem()
|
||||
{
|
||||
return array ('label' => $this->GetLabel(), 'url' => $this->sURL, 'target' => $this->sTarget);
|
||||
return array ('label' => $this->GetLabel(), 'url' => $this->sURL, 'target' => $this->sTarget, 'css_classes' => $this->aCssClasses);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -451,7 +521,7 @@ class JSPopupMenuItem extends ApplicationPopupMenuItem
|
||||
public function GetMenuItem()
|
||||
{
|
||||
// Note: the semicolumn is a must here!
|
||||
return array ('label' => $this->GetLabel(), 'onclick' => $this->sJSCode.'; return false;', 'url' => '#');
|
||||
return array ('label' => $this->GetLabel(), 'onclick' => $this->sJSCode.'; return false;', 'url' => '#', 'css_classes' => $this->aCssClasses);
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
@@ -483,10 +553,34 @@ class SeparatorPopupMenuItem extends ApplicationPopupMenuItem
|
||||
/** @ignore */
|
||||
public function GetMenuItem()
|
||||
{
|
||||
return array ('label' => '<hr class="menu-separator">', 'url' => '');
|
||||
return array ('label' => '<hr class="menu-separator">', 'url' => '', 'css_classes' => $this->aCssClasses);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for adding an item as a button that browses to the given URL
|
||||
*
|
||||
* @package Extensibility
|
||||
* @api
|
||||
* @since 2.0
|
||||
*/
|
||||
class URLButtonItem extends URLPopupMenuItem
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for adding an item as a button that runs some JS code
|
||||
*
|
||||
* @package Extensibility
|
||||
* @api
|
||||
* @since 2.0
|
||||
*/
|
||||
class JSButtonItem extends JSPopupMenuItem
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this interface to add content to any iTopWebPage
|
||||
*
|
||||
@@ -528,6 +622,128 @@ interface iPageUIExtension
|
||||
public function GetBannerHtml(iTopWebPage $oPage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this interface to add content to any enhanced portal page
|
||||
*
|
||||
* IMPORTANT! Experimental API, may be removed at anytime, we don't recommend to use it just now!
|
||||
*
|
||||
* @package Extensibility
|
||||
* @api
|
||||
* @since 2.4
|
||||
*/
|
||||
interface iPortalUIExtension
|
||||
{
|
||||
const ENUM_PORTAL_EXT_UI_BODY = 'Body';
|
||||
const ENUM_PORTAL_EXT_UI_NAVIGATION_MENU = 'NavigationMenu';
|
||||
const ENUM_PORTAL_EXT_UI_MAIN_CONTENT = 'MainContent';
|
||||
|
||||
/**
|
||||
* Returns an array of CSS file urls
|
||||
*
|
||||
* @param \Silex\Application $oApp
|
||||
* @return array
|
||||
*/
|
||||
public function GetCSSFiles(\Silex\Application $oApp);
|
||||
/**
|
||||
* Returns inline (raw) CSS
|
||||
*
|
||||
* @param \Silex\Application $oApp
|
||||
* @return string
|
||||
*/
|
||||
public function GetCSSInline(\Silex\Application $oApp);
|
||||
/**
|
||||
* Returns an array of JS file urls
|
||||
*
|
||||
* @param \Silex\Application $oApp
|
||||
* @return array
|
||||
*/
|
||||
public function GetJSFiles(\Silex\Application $oApp);
|
||||
/**
|
||||
* Returns raw JS code
|
||||
*
|
||||
* @param \Silex\Application $oApp
|
||||
* @return string
|
||||
*/
|
||||
public function GetJSInline(\Silex\Application $oApp);
|
||||
/**
|
||||
* Returns raw HTML code to put at the end of the <body> tag
|
||||
*
|
||||
* @param \Silex\Application $oApp
|
||||
* @return string
|
||||
*/
|
||||
public function GetBodyHTML(\Silex\Application $oApp);
|
||||
/**
|
||||
* Returns raw HTML code to put at the end of the #main-wrapper element
|
||||
*
|
||||
* @param \Silex\Application $oApp
|
||||
* @return string
|
||||
*/
|
||||
public function GetMainContentHTML(\Silex\Application $oApp);
|
||||
/**
|
||||
* Returns raw HTML code to put at the end of the #topbar and #sidebar elements
|
||||
*
|
||||
* @param \Silex\Application $oApp
|
||||
* @return string
|
||||
*/
|
||||
public function GetNavigationMenuHTML(\Silex\Application $oApp);
|
||||
}
|
||||
|
||||
/**
|
||||
* IMPORTANT! Experimental API, may be removed at anytime, we don't recommend to use it just now!
|
||||
*/
|
||||
abstract class AbstractPortalUIExtension implements iPortalUIExtension
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetCSSFiles(\Silex\Application $oApp)
|
||||
{
|
||||
return array();
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetCSSInline(\Silex\Application $oApp)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetJSFiles(\Silex\Application $oApp)
|
||||
{
|
||||
return array();
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetJSInline(\Silex\Application $oApp)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetBodyHTML(\Silex\Application $oApp)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetMainContentHTML(\Silex\Application $oApp)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetNavigationMenuHTML(\Silex\Application $oApp)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this interface to add new operations to the REST/JSON web service
|
||||
*
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -23,7 +23,7 @@ require_once(APPROOT.'core/modelreflection.class.inc.php');
|
||||
/**
|
||||
* A user editable dashboard page
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
abstract class Dashboard
|
||||
@@ -515,8 +515,6 @@ class RuntimeDashboard extends Dashboard
|
||||
// Assuming there is at most one couple {user, menu}!
|
||||
$oUserDashboard = $oUDSet->Fetch();
|
||||
$oUserDashboard->Set('contents', $sXml);
|
||||
|
||||
$oUserDashboard->DBUpdate();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -525,9 +523,10 @@ class RuntimeDashboard extends Dashboard
|
||||
$oUserDashboard->Set('user_id', UserRights::GetUserId());
|
||||
$oUserDashboard->Set('menu_code', $this->sId);
|
||||
$oUserDashboard->Set('contents', $sXml);
|
||||
|
||||
$oUserDashboard->DBInsert();
|
||||
}
|
||||
}
|
||||
utils::PushArchiveMode(false);
|
||||
$oUserDashboard->DBWrite();
|
||||
utils::PopArchiveMode();
|
||||
}
|
||||
|
||||
public function Revert()
|
||||
@@ -540,7 +539,9 @@ class RuntimeDashboard extends Dashboard
|
||||
{
|
||||
// Assuming there is at most one couple {user, menu}!
|
||||
$oUserDashboard = $oUDSet->Fetch();
|
||||
utils::PushArchiveMode(false);
|
||||
$oUserDashboard->DBDelete();
|
||||
utils::PopArchiveMode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -754,33 +755,65 @@ EOF
|
||||
|
||||
public static function GetDashletCreationForm($sOQL = null)
|
||||
{
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sContextMenuId = $oAppContext->GetCurrentValue('menu', null);
|
||||
|
||||
$oForm = new DesignerForm();
|
||||
|
||||
// Get the list of all 'dashboard' menus in which we can insert a dashlet
|
||||
$aAllMenus = ApplicationMenu::ReflectionMenuNodes();
|
||||
$sRootMenuId = ApplicationMenu::GetRootMenuId($sContextMenuId);
|
||||
$aAllowedDashboards = array();
|
||||
foreach($aAllMenus as $idx => $aMenu)
|
||||
$sDefaultDashboard = null;
|
||||
|
||||
// Store the parent menus for acces check
|
||||
$aParentMenus = array();
|
||||
foreach($aAllMenus as $idx => $aMenu)
|
||||
{
|
||||
/** @var MenuNode $oMenu */
|
||||
$oMenu = $aMenu['node'];
|
||||
if (count(ApplicationMenu::GetChildren($oMenu->GetIndex())) > 0)
|
||||
{
|
||||
$aParentMenus[$oMenu->GetMenuId()] = $aMenu;
|
||||
}
|
||||
}
|
||||
|
||||
foreach($aAllMenus as $idx => $aMenu)
|
||||
{
|
||||
$oMenu = $aMenu['node'];
|
||||
$sParentId = $aMenu['parent'];
|
||||
if ($oMenu instanceof DashboardMenuNode)
|
||||
{
|
||||
$sMenuLabel = $oMenu->GetTitle();
|
||||
$sParentLabel = Dict::S('Menu:'.$sParentId);
|
||||
if ($sParentLabel != $sMenuLabel)
|
||||
{
|
||||
$aAllowedDashboards[$oMenu->GetMenuId()] = $sParentLabel.' - '.$sMenuLabel;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aAllowedDashboards[$oMenu->GetMenuId()] = $sMenuLabel;
|
||||
}
|
||||
}
|
||||
if ($oMenu instanceof DashboardMenuNode)
|
||||
{
|
||||
// Get the root parent for access check
|
||||
$sParentId = $aMenu['parent'];
|
||||
$aParentMenu = $aParentMenus[$sParentId];
|
||||
while (isset($aParentMenus[$aParentMenu['parent']]))
|
||||
{
|
||||
// grand parent exists
|
||||
$sParentId = $aParentMenu['parent'];
|
||||
$aParentMenu = $aParentMenus[$sParentId];
|
||||
}
|
||||
$oParentMenu = $aParentMenu['node'];
|
||||
if ($oMenu->IsEnabled() && $oParentMenu->IsEnabled())
|
||||
{
|
||||
$sMenuLabel = $oMenu->GetTitle();
|
||||
$sParentLabel = Dict::S('Menu:'.$sParentId);
|
||||
if ($sParentLabel != $sMenuLabel)
|
||||
{
|
||||
$aAllowedDashboards[$oMenu->GetMenuId()] = $sParentLabel.' - '.$sMenuLabel;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aAllowedDashboards[$oMenu->GetMenuId()] = $sMenuLabel;
|
||||
}
|
||||
if (empty($sDefaultDashboard) && ($sRootMenuId == ApplicationMenu::GetRootMenuId($oMenu->GetMenuId())))
|
||||
{
|
||||
$sDefaultDashboard = $oMenu->GetMenuId();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
asort($aAllowedDashboards);
|
||||
|
||||
$aKeys = array_keys($aAllowedDashboards); // Select the first one by default
|
||||
$sDefaultDashboard = $aKeys[0];
|
||||
$oField = new DesignerComboField('menu_id', Dict::S('UI:DashletCreation:Dashboard'), $sDefaultDashboard);
|
||||
$oField->SetAllowedValues($aAllowedDashboards);
|
||||
$oField->SetMandatory(true);
|
||||
@@ -841,7 +874,7 @@ EOF
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
$('#dashlet_creation_dlg').dialog({
|
||||
width: 400,
|
||||
width: 600,
|
||||
modal: true,
|
||||
title: '$sDialogTitle',
|
||||
buttons: [
|
||||
|
||||
@@ -104,7 +104,7 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
|
||||
// Trim the list of cells to remove the invisible/empty ones at the end of the array
|
||||
$aCells = $this->TrimCellsArray($aCells);
|
||||
|
||||
$oPage->add('<table style="width:100%"><tbody>');
|
||||
$oPage->add('<table style="width:100%;table-layout:fixed;"><tbody>');
|
||||
$iCellIdx = 0;
|
||||
$fColSize = 100 / $this->iNbCols;
|
||||
$sStyle = $bEditMode ? 'border: 1px #ccc dashed; width:'.$fColSize.'%;' : 'width: '.$fColSize.'%;';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2012-2013 Combodo SARL
|
||||
// Copyright (C) 2012-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -21,7 +21,7 @@ require_once(APPROOT.'application/forms.class.inc.php');
|
||||
/**
|
||||
* Base class for all 'dashlets' (i.e. widgets to be inserted into a dashboard)
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
abstract class Dashlet
|
||||
@@ -530,7 +530,7 @@ abstract class DashletGroupBy extends Dashlet
|
||||
$this->sGroupByAttCode = $sGroupBy;
|
||||
$this->sFunction = null;
|
||||
}
|
||||
if ($this->oModelReflection->IsValidAttCode($sClass, $this->sGroupByAttCode))
|
||||
if (($sClass != '') && $this->oModelReflection->IsValidAttCode($sClass, $this->sGroupByAttCode))
|
||||
{
|
||||
$sAttLabel = $this->oModelReflection->GetLabel($sClass, $this->sGroupByAttCode);
|
||||
if (!is_null($this->sFunction))
|
||||
@@ -583,6 +583,7 @@ abstract class DashletGroupBy extends Dashlet
|
||||
|
||||
// First perform the query - if the OQL is not ok, it will generate an exception : no need to go further
|
||||
$oFilter = DBObjectSearch::FromOQL($sQuery);
|
||||
$oFilter->SetShowObsoleteData(utils::ShowObsoleteData());
|
||||
|
||||
$sClass = $oFilter->GetClass();
|
||||
$sClassAlias = $oFilter->GetClassAlias();
|
||||
@@ -685,7 +686,7 @@ abstract class DashletGroupBy extends Dashlet
|
||||
}
|
||||
foreach ($aValues as $sValue)
|
||||
{
|
||||
$aDisplayValues[] = array('label' => $sValue, 'count' => (int)rand(1, 15));
|
||||
$aDisplayValues[] = array('label' => $sValue, 'value' => (int)rand(1, 15));
|
||||
}
|
||||
}
|
||||
elseif (is_subclass_of($sAttributeType, 'AttributeEnum') || $sAttributeType == 'AttributeEnum')
|
||||
@@ -698,16 +699,16 @@ abstract class DashletGroupBy extends Dashlet
|
||||
$iCount = (int) rand(2, 100);
|
||||
$aDisplayValues[] = array(
|
||||
'label' => $sValueLabel,
|
||||
'count' => $iCount
|
||||
'value' => $iCount
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$aDisplayValues[] = array('label' => 'a', 'count' => 123);
|
||||
$aDisplayValues[] = array('label' => 'b', 'count' => 321);
|
||||
$aDisplayValues[] = array('label' => 'c', 'count' => 456);
|
||||
$aDisplayValues[] = array('label' => 'a', 'value' => 123);
|
||||
$aDisplayValues[] = array('label' => 'b', 'value' => 321);
|
||||
$aDisplayValues[] = array('label' => 'c', 'value' => 456);
|
||||
}
|
||||
}
|
||||
return $aDisplayValues;
|
||||
@@ -905,82 +906,42 @@ class DashletGroupByPie extends DashletGroupBy
|
||||
{
|
||||
$sTitle = $this->aProperties['title'];
|
||||
|
||||
$sBlockId = 'block_fake_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM)
|
||||
|
||||
$HTMLsTitle = ($sTitle != '') ? '<h1 style="text-align:center">'.htmlentities($sTitle, ENT_QUOTES, 'UTF-8').'</h1>' : '';
|
||||
$oPage->add("<div style=\"background-color:#fff;padding:0.25em;\">$HTMLsTitle<div id=\"$sBlockId\" style=\"background-color:#fff;\"></div></div>");
|
||||
|
||||
$aDisplayValues = $this->MakeSimulatedData();
|
||||
|
||||
require_once(APPROOT.'/pages/php-ofc-library/open-flash-chart.php');
|
||||
$oChart = new open_flash_chart();
|
||||
|
||||
$aGroupBy = array();
|
||||
$aLabels = array();
|
||||
foreach($aDisplayValues as $iRow => $aDisplayData)
|
||||
$aColumns = array();
|
||||
$aNames = array();
|
||||
foreach($aDisplayValues as $idx => $aValue)
|
||||
{
|
||||
$aLabels[$iRow] = $aDisplayData['label'];
|
||||
$aGroupBy[$iRow] = (int) $aDisplayData['count'];
|
||||
$aColumns[] = array('series_'.$idx, (int)$aValue['value']);
|
||||
$aNames['series_'.$idx] = $aValue['label'];
|
||||
}
|
||||
|
||||
$oChartElement = new pie();
|
||||
$oChartElement->set_start_angle( 35 );
|
||||
$oChartElement->set_animate( true );
|
||||
$oChartElement->set_tooltip( '#label# - #val# (#percent#)' );
|
||||
$oChartElement->set_colours( array('#FF8A00', '#909980', '#2C2B33', '#CCC08D', '#596664') );
|
||||
|
||||
$aData = array();
|
||||
foreach($aGroupBy as $iRow => $iCount)
|
||||
{
|
||||
$sFlashLabel = html_entity_decode($aLabels[$iRow], ENT_QUOTES, 'UTF-8');
|
||||
$PieValue = new pie_value($iCount, $sFlashLabel);
|
||||
$aData[] = $PieValue;
|
||||
}
|
||||
|
||||
$oChartElement->set_values($aData);
|
||||
$oChart->x_axis = null;
|
||||
|
||||
if (!empty($sTitle))
|
||||
{
|
||||
// The title has been given in an url, and urlencoded...
|
||||
// and urlencode transforms utf-8 into something similar to ISO-8859-1
|
||||
// Example: é (C3A9 becomes %E9)
|
||||
// As a consequence, json_encode (called within open-flash-chart.php)
|
||||
// was returning 'null' and the graph was not displayed at all
|
||||
// To make sure that the graph is displayed AND to get a correct title
|
||||
// (at least for european characters) let's transform back into utf-8 !
|
||||
$sTitle = iconv("ISO-8859-1", "UTF-8//IGNORE", $sTitle);
|
||||
|
||||
// If the title is a dictionnary entry, fetch it
|
||||
$sTitle = $this->oModelReflection->DictString($sTitle);
|
||||
|
||||
$oTitle = new title($sTitle);
|
||||
$oChart->set_title($oTitle);
|
||||
$oTitle->set_style("{font-size: 16px; font-family: Tahoma; font-weight: bold; text-align: center;}");
|
||||
}
|
||||
$oChart->set_bg_colour('#FFFFFF');
|
||||
$oChart->add_element($oChartElement);
|
||||
|
||||
$sData = $oChart->toPrettyString();
|
||||
$sData = json_encode($sData);
|
||||
$oPage->add_script(
|
||||
<<< EOF
|
||||
function ofc_get_data_dashlet_{$this->sId}()
|
||||
{
|
||||
return $sData;
|
||||
}
|
||||
EOF
|
||||
);
|
||||
|
||||
$oPage->add('<div class="dashlet-content">');
|
||||
$oPage->add("<div id=\"dashlet_chart_{$this->sId}\">If the chart does not display, <a href=\"http://get.adobe.com/flash/\" target=\"_blank\">install Flash</a></div>\n");
|
||||
$oPage->add('</div>');
|
||||
|
||||
// $oPage->add_script("function ofc_resize(left, width, top, height) { /* do nothing special */ }");
|
||||
$sJSColumns = json_encode($aColumns);
|
||||
$sJSNames = json_encode($aNames);
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
swfobject.embedSWF( "../images/open-flash-chart.swf",
|
||||
"dashlet_chart_{$this->sId}",
|
||||
"100%", "300","9.0.0",
|
||||
"expressInstall.swf",
|
||||
{"get-data":"ofc_get_data_dashlet_{$this->sId}", "id":"dashlet_chart_{$this->sId}"},
|
||||
{'wmode': 'transparent'}
|
||||
);
|
||||
window.setTimeout(function() {
|
||||
var chart = c3.generate({
|
||||
bindto: '#{$sBlockId}',
|
||||
data: {
|
||||
columns: $sJSColumns,
|
||||
type: 'pie',
|
||||
names: $sJSNames,
|
||||
},
|
||||
legend: {
|
||||
show: true,
|
||||
position: 'right',
|
||||
},
|
||||
tooltip: {
|
||||
format: {
|
||||
value: function (value, ratio, id) { return value; }
|
||||
}
|
||||
}
|
||||
});}, 100);
|
||||
EOF
|
||||
);
|
||||
}
|
||||
@@ -1008,106 +969,69 @@ class DashletGroupByBars extends DashletGroupBy
|
||||
{
|
||||
$sTitle = $this->aProperties['title'];
|
||||
|
||||
$sBlockId = 'block_fake_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM)
|
||||
|
||||
$HTMLsTitle = ($sTitle != '') ? '<h1 style="text-align:center">'.htmlentities($sTitle, ENT_QUOTES, 'UTF-8').'</h1>' : '';
|
||||
$oPage->add("<div style=\"background-color:#fff;padding:0.25em;\">$HTMLsTitle<div id=\"$sBlockId\" style=\"background-color:#fff;\"></div></div>");
|
||||
|
||||
$aDisplayValues = $this->MakeSimulatedData();
|
||||
|
||||
require_once(APPROOT.'/pages/php-ofc-library/open-flash-chart.php');
|
||||
$oChart = new open_flash_chart();
|
||||
|
||||
$aGroupBy = array();
|
||||
$aLabels = array();
|
||||
foreach($aDisplayValues as $iRow => $aDisplayData)
|
||||
$aNames = array();
|
||||
foreach($aDisplayValues as $idx => $aValue)
|
||||
{
|
||||
$aLabels[$iRow] = $aDisplayData['label'];
|
||||
$aGroupBy[$iRow] = (int) $aDisplayData['count'];
|
||||
$aNames[$idx] = $aValue['label'];
|
||||
}
|
||||
|
||||
$oChartElement = new bar_glass();
|
||||
|
||||
$aData = array();
|
||||
$aChartLabels = array();
|
||||
$maxValue = 0;
|
||||
foreach($aGroupBy as $iRow => $iCount)
|
||||
{
|
||||
$oBarValue = new bar_value($iCount);
|
||||
$aData[] = $oBarValue;
|
||||
if ($iCount > $maxValue) $maxValue = $iCount;
|
||||
$aChartLabels[] = html_entity_decode($aLabels[$iRow], ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
$oYAxis = new y_axis();
|
||||
$aMagicValues = array(1,2,5,10);
|
||||
$iMultiplier = 1;
|
||||
$index = 0;
|
||||
$iTop = $aMagicValues[$index % count($aMagicValues)]*$iMultiplier;
|
||||
while($maxValue > $iTop)
|
||||
{
|
||||
$index++;
|
||||
$iTop = $aMagicValues[$index % count($aMagicValues)]*$iMultiplier;
|
||||
if (($index % count($aMagicValues)) == 0)
|
||||
{
|
||||
$iMultiplier = $iMultiplier * 10;
|
||||
}
|
||||
}
|
||||
//echo "oYAxis->set_range(0, $iTop, $iMultiplier);\n";
|
||||
$oYAxis->set_range(0, $iTop, $iMultiplier);
|
||||
$oChart->set_y_axis( $oYAxis );
|
||||
|
||||
$oChartElement->set_values( $aData );
|
||||
$oXAxis = new x_axis();
|
||||
$oXLabels = new x_axis_labels();
|
||||
// set them vertical
|
||||
$oXLabels->set_vertical();
|
||||
// set the label text
|
||||
$oXLabels->set_labels($aChartLabels);
|
||||
// Add the X Axis Labels to the X Axis
|
||||
$oXAxis->set_labels( $oXLabels );
|
||||
$oChart->set_x_axis( $oXAxis );
|
||||
|
||||
if (!empty($sTitle))
|
||||
{
|
||||
// The title has been given in an url, and urlencoded...
|
||||
// and urlencode transforms utf-8 into something similar to ISO-8859-1
|
||||
// Example: é (C3A9 becomes %E9)
|
||||
// As a consequence, json_encode (called within open-flash-chart.php)
|
||||
// was returning 'null' and the graph was not displayed at all
|
||||
// To make sure that the graph is displayed AND to get a correct title
|
||||
// (at least for european characters) let's transform back into utf-8 !
|
||||
$sTitle = iconv("ISO-8859-1", "UTF-8//IGNORE", $sTitle);
|
||||
$sJSNames = json_encode($aNames);
|
||||
|
||||
// If the title is a dictionnary entry, fetch it
|
||||
$sTitle = $this->oModelReflection->DictString($sTitle);
|
||||
|
||||
$oTitle = new title($sTitle);
|
||||
$oChart->set_title($oTitle);
|
||||
$oTitle->set_style("{font-size: 16px; font-family: Tahoma; font-weight: bold; text-align: center;}");
|
||||
}
|
||||
$oChart->set_bg_colour('#FFFFFF');
|
||||
$oChart->add_element($oChartElement);
|
||||
|
||||
$sData = $oChart->toPrettyString();
|
||||
$sData = json_encode($sData);
|
||||
$oPage->add_script(
|
||||
<<< EOF
|
||||
function ofc_get_data_dashlet_{$this->sId}()
|
||||
{
|
||||
return $sData;
|
||||
}
|
||||
EOF
|
||||
);
|
||||
|
||||
$oPage->add('<div class="dashlet-content">');
|
||||
$oPage->add("<div id=\"dashlet_chart_{$this->sId}\">If the chart does not display, <a href=\"http://get.adobe.com/flash/\" target=\"_blank\">install Flash</a></div>\n");
|
||||
$oPage->add('</div>');
|
||||
|
||||
// $oPage->add_script("function ofc_resize(left, width, top, height) { /* do nothing special */ }");
|
||||
$sJson = json_encode($aDisplayValues);
|
||||
$sJSCount = json_encode(Dict::S('UI:GroupBy:Count'));
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
swfobject.embedSWF( "../images/open-flash-chart.swf",
|
||||
"dashlet_chart_{$this->sId}",
|
||||
"100%", "300","9.0.0",
|
||||
"expressInstall.swf",
|
||||
{"get-data":"ofc_get_data_dashlet_{$this->sId}", "id":"dashlet_chart_{$this->sId}"},
|
||||
{'wmode': 'transparent'}
|
||||
);
|
||||
window.setTimeout(function() {
|
||||
var chart = c3.generate({
|
||||
bindto: '#{$sBlockId}',
|
||||
data: {
|
||||
json: $sJson,
|
||||
keys: {
|
||||
x: 'label',
|
||||
value: ["value"]
|
||||
},
|
||||
selection: {
|
||||
enabled: true
|
||||
},
|
||||
type: 'bar'
|
||||
},
|
||||
axis: {
|
||||
x: {
|
||||
tick: {
|
||||
culling: {max: 25}, // Maximum 24 labels on x axis (2 years).
|
||||
centered: true,
|
||||
rotate: 90,
|
||||
multiline: false
|
||||
},
|
||||
type: 'category' // this needed to load string x value
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
y: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
show: false,
|
||||
},
|
||||
tooltip: {
|
||||
grouped: false,
|
||||
format: {
|
||||
title: function() { return '' },
|
||||
name: function (name, ratio, id, index) {
|
||||
var aNames = $sJSNames;
|
||||
return aNames[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}, 100);
|
||||
EOF
|
||||
);
|
||||
}
|
||||
@@ -1138,7 +1062,7 @@ class DashletGroupByTable extends DashletGroupBy
|
||||
$iTotal = 0;
|
||||
foreach($aDisplayValues as $iRow => $aDisplayData)
|
||||
{
|
||||
$iTotal += $aDisplayData['count'];
|
||||
$iTotal += $aDisplayData['value'];
|
||||
}
|
||||
|
||||
$oPage->add('<div class="dashlet-content">');
|
||||
@@ -1159,7 +1083,7 @@ class DashletGroupByTable extends DashletGroupBy
|
||||
{
|
||||
$oPage->add('<tr class="even">');
|
||||
$oPage->add('<td class=""><span title="Active">'.$aDisplayData['label'].'</span></td>');
|
||||
$oPage->add('<td class=""><a>'.$aDisplayData['count'].'</a></td>');
|
||||
$oPage->add('<td class=""><a>'.$aDisplayData['value'].'</a></td>');
|
||||
$oPage->add('</tr>');
|
||||
}
|
||||
$oPage->add('</tbody>');
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -18,7 +18,7 @@
|
||||
/**
|
||||
* Data Table to display a set of objects in a tabular manner in HTML
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -31,7 +31,8 @@ class DataTable
|
||||
protected $iNbObjects; // Total number of objects inthe set
|
||||
protected $bUseCustomSettings; // Whether or not the current display uses custom settings
|
||||
protected $oDefaultSettings; // the default settings for displaying such a list
|
||||
|
||||
protected $bShowObsoleteData;
|
||||
|
||||
/**
|
||||
* @param $iListId mixed Unique ID for this div/table in the page
|
||||
* @param $oSet DBObjectSet The set of data to display
|
||||
@@ -47,6 +48,7 @@ class DataTable
|
||||
$this->iNbObjects = $oSet->Count();
|
||||
$this->bUseCustomSettings = false;
|
||||
$this->oDefaultSettings = null;
|
||||
$this->bShowObsoleteData = $oSet->GetShowObsoleteData();
|
||||
}
|
||||
|
||||
public function Display(WebPage $oPage, DataTableSettings $oSettings, $bActionsMenu, $sSelectMode, $bViewLink, $aExtraParams)
|
||||
@@ -145,7 +147,9 @@ class DataTable
|
||||
$sHtml .= "<tr><td class=\"datacontents\">$sDataTable</td></tr>";
|
||||
$sHtml .= "</table>\n";
|
||||
$oPage->add_at_the_end($sConfigDlg);
|
||||
|
||||
|
||||
$aExtraParams['show_obsolete_data'] = $this->bShowObsoleteData;
|
||||
|
||||
$aOptions = array(
|
||||
'sPersistentId' => '',
|
||||
'sFilter' => $this->oSet->GetFilter()->serialize(),
|
||||
@@ -486,6 +490,7 @@ EOF;
|
||||
{
|
||||
$aExtraParams['query_params'][$sName] = $sValue;
|
||||
}
|
||||
$aExtraParams['show_obsolete_data'] = $this->bShowObsoleteData;
|
||||
|
||||
$sHtml .= "<tr><td>";
|
||||
$sHtml .= $oPage->GetTable($aAttribs, $aValues);
|
||||
@@ -928,8 +933,15 @@ class DataTableSettings implements Serializable
|
||||
}
|
||||
else if ($oAttDef->IsExternalField())
|
||||
{
|
||||
$oExtAttDef = $oAttDef->GetExtAttDef();
|
||||
$sLabel = Dict::Format('UI:ExtField_AsRemoteField', $oAttDef->GetLabel(), $oExtAttDef->GetLabel());
|
||||
if ($oAttDef->IsFriendlyName())
|
||||
{
|
||||
$sLabel = Dict::Format('UI:ExtKey_AsFriendlyName', $oAttDef->GetLabel());
|
||||
}
|
||||
else
|
||||
{
|
||||
$oExtAttDef = $oAttDef->GetExtAttDef();
|
||||
$sLabel = Dict::Format('UI:ExtField_AsRemoteField', $oAttDef->GetLabel(), $oExtAttDef->GetLabel());
|
||||
}
|
||||
}
|
||||
elseif ($oAttDef instanceof AttributeFriendlyName)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -19,7 +19,7 @@
|
||||
/**
|
||||
* DisplayBlock and derived class
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -49,6 +49,7 @@ class DisplayBlock
|
||||
protected $m_bAsynchronous;
|
||||
protected $m_aParams;
|
||||
protected $m_oSet;
|
||||
protected $m_bShowObsoleteData = null;
|
||||
|
||||
public function __construct(DBSearch $oFilter, $sStyle = 'list', $bAsynchronous = false, $aParams = array(), $oSet = null)
|
||||
{
|
||||
@@ -58,6 +59,15 @@ class DisplayBlock
|
||||
$this->m_bAsynchronous = $bAsynchronous;
|
||||
$this->m_aParams = $aParams;
|
||||
$this->m_oSet = $oSet;
|
||||
if (array_key_exists('show_obsolete_data', $aParams))
|
||||
{
|
||||
$this->m_bShowObsoleteData = $aParams['show_obsolete_data'];
|
||||
}
|
||||
if ($this->m_bShowObsoleteData === null)
|
||||
{
|
||||
// User defined
|
||||
$this->m_bShowObsoleteData = utils::ShowObsoleteData();
|
||||
}
|
||||
}
|
||||
|
||||
public function GetFilter()
|
||||
@@ -390,6 +400,7 @@ class DisplayBlock
|
||||
|
||||
$this->m_oSet = new CMDBObjectSet($this->m_oFilter, $aOrderBy, $aQueryParams);
|
||||
}
|
||||
$this->m_oSet->SetShowObsoleteData($this->m_bShowObsoleteData);
|
||||
switch($this->m_sStyle)
|
||||
{
|
||||
case 'count':
|
||||
@@ -650,7 +661,7 @@ class DisplayBlock
|
||||
case 'links':
|
||||
//$bDashboardMode = isset($aExtraParams['dashboard']) ? ($aExtraParams['dashboard'] == 'true') : false;
|
||||
//$bSelectMode = isset($aExtraParams['select']) ? ($aExtraParams['select'] == 'true') : false;
|
||||
if ( ($this->m_oSet->Count()> 0) && (UserRights::IsActionAllowed($this->m_oSet->GetClass(), UR_ACTION_READ, $this->m_oSet) == UR_ALLOWED_YES) )
|
||||
if ( ($this->m_oSet->Count(1)> 0) && (UserRights::IsActionAllowed($this->m_oSet->GetClass(), UR_ACTION_READ, $this->m_oSet) == UR_ALLOWED_YES) )
|
||||
{
|
||||
//$sLinkage = isset($aExtraParams['linkage']) ? $aExtraParams['linkage'] : '';
|
||||
$sHtml .= cmdbAbstractObject::GetDisplaySet($oPage, $this->m_oSet, $aExtraParams);
|
||||
@@ -709,7 +720,8 @@ class DisplayBlock
|
||||
{
|
||||
$aQueryParams = $aExtraParams['query_params'];
|
||||
}
|
||||
$this->m_oSet = new CMDBObjectSet($this->m_oFilter, array(), $aQueryParams);
|
||||
$this->m_oSet = new CMDBObjectSet($this->m_oFilter, array(), $aQueryParams);
|
||||
$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());
|
||||
@@ -752,7 +764,8 @@ class DisplayBlock
|
||||
{
|
||||
$aQueryParams = $aExtraParams['query_params'];
|
||||
}
|
||||
$this->m_oSet = new CMDBObjectSet($this->m_oFilter, array(), $aQueryParams);
|
||||
$this->m_oSet = new CMDBObjectSet($this->m_oFilter, array(), $aQueryParams);
|
||||
$this->m_oSet->SetShowObsoleteData($this->m_bShowObsoleteData);
|
||||
}
|
||||
// Summary details
|
||||
$aCounts = array();
|
||||
@@ -761,20 +774,38 @@ class DisplayBlock
|
||||
{
|
||||
$aStates = explode(',', $sStatesList);
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sStateAttrCode);
|
||||
|
||||
// Generate one count + group by query [#1330]
|
||||
$sClassAlias = $this->m_oFilter->GetClassAlias();
|
||||
$oGroupByExpr = Expression::FromOQL($sClassAlias.'.'.$sStateAttrCode);
|
||||
$aGroupBy = array('group1' => $oGroupByExpr);
|
||||
$sCountGroupByQuery = $this->m_oFilter->MakeGroupByQuery(array(), $aGroupBy, false);
|
||||
$aCountGroupByResults = CMDBSource::QueryToArray($sCountGroupByQuery);
|
||||
$aCountsQueryResults = array();
|
||||
foreach ($aCountGroupByResults as $aCountGroupBySingleResult)
|
||||
{
|
||||
$aCountsQueryResults[$aCountGroupBySingleResult[0]] = $aCountGroupBySingleResult[1];
|
||||
}
|
||||
|
||||
foreach($aStates as $sStateValue)
|
||||
{
|
||||
$oFilter = $this->m_oFilter->DeepClone();
|
||||
$oFilter->AddCondition($sStateAttrCode, $sStateValue, '=');
|
||||
$oSet = new DBObjectSet($oFilter);
|
||||
$aCounts[$sStateValue] = $oSet->Count();
|
||||
$aStateLabels[$sStateValue] = htmlentities($oAttDef->GetValueLabel($sStateValue), ENT_QUOTES, 'UTF-8');
|
||||
|
||||
$aCounts[$sStateValue] = (array_key_exists($sStateValue, $aCountsQueryResults))
|
||||
? $aCountsQueryResults[$sStateValue]
|
||||
: 0;
|
||||
|
||||
if ($aCounts[$sStateValue] == 0)
|
||||
{
|
||||
$aCounts[$sStateValue] = '-';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sHyperlink = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&'.$oAppContext->GetForLink().'&filter='.urlencode($oFilter->serialize());
|
||||
$oSingleGroupByValueFilter = $this->m_oFilter->DeepClone();
|
||||
$oSingleGroupByValueFilter->AddCondition($sStateAttrCode, $sStateValue, '=');
|
||||
$sHyperlink = utils::GetAbsoluteUrlAppRoot()
|
||||
.'pages/UI.php?operation=search&'.$oAppContext->GetForLink()
|
||||
.'&filter='.urlencode($oSingleGroupByValueFilter->serialize());
|
||||
$aCounts[$sStateValue] = "<a href=\"$sHyperlink\">{$aCounts[$sStateValue]}</a>";
|
||||
}
|
||||
}
|
||||
@@ -795,9 +826,17 @@ 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';
|
||||
// Pass the parameters via POST, since expression may be very long
|
||||
$aParamsToPost = array(
|
||||
'expression' => $this->m_oFilter->ToOQL(true),
|
||||
'format' => 'csv',
|
||||
'filename' => $sCsvFile,
|
||||
'charset' => 'UTF-8',
|
||||
);
|
||||
if ($bAdvancedMode)
|
||||
{
|
||||
$sDownloadLink .= '&fields_advanced=1';
|
||||
$aParamsToPost['fields_advance'] = 1;
|
||||
$sChecked = 'CHECKED';
|
||||
}
|
||||
else
|
||||
@@ -805,7 +844,7 @@ class DisplayBlock
|
||||
$sLinkToToggle = $sLinkToToggle.'&advanced=1';
|
||||
$sChecked = '';
|
||||
}
|
||||
$sAjaxLink = $sDownloadLink.'&charset=UTF-8'; // Includes &fields_advanced=1 if in advanced mode
|
||||
$sAjaxLink = utils::GetAbsoluteUrlAppRoot().'webservices/export.php';
|
||||
|
||||
/*
|
||||
$sCSVData = cmdbAbstractObject::GetSetAsCSV($this->m_oSet, array('fields_advanced' => $bAdvancedMode));
|
||||
@@ -856,7 +895,8 @@ class DisplayBlock
|
||||
$sHtml .= "<div id=\"csv_content_loading\"><div style=\"width: 250px; height: 20px; background: url(../setup/orange-progress.gif); border: 1px #999 solid; margin-left:auto; margin-right: auto; text-align: center;\">".Dict::S('UI:Loading')."</div></div><textarea id=\"csv_content\" style=\"display:none;\">\n";
|
||||
//$sHtml .= htmlentities($sCSVData, ENT_QUOTES, 'UTF-8');
|
||||
$sHtml .= "</textarea>\n";
|
||||
$oPage->add_ready_script("$.post('$sAjaxLink', {}, function(data) { $('#csv_content').html(data); $('#csv_content_loading').hide(); $('#csv_content').show();} );");
|
||||
$sJsonParams = json_encode($aParamsToPost);
|
||||
$oPage->add_ready_script("$.post('$sAjaxLink', $sJsonParams, function(data) { $('#csv_content').html(data); $('#csv_content_loading').hide(); $('#csv_content').show();} );");
|
||||
break;
|
||||
|
||||
case 'modify':
|
||||
@@ -951,7 +991,6 @@ EOF
|
||||
$sContextParam = $oContext->GetForLink();
|
||||
|
||||
$aGroupBy = array();
|
||||
$aLabels = array();
|
||||
$iTotalCount = 0;
|
||||
$aValues = array();
|
||||
$aURLs = array();
|
||||
@@ -959,7 +998,6 @@ EOF
|
||||
{
|
||||
$sValue = $aRow['grouped_by_1'];
|
||||
$sHtmlValue = $oGroupByExp->MakeValueLabel($this->m_oFilter, $sValue, $sValue);
|
||||
$aLabels[$iRow] = strip_tags($sHtmlValue);
|
||||
$aGroupBy[(int)$iRow] = (int) $aRow['_itop_count_'];
|
||||
$iTotalCount += $aRow['_itop_count_'];
|
||||
$aValues[] = array('label' => html_entity_decode(strip_tags($sHtmlValue), ENT_QUOTES, 'UTF-8'), 'label_html' => $sHtmlValue, 'value' => (int) $aRow['_itop_count_']);
|
||||
@@ -979,7 +1017,7 @@ EOF
|
||||
$aNames = array();
|
||||
foreach($aValues as $idx => $aValue)
|
||||
{
|
||||
$aNames[$idx] = $aValue['label_html'];
|
||||
$aNames[$idx] = $aValue['label'];
|
||||
}
|
||||
$sJSNames = json_encode($aNames);
|
||||
|
||||
@@ -1006,6 +1044,12 @@ var chart = c3.generate({
|
||||
},
|
||||
axis: {
|
||||
x: {
|
||||
tick: {
|
||||
culling: {max: 25}, // Maximum 24 labels on x axis (2 years).
|
||||
centered: true,
|
||||
rotate: 90,
|
||||
multiline: false
|
||||
},
|
||||
type: 'category' // this needed to load string x value
|
||||
}
|
||||
},
|
||||
@@ -1057,6 +1101,7 @@ var chart = c3.generate({
|
||||
},
|
||||
legend: {
|
||||
show: true,
|
||||
position: 'right',
|
||||
},
|
||||
tooltip: {
|
||||
format: {
|
||||
@@ -1339,6 +1384,12 @@ class MenuBlock extends DisplayBlock
|
||||
$aActions = array();
|
||||
$sUIPage = cmdbAbstractObject::ComputeStandardUIPage($sClass);
|
||||
$sRootUrl = utils::GetAbsoluteUrlAppRoot();
|
||||
// Common params that will be applied to actions
|
||||
$aActionParams = array();
|
||||
if(isset($aExtraParams['menu_actions_target']))
|
||||
{
|
||||
$aActionParams['target'] = $aExtraParams['menu_actions_target'];
|
||||
}
|
||||
// 1:n links, populate the target object as a default value when creating a new linked object
|
||||
if (isset($aExtraParams['target_attr']))
|
||||
{
|
||||
@@ -1358,7 +1409,7 @@ class MenuBlock extends DisplayBlock
|
||||
{
|
||||
case 0:
|
||||
// No object in the set, the only possible action is "new"
|
||||
if ($bIsCreationAllowed) { $aActions['UI:Menu:New'] = array ('label' => Dict::S('UI:Menu:New'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=new&class=$sClass{$sContext}{$sDefault}"); }
|
||||
if ($bIsCreationAllowed) { $aActions['UI:Menu:New'] = array ('label' => Dict::S('UI:Menu:New'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=new&class=$sClass{$sContext}{$sDefault}") + $aActionParams; }
|
||||
break;
|
||||
|
||||
case 1:
|
||||
@@ -1367,7 +1418,7 @@ class MenuBlock extends DisplayBlock
|
||||
{
|
||||
if (!isset($aExtraParams['link_attr']))
|
||||
{
|
||||
if ($bIsCreationAllowed) { $aActions['UI:Menu:New'] = array ('label' => Dict::S('UI:Menu:New'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=new&class=$sClass{$sContext}{$sDefault}"); }
|
||||
if ($bIsCreationAllowed) { $aActions['UI:Menu:New'] = array ('label' => Dict::S('UI:Menu:New'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=new&class=$sClass{$sContext}{$sDefault}") + $aActionParams; }
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1402,9 +1453,9 @@ class MenuBlock extends DisplayBlock
|
||||
// Just one object in the set, possible actions are "new / clone / modify and delete"
|
||||
if (!isset($aExtraParams['link_attr']))
|
||||
{
|
||||
if ($bIsModifyAllowed) { $aActions['UI:Menu:Modify'] = array ('label' => Dict::S('UI:Menu:Modify'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=modify&class=$sClass&id=$id{$sContext}#"); }
|
||||
if ($bIsCreationAllowed) { $aActions['UI:Menu:New'] = array ('label' => Dict::S('UI:Menu:New'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=new&class=$sClass{$sContext}{$sDefault}"); }
|
||||
if ($bIsDeleteAllowed) { $aActions['UI:Menu:Delete'] = array ('label' => Dict::S('UI:Menu:Delete'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=delete&class=$sClass&id=$id{$sContext}"); }
|
||||
if ($bIsModifyAllowed) { $aActions['UI:Menu:Modify'] = array ('label' => Dict::S('UI:Menu:Modify'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=modify&class=$sClass&id=$id{$sContext}#") + $aActionParams; }
|
||||
if ($bIsCreationAllowed) { $aActions['UI:Menu:New'] = array ('label' => Dict::S('UI:Menu:New'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=new&class=$sClass{$sContext}{$sDefault}") + $aActionParams; }
|
||||
if ($bIsDeleteAllowed) { $aActions['UI:Menu:Delete'] = array ('label' => Dict::S('UI:Menu:Delete'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=delete&class=$sClass&id=$id{$sContext}") + $aActionParams; }
|
||||
// Transitions / Stimuli
|
||||
if (!$bLocked)
|
||||
{
|
||||
@@ -1419,7 +1470,7 @@ class MenuBlock extends DisplayBlock
|
||||
switch($iActionAllowed)
|
||||
{
|
||||
case UR_ALLOWED_YES:
|
||||
$aActions[$sStimulusCode] = array('label' => $aStimuli[$sStimulusCode]->GetLabel(), 'url' => "{$sRootUrl}pages/UI.php?operation=stimulus&stimulus=$sStimulusCode&class=$sClass&id=$id{$sContext}");
|
||||
$aActions[$sStimulusCode] = array('label' => $aStimuli[$sStimulusCode]->GetLabel(), 'url' => "{$sRootUrl}pages/UI.php?operation=stimulus&stimulus=$sStimulusCode&class=$sClass&id=$id{$sContext}") + $aActionParams;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -1437,11 +1488,11 @@ class MenuBlock extends DisplayBlock
|
||||
{
|
||||
if (array_key_exists('down', $aRelationInfo))
|
||||
{
|
||||
$aActions[$sRelationCode.'_down'] = array ('label' => $aRelationInfo['down'], 'url' => "{$sRootUrl}pages/$sUIPage?operation=swf_navigator&relation=$sRelationCode&direction=down&class=$sClass&id=$id{$sContext}");
|
||||
$aActions[$sRelationCode.'_down'] = array ('label' => $aRelationInfo['down'], 'url' => "{$sRootUrl}pages/$sUIPage?operation=swf_navigator&relation=$sRelationCode&direction=down&class=$sClass&id=$id{$sContext}") + $aActionParams;
|
||||
}
|
||||
if (array_key_exists('up', $aRelationInfo))
|
||||
{
|
||||
$aActions[$sRelationCode.'_up'] = array ('label' => $aRelationInfo['up'], 'url' => "{$sRootUrl}pages/$sUIPage?operation=swf_navigator&relation=$sRelationCode&direction=up&class=$sClass&id=$id{$sContext}");
|
||||
$aActions[$sRelationCode.'_up'] = array ('label' => $aRelationInfo['up'], 'url' => "{$sRootUrl}pages/$sUIPage?operation=swf_navigator&relation=$sRelationCode&direction=up&class=$sClass&id=$id{$sContext}") + $aActionParams;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1498,7 +1549,7 @@ class MenuBlock extends DisplayBlock
|
||||
$oSet->Rewind();
|
||||
foreach($oExtensionInstance->EnumAllowedActions($oSet) as $sLabel => $sUrl)
|
||||
{
|
||||
$aActions[$sLabel] = array ('label' => $sLabel, 'url' => $sUrl);
|
||||
$aActions[$sLabel] = array ('label' => $sLabel, 'url' => $sUrl) + $aActionParams;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1517,23 +1568,23 @@ class MenuBlock extends DisplayBlock
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sTargetAttr);
|
||||
$sTargetClass = $oAttDef->GetTargetClass();
|
||||
$bIsDeleteAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_DELETE, $oSet);
|
||||
if ($bIsModifyAllowed) { $aActions['UI:Menu:Add'] = array ('label' => Dict::S('UI:Menu:Add'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=modify_links&class=$sClass&link_attr=".$aExtraParams['link_attr']."&target_class=$sTargetClass&id=$id&addObjects=true{$sContext}"); }
|
||||
if ($bIsBulkModifyAllowed) { $aActions['UI:Menu:Manage'] = array ('label' => Dict::S('UI:Menu:Manage'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=modify_links&class=$sClass&link_attr=".$aExtraParams['link_attr']."&target_class=$sTargetClass&id=$id{$sContext}"); }
|
||||
//if ($bIsBulkDeleteAllowed) { $aActions[] = array ('label' => 'Remove All...', 'url' => "#"); }
|
||||
if ($bIsModifyAllowed) { $aActions['UI:Menu:Add'] = array ('label' => Dict::S('UI:Menu:Add'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=modify_links&class=$sClass&link_attr=".$aExtraParams['link_attr']."&target_class=$sTargetClass&id=$id&addObjects=true{$sContext}") + $aActionParams; }
|
||||
if ($bIsBulkModifyAllowed) { $aActions['UI:Menu:Manage'] = array ('label' => Dict::S('UI:Menu:Manage'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=modify_links&class=$sClass&link_attr=".$aExtraParams['link_attr']."&target_class=$sTargetClass&id=$id{$sContext}") + $aActionParams; }
|
||||
//if ($bIsBulkDeleteAllowed) { $aActions[] = array ('label' => 'Remove All...', 'url' => "#") + $aActionParams; }
|
||||
}
|
||||
else
|
||||
{
|
||||
// many objects in the set, possible actions are: new / modify all / delete all
|
||||
if ($bIsCreationAllowed) { $aActions['UI:Menu:New'] = array ('label' => Dict::S('UI:Menu:New'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=new&class=$sClass{$sContext}{$sDefault}"); }
|
||||
if ($bIsBulkModifyAllowed) { $aActions['UI:Menu:ModifyAll'] = array ('label' => Dict::S('UI:Menu:ModifyAll'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=select_for_modify_all&class=$sClass&filter=".urlencode($sFilter)."{$sContext}"); }
|
||||
if ($bIsBulkDeleteAllowed) { $aActions['UI:Menu:BulkDelete'] = array ('label' => Dict::S('UI:Menu:BulkDelete'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=select_for_deletion&filter=".urlencode($sFilter)."{$sContext}"); }
|
||||
if ($bIsCreationAllowed) { $aActions['UI:Menu:New'] = array ('label' => Dict::S('UI:Menu:New'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=new&class=$sClass{$sContext}{$sDefault}") + $aActionParams; }
|
||||
if ($bIsBulkModifyAllowed) { $aActions['UI:Menu:ModifyAll'] = array ('label' => Dict::S('UI:Menu:ModifyAll'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=select_for_modify_all&class=$sClass&filter=".urlencode($sFilter)."{$sContext}") + $aActionParams; }
|
||||
if ($bIsBulkDeleteAllowed) { $aActions['UI:Menu:BulkDelete'] = array ('label' => Dict::S('UI:Menu:BulkDelete'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=select_for_deletion&filter=".urlencode($sFilter)."{$sContext}") + $aActionParams; }
|
||||
|
||||
// Stimuli
|
||||
$aStates = MetaModel::EnumStates($sClass);
|
||||
// Do not perform time consuming computations if there are too may objects in the list
|
||||
$iLimit = MetaModel::GetConfig()->Get('complex_actions_limit');
|
||||
|
||||
if ((count($aStates) > 0) && (($iLimit == 0) || ($oSet->Count() < $iLimit)))
|
||||
if ((count($aStates) > 0) && (($iLimit == 0) || ($oSet->Count($iLimit + 1) < $iLimit)))
|
||||
{
|
||||
// Life cycle actions may be available... if all objects are in the same state
|
||||
//
|
||||
@@ -1567,7 +1618,7 @@ class MenuBlock extends DisplayBlock
|
||||
{
|
||||
case UR_ALLOWED_YES:
|
||||
case UR_ALLOWED_DEPENDS:
|
||||
$aActions[$sStimulusCode] = array('label' => $aStimuli[$sStimulusCode]->GetLabel(), 'url' => "{$sRootUrl}pages/UI.php?operation=select_bulk_stimulus&stimulus=$sStimulusCode&state=$sState&class=$sClass&filter=".urlencode($sFilter)."{$sContext}");
|
||||
$aActions[$sStimulusCode] = array('label' => $aStimuli[$sStimulusCode]->GetLabel(), 'url' => "{$sRootUrl}pages/UI.php?operation=select_bulk_stimulus&stimulus=$sStimulusCode&state=$sState&class=$sClass&filter=".urlencode($sFilter)."{$sContext}") + $aActionParams;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -1603,7 +1654,7 @@ class MenuBlock extends DisplayBlock
|
||||
else
|
||||
{
|
||||
// Backward compatibility with old plugins
|
||||
$aActions[$sLabel] = array ('label' => $sLabel, 'url' => $data);
|
||||
$aActions[$sLabel] = array ('label' => $sLabel, 'url' => $data) + $aActionParams;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,6 +360,7 @@ EOF
|
||||
<<<EOF
|
||||
$('#$sDialogId').dialog({
|
||||
height: 'auto',
|
||||
maxHeight: $(window).height() - 8,
|
||||
width: $iDialogWidth,
|
||||
modal: true,
|
||||
autoOpen: $sAutoOpen,
|
||||
@@ -531,7 +532,7 @@ EOF
|
||||
|
||||
public function GetFieldId($sCode)
|
||||
{
|
||||
return $this->GetPrefix().'attr_'.$sCode;
|
||||
return $this->GetPrefix().'attr_'.utils::GetSafeId($sCode.$this->GetSuffix());
|
||||
}
|
||||
|
||||
public function GetFieldName($sCode)
|
||||
@@ -881,7 +882,7 @@ class DesignerTextField extends DesignerFormField
|
||||
$this->sValidationPattern = $sValidationPattern;
|
||||
}
|
||||
|
||||
public function SetForbiddenValues($aValues, $sExplain)
|
||||
public function SetForbiddenValues($aValues, $sExplain, $bCaseSensitive = true)
|
||||
{
|
||||
$aForbiddenValues = $aValues;
|
||||
|
||||
@@ -893,7 +894,7 @@ class DesignerTextField extends DesignerFormField
|
||||
|
||||
}
|
||||
|
||||
$this->aForbiddenValues[] = array('values' => $aForbiddenValues, 'message' => $sExplain);
|
||||
$this->aForbiddenValues[] = array('values' => $aForbiddenValues, 'message' => $sExplain, 'case_sensitive' => $bCaseSensitive);
|
||||
}
|
||||
|
||||
public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
|
||||
@@ -1367,6 +1368,36 @@ class RunTimeIconSelectionField extends DesignerIconSelectionField
|
||||
}
|
||||
|
||||
static protected function FindIconsOnDisk($sBaseDir, $sDir = '')
|
||||
{
|
||||
$aFiles = null;
|
||||
$sKey = $sBaseDir.'/'.$sDir;
|
||||
$sShortKey = abs(crc32($sKey));
|
||||
$sCacheFile = utils::GetCachePath().'available-icons-'.$sShortKey.'.php';
|
||||
$sCacheClass = 'AvailableIcons_'.$sShortKey;
|
||||
if (file_exists($sCacheFile))
|
||||
{
|
||||
require_once($sCacheFile);
|
||||
if ($sCacheClass::$sKey === $sKey) // crc32 collision detection
|
||||
{
|
||||
$aFiles = $sCacheClass::$aIconFiles;
|
||||
}
|
||||
}
|
||||
if ($aFiles === null)
|
||||
{
|
||||
$aFiles = self::_FindIconsOnDisk($sBaseDir, $sDir);
|
||||
$sAvailableIcons = '<?php'.PHP_EOL;
|
||||
$sAvailableIcons .= '// Generated and used by '.__METHOD__.PHP_EOL;
|
||||
$sAvailableIcons .= 'class '.$sCacheClass.PHP_EOL;
|
||||
$sAvailableIcons .= '{'.PHP_EOL;
|
||||
$sAvailableIcons .= ' static $sKey = '.var_export($sKey, true).';'.PHP_EOL;
|
||||
$sAvailableIcons .= ' static $aIconFiles = '.var_export($aFiles, true).';'.PHP_EOL;
|
||||
$sAvailableIcons .= '}'.PHP_EOL;
|
||||
file_put_contents($sCacheFile, $sAvailableIcons, LOCK_EX);
|
||||
}
|
||||
return $aFiles;
|
||||
}
|
||||
|
||||
static protected function _FindIconsOnDisk($sBaseDir, $sDir = '')
|
||||
{
|
||||
$aResult = array();
|
||||
// Populate automatically the list of icon files
|
||||
@@ -1378,7 +1409,7 @@ class RunTimeIconSelectionField extends DesignerIconSelectionField
|
||||
if (($sFile != '.') && ($sFile != '..') && ($sFile != 'lifecycle') && is_dir($sBaseDir.'/'.$sDir.'/'.$sFile))
|
||||
{
|
||||
$sDirSubPath = ($sDir == '') ? $sFile : $sDir.'/'.$sFile;
|
||||
$aResult = array_merge($aResult, self::FindIconsOnDisk($sBaseDir, $sDirSubPath));
|
||||
$aResult = array_merge($aResult, self::_FindIconsOnDisk($sBaseDir, $sDirSubPath));
|
||||
}
|
||||
if (preg_match("/\.(png|jpg|jpeg|gif)$/i", $sFile, $aMatches)) // png, jp(e)g and gif are considered valid
|
||||
{
|
||||
@@ -1408,8 +1439,12 @@ class RunTimeIconSelectionField extends DesignerIconSelectionField
|
||||
|
||||
public function GetDefaultValue($sClass = 'Contact')
|
||||
{
|
||||
$sIconPath = MetaModel::GetClassIcon($sClass, false);
|
||||
$sIcon = str_replace(utils::GetAbsoluteUrlModulesRoot(), '', $sIconPath);
|
||||
$sIcon = '';
|
||||
if (MetaModel::IsValidClass($sClass))
|
||||
{
|
||||
$sIconPath = MetaModel::GetClassIcon($sClass, false);
|
||||
$sIcon = str_replace(utils::GetAbsoluteUrlModulesRoot(), '', $sIconPath);
|
||||
}
|
||||
return $sIcon;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* Class iTopWebPage
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -34,7 +34,7 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage
|
||||
{
|
||||
private $m_sMenu;
|
||||
// private $m_currentOrganization;
|
||||
private $m_sMessage;
|
||||
private $m_aMessages;
|
||||
private $m_sInitScript;
|
||||
protected $m_oTabs;
|
||||
protected $bBreadCrumbEnabled;
|
||||
@@ -63,8 +63,10 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage
|
||||
$this->bBreadCrumbEnabled = false;
|
||||
}
|
||||
|
||||
utils::InitArchiveMode();
|
||||
|
||||
$this->m_sMenu = "";
|
||||
$this->m_sMessage = '';
|
||||
$this->m_aMessages = array();
|
||||
$this->SetRootUrl(utils::GetAbsoluteUrlAppRoot());
|
||||
$this->add_header("Content-type: text/html; charset=utf-8");
|
||||
$this->add_header("Cache-control: no-cache");
|
||||
@@ -75,6 +77,7 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage
|
||||
$this->add_linked_stylesheet("../css/jquery.multiselect.css");
|
||||
$this->add_linked_stylesheet("../css/magnific-popup.css");
|
||||
$this->add_linked_stylesheet("../css/c3.min.css");
|
||||
$this->add_linked_stylesheet("../css/font-awesome/css/font-awesome.min.css");
|
||||
|
||||
$this->add_linked_script('../js/jquery.layout.min.js');
|
||||
$this->add_linked_script('../js/jquery.ba-bbq.min.js');
|
||||
@@ -121,6 +124,23 @@ function ShowAboutBox()
|
||||
});
|
||||
return false;
|
||||
}
|
||||
function ArchiveMode(bEnable)
|
||||
{
|
||||
var sPrevUrl = StripArchiveArgument(window.location.search);
|
||||
if (bEnable)
|
||||
{
|
||||
window.location.search = sPrevUrl + '&with-archive=1';
|
||||
}
|
||||
else
|
||||
{
|
||||
window.location.search = sPrevUrl + '&with-archive=0';
|
||||
}
|
||||
}
|
||||
function StripArchiveArgument(sUrl)
|
||||
{
|
||||
var res = sUrl.replace(/&with-archive=[01]/g, '');
|
||||
return res;
|
||||
}
|
||||
EOF
|
||||
);
|
||||
}
|
||||
@@ -191,7 +211,8 @@ EOF;
|
||||
$sJSDatePickerOptions = json_encode($aPickerOptions);
|
||||
|
||||
// Time picker additional options
|
||||
|
||||
$aPickerOptions['showOn'] = '';
|
||||
$aPickerOptions['buttonImage'] = null;
|
||||
$aPickerOptions['timeFormat'] = $oTimeFormat->ToDatePicker();
|
||||
$aPickerOptions['controlType'] = 'select';
|
||||
$aPickerOptions['closeText'] = Dict::S('UI:Button:Ok');
|
||||
@@ -208,7 +229,41 @@ EOF;
|
||||
}";
|
||||
$sJSDateTimePickerOptions = substr($sJSDateTimePickerOptions, 0, -1).$aMoreJSOptions;
|
||||
}
|
||||
|
||||
$this->add_script(
|
||||
<<< EOF
|
||||
function PrepareWidgets()
|
||||
{
|
||||
// note: each action implemented here must be idempotent,
|
||||
// because this helper function might be called several times on a given page
|
||||
|
||||
// Note: Trigger image is wrapped in a span so we can display it we want
|
||||
$(".date-pick").datepicker($sJSDatePickerOptions)
|
||||
.next("img").wrap("<span>");
|
||||
|
||||
// Hack for the date and time picker addon issue on Chrome (see #1305)
|
||||
// The workaround is to instantiate the widget on demand
|
||||
// It relies on the same markup, thus reverting to the original implementation should be straightforward
|
||||
$(".datetime-pick:not(.is-widget-ready)").each(function(){
|
||||
var oInput = this;
|
||||
$(oInput).addClass('is-widget-ready');
|
||||
$('<span><img class="datetime-pick-button" src="../images/calendar.png"></span>')
|
||||
.insertAfter($(this))
|
||||
.on('click', function(){
|
||||
$(oInput)
|
||||
.datetimepicker($sJSDateTimePickerOptions)
|
||||
.datetimepicker('show')
|
||||
.datetimepicker('option', 'onClose', function(dateText,inst){
|
||||
$(oInput).datetimepicker('destroy');
|
||||
})
|
||||
.on('click keypress', function(){
|
||||
$(oInput).datetimepicker('hide');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
EOF
|
||||
);
|
||||
|
||||
$this->m_sInitScript =
|
||||
<<< EOF
|
||||
try
|
||||
@@ -275,7 +330,7 @@ EOF;
|
||||
|
||||
// This selector will be reused when selecting actual tab widget A elements.
|
||||
var tab_a_selector = 'ul.ui-tabs-nav a';
|
||||
|
||||
|
||||
// Ugly patch for a change in the behavior of jQuery UI:
|
||||
// Before jQuery UI 1.9, tabs were always considered as "local" (opposed to Ajax)
|
||||
// when their href was beginning by #. Starting with 1.9, a <base> tag in the page
|
||||
@@ -444,8 +499,7 @@ EOF
|
||||
|
||||
// End of Tabs handling
|
||||
|
||||
$(".date-pick").datepicker($sJSDatePickerOptions);
|
||||
$(".datetime-pick").datetimepicker($sJSDateTimePickerOptions);
|
||||
PrepareWidgets();
|
||||
|
||||
// Make sortable, everything that claims to be sortable
|
||||
$('.sortable').sortable( {axis: 'y', cursor: 'move', handle: '.drag_handle', stop: function()
|
||||
@@ -460,7 +514,7 @@ EOF
|
||||
}
|
||||
});
|
||||
docWidth = $(document).width();
|
||||
$('#ModalDlg').dialog({ autoOpen: false, modal: true, width: 0.8*docWidth }); // JQuery UI dialogs
|
||||
$('#ModalDlg').dialog({ autoOpen: false, modal: true, width: 0.8*docWidth, height: 'auto', maxHeight: $(window).height() - 50 }); // JQuery UI dialogs
|
||||
ShowDebug();
|
||||
$('#logOffBtn>ul').popupmenu();
|
||||
|
||||
@@ -700,7 +754,7 @@ EOF
|
||||
|
||||
if (UserRights::IsAdministrator() && ExecutionKPI::IsEnabled())
|
||||
{
|
||||
$sNorthPane .= '<div id="admin-banner"><span style="padding:5px;">'.ExecutionKPI::GetDescription().'<span></div>';
|
||||
$sNorthPane .= '<div class="app-message"><span style="padding:5px;">'.ExecutionKPI::GetDescription().'<span></div>';
|
||||
}
|
||||
|
||||
//$sSouthPane = '<p>Peak memory Usage: '.sprintf('%.3f MB', memory_get_peak_usage(true) / (1024*1024)).'</p>';
|
||||
@@ -908,12 +962,12 @@ EOF
|
||||
if (ITOP_REVISION == '$WCREV$')
|
||||
{
|
||||
// This is NOT a version built using the buil system, just display the main version
|
||||
$sVersionString = Dict::Format('UI:iTopVersion:Short', ITOP_VERSION);
|
||||
$sVersionString = Dict::Format('UI:iTopVersion:Short', ITOP_APPLICATION, ITOP_VERSION);
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is a build made from SVN, let display the full information
|
||||
$sVersionString = Dict::Format('UI:iTopVersion:Long', ITOP_VERSION, ITOP_REVISION, ITOP_BUILD_DATE);
|
||||
$sVersionString = Dict::Format('UI:iTopVersion:Long', ITOP_APPLICATION, ITOP_VERSION, ITOP_REVISION, ITOP_BUILD_DATE);
|
||||
}
|
||||
|
||||
// Render the text of the global search form
|
||||
@@ -948,9 +1002,39 @@ EOF
|
||||
$sLogOffMenu .= "<li><span>$sLogonMessage</span></li>\n";
|
||||
$aActions = array();
|
||||
|
||||
$aAllowedPortals = UserRights::GetAllowedPortals();
|
||||
if(count($aAllowedPortals) > 1)
|
||||
{
|
||||
// Adding portals
|
||||
foreach($aAllowedPortals as $aAllowedPortal)
|
||||
{
|
||||
if($aAllowedPortal['id'] !== 'backoffice')
|
||||
{
|
||||
$oPortalMenuItem = new URLPopupMenuItem('portal:'.$aAllowedPortal['id'], Dict::S($aAllowedPortal['label']), $aAllowedPortal['url'], '_blank');
|
||||
$aActions[$oPortalMenuItem->GetUID()] = $oPortalMenuItem->GetMenuItem();
|
||||
}
|
||||
}
|
||||
// Adding a separator
|
||||
$oPortalSeparatorMenuItem = new SeparatorPopupMenuItem();
|
||||
$aActions[$oPortalSeparatorMenuItem->GetUID()] = $oPortalSeparatorMenuItem->GetMenuItem();
|
||||
}
|
||||
|
||||
$oPrefs = new URLPopupMenuItem('UI:Preferences', Dict::S('UI:Preferences'), utils::GetAbsoluteUrlAppRoot()."pages/preferences.php?".$oAppContext->GetForLink());
|
||||
$aActions[$oPrefs->GetUID()] = $oPrefs->GetMenuItem();
|
||||
|
||||
|
||||
if (utils::IsArchiveMode())
|
||||
{
|
||||
$oExitArchive = new JSPopupMenuItem('UI:ArchiveModeOff', Dict::S('UI:ArchiveModeOff'), 'return ArchiveMode(false);');
|
||||
$aActions[$oExitArchive->GetUID()] = $oExitArchive->GetMenuItem();
|
||||
|
||||
$sIcon = '<span class="fa fa-lock fa-1x"></span>';
|
||||
$this->AddApplicationMessage(Dict::S('UI:ArchiveMode:Banner'), $sIcon, Dict::S('UI:ArchiveMode:Banner+'));
|
||||
}
|
||||
elseif (UserRights::CanBrowseArchive())
|
||||
{
|
||||
$oBrowseArchive = new JSPopupMenuItem('UI:ArchiveModeOn', Dict::S('UI:ArchiveModeOn'), 'return ArchiveMode(true);');
|
||||
$aActions[$oBrowseArchive->GetUID()] = $oBrowseArchive->GetMenuItem();
|
||||
}
|
||||
if (utils::CanLogOff())
|
||||
{
|
||||
$oLogOff = new URLPopupMenuItem('UI:LogOffMenu', Dict::S('UI:LogOffMenu'), utils::GetAbsoluteUrlAppRoot().'pages/logoff.php?operation=do_logoff');
|
||||
@@ -982,26 +1066,34 @@ EOF
|
||||
$sRestrictions = Dict::S('UI:AccessRO-Users');
|
||||
}
|
||||
|
||||
$sApplicationBanner = '';
|
||||
if (strlen($sRestrictions) > 0)
|
||||
{
|
||||
$sIcon =
|
||||
<<<EOF
|
||||
<span class="fa-stack fa-sm">
|
||||
<i class="fa fa-pencil fa-flip-horizontal fa-stack-1x"></i>
|
||||
<i class="fa fa-ban fa-stack-2x text-danger"></i>
|
||||
</span>
|
||||
EOF;
|
||||
|
||||
$sAdminMessage = trim(MetaModel::GetConfig()->Get('access_message'));
|
||||
$sApplicationBanner .= '<div id="admin-banner">';
|
||||
$sApplicationBanner .= '<img src="../images/locked.png" style="vertical-align:middle;">';
|
||||
$sApplicationBanner .= ' <b>'.$sRestrictions.'</b>';
|
||||
if (strlen($sAdminMessage) > 0)
|
||||
{
|
||||
$sApplicationBanner .= ' <b>'.$sAdminMessage.'</b>';
|
||||
$sRestrictions .= ' '.$sAdminMessage;
|
||||
}
|
||||
$sApplicationBanner .= '</div>';
|
||||
$this->AddApplicationMessage($sRestrictions, $sIcon);
|
||||
}
|
||||
|
||||
if(strlen($this->m_sMessage))
|
||||
$sApplicationMessages = '';
|
||||
foreach ($this->m_aMessages as $aMessage)
|
||||
{
|
||||
$sApplicationBanner .= '<div id="admin-banner"><span style="padding:5px;">'.$this->m_sMessage.'<span></div>';
|
||||
$sHtmlIcon = $aMessage['icon'] ? $aMessage['icon'] : '';
|
||||
$sHtmlMessage = $aMessage['message'];
|
||||
$sTitleAttr = $aMessage['tip'] ? 'title="'.htmlentities($aMessage['tip'], ENT_QUOTES, 'UTF-8').'"' : '';
|
||||
$sApplicationMessages .= '<div class="app-message" '.$sTitleAttr.'><span class="app-message-icon">'.$sHtmlIcon.'</span><span class="app-message-body">'.$sHtmlMessage.'</div></span>';
|
||||
}
|
||||
|
||||
$sApplicationBanner .= $sBannerExtraHtml;
|
||||
$sApplicationBanner = "<div class=\"app-banner ui-helper-clearfix\">$sApplicationMessages$sBannerExtraHtml</div>";
|
||||
|
||||
if (!empty($sNorthPane))
|
||||
{
|
||||
@@ -1071,9 +1163,12 @@ EOF
|
||||
$sHtml .= ' <div id="itop-breadcrumb"></div>';
|
||||
$sHtml .= ' </td>';
|
||||
$sHtml .= ' <td id="top-bar-table-search">';
|
||||
$sHtml .= ' <div id="global-search"><form action="'.utils::GetAbsoluteUrlAppRoot().'pages/UI.php"><table><tr><td></td><td><div id="global-search-area"><input id="global-search-input" type="text" name="text" placeholder="'.$sText.'"></input><div '.$sOnClick.' id="global-search-image"></div></div></td>';
|
||||
$sHtml .= ' <td><a id="help-link" href="'.$sOnlineHelpUrl.'" target="_blank"><img title="'.Dict::S('UI:Help').'" src="../images/help.png?itopversion='.ITOP_VERSION.'"/></td>';
|
||||
$sHtml .= ' <td>'.self::FilterXSS($sLogOffMenu).'</td><td><input type="hidden" name="operation" value="full_text"/></td></tr></table></form></div>';
|
||||
$sHtml .= ' <div id="global-search"><form action="'.utils::GetAbsoluteUrlAppRoot().'pages/UI.php">';
|
||||
$sHtml .= ' <table id="top-left-buttons-area"><tr>';
|
||||
$sHtml .= ' <td id="top-left-global-search-cell"><div id="global-search-area"><input id="global-search-input" type="text" name="text" placeholder="'.$sText.'"></input><div '.$sOnClick.' id="global-search-image"><input type="hidden" name="operation" value="full_text"/></div></div></td>';
|
||||
$sHtml .= ' <td id="top-left-help-cell"><a id="help-link" href="'.$sOnlineHelpUrl.'" target="_blank"><img title="'.Dict::S('UI:Help').'" src="../images/help.png?itopversion='.ITOP_VERSION.'"/></td>';
|
||||
$sHtml .= ' <td id="top-left-logoff-cell">'.self::FilterXSS($sLogOffMenu).'</td>';
|
||||
$sHtml .= ' </tr></table></form></div>';
|
||||
$sHtml .= ' </td>';
|
||||
$sHtml .= ' </tr>';
|
||||
$sHtml .= ' </table>';
|
||||
@@ -1301,10 +1396,26 @@ EOF
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the message to be displayed in the 'admin-banner' section at the top of the page
|
||||
* Set the message to be displayed in the 'app-banner' section at the top of the page
|
||||
*/
|
||||
public function SetMessage($sMessage)
|
||||
public function SetMessage($sHtmlMessage)
|
||||
{
|
||||
$this->m_sMessage = $sMessage;
|
||||
$sHtmlIcon = '<span class="fa fa-comment fa-1x"></span>';
|
||||
$this->AddApplicationMessage($sHtmlMessage, $sHtmlIcon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add message to be displayed in the 'app-banner' section at the top of the page
|
||||
*/
|
||||
public function AddApplicationMessage($sHtmlMessage, $sHtmlIcon = null, $sTip = null)
|
||||
{
|
||||
if (strlen($sHtmlMessage))
|
||||
{
|
||||
$this->m_aMessages[] = array(
|
||||
'icon' => $sHtmlIcon,
|
||||
'message' => $sHtmlMessage,
|
||||
'tip' => $sTip
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* Class LoginWebPage
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -56,8 +56,13 @@ class LoginWebPage extends NiceWebPage
|
||||
|
||||
protected static $m_sLoginFailedMessage = '';
|
||||
|
||||
public function __construct($sTitle = 'iTop Login')
|
||||
public function __construct($sTitle = null)
|
||||
{
|
||||
if($sTitle === null)
|
||||
{
|
||||
$sTitle = Dict::S('UI:Login:Title');
|
||||
}
|
||||
|
||||
parent::__construct($sTitle);
|
||||
$this->SetStyleSheet();
|
||||
$this->add_header("Cache-control: no-cache");
|
||||
@@ -90,7 +95,7 @@ class LoginWebPage extends NiceWebPage
|
||||
$sLogo = 'itop-logo-external.png';
|
||||
$sBrandingLogo = 'login-logo.png';
|
||||
}
|
||||
$sVersionShort = Dict::Format('UI:iTopVersion:Short', ITOP_VERSION);
|
||||
$sVersionShort = Dict::Format('UI:iTopVersion:Short', ITOP_APPLICATION, ITOP_VERSION);
|
||||
$sIconUrl = Utils::GetConfig()->Get('app_icon_url');
|
||||
$sDisplayIcon = utils::GetAbsoluteUrlAppRoot().'images/'.$sLogo.'?itopversion='.ITOP_VERSION;
|
||||
if (file_exists(MODULESROOT.'branding/'.$sBrandingLogo))
|
||||
@@ -112,7 +117,7 @@ class LoginWebPage extends NiceWebPage
|
||||
|
||||
case 'basic':
|
||||
case 'url':
|
||||
$this->add_header('WWW-Authenticate: Basic realm="'.Dict::Format('UI:iTopVersion:Short', ITOP_VERSION));
|
||||
$this->add_header('WWW-Authenticate: Basic realm="'.Dict::Format('UI:iTopVersion:Short', ITOP_APPLICATION, ITOP_VERSION));
|
||||
$this->add_header('HTTP/1.0 401 Unauthorized');
|
||||
$this->add_header('Content-type: text/html; charset=iso-8859-1');
|
||||
// Note: displayed when the user will click on Cancel
|
||||
@@ -430,6 +435,8 @@ EOF
|
||||
// Unset all of the session variables.
|
||||
unset($_SESSION['auth_user']);
|
||||
unset($_SESSION['login_mode']);
|
||||
unset($_SESSION['archive_mode']);
|
||||
unset($_SESSION['impersonate_user']);
|
||||
UserRights::_ResetSessionCache();
|
||||
// If it's desired to kill the session, also delete the session cookie.
|
||||
// Note: This will destroy the session, and not just the session data!
|
||||
@@ -572,96 +579,95 @@ EOF
|
||||
break;
|
||||
}
|
||||
$index++;
|
||||
|
||||
//echo "\nsLoginMode: $sLoginMode (user: $sAuthUser / pwd: $sAuthPwd\n)";
|
||||
if ($sLoginMode == '')
|
||||
}
|
||||
//echo "\nsLoginMode: $sLoginMode (user: $sAuthUser / pwd: $sAuthPwd\n)";
|
||||
if ($sLoginMode == '')
|
||||
{
|
||||
// First connection
|
||||
$sDesiredLoginMode = utils::ReadParam('login_mode');
|
||||
if (in_array($sDesiredLoginMode, $aAllowedLoginTypes))
|
||||
{
|
||||
// First connection
|
||||
$sDesiredLoginMode = utils::ReadParam('login_mode');
|
||||
if (in_array($sDesiredLoginMode, $aAllowedLoginTypes))
|
||||
$sLoginMode = $sDesiredLoginMode;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sLoginMode = $aAllowedLoginTypes[0]; // First in the list...
|
||||
}
|
||||
if (array_key_exists('HTTP_X_COMBODO_AJAX', $_SERVER))
|
||||
{
|
||||
// X-Combodo-Ajax is a special header automatically added to all ajax requests
|
||||
// Let's reply that we're currently logged-out
|
||||
header('HTTP/1.0 401 Unauthorized');
|
||||
exit;
|
||||
}
|
||||
if (($iOnExit == self::EXIT_HTTP_401) || ($sLoginMode == 'basic'))
|
||||
{
|
||||
header('WWW-Authenticate: Basic realm="'.Dict::Format('UI:iTopVersion:Short', ITOP_APPLICATION, ITOP_VERSION));
|
||||
header('HTTP/1.0 401 Unauthorized');
|
||||
header('Content-type: text/html; charset=iso-8859-1');
|
||||
exit;
|
||||
}
|
||||
else if($iOnExit == self::EXIT_RETURN)
|
||||
{
|
||||
if (($sAuthUser !== '') && ($sAuthPwd === null))
|
||||
{
|
||||
$sLoginMode = $sDesiredLoginMode;
|
||||
return self::EXIT_CODE_MISSINGPASSWORD;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sLoginMode = $aAllowedLoginTypes[0]; // First in the list...
|
||||
}
|
||||
if (array_key_exists('HTTP_X_COMBODO_AJAX', $_SERVER))
|
||||
{
|
||||
// X-Combodo-Ajax is a special header automatically added to all ajax requests
|
||||
// Let's reply that we're currently logged-out
|
||||
header('HTTP/1.0 401 Unauthorized');
|
||||
exit;
|
||||
return self::EXIT_CODE_MISSINGLOGIN;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$oPage = self::NewLoginWebPage();
|
||||
$oPage->DisplayLoginForm( $sLoginMode, false /* no previous failed attempt */);
|
||||
$oPage->output();
|
||||
exit;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, $sLoginMode, $sAuthentication))
|
||||
{
|
||||
//echo "Check Credentials returned false for user $sAuthUser!";
|
||||
self::ResetSession();
|
||||
if (($iOnExit == self::EXIT_HTTP_401) || ($sLoginMode == 'basic'))
|
||||
{
|
||||
header('WWW-Authenticate: Basic realm="'.Dict::Format('UI:iTopVersion:Short', ITOP_VERSION));
|
||||
header('WWW-Authenticate: Basic realm="'.Dict::Format('UI:iTopVersion:Short', ITOP_APPLICATION, ITOP_VERSION));
|
||||
header('HTTP/1.0 401 Unauthorized');
|
||||
header('Content-type: text/html; charset=iso-8859-1');
|
||||
exit;
|
||||
}
|
||||
else if($iOnExit == self::EXIT_RETURN)
|
||||
{
|
||||
if (($sAuthUser !== '') && ($sAuthPwd === null))
|
||||
{
|
||||
return self::EXIT_CODE_MISSINGPASSWORD;
|
||||
}
|
||||
else
|
||||
{
|
||||
return self::EXIT_CODE_MISSINGLOGIN;
|
||||
}
|
||||
return self::EXIT_CODE_WRONGCREDENTIALS;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oPage = self::NewLoginWebPage();
|
||||
$oPage->DisplayLoginForm( $sLoginMode, false /* no previous failed attempt */);
|
||||
$oPage->DisplayLoginForm( $sLoginMode, true /* failed attempt */);
|
||||
$oPage->output();
|
||||
exit;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, $sLoginMode, $sAuthentication))
|
||||
// User is Ok, let's save it in the session and proceed with normal login
|
||||
UserRights::Login($sAuthUser, $sAuthentication); // Login & set the user's language
|
||||
|
||||
if (MetaModel::GetConfig()->Get('log_usage'))
|
||||
{
|
||||
//echo "Check Credentials returned false for user $sAuthUser!";
|
||||
self::ResetSession();
|
||||
if (($iOnExit == self::EXIT_HTTP_401) || ($sLoginMode == 'basic'))
|
||||
{
|
||||
header('WWW-Authenticate: Basic realm="'.Dict::Format('UI:iTopVersion:Short', ITOP_VERSION));
|
||||
header('HTTP/1.0 401 Unauthorized');
|
||||
header('Content-type: text/html; charset=iso-8859-1');
|
||||
exit;
|
||||
}
|
||||
else if($iOnExit == self::EXIT_RETURN)
|
||||
{
|
||||
return self::EXIT_CODE_WRONGCREDENTIALS;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oPage = self::NewLoginWebPage();
|
||||
$oPage->DisplayLoginForm( $sLoginMode, true /* failed attempt */);
|
||||
$oPage->output();
|
||||
exit;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// User is Ok, let's save it in the session and proceed with normal login
|
||||
UserRights::Login($sAuthUser, $sAuthentication); // Login & set the user's language
|
||||
|
||||
if (MetaModel::GetConfig()->Get('log_usage'))
|
||||
{
|
||||
$oLog = new EventLoginUsage();
|
||||
$oLog->Set('userinfo', UserRights::GetUser());
|
||||
$oLog->Set('user_id', UserRights::GetUserObject()->GetKey());
|
||||
$oLog->Set('message', 'Successful login');
|
||||
$oLog->DBInsertNoReload();
|
||||
}
|
||||
|
||||
$_SESSION['auth_user'] = $sAuthUser;
|
||||
$_SESSION['login_mode'] = $sLoginMode;
|
||||
UserRights::_InitSessionCache();
|
||||
$oLog = new EventLoginUsage();
|
||||
$oLog->Set('userinfo', UserRights::GetUser());
|
||||
$oLog->Set('user_id', UserRights::GetUserObject()->GetKey());
|
||||
$oLog->Set('message', 'Successful login');
|
||||
$oLog->DBInsertNoReload();
|
||||
}
|
||||
|
||||
$_SESSION['auth_user'] = $sAuthUser;
|
||||
$_SESSION['login_mode'] = $sLoginMode;
|
||||
UserRights::_InitSessionCache();
|
||||
}
|
||||
}
|
||||
return self::EXIT_CODE_OK;
|
||||
|
||||
@@ -148,6 +148,7 @@ class ApplicationMenu
|
||||
// the menu already exists, let's combine the conditions that make it visible
|
||||
self::$aMenusIndex[$index]['node']->AddCondition($oMenuNode);
|
||||
}
|
||||
|
||||
return $index;
|
||||
}
|
||||
|
||||
@@ -174,7 +175,7 @@ class ApplicationMenu
|
||||
{
|
||||
$oMenuNode = self::GetMenuNode($aMenu['index']);
|
||||
if (!$oMenuNode->IsEnabled()) continue; // Don't display a non-enabled menu
|
||||
$oPage->AddToMenu('<h3>'.$oMenuNode->GetTitle().'</h3>');
|
||||
$oPage->AddToMenu('<h3 id="'.utils::GetSafeId('AccordionMenu_'.$oMenuNode->GetMenuID()).'">'.$oMenuNode->GetTitle().'</h3>');
|
||||
$oPage->AddToMenu('<div>');
|
||||
$aChildren = self::GetChildren($aMenu['index']);
|
||||
if (count($aChildren) > 0)
|
||||
@@ -217,11 +218,11 @@ EOF
|
||||
$sHyperlink = $oMenu->GetHyperlink($aExtraParams);
|
||||
if ($sHyperlink != '')
|
||||
{
|
||||
$oPage->AddToMenu('<li'.$sCSSClass.'><a href="'.$oMenu->GetHyperlink($aExtraParams).'">'.$oMenu->GetTitle().'</a></li>');
|
||||
$oPage->AddToMenu('<li id="'.utils::GetSafeId('AccordionMenu_'.$oMenu->GetMenuID()).'"'.$sCSSClass.'><a href="'.$oMenu->GetHyperlink($aExtraParams).'">'.$oMenu->GetTitle().'</a></li>');
|
||||
}
|
||||
else
|
||||
{
|
||||
$oPage->AddToMenu('<li'.$sCSSClass.'>'.$oMenu->GetTitle().'</li>');
|
||||
$oPage->AddToMenu('<li id="'.utils::GetSafeId('AccordionMenu_'.$oMenu->GetMenuID()).'"'.$sCSSClass.'>'.$oMenu->GetTitle().'</li>');
|
||||
}
|
||||
$aCurrentMenu = self::$aMenusIndex[$index];
|
||||
if ($iActiveMenu == $index)
|
||||
@@ -320,6 +321,21 @@ EOF
|
||||
}
|
||||
return $sDefaultMenuId;
|
||||
}
|
||||
|
||||
static public function GetRootMenuId($sMenuId)
|
||||
{
|
||||
$iMenuIndex = self::GetMenuIndexById($sMenuId);
|
||||
if ($iMenuIndex == -1)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
$oMenu = ApplicationMenu::GetMenuNode($iMenuIndex);
|
||||
while ($oMenu->GetParentIndex() != -1)
|
||||
{
|
||||
$oMenu = ApplicationMenu::GetMenuNode($oMenu->GetParentIndex());
|
||||
}
|
||||
return $oMenu->GetMenuId();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -418,7 +434,12 @@ abstract class MenuNode
|
||||
{
|
||||
return $this->sMenuId;
|
||||
}
|
||||
|
||||
|
||||
public function GetParentIndex()
|
||||
{
|
||||
return $this->iParentIndex;
|
||||
}
|
||||
|
||||
public function GetTitle()
|
||||
{
|
||||
return Dict::S("Menu:$this->sMenuId", str_replace('_', ' ', $this->sMenuId));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -19,7 +19,7 @@
|
||||
/**
|
||||
* Class PortalWebPage
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2013 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -96,15 +96,88 @@ class PortalWebPage extends NiceWebPage
|
||||
$this->add_linked_script("../js/ckeditor/ckeditor.js");
|
||||
$this->add_linked_script("../js/ckeditor/adapters/jquery.js");
|
||||
|
||||
$this->add_linked_script("../js/jquery-ui-timepicker-addon.js");
|
||||
$this->add_linked_script("../js/jquery-ui-timepicker-addon-i18n.min.js");
|
||||
$this->add_linked_stylesheet("../css/jquery-ui-timepicker-addon.css");
|
||||
|
||||
$sJSDisconnectedMessage = json_encode(Dict::S('UI:DisconnectedDlgMessage'));
|
||||
$sJSTitle = json_encode(Dict::S('UI:DisconnectedDlgTitle'));
|
||||
$sJSLoginAgain = json_encode(Dict::S('UI:LoginAgain'));
|
||||
$sJSStayOnThePage = json_encode(Dict::S('UI:StayOnThePage'));
|
||||
$sJSDaysMin = json_encode(array(Dict::S('DayOfWeek-Sunday-Min'), Dict::S('DayOfWeek-Monday-Min'), Dict::S('DayOfWeek-Tuesday-Min'), Dict::S('DayOfWeek-Wednesday-Min'),
|
||||
Dict::S('DayOfWeek-Thursday-Min'), Dict::S('DayOfWeek-Friday-Min'), Dict::S('DayOfWeek-Saturday-Min')));
|
||||
$sJSMonthsShort = json_encode(array(Dict::S('Month-01-Short'), Dict::S('Month-02-Short'), Dict::S('Month-03-Short'), Dict::S('Month-04-Short'), Dict::S('Month-05-Short'), Dict::S('Month-06-Short'),
|
||||
Dict::S('Month-07-Short'), Dict::S('Month-08-Short'), Dict::S('Month-09-Short'), Dict::S('Month-10-Short'), Dict::S('Month-11-Short'), Dict::S('Month-12-Short')));
|
||||
$iFirstDayOfWeek = (int) Dict::S('Calendar-FirstDayOfWeek');
|
||||
$aDaysMin = array(Dict::S('DayOfWeek-Sunday-Min'), Dict::S('DayOfWeek-Monday-Min'), Dict::S('DayOfWeek-Tuesday-Min'), Dict::S('DayOfWeek-Wednesday-Min'),
|
||||
Dict::S('DayOfWeek-Thursday-Min'), Dict::S('DayOfWeek-Friday-Min'), Dict::S('DayOfWeek-Saturday-Min'));
|
||||
$aMonthsShort = array(Dict::S('Month-01-Short'), Dict::S('Month-02-Short'), Dict::S('Month-03-Short'), Dict::S('Month-04-Short'), Dict::S('Month-05-Short'), Dict::S('Month-06-Short'),
|
||||
Dict::S('Month-07-Short'), Dict::S('Month-08-Short'), Dict::S('Month-09-Short'), Dict::S('Month-10-Short'), Dict::S('Month-11-Short'), Dict::S('Month-12-Short'));
|
||||
$sTimeFormat = AttributeDateTime::GetFormat()->ToTimeFormat();
|
||||
$oTimeFormat = new DateTimeFormat($sTimeFormat);
|
||||
$sJSLangShort = json_encode(strtolower(substr(Dict::GetUserLanguage(), 0, 2)));
|
||||
|
||||
// Date picker options
|
||||
$aPickerOptions = array(
|
||||
'showOn' => 'button',
|
||||
'buttonImage' => '../images/calendar.png',
|
||||
'buttonImageOnly' => true,
|
||||
'dateFormat' => AttributeDate::GetFormat()->ToDatePicker(),
|
||||
'constrainInput' => false,
|
||||
'changeMonth' => true,
|
||||
'changeYear' => true,
|
||||
'dayNamesMin' => $aDaysMin,
|
||||
'monthNamesShort' => $aMonthsShort,
|
||||
'firstDay' => (int) Dict::S('Calendar-FirstDayOfWeek'),
|
||||
);
|
||||
$sJSDatePickerOptions = json_encode($aPickerOptions);
|
||||
|
||||
// Time picker additional options
|
||||
$aPickerOptions['showOn'] = '';
|
||||
$aPickerOptions['buttonImage'] = null;
|
||||
$aPickerOptions['timeFormat'] = $oTimeFormat->ToDatePicker();
|
||||
$aPickerOptions['controlType'] = 'select';
|
||||
$aPickerOptions['closeText'] = Dict::S('UI:Button:Ok');
|
||||
$sJSDateTimePickerOptions = json_encode($aPickerOptions);
|
||||
if ($sJSLangShort != '"en"')
|
||||
{
|
||||
// More options that cannot be passed via json_encode since they must be evaluated client-side
|
||||
$aMoreJSOptions = ",
|
||||
'timeText': $.timepicker.regional[$sJSLangShort].timeText,
|
||||
'hourText': $.timepicker.regional[$sJSLangShort].hourText,
|
||||
'minuteText': $.timepicker.regional[$sJSLangShort].minuteText,
|
||||
'secondText': $.timepicker.regional[$sJSLangShort].secondText,
|
||||
'currentText': $.timepicker.regional[$sJSLangShort].currentText
|
||||
}";
|
||||
$sJSDateTimePickerOptions = substr($sJSDateTimePickerOptions, 0, -1).$aMoreJSOptions;
|
||||
}
|
||||
$this->add_script(
|
||||
<<< EOF
|
||||
function PrepareWidgets()
|
||||
{
|
||||
// note: each action implemented here must be idempotent,
|
||||
// because this helper function might be called several times on a given page
|
||||
|
||||
$(".date-pick").datepicker($sJSDatePickerOptions);
|
||||
|
||||
// Hack for the date and time picker addon issue on Chrome (see #1305)
|
||||
// The workaround is to instantiate the widget on demand
|
||||
// It relies on the same markup, thus reverting to the original implementation should be straightforward
|
||||
$(".datetime-pick:not(.is-widget-ready)").each(function(){
|
||||
var oInput = this;
|
||||
$(oInput).addClass('is-widget-ready');
|
||||
$('<img class="datetime-pick-button" src="../images/calendar.png">')
|
||||
.insertAfter($(this))
|
||||
.on('click', function(){
|
||||
$(oInput)
|
||||
.datetimepicker($sJSDateTimePickerOptions)
|
||||
.datetimepicker('show')
|
||||
.datetimepicker('option', 'onClose', function(dateText,inst){
|
||||
$(oInput).datetimepicker('destroy');
|
||||
})
|
||||
.on('click keypress', function(){
|
||||
$(oInput).datetimepicker('hide');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
EOF
|
||||
);
|
||||
|
||||
$this->add_ready_script(
|
||||
<<<EOF
|
||||
@@ -146,34 +219,10 @@ try
|
||||
}
|
||||
});
|
||||
|
||||
$(".date-pick").datepicker({
|
||||
showOn: 'button',
|
||||
buttonImage: '../images/calendar.png',
|
||||
buttonImageOnly: true,
|
||||
dateFormat: 'yy-mm-dd',
|
||||
constrainInput: false,
|
||||
changeMonth: true,
|
||||
changeYear: true,
|
||||
dayNamesMin: $sJSDaysMin,
|
||||
monthNamesShort: $sJSMonthsShort,
|
||||
firstDay: $iFirstDayOfWeek
|
||||
});
|
||||
|
||||
$(".datetime-pick").datepicker({
|
||||
showOn: 'button',
|
||||
buttonImage: '../images/calendar.png',
|
||||
buttonImageOnly: true,
|
||||
dateFormat: 'yy-mm-dd 00:00:00',
|
||||
constrainInput: false,
|
||||
changeMonth: true,
|
||||
changeYear: true,
|
||||
dayNamesMin: $sJSDaysMin,
|
||||
monthNamesShort: $sJSMonthsShort,
|
||||
firstDay: $iFirstDayOfWeek
|
||||
});
|
||||
PrepareWidgets();
|
||||
|
||||
//$('.resizable').resizable(); // Make resizable everything that claims to be resizable !
|
||||
$('.caselog_header').click( function () { $(this).toggleClass('open').next('.caselog_entry_html').toggle(); });
|
||||
$('.caselog_header').click( function () { $(this).toggleClass('open').next('.caselog_entry,.caselog_entry_html').toggle(); });
|
||||
|
||||
$(document).ajaxSend(function(event, jqxhr, options) {
|
||||
jqxhr.setRequestHeader('X-Combodo-Ajax', 'true');
|
||||
@@ -338,7 +387,7 @@ EOF
|
||||
{
|
||||
$sReadOnly = Dict::S('UI:AccessRO-Users');
|
||||
$sAdminMessage = trim(MetaModel::GetConfig()->Get('access_message'));
|
||||
$sApplicationBanner .= '<div id="admin-banner">';
|
||||
$sApplicationBanner .= '<div class="app-message">';
|
||||
$sApplicationBanner .= '<img src="../images/locked.png" style="vertical-align:middle;">';
|
||||
$sApplicationBanner .= ' <b>'.$sReadOnly.'</b>';
|
||||
if (strlen($sAdminMessage) > 0)
|
||||
@@ -767,7 +816,7 @@ EOF
|
||||
$sClass = get_class($oObj);
|
||||
|
||||
$sStimulus = trim(utils::ReadPostedParam('apply_stimulus', ''));
|
||||
$sTargetState = '';
|
||||
$aExpectedAttributes = array();
|
||||
if (!empty($sStimulus))
|
||||
{
|
||||
// Compute the target state
|
||||
@@ -777,10 +826,10 @@ EOF
|
||||
{
|
||||
throw new ApplicationException(Dict::Format('UI:Error:Invalid_Stimulus_On_Object_In_State', $sStimulus, $oObj->GetName(), $oObj->GetStateLabel()));
|
||||
}
|
||||
$sTargetState = $aTransitions[$sStimulus]['target_state'];
|
||||
}
|
||||
$aExpectedAttributes = $oObj->GetTransitionAttributes($sStimulus /*, current state*/);
|
||||
}
|
||||
|
||||
$oObj->UpdateObjectFromPostedForm('' /* form prefix */, $aAttList, $sTargetState);
|
||||
$oObj->UpdateObjectFromPostedForm('' /* form prefix */, $aAttList, $aExpectedAttributes);
|
||||
|
||||
// Optional: apply a stimulus
|
||||
//
|
||||
|
||||
@@ -1,531 +0,0 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 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/>
|
||||
|
||||
/**
|
||||
* SqlBlock - display tables or charts, given an SQL query - use cautiously!
|
||||
*
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
require_once(APPROOT.'/application/webpage.class.inc.php');
|
||||
require_once(APPROOT.'/application/utils.inc.php');
|
||||
|
||||
require_once(APPROOT.'/pages/php-ofc-library/open-flash-chart.php');
|
||||
|
||||
/**
|
||||
* Helper class to design optimized dashboards, based on an SQL query
|
||||
*
|
||||
*/
|
||||
class SqlBlock
|
||||
{
|
||||
protected $m_sQuery;
|
||||
protected $m_aColumns;
|
||||
protected $m_sTitle;
|
||||
protected $m_sType;
|
||||
protected $m_aParams;
|
||||
|
||||
public function __construct($sQuery, $aColumns, $sTitle, $sType, $aParams = array())
|
||||
{
|
||||
$this->m_sQuery = $sQuery;
|
||||
$this->m_aColumns = $aColumns;
|
||||
$this->m_sTitle = $sTitle;
|
||||
$this->m_sType = $sType;
|
||||
$this->m_aParams = $aParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a SqlBlock object from an XML template
|
||||
/*
|
||||
*
|
||||
* <sqlblock>
|
||||
* <sql>SELECT date_format(start_date, '%d') AS Date, count(*) AS Count FROM ticket WHERE DATE_SUB(NOW(), INTERVAL 15 DAY) < start_date AND finalclass = 'UserIssue' GROUP BY date_format(start_date, '%d') AND $CONDITION(param1, ticket.org_id)$</sql>
|
||||
* <type>table</type>
|
||||
* <title>UserRequest:Overview-Title</title>
|
||||
* <parameter>
|
||||
* <name>param1</name>
|
||||
* <type>context</type>
|
||||
* <mapping>org_id</mapping>
|
||||
* </parameter>
|
||||
* <column>
|
||||
* <name>Date</name>
|
||||
* <label>UserRequest:Overview-Date</label>
|
||||
* <drilldown></drilldown>
|
||||
* </column>
|
||||
* <column>
|
||||
* <name>Count</name>
|
||||
* <label>UserRequest:Overview-Count</label>
|
||||
* <drilldown>SELECT UserIssue WHERE date_format(start_date, '%d') = :Date</drilldown>
|
||||
* </column>
|
||||
* </sqlblock>
|
||||
*
|
||||
* Tags
|
||||
* - sql: a (My)SQL query. Do not forget to use html entities (e.g. < for <)
|
||||
* - type: table (default), bars or pie. If bars or pie is selected only the two first columns are taken into account.
|
||||
* - title: optional title, typed in clear or given as a dictionnary entry
|
||||
* - parameter: specifies how to map the context parameters (namely org_id) to a given named parameter in the query.
|
||||
* The expression $CONDITION(<param_name>, <sql_column_name>) will be automatically replaced by:
|
||||
* either the string "1" if there is no restriction on the organisation in iTop
|
||||
* or the string "(<sql_column_name>=<value_of_org_id>)" if there is a limitation to one organizations in iTop
|
||||
* or the string "(<sql_column_name> IN (<values_of_org_id>))" if there is a limitation to a given set of organizations in iTop
|
||||
* - column: specification of a column (not displayed if omitted)
|
||||
* - column / name: name of the column in the SQL query (use aliases)
|
||||
* - column / label: label, typed in clear or given as a dictionnary entry
|
||||
* - column / drilldown: NOT IMPLEMENTED YET - OQL with parameters corresponding to column names (in the query)
|
||||
*
|
||||
* @param $sTemplate string The XML template
|
||||
* @return DisplayBlock The DisplayBlock object, or null if the template is invalid
|
||||
*/
|
||||
public static function FromTemplate($sTemplate)
|
||||
{
|
||||
$oXml = simplexml_load_string('<root>'.$sTemplate.'</root>', 'SimpleXMLElement', LIBXML_NOCDATA);
|
||||
if (false)
|
||||
{
|
||||
// Debug
|
||||
echo "<pre>\n";
|
||||
print_r($oXml);
|
||||
echo "</pre>\n";
|
||||
}
|
||||
|
||||
if (isset($oXml->title))
|
||||
{
|
||||
$sTitle = (string)$oXml->title;
|
||||
}
|
||||
if (isset($oXml->type))
|
||||
{
|
||||
$sType = (string)$oXml->type;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sType = 'table';
|
||||
}
|
||||
if (!isset($oXml->sql))
|
||||
{
|
||||
throw new Exception('Missing tag "sql" in sqlblock');
|
||||
}
|
||||
$sQuery = (string)$oXml->sql;
|
||||
|
||||
$aColumns = array();
|
||||
if (isset($oXml->column))
|
||||
{
|
||||
foreach ($oXml->column AS $oColumnData)
|
||||
{
|
||||
if (!isset($oColumnData->name))
|
||||
{
|
||||
throw new Exception("Missing tag 'name' in sqlblock/column");
|
||||
}
|
||||
$sName = (string) $oColumnData->name;
|
||||
if (strlen($sName) == 0)
|
||||
{
|
||||
throw new Exception("Empty tag 'name' in sqlblock/column");
|
||||
}
|
||||
|
||||
$aColumns[$sName] = array();
|
||||
if (isset($oColumnData->label))
|
||||
{
|
||||
$sLabel = (string)$oColumnData->label;
|
||||
if (strlen($sLabel) > 0)
|
||||
{
|
||||
$aColumns[$sName]['label'] = Dict::S($sLabel);
|
||||
}
|
||||
}
|
||||
if (isset($oColumnData->drilldown))
|
||||
{
|
||||
$sDrillDown = (string)$oColumnData->drilldown;
|
||||
if (strlen($sDrillDown) > 0)
|
||||
{
|
||||
$aColumns[$sName]['drilldown'] = $sDrillDown;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$aParams = array();
|
||||
if (isset($oXml->parameter))
|
||||
{
|
||||
foreach ($oXml->parameter AS $oParamData)
|
||||
{
|
||||
if (!isset($oParamData->name))
|
||||
{
|
||||
throw new Exception("Missing tag 'name' for parameter in sqlblock/column");
|
||||
}
|
||||
$sName = (string) $oParamData->name;
|
||||
if (strlen($sName) == 0)
|
||||
{
|
||||
throw new Exception("Empty tag 'name' for parameter in sqlblock/column");
|
||||
}
|
||||
if (!isset($oParamData->mapping))
|
||||
{
|
||||
throw new Exception("Missing tag 'mapping' for parameter in sqlblock/column");
|
||||
}
|
||||
$sMapping = (string) $oParamData->mapping;
|
||||
if (strlen($sMapping) == 0)
|
||||
{
|
||||
throw new Exception("Empty tag 'mapping' for parameter in sqlblock/column");
|
||||
}
|
||||
|
||||
if (isset($oParamData->type))
|
||||
{
|
||||
$sParamType = $oParamData->type;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sParamType = 'context';
|
||||
}
|
||||
$aParams[$sName] = array('mapping' => $sMapping, 'type' => $sParamType);
|
||||
}
|
||||
}
|
||||
|
||||
return new SqlBlock($sQuery, $aColumns, $sTitle, $sType, $aParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the defined parameters into the SQL query
|
||||
* @return string the SQL query to execute
|
||||
*/
|
||||
public function BuildQuery()
|
||||
{
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sQuery = $this->m_sQuery;
|
||||
$sQuery = str_replace('$DB_PREFIX$', MetaModel::GetConfig()->GetDBSubname(), $sQuery); // put the tables DB prefix (if any)
|
||||
foreach($this->m_aParams as $sName => $aParam)
|
||||
{
|
||||
if ($aParam['type'] == 'context')
|
||||
{
|
||||
$sSearchPattern = '/\$CONDITION\('.$sName.',([^\)]+)\)\$/';
|
||||
$value = $oAppContext->GetCurrentValue($aParam['mapping']);
|
||||
if (empty($value))
|
||||
{
|
||||
$sSQLExpr = '(1)';
|
||||
}
|
||||
else
|
||||
{
|
||||
// Special case for managing the hierarchy of organizations
|
||||
if (($aParam['mapping'] == 'org_id') && ( MetaModel::IsValidClass('Organization')))
|
||||
{
|
||||
$sHierarchicalKeyCode = MetaModel::IsHierarchicalClass('Organization');
|
||||
if ($sHierarchicalKeyCode != false)
|
||||
{
|
||||
// organizations are in hierarchy... gather all the orgs below the given one...
|
||||
$sOQL = "SELECT Organization AS node JOIN Organization AS root ON node.$sHierarchicalKeyCode BELOW root.id WHERE root.id = :value";
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), array(), array('value' => $value));
|
||||
$aOrgIds = array();
|
||||
while($oOrg = $oSet->Fetch())
|
||||
{
|
||||
$aOrgIds[]= $oOrg->GetKey();
|
||||
}
|
||||
$sSQLExpr = '($1 IN('.implode(',', $aOrgIds).'))';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sSQLExpr = '($1 = '.CMDBSource::Quote($value).')';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$sSQLExpr = '($1 = '.CMDBSource::Quote($value).')';
|
||||
}
|
||||
}
|
||||
$sQuery = preg_replace($sSearchPattern, $sSQLExpr, $sQuery);
|
||||
}
|
||||
}
|
||||
return $sQuery;
|
||||
}
|
||||
|
||||
public function RenderContent(WebPage $oPage, $aExtraParams = array())
|
||||
{
|
||||
if (empty($aExtraParams['currentId']))
|
||||
{
|
||||
$sId = 'sqlblock_'.$oPage->GetUniqueId(); // Works only if the page is not an Ajax one !
|
||||
}
|
||||
else
|
||||
{
|
||||
$sId = $aExtraParams['currentId'];
|
||||
}
|
||||
// $oPage->add($this->GetRenderContent($oPage, $aExtraParams, $sId));
|
||||
|
||||
$sQuery = $this->BuildQuery();
|
||||
$res = CMDBSource::Query($sQuery);
|
||||
$aQueryCols = CMDBSource::GetColumns($res);
|
||||
|
||||
// Prepare column definitions (check + give default values)
|
||||
//
|
||||
foreach($this->m_aColumns as $sName => $aColumnData)
|
||||
{
|
||||
if (!in_array($sName, $aQueryCols))
|
||||
{
|
||||
throw new Exception("Unknown column name '$sName' in sqlblock column");
|
||||
}
|
||||
if (!isset($aColumnData['label']))
|
||||
{
|
||||
$this->m_aColumns[$sName]['label'] = $sName;
|
||||
}
|
||||
if (isset($aColumnData['drilldown']) && !empty($aColumnData['drilldown']))
|
||||
{
|
||||
// Check if the OQL is valid
|
||||
try
|
||||
{
|
||||
$this->m_aColumns[$sName]['filter'] = DBObjectSearch::FromOQL($aColumnData['drilldown']);
|
||||
}
|
||||
catch(OQLException $e)
|
||||
{
|
||||
unset($aColumnData['drilldown']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (strlen($this->m_sTitle) > 0)
|
||||
{
|
||||
$oPage->add("<h2>".Dict::S($this->m_sTitle)."</h2>\n");
|
||||
}
|
||||
|
||||
switch ($this->m_sType)
|
||||
{
|
||||
case 'bars':
|
||||
case 'pie':
|
||||
$aColNames = array_keys($this->m_aColumns);
|
||||
$sXColName = $aColNames[0];
|
||||
$sYColName = $aColNames[1];
|
||||
$aData = array();
|
||||
$aRows = array();
|
||||
while($aRow = CMDBSource::FetchArray($res))
|
||||
{
|
||||
$aData[$aRow[$sXColName]] = $aRow[$sYColName];
|
||||
$aRows[$aRow[$sXColName]] = $aRow;
|
||||
}
|
||||
$this->RenderChart($oPage, $sId, $aData, $this->m_aColumns[$sYColName]['drilldown'], $aRows);
|
||||
break;
|
||||
|
||||
default:
|
||||
case 'table':
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sContext = $oAppContext->GetForLink();
|
||||
if (!empty($sContext))
|
||||
{
|
||||
$sContext = '&'.$sContext;
|
||||
}
|
||||
$aDisplayConfig = array();
|
||||
foreach($this->m_aColumns as $sName => $aColumnData)
|
||||
{
|
||||
$aDisplayConfig[$sName] = array('label' => $aColumnData['label'], 'description' => '');
|
||||
}
|
||||
|
||||
$aDisplayData = array();
|
||||
while($aRow = CMDBSource::FetchArray($res))
|
||||
{
|
||||
$aSQLColNames = array_keys($aRow);
|
||||
$aDisplayRow = array();
|
||||
foreach($this->m_aColumns as $sName => $aColumnData)
|
||||
{
|
||||
if (isset($aColumnData['filter']))
|
||||
{
|
||||
$sFilter = $aColumnData['drilldown'];
|
||||
$sClass = $aColumnData['filter']->GetClass();
|
||||
$sFilter = str_replace('SELECT '.$sClass, '', $sFilter);
|
||||
foreach($aSQLColNames as $sColName)
|
||||
{
|
||||
$sFilter = str_replace(':'.$sColName, "'".addslashes( $aRow[$sColName] )."'", $sFilter);
|
||||
}
|
||||
$sURL = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search_oql&search_form=0&oql_class='.$sClass.'&oql_clause='.urlencode($sFilter).'&format=html'.$sContext;
|
||||
$aDisplayRow[$sName] = '<a href="'.$sURL.'">'.$aRow[$sName]."</a>";
|
||||
}
|
||||
else
|
||||
{
|
||||
$aDisplayRow[$sName] = $aRow[$sName];
|
||||
}
|
||||
}
|
||||
$aDisplayData[] = $aDisplayRow;
|
||||
}
|
||||
$oPage->table($aDisplayConfig, $aDisplayData);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function GetRenderContent(WebPage $oPage, $aExtraParams = array(), $sId)
|
||||
{
|
||||
$sHtml = '';
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
protected function RenderChart($oPage, $sId, $aValues, $sDrillDown = '', $aRows = array())
|
||||
{
|
||||
// 1- Compute Open Flash Chart data
|
||||
//
|
||||
$aValueKeys = array();
|
||||
$index = 0;
|
||||
if ((count($aValues) > 0) && ($sDrillDown != ''))
|
||||
{
|
||||
$oFilter = DBObjectSearch::FromOQL($sDrillDown);
|
||||
$sClass = $oFilter->GetClass();
|
||||
$sOQLClause = str_replace('SELECT '.$sClass, '', $sDrillDown);
|
||||
$aSQLColNames = array_keys(current($aRows)); // Read the list of columns from the current (i.e. first) element of the array
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sURL = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search_oql&search_form=0&oql_class='.$sClass.'&format=html&'.$oAppContext->GetForLink().'&oql_clause=';
|
||||
}
|
||||
$aURLs = array();
|
||||
foreach($aValues as $key => $value)
|
||||
{
|
||||
// Make sure that values are integers (so that max() will work....)
|
||||
// and build an array of STRING with the keys (numeric keys are transformed into string by PHP :-(
|
||||
$aValues[$key] = (int)$value;
|
||||
$aValueKeys[] = (string)$key;
|
||||
|
||||
// Build the custom query for the 'drill down' on each element
|
||||
if ($sDrillDown != '')
|
||||
{
|
||||
$sFilter = $sOQLClause;
|
||||
foreach($aSQLColNames as $sColName)
|
||||
{
|
||||
$sFilter = str_replace(':'.$sColName, "'".addslashes( $aRows[$key][$sColName] )."'", $sFilter);
|
||||
$aURLs[$index] = $sURL.urlencode($sFilter);
|
||||
}
|
||||
}
|
||||
$index++;
|
||||
}
|
||||
|
||||
$oChart = new open_flash_chart();
|
||||
|
||||
if ($this->m_sType == 'bars')
|
||||
{
|
||||
$oChartElement = new bar_glass();
|
||||
|
||||
if (count($aValues) > 0)
|
||||
{
|
||||
$maxValue = max($aValues);
|
||||
}
|
||||
else
|
||||
{
|
||||
$maxValue = 1;
|
||||
}
|
||||
$oYAxis = new y_axis();
|
||||
$aMagicValues = array(1,2,5,10);
|
||||
$iMultiplier = 1;
|
||||
$index = 0;
|
||||
$iTop = $aMagicValues[$index % count($aMagicValues)]*$iMultiplier;
|
||||
while($maxValue > $iTop)
|
||||
{
|
||||
$index++;
|
||||
$iTop = $aMagicValues[$index % count($aMagicValues)]*$iMultiplier;
|
||||
if (($index % count($aMagicValues)) == 0)
|
||||
{
|
||||
$iMultiplier = $iMultiplier * 10;
|
||||
}
|
||||
}
|
||||
//echo "oYAxis->set_range(0, $iTop, $iMultiplier);\n";
|
||||
$oYAxis->set_range(0, $iTop, $iMultiplier);
|
||||
$oChart->set_y_axis( $oYAxis );
|
||||
$aBarValues = array();
|
||||
foreach($aValues as $iValue)
|
||||
{
|
||||
$oBarValue = new bar_value($iValue);
|
||||
$oBarValue->on_click("ofc_drilldown_{$sId}");
|
||||
$aBarValues[] = $oBarValue;
|
||||
}
|
||||
$oChartElement->set_values($aBarValues);
|
||||
//$oChartElement->set_values(array_values($aValues));
|
||||
$oXAxis = new x_axis();
|
||||
$oXLabels = new x_axis_labels();
|
||||
// set them vertical
|
||||
$oXLabels->set_vertical();
|
||||
// set the label text
|
||||
$oXLabels->set_labels($aValueKeys);
|
||||
// Add the X Axis Labels to the X Axis
|
||||
$oXAxis->set_labels( $oXLabels );
|
||||
$oChart->set_x_axis( $oXAxis );
|
||||
}
|
||||
else
|
||||
{
|
||||
$oChartElement = new pie();
|
||||
$oChartElement->set_start_angle( 35 );
|
||||
$oChartElement->set_animate( true );
|
||||
$oChartElement->set_tooltip( '#label# - #val# (#percent#)' );
|
||||
$oChartElement->set_colours( array('#FF8A00', '#909980', '#2C2B33', '#CCC08D', '#596664') );
|
||||
|
||||
$aData = array();
|
||||
foreach($aValues as $sValue => $iValue)
|
||||
{
|
||||
$oPieValue = new pie_value($iValue, $sValue); //@@ BUG: not passed via ajax !!!
|
||||
$oPieValue->on_click("ofc_drilldown_{$sId}");
|
||||
$aData[] = $oPieValue;
|
||||
}
|
||||
|
||||
$oChartElement->set_values( $aData );
|
||||
$oChart->x_axis = null;
|
||||
}
|
||||
|
||||
// Title given in HTML
|
||||
//$oTitle = new title($this->m_sTitle);
|
||||
//$oChart->set_title($oTitle);
|
||||
$oChart->set_bg_colour('#FFFFFF');
|
||||
$oChart->add_element( $oChartElement );
|
||||
|
||||
$sData = $oChart->toPrettyString();
|
||||
$sData = json_encode($sData);
|
||||
|
||||
// 2- Declare the Javascript function that will render the chart data\
|
||||
//
|
||||
$oPage->add_script(
|
||||
<<< EOF
|
||||
function ofc_get_data_{$sId}()
|
||||
{
|
||||
return $sData;
|
||||
}
|
||||
EOF
|
||||
);
|
||||
|
||||
if (count($aURLs) > 0)
|
||||
{
|
||||
$sURLList = '';
|
||||
foreach($aURLs as $index => $sURL)
|
||||
{
|
||||
$sURLList .= "\taURLs[$index] = '".addslashes($sURL)."';\n";
|
||||
}
|
||||
|
||||
$oPage->add_script(
|
||||
<<< EOF
|
||||
function ofc_drilldown_{$sId}(index)
|
||||
{
|
||||
var aURLs = new Array();
|
||||
{$sURLList}
|
||||
var sURL = aURLs[index];
|
||||
|
||||
window.location.href = sURL; // Navigate !
|
||||
}
|
||||
EOF
|
||||
);
|
||||
}
|
||||
|
||||
// 3- Insert the Open Flash chart
|
||||
//
|
||||
$oPage->add("<div id=\"$sId\"><div>\n");
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
swfobject.embedSWF( "../images/open-flash-chart.swf",
|
||||
"{$sId}",
|
||||
"100%", "300","9.0.0",
|
||||
"expressInstall.swf",
|
||||
{"get-data":"ofc_get_data_{$sId}", "id":"{$sId}"},
|
||||
{'wmode': 'transparent'}
|
||||
);
|
||||
EOF
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -1,49 +1,62 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2016 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/>
|
||||
|
||||
|
||||
/**
|
||||
* File to include to initialize the datamodel in memory
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
require_once(APPROOT.'/core/cmdbobject.class.inc.php');
|
||||
require_once(APPROOT.'/application/utils.inc.php');
|
||||
require_once(APPROOT.'/core/contexttag.class.inc.php');
|
||||
session_name('itop-'.md5(APPROOT));
|
||||
session_start();
|
||||
$sSwitchEnv = utils::ReadParam('switch_env', null);
|
||||
if (($sSwitchEnv != null) && (file_exists(APPCONF.$sSwitchEnv.'/'.ITOP_CONFIG_FILE)))
|
||||
{
|
||||
$_SESSION['itop_env'] = $sSwitchEnv;
|
||||
$sEnv = $sSwitchEnv;
|
||||
// TODO: reset the credentials as well ??
|
||||
}
|
||||
else if (isset($_SESSION['itop_env']))
|
||||
{
|
||||
$sEnv = $_SESSION['itop_env'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$sEnv = ITOP_DEFAULT_ENV;
|
||||
$_SESSION['itop_env'] = ITOP_DEFAULT_ENV;
|
||||
}
|
||||
$sConfigFile = APPCONF.$sEnv.'/'.ITOP_CONFIG_FILE;
|
||||
MetaModel::Startup($sConfigFile, false /* $bModelOnly */, true /* $bAllowCache */, false /* $bTraceSourceFiles */, $sEnv);
|
||||
<?php
|
||||
// Copyright (C) 2010-2016 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/>
|
||||
|
||||
|
||||
/**
|
||||
* File to include to initialize the datamodel in memory
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
require_once(APPROOT.'/core/cmdbobject.class.inc.php');
|
||||
require_once(APPROOT.'/application/utils.inc.php');
|
||||
require_once(APPROOT.'/core/contexttag.class.inc.php');
|
||||
session_name('itop-'.md5(APPROOT));
|
||||
session_start();
|
||||
$sSwitchEnv = utils::ReadParam('switch_env', null);
|
||||
$bAllowCache = true;
|
||||
if (($sSwitchEnv != null) && (file_exists(APPCONF.$sSwitchEnv.'/'.ITOP_CONFIG_FILE)) && isset($_SESSION['itop_env']) && ($_SESSION['itop_env'] !== $sSwitchEnv))
|
||||
{
|
||||
$_SESSION['itop_env'] = $sSwitchEnv;
|
||||
$sEnv = $sSwitchEnv;
|
||||
$bAllowCache = false;
|
||||
// Reset the opcache since otherwise the PHP "model" files may still be cached !!
|
||||
if (function_exists('opcache_reset'))
|
||||
{
|
||||
// Zend opcode cache
|
||||
opcache_reset();
|
||||
}
|
||||
if (function_exists('apc_clear_cache'))
|
||||
{
|
||||
// APC(u) cache
|
||||
apc_clear_cache();
|
||||
}
|
||||
// TODO: reset the credentials as well ??
|
||||
}
|
||||
else if (isset($_SESSION['itop_env']))
|
||||
{
|
||||
$sEnv = $_SESSION['itop_env'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$sEnv = ITOP_DEFAULT_ENV;
|
||||
$_SESSION['itop_env'] = ITOP_DEFAULT_ENV;
|
||||
}
|
||||
$sConfigFile = APPCONF.$sEnv.'/'.ITOP_CONFIG_FILE;
|
||||
MetaModel::Startup($sConfigFile, false /* $bModelOnly */, $bAllowCache, false /* $bTraceSourceFiles */, $sEnv);
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* Class DisplayTemplate
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -353,11 +353,15 @@ class ObjectDetailsTemplate extends DisplayTemplate
|
||||
if ($iFlags & OPT_ATT_SLAVE)
|
||||
{
|
||||
$iSynchroFlags = $this->m_oObj->GetSynchroReplicaFlags($sAttCode, $aReasons);
|
||||
$sSynchroIcon = " <img id=\"synchro_$sInputId\" src=\"../images/transp-lock.png\" style=\"vertical-align:middle\"/>";
|
||||
$sSynchroIcon = " <img id=\"synchro_$iInputId\" src=\"../images/transp-lock.png\" style=\"vertical-align:middle\"/>";
|
||||
$sTip = '';
|
||||
foreach($aReasons as $aRow)
|
||||
{
|
||||
$sTip .= "<p>Synchronized with {$aRow['name']} - {$aRow['description']}</p>";
|
||||
$sDescription = htmlentities($aRow['description'], ENT_QUOTES, 'UTF-8');
|
||||
$sDescription = str_replace(array("\r\n", "\n"), "<br/>", $sDescription);
|
||||
$sTip .= "<div class='synchro-source'>";
|
||||
$sTip .= "<div class='synchro-source-title'>Synchronized with {$aRow['name']}</div>";
|
||||
$sTip .= "<div class='synchro-source-description'>$sDescription</div>";
|
||||
}
|
||||
$oPage->add_ready_script("$('#synchro_$iInputId').qtip( { content: '$sTip', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );");
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -54,7 +54,7 @@
|
||||
* | | +--------+ +-----+ | |
|
||||
* | +--------------------------------------------+ |
|
||||
* +------------------------------------------------+
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -101,7 +101,7 @@ class UIExtKeyWidget
|
||||
* @param Hash $aArgs Extra context arguments
|
||||
* @return string The HTML fragment to be inserted into the page
|
||||
*/
|
||||
public function Display(WebPage $oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName, $sFormPrefix = '', $aArgs = array(), $bSearchMode = null, $sDisplayStyle = 'select', $bSearchMultiple = true)
|
||||
public function Display(WebPage $oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, DBObjectset $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName, $sFormPrefix = '', $aArgs = array(), $bSearchMode = null, $sDisplayStyle = 'select', $bSearchMultiple = true)
|
||||
{
|
||||
if (!is_null($bSearchMode))
|
||||
{
|
||||
@@ -111,12 +111,12 @@ class UIExtKeyWidget
|
||||
$oPage->add_linked_script('../js/extkeywidget.js');
|
||||
$oPage->add_linked_script('../js/forms-json-utils.js');
|
||||
|
||||
$bCreate = (!$this->bSearchMode) && (!MetaModel::IsAbstract($this->sTargetClass)) && (UserRights::IsActionAllowed($this->sTargetClass, UR_ACTION_BULK_MODIFY) && $bAllowTargetCreation);
|
||||
$bCreate = (!$this->bSearchMode) && (UserRights::IsActionAllowed($this->sTargetClass, UR_ACTION_BULK_MODIFY) && $bAllowTargetCreation);
|
||||
$bExtensions = true;
|
||||
$sMessage = Dict::S('UI:Message:EmptyList:UseSearchForm');
|
||||
$sAttrFieldPrefix = ($this->bSearchMode) ? '' : 'attr_';
|
||||
|
||||
$sHTMLValue = "<span style=\"white-space:nowrap\">"; // no wrap
|
||||
$sHTMLValue = "<div class=\"field_input_zone field_input_extkey\">";
|
||||
$sFilter = addslashes($oAllowedValues->GetFilter()->ToOQL());
|
||||
if($this->bSearchMode)
|
||||
{
|
||||
@@ -141,16 +141,22 @@ class UIExtKeyWidget
|
||||
{
|
||||
throw new Exception('Implementation: null value for allowed values definition');
|
||||
}
|
||||
elseif ($oAllowedValues->Count() < $iMaxComboLength)
|
||||
$oAllowedValues->SetShowObsoleteData(utils::ShowObsoleteData());
|
||||
// Don't automatically launch the search if the table is huge
|
||||
$bDoSearch = !utils::IsHighCardinality($this->sTargetClass);
|
||||
$sJSDoSearch = $bDoSearch ? 'true' : 'false';
|
||||
|
||||
// We just need to compare the number of entries with MaxComboLength, so no need to get the real count.
|
||||
if ($oAllowedValues->Count($iMaxComboLength * 2) < $iMaxComboLength)
|
||||
{
|
||||
// Discrete list of values, use a SELECT or RADIO buttons depending on the config
|
||||
// Discrete list of values, use a SELECT or RADIO buttons depending on the config
|
||||
switch($sDisplayStyle)
|
||||
{
|
||||
case 'radio':
|
||||
case 'radio_horizontal':
|
||||
case 'radio_vertical':
|
||||
$sValidationField = "<span id=\"v_{$this->iId}\"></span><span id=\"fstatus_{$this->iId}\"></span>";
|
||||
$sHTMLValue = '';
|
||||
$sValidationField = null;
|
||||
|
||||
$bVertical = ($sDisplayStyle != 'radio_horizontal');
|
||||
$bExtensions = false;
|
||||
$oAllowedValues->Rewind();
|
||||
@@ -159,7 +165,7 @@ class UIExtKeyWidget
|
||||
{
|
||||
$aAllowedValues[$oObj->GetKey()] = $oObj->GetName();
|
||||
}
|
||||
$sHTMLValue = $oPage->GetRadioButtons($aAllowedValues, $value, $this->iId, "{$sAttrFieldPrefix}{$sFieldName}", $bMandatory, $bVertical, $sValidationField);
|
||||
$sHTMLValue .= $oPage->GetRadioButtons($aAllowedValues, $value, $this->iId, "{$sAttrFieldPrefix}{$sFieldName}", false /* $bMandatory will be placed manually */, $bVertical, $sValidationField);
|
||||
$aEventsList[] ='change';
|
||||
break;
|
||||
|
||||
@@ -169,25 +175,27 @@ class UIExtKeyWidget
|
||||
$sSelectMode = 'true';
|
||||
|
||||
$sHelpText = ''; //$this->oAttDef->GetHelpOnEdition();
|
||||
|
||||
$sHTMLValue .= "<div class=\"field_select_wrapper\">\n";
|
||||
|
||||
if ($this->bSearchMode)
|
||||
{
|
||||
if ($bSearchMultiple)
|
||||
{
|
||||
$sHTMLValue = "<select class=\"multiselect\" multiple title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}[]\" id=\"$this->iId\">\n";
|
||||
$sHTMLValue .= "<select class=\"multiselect\" multiple title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}[]\" id=\"$this->iId\">\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sHTMLValue = "<select title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" id=\"$this->iId\">\n";
|
||||
$sHTMLValue .= "<select title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" id=\"$this->iId\">\n";
|
||||
$sDisplayValue = isset($aArgs['sDefaultValue']) ? $aArgs['sDefaultValue'] : Dict::S('UI:SearchValue:Any');
|
||||
$sHTMLValue .= "<option value=\"\">$sDisplayValue</option>\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$sHTMLValue = "<select title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" id=\"$this->iId\">\n";
|
||||
$sHTMLValue .= "<select title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" id=\"$this->iId\">\n";
|
||||
$sHTMLValue .= "<option value=\"\">".Dict::S('UI:SelectOne')."</option>\n";
|
||||
}
|
||||
|
||||
$oAllowedValues->Rewind();
|
||||
while($oObj = $oAllowedValues->Fetch())
|
||||
{
|
||||
@@ -206,6 +214,8 @@ class UIExtKeyWidget
|
||||
$sHTMLValue .= "<option value=\"$key\"$sSelected>$display_value</option>\n";
|
||||
}
|
||||
$sHTMLValue .= "</select>\n";
|
||||
$sHTMLValue .= "</div>\n";
|
||||
|
||||
if (($this->bSearchMode) && $bSearchMultiple)
|
||||
{
|
||||
$aOptions = array(
|
||||
@@ -221,7 +231,7 @@ class UIExtKeyWidget
|
||||
}
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', true, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode);
|
||||
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', true, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode, $sJSDoSearch);
|
||||
oACWidget_{$this->iId}.emptyHtml = "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>$sMessage</p></div>";
|
||||
$('#$this->iId').bind('update', function() { oACWidget_{$this->iId}.Update(); } );
|
||||
$('#$this->iId').bind('change', function() { $(this).trigger('extkeychange') } );
|
||||
@@ -256,8 +266,8 @@ EOF
|
||||
$iFieldSize = isset($aArgs['iFieldSize']) ? $aArgs['iFieldSize'] : 20; //@@@ $this->oAttDef->GetMaxSize();
|
||||
|
||||
// the input for the auto-complete
|
||||
$sHTMLValue = "<input count=\"".$oAllowedValues->Count()."\" type=\"text\" id=\"label_$this->iId\" size=\"$iFieldSize\" value=\"$sDisplayValue\"/> ";
|
||||
$sHTMLValue .= "<img id=\"mini_search_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_search.gif?itopversion=".ITOP_VERSION."\" onClick=\"oACWidget_{$this->iId}.Search();\"/>";
|
||||
$sHTMLValue .= "<input class=\"field_autocomplete\" type=\"text\" id=\"label_$this->iId\" value=\"$sDisplayValue\"/>";
|
||||
$sHTMLValue .= "<span class=\"field_input_btn\"><img id=\"mini_search_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_search.gif?itopversion=".ITOP_VERSION."\" onClick=\"oACWidget_{$this->iId}.Search();\"/></span>";
|
||||
|
||||
// another hidden input to store & pass the object's Id
|
||||
$sHTMLValue .= "<input type=\"hidden\" id=\"$this->iId\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" value=\"".htmlentities($value, ENT_QUOTES, 'UTF-8')."\" />\n";
|
||||
@@ -266,7 +276,7 @@ EOF
|
||||
// Scripts to start the autocomplete and bind some events to it
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', false, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode);
|
||||
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', false, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode, $sJSDoSearch);
|
||||
oACWidget_{$this->iId}.emptyHtml = "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>$sMessage</p></div>";
|
||||
$('#label_$this->iId').autocomplete(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', { scroll:true, minChars:{$iMinChars}, autoFill:false, matchContains:true, mustMatch: true, keyHolder:'#{$this->iId}', extraParams:{operation:'ac_extkey', sTargetClass:'{$this->sTargetClass}',sFilter:'$sFilter',bSearchMode:$JSSearchMode, json: function() { return $sWizHelperJSON; } }});
|
||||
$('#label_$this->iId').keyup(function() { if ($(this).val() == '') { $('#$this->iId').val(''); } } ); // Useful for search forms: empty value in the "label", means no value, immediatly !
|
||||
@@ -281,7 +291,7 @@ EOF
|
||||
}
|
||||
if ($bExtensions && MetaModel::IsHierarchicalClass($this->sTargetClass) !== false)
|
||||
{
|
||||
$sHTMLValue .= "<img id=\"mini_tree_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_tree.gif?itopversion=".ITOP_VERSION."\" onClick=\"oACWidget_{$this->iId}.HKDisplay();\"/> ";
|
||||
$sHTMLValue .= "<span class=\"field_input_btn\"><img id=\"mini_tree_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_tree.gif?itopversion=".ITOP_VERSION."\" onClick=\"oACWidget_{$this->iId}.HKDisplay();\"/></span>";
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
if ($('#ac_tree_{$this->iId}').length == 0)
|
||||
@@ -293,7 +303,9 @@ EOF
|
||||
}
|
||||
if ($bCreate && $bExtensions)
|
||||
{
|
||||
$sHTMLValue .= "<img id=\"mini_add_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_add.gif?itopversion=".ITOP_VERSION."\" onClick=\"oACWidget_{$this->iId}.CreateObject();\"/> ";
|
||||
$sCallbackName = (MetaModel::IsAbstract($this->sTargetClass)) ? 'SelectObjectClass' : 'CreateObject';
|
||||
|
||||
$sHTMLValue .= "<span class=\"field_input_btn\"><img id=\"mini_add_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_add.gif?itopversion=".ITOP_VERSION."\" onClick=\"oACWidget_{$this->iId}.{$sCallbackName}();\"/></span>";
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
if ($('#ajax_{$this->iId}').length == 0)
|
||||
@@ -303,11 +315,14 @@ EOF
|
||||
EOF
|
||||
);
|
||||
}
|
||||
if (($sDisplayStyle == 'select') || ($sDisplayStyle == 'list'))
|
||||
{
|
||||
$sHTMLValue .= "<span id=\"v_{$this->iId}\"></span><span id=\"fstatus_{$this->iId}\"></span>";
|
||||
}
|
||||
$sHTMLValue .= "</span>"; // end of no wrap
|
||||
$sHTMLValue .= "</div>";
|
||||
|
||||
// Note: This test is no longer necessary as we changed the markup to extract validation decoration in the standard .field_input_xxx container
|
||||
//if (($sDisplayStyle == 'select') || ($sDisplayStyle == 'list'))
|
||||
//{
|
||||
$sHTMLValue .= "<span class=\"form_validation\" id=\"v_{$this->iId}\"></span><span class=\"field_status\" id=\"fstatus_{$this->iId}\"></span>";
|
||||
//}
|
||||
|
||||
return $sHTMLValue;
|
||||
}
|
||||
|
||||
@@ -328,7 +343,7 @@ EOF
|
||||
$aParams = array();
|
||||
$oFilter = new DBObjectSearch($this->sTargetClass);
|
||||
}
|
||||
$bOpen = MetaModel::GetConfig()->Get('legacy_search_drawer_open');
|
||||
$bOpen = MetaModel::GetConfig()->Get('legacy_search_drawer_open') || utils::IsHighCardinality($this->sTargetClass);
|
||||
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
|
||||
$oBlock = new DisplayBlock($oFilter, 'search', false, $aParams);
|
||||
$sHTML .= $oBlock->GetDisplay($oPage, $this->iId, array('open' => $bOpen, 'currentId' => $this->iId));
|
||||
@@ -372,7 +387,11 @@ EOF
|
||||
$oFilter->ChangeClass($sRemoteClass);
|
||||
}
|
||||
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
|
||||
$oBlock = new DisplayBlock($oFilter, 'list', false, array('query_params' => array('this' => $oObj)));
|
||||
|
||||
// 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->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
|
||||
}
|
||||
|
||||
@@ -389,15 +408,40 @@ EOF
|
||||
{
|
||||
throw new Exception('Implementation: null value for allowed values definition');
|
||||
}
|
||||
|
||||
// Current extkey value, so we can display event if it is not available anymore (eg. archived).
|
||||
$iCurrentExtKeyId = (is_null($oObj) || $this->sAttCode === '') ? 0 : $oObj->Get($this->sAttCode);
|
||||
|
||||
$oValuesSet = new ValueSetObjects($sFilter, 'friendlyname'); // Bypass GetName() to avoid the encoding by htmlentities
|
||||
$iMax = 150;
|
||||
$oValuesSet->SetLimit($iMax);
|
||||
$oValuesSet->SetSort(false);
|
||||
$oValuesSet->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
|
||||
$aValues = $oValuesSet->GetValues(array('this' => $oObj), $sContains);
|
||||
foreach($aValues as $sKey => $sFriendlyName)
|
||||
|
||||
$aValuesEquals = $oValuesSet->GetValues(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'equals_start_with');
|
||||
//asort($aValuesEquals);
|
||||
foreach($aValuesEquals as $sKey => $sFriendlyName)
|
||||
{
|
||||
$oP->add(trim($sFriendlyName)."\t".$sKey."\n");
|
||||
$iMax--;
|
||||
}
|
||||
if ($iMax <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$oValuesSet->SetLimit($iMax);
|
||||
$aValuesContains = $oValuesSet->GetValues(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'contains');
|
||||
asort($aValuesContains);
|
||||
foreach($aValuesContains as $sKey => $sFriendlyName)
|
||||
{
|
||||
if (!isset($aValuesEquals[$sKey]))
|
||||
{
|
||||
$oP->add(trim($sFriendlyName)."\t".$sKey."\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the display name of the selected object, to fill back the autocomplete
|
||||
*/
|
||||
@@ -416,7 +460,49 @@ EOF
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the form to select a leaf class from the $this->sTargetClass (that should be abstract)
|
||||
* Note: Inspired from UILinksWidgetDirect::GetObjectCreationDialog()
|
||||
*
|
||||
* @param WebPage $oPage
|
||||
*/
|
||||
public function GetClassSelectionForm(WebPage $oPage)
|
||||
{
|
||||
// For security reasons: check that the "proposed" class is actually a subclass of the linked class
|
||||
// and that the current user is allowed to create objects of this class
|
||||
$aSubClasses = MetaModel::EnumChildClasses($this->sTargetClass);
|
||||
$aPossibleClasses = array();
|
||||
foreach($aSubClasses as $sCandidateClass)
|
||||
{
|
||||
if (!MetaModel::IsAbstract($sCandidateClass) && (UserRights::IsActionAllowed($sCandidateClass, UR_ACTION_MODIFY) == UR_ALLOWED_YES))
|
||||
{
|
||||
$aPossibleClasses[$sCandidateClass] = MetaModel::GetName($sCandidateClass);
|
||||
}
|
||||
}
|
||||
|
||||
$sDialogTitle = '';
|
||||
$oPage->add('<div id="ac_create_'.$this->iId.'"><div class="wizContainer" style="vertical-align:top;"><div id="dcr_'.$this->iId.'">');
|
||||
$oPage->add('<form>');
|
||||
|
||||
$sClassLabel = MetaModel::GetName($this->sTargetClass);
|
||||
$oPage->add('<p>'.Dict::Format('UI:SelectTheTypeOf_Class_ToCreate', $sClassLabel));
|
||||
$oPage->add('<nobr><select name="class">');
|
||||
asort($aPossibleClasses);
|
||||
foreach($aPossibleClasses as $sClassName => $sClassLabel)
|
||||
{
|
||||
$oPage->add("<option value=\"$sClassName\">$sClassLabel</option>");
|
||||
}
|
||||
$oPage->add('</select>');
|
||||
$oPage->add(' <button type="submit" class="action" style="margin-top:15px;"><span>' . Dict::S('UI:Button:Ok') . '</span></button></nobr></p>');
|
||||
|
||||
$oPage->add('</form>');
|
||||
$oPage->add('</div></div></div>');
|
||||
$oPage->add_ready_script("\$('#ac_create_$this->iId').dialog({ width: 'auto', height: 'auto', maxHeight: $(window).height() - 50, autoOpen: false, modal: true, title: '$sDialogTitle'});\n");
|
||||
$oPage->add_ready_script("$('#dcr_{$this->iId} form').removeAttr('onsubmit');");
|
||||
$oPage->add_ready_script("$('#dcr_{$this->iId} form').bind('submit.uilinksWizard', oACWidget_{$this->iId}.DoSelectObjectClass);");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the form to create a new object of the 'target' class
|
||||
*/
|
||||
@@ -484,9 +570,9 @@ EOF
|
||||
}
|
||||
try
|
||||
{
|
||||
$oFilter = DBObjectSearch::FromOQL($sFilter);
|
||||
$oFilter = DBObjectSearch::FromOQL($sFilter);
|
||||
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
|
||||
$oSet = new DBObjectSet($oFilter, array(), array('this' => $oObj));
|
||||
$oSet = new DBObjectSet($oFilter, array(), array('this' => $oObj, 'current_extkey_id' => $currValue));
|
||||
}
|
||||
catch(MissingQueryArgument $e)
|
||||
{
|
||||
@@ -497,6 +583,7 @@ EOF
|
||||
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
|
||||
$oSet = new DBObjectSet($oFilter);
|
||||
}
|
||||
$oSet->SetShowObsoleteData(utils::ShowObsoleteData());
|
||||
|
||||
$sHKAttCode = MetaModel::IsHierarchicalClass($this->sTargetClass);
|
||||
$this->DumpTree($oPage, $oSet, $sHKAttCode, $currValue);
|
||||
@@ -600,4 +687,3 @@ EOF
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,9 +20,8 @@
|
||||
* Class UIHTMLEditorWidget
|
||||
* UI wdiget for displaying and editing one-way encrypted passwords
|
||||
*
|
||||
* @author Phil Eddies
|
||||
* @author Romain Quetiez
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -65,7 +64,7 @@ class UIHTMLEditorWidget
|
||||
$sHelpText = $this->m_sHelpText;
|
||||
$sValidationField = $this->m_sValidationField;
|
||||
|
||||
$sHtmlValue = "<table><tr><td><textarea class=\"htmlEditor\" title=\"$sHelpText\" name=\"attr_{$this->m_sFieldPrefix}{$sCode}\" rows=\"10\" cols=\"10\" id=\"$iId\">$sValue</textarea></td><td>$sValidationField</td></tr></table>";
|
||||
$sHtmlValue = "<div class=\"field_input_zone field_input_html\"><textarea class=\"htmlEditor\" title=\"$sHelpText\" name=\"attr_{$this->m_sFieldPrefix}{$sCode}\" rows=\"10\" cols=\"10\" id=\"$iId\">$sValue</textarea></div>$sValidationField";
|
||||
|
||||
// Replace the text area with CKEditor
|
||||
// To change the default settings of the editor,
|
||||
@@ -99,9 +98,26 @@ class UIHTMLEditorWidget
|
||||
|
||||
// Could also be bound to 'instanceReady.ckeditor'
|
||||
$oPage->add_ready_script("$('#$iId').bind('validate', function(evt, sFormId) { return ValidateCKEditField('$iId', '', {$this->m_sMandatory}, sFormId, '') } );\n");
|
||||
$oPage->add_ready_script("$('#$iId').bind('update', function() { BlockField('cke_$iId', $('#$iId').attr('disabled')); $(this).data('ckeditorInstance').setReadOnly($(this).prop('disabled')); } );\n");
|
||||
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
$('#$iId').bind('update', function(evt){
|
||||
BlockField('cke_$iId', $('#$iId').attr('disabled'));
|
||||
//Delayed execution - ckeditor must be properly initialized before setting readonly
|
||||
var retryCount = 0;
|
||||
var oMe = $('#$iId');
|
||||
var delayedSetReadOnly = function () {
|
||||
if (oMe.data('ckeditorInstance').editable() == undefined && retryCount++ < 10) {
|
||||
setTimeout(delayedSetReadOnly, retryCount * 100); //Wait a while longer each iteration
|
||||
}
|
||||
else
|
||||
{
|
||||
oMe.data('ckeditorInstance').setReadOnly(oMe.prop('disabled'));
|
||||
}
|
||||
};
|
||||
setTimeout(delayedSetReadOnly, 50);
|
||||
});
|
||||
EOF
|
||||
);
|
||||
return $sHtmlValue;
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -19,7 +19,7 @@
|
||||
/**
|
||||
* Class UILinksWidgetDirect
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -30,7 +30,14 @@ class UILinksWidgetDirect
|
||||
protected $sInputid;
|
||||
protected $sNameSuffix;
|
||||
protected $sLinkedClass;
|
||||
|
||||
|
||||
/**
|
||||
* UILinksWidgetDirect constructor.
|
||||
* @param string $sClass
|
||||
* @param string $sAttCode
|
||||
* @param string $sInputId
|
||||
* @param string $sNameSuffix
|
||||
*/
|
||||
public function __construct($sClass, $sAttCode, $sInputId, $sNameSuffix = '')
|
||||
{
|
||||
$this->sClass = $sClass;
|
||||
@@ -71,8 +78,15 @@ class UILinksWidgetDirect
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function Display(WebPage $oPage, DBObjectSet $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj)
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param DBObjectSet|ormLinkSet $oValue
|
||||
* @param array $aArgs
|
||||
* @param string $sFormPrefix
|
||||
* @param DBObject $oCurrentObj
|
||||
*/
|
||||
public function Display(WebPage $oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj)
|
||||
{
|
||||
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
|
||||
switch($oLinksetDef->GetEditMode())
|
||||
@@ -115,8 +129,16 @@ class UILinksWidgetDirect
|
||||
$this->DisplayAsBlock($oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, true /* bDisplayMenu*/);
|
||||
}
|
||||
}
|
||||
|
||||
protected function DisplayAsBlock(WebPage $oPage, DBObjectSet $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, $bDisplayMenu)
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param DBObjectSet $oValue
|
||||
* @param array $aArgs
|
||||
* @param string $sFormPrefix
|
||||
* @param DBObject $oCurrentObj
|
||||
* @param bool $bDisplayMenu
|
||||
*/
|
||||
protected function DisplayAsBlock(WebPage $oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, $bDisplayMenu)
|
||||
{
|
||||
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
|
||||
$sTargetClass = $oLinksetDef->GetLinkedClass();
|
||||
@@ -143,6 +165,7 @@ class UILinksWidgetDirect
|
||||
'target_attr' => $oLinksetDef->GetExtKeyToMe(),
|
||||
'object_id' => $oCurrentObj ? $oCurrentObj->GetKey() : null,
|
||||
'menu' => $bDisplayMenu,
|
||||
'menu_actions_target' => '_blank',
|
||||
'default' => $aDefaults,
|
||||
'table_id' => $this->sClass.'_'.$this->sAttCode,
|
||||
);
|
||||
@@ -151,45 +174,11 @@ class UILinksWidgetDirect
|
||||
$oBlock->Display($oPage, $this->sInputid, $aParams);
|
||||
}
|
||||
}
|
||||
|
||||
protected function DisplayEditInPlace(WebPage $oPage, DBObjectSet $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, $aButtons = array('create', 'delete'))
|
||||
{
|
||||
$aAttribs = $this->GetTableConfig();
|
||||
|
||||
$oValue->Rewind();
|
||||
$oPage->add('<table class="listContainer" id="'.$this->sInputid.'"><tr><td>');
|
||||
|
||||
$aData = array();
|
||||
while($oLinkObj = $oValue->Fetch())
|
||||
{
|
||||
$aRow = array();
|
||||
$aRow['form::select'] = '<input type="checkbox" class="selectList'.$this->sInputid.'" value="'.$oLinkObj->GetKey().'"/>';
|
||||
foreach($this->aZlist as $sLinkedAttCode)
|
||||
{
|
||||
$aRow[$sLinkedAttCode] = $oLinkObj->GetAsHTML($sLinkedAttCode);
|
||||
}
|
||||
$aData[] = $aRow;
|
||||
}
|
||||
$oPage->table($aAttribs, $aData);
|
||||
$oPage->add('</td></tr></table>'); //listcontainer
|
||||
$sInputName = $sFormPrefix.'attr_'.$this->sAttCode;
|
||||
$aLabels = array(
|
||||
'delete' => Dict::S('UI:Button:Delete'),
|
||||
// 'modify' => 'Modify...' ,
|
||||
'creation_title' => Dict::Format('UI:CreationTitle_Class', MetaModel::GetName($this->sLinkedClass)),
|
||||
'create' => Dict::Format('UI:ClickToCreateNew', MetaModel::GetName($this->sLinkedClass)),
|
||||
'remove' => Dict::S('UI:Button:Remove'),
|
||||
'add' => Dict::Format('UI:AddAnExisting_Class', MetaModel::GetName($this->sLinkedClass)),
|
||||
'selection_title' => Dict::Format('UI:SelectionOf_Class', MetaModel::GetName($this->sLinkedClass)),
|
||||
);
|
||||
$oContext = new ApplicationContext();
|
||||
$sSubmitUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?'.$oContext->GetForLink();
|
||||
$sJSONLabels = json_encode($aLabels);
|
||||
$sJSONButtons = json_encode($aButtons);
|
||||
$sWizHelper = 'oWizardHelper'.$sFormPrefix;
|
||||
$oPage->add_ready_script("$('#{$this->sInputid}').directlinks({class_name: '$this->sClass', att_code: '$this->sAttCode', input_name:'$sInputName', labels: $sJSONLabels, submit_to: '$sSubmitUrl', buttons: $sJSONButtons, oWizardHelper: $sWizHelper });");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param string $sProposedRealClass
|
||||
*/
|
||||
public function GetObjectCreationDlg(WebPage $oPage, $sProposedRealClass = '')
|
||||
{
|
||||
// For security reasons: check that the "proposed" class is actually a subclass of the linked class
|
||||
@@ -215,14 +204,14 @@ class UILinksWidgetDirect
|
||||
$aKeys = array_keys($aPossibleClasses);
|
||||
$sRealClass = $aKeys[0];
|
||||
}
|
||||
|
||||
|
||||
if ($sRealClass != '')
|
||||
{
|
||||
$oPage->add("<h1>".MetaModel::GetClassIcon($sRealClass)." ".Dict::Format('UI:CreationTitle_Class', MetaModel::GetName($sRealClass))."</h1>\n");
|
||||
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
|
||||
$sExtKeyToMe = $oLinksetDef->GetExtKeyToMe();
|
||||
$aFieldFlags = array( $sExtKeyToMe => OPT_ATT_HIDDEN);
|
||||
cmdbAbstractObject::DisplayCreationForm($oPage, $sRealClass, null, array(), array('formPrefix' => $this->sInputid, 'noRelations' => true, 'fieldsFlags' => $aFieldFlags));
|
||||
cmdbAbstractObject::DisplayCreationForm($oPage, $sRealClass, null, array(), array('formPrefix' => $this->sInputid, 'noRelations' => true, 'fieldsFlags' => $aFieldFlags));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -239,7 +228,61 @@ class UILinksWidgetDirect
|
||||
}
|
||||
$oPage->add('</div></div>');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param DBObjectSet $oValue
|
||||
* @param array $aArgs
|
||||
* @param string $sFormPrefix
|
||||
* @param DBObject $oCurrentObj
|
||||
* @param array $aButtons
|
||||
*/
|
||||
protected function DisplayEditInPlace(WebPage $oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, $aButtons = array('create', 'delete'))
|
||||
{
|
||||
$aAttribs = $this->GetTableConfig();
|
||||
|
||||
$oValue->Rewind();
|
||||
$oPage->add('<table class="listContainer" id="'.$this->sInputid.'"><tr><td>');
|
||||
|
||||
$aData = array();
|
||||
while($oLinkObj = $oValue->Fetch())
|
||||
{
|
||||
$aRow = array();
|
||||
$aRow['form::select'] = '<input type="checkbox" class="selectList'.$this->sInputid.'" value="'.$oLinkObj->GetKey().'"/>';
|
||||
foreach($this->aZlist as $sLinkedAttCode)
|
||||
{
|
||||
$aRow[$sLinkedAttCode] = $oLinkObj->GetAsHTML($sLinkedAttCode);
|
||||
}
|
||||
$aData[] = $aRow;
|
||||
}
|
||||
$oPage->table($aAttribs, $aData);
|
||||
$oPage->add('</td></tr></table>'); //listcontainer
|
||||
$sInputName = $sFormPrefix.'attr_'.$this->sAttCode;
|
||||
$aLabels = array(
|
||||
'delete' => Dict::S('UI:Button:Delete'),
|
||||
// 'modify' => 'Modify...' ,
|
||||
'creation_title' => Dict::Format('UI:CreationTitle_Class', MetaModel::GetName($this->sLinkedClass)),
|
||||
'create' => Dict::Format('UI:ClickToCreateNew', MetaModel::GetName($this->sLinkedClass)),
|
||||
'remove' => Dict::S('UI:Button:Remove'),
|
||||
'add' => Dict::Format('UI:AddAnExisting_Class', MetaModel::GetName($this->sLinkedClass)),
|
||||
'selection_title' => Dict::Format('UI:SelectionOf_Class', MetaModel::GetName($this->sLinkedClass)),
|
||||
);
|
||||
$oContext = new ApplicationContext();
|
||||
$sSubmitUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?'.$oContext->GetForLink();
|
||||
$sJSONLabels = json_encode($aLabels);
|
||||
$sJSONButtons = json_encode($aButtons);
|
||||
$sWizHelper = 'oWizardHelper'.$sFormPrefix;
|
||||
// Don't automatically launch the search if the table is huge
|
||||
$bDoSearch = !utils::IsHighCardinality($this->sLinkedClass);
|
||||
$sJSDoSearch = $bDoSearch ? 'true' : 'false';
|
||||
$oPage->add_ready_script("$('#{$this->sInputid}').directlinks({class_name: '$this->sClass', att_code: '$this->sAttCode', input_name:'$sInputName', labels: $sJSONLabels, submit_to: '$sSubmitUrl', buttons: $sJSONButtons, oWizardHelper: $sWizHelper, do_search: $sJSDoSearch});");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param DBObject $oCurrentObj
|
||||
* @throws Exception
|
||||
*/
|
||||
public function GetObjectsSelectionDlg($oPage, $oCurrentObj)
|
||||
{
|
||||
$sHtml = "<div class=\"wizContainer\" style=\"vertical-align:top;\">\n";
|
||||
@@ -262,7 +305,7 @@ class UILinksWidgetDirect
|
||||
{
|
||||
$this->SetSearchDefaultFromContext($oCurrentObj, $oFilter);
|
||||
}
|
||||
$bOpen = MetaModel::GetConfig()->Get('legacy_search_drawer_open');
|
||||
$bOpen = MetaModel::GetConfig()->Get('legacy_search_drawer_open') || utils::IsHighCardinality($this->sLinkedClass);
|
||||
$oBlock = new DisplayBlock($oFilter, 'search', false);
|
||||
$sHtml .= $oBlock->GetDisplay($oPage, "SearchFormToAdd_{$this->sInputid}", array('open' => $bOpen));
|
||||
$sHtml .= "<form id=\"ObjectsAddForm_{$this->sInputid}\">\n";
|
||||
@@ -274,16 +317,15 @@ class UILinksWidgetDirect
|
||||
$sHtml .= "</div>\n";
|
||||
$sHtml .= "</form>\n";
|
||||
$oPage->add($sHtml);
|
||||
//$oPage->add_ready_script("$('#SearchFormToAdd_{$this->sAttCode}{$this->sNameSuffix} form').bind('submit.uilinksWizard', oWidget{$this->sInputId}.SearchObjectsToAdd);");
|
||||
//$oPage->add_ready_script("$('#SearchFormToAdd_{$this->sAttCode}{$this->sNameSuffix}').resize(oWidget{$this->siInputId}.UpdateSizes);");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Search for objects to be linked to the current object (i.e "remote" objects)
|
||||
* @param WebPage $oP The page used for the output (usually an AjaxWebPage)
|
||||
* @param string $sRemoteClass Name of the "remote" class to perform the search on, must be a derived class of $this->sLinkedClass
|
||||
* @param array $aAlreadyLinked Array of indentifiers of objects which are already linke to the current object (or about to be linked)
|
||||
* @param DBObject $oCurrentObj The object currently being edited... if known...
|
||||
* @throws Exception
|
||||
*/
|
||||
public function SearchObjectsToAdd(WebPage $oP, $sRemoteClass = '', $aAlreadyLinked = array(), $oCurrentObj = null)
|
||||
{
|
||||
@@ -328,6 +370,10 @@ class UILinksWidgetDirect
|
||||
$oBlock->Display($oP, "ResultsToAdd_{$this->sInputid}", array('menu' => false, 'cssCount'=> '#count_'.$this->sInputid , 'selection_mode' => true, 'table_id' => 'add_'.$this->sInputid)); // Don't display the 'Actions' menu on the results
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WebPage $oP
|
||||
* @param $oFullSetFilter
|
||||
*/
|
||||
public function DoAddObjects(WebPage $oP, $oFullSetFilter)
|
||||
{
|
||||
$aLinkedObjectIds = utils::ReadMultipleSelection($oFullSetFilter);
|
||||
@@ -355,7 +401,14 @@ class UILinksWidgetDirect
|
||||
}
|
||||
return $aAttribs;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param string $sRealClass
|
||||
* @param array $aValues
|
||||
* @param int $iTempId
|
||||
* @return mixed
|
||||
*/
|
||||
public function GetRow($oPage, $sRealClass, $aValues, $iTempId)
|
||||
{
|
||||
if ($sRealClass == '')
|
||||
@@ -367,7 +420,13 @@ class UILinksWidgetDirect
|
||||
|
||||
return $this->GetObjectRow($oPage, $oLinkObj, $iTempId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param $oLinkObj
|
||||
* @param int $iTempId
|
||||
* @return mixed
|
||||
*/
|
||||
protected function GetObjectRow($oPage, $oLinkObj, $iTempId)
|
||||
{
|
||||
$aAttribs = $this->GetTableConfig();
|
||||
@@ -383,7 +442,7 @@ class UILinksWidgetDirect
|
||||
/**
|
||||
* Initializes the default search parameters based on 1) a 'current' object and 2) the silos defined by the context
|
||||
* @param DBObject $oSourceObj
|
||||
* @param DBSearch $oSearch
|
||||
* @param DBSearch|DBObjectSearch $oSearch
|
||||
*/
|
||||
protected function SetSearchDefaultFromContext($oSourceObj, &$oSearch)
|
||||
{
|
||||
@@ -402,7 +461,6 @@ class UILinksWidgetDirect
|
||||
|
||||
if (MetaModel::IsValidAttCode($sSrcClass, $sAttCode))
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($sSrcClass, $sAttCode);
|
||||
$defaultValue = $oSourceObj->Get($sAttCode);
|
||||
|
||||
// Find the attcode for the same 'context' parameter in the destination class
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* Class UILinksWidget
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -39,7 +39,15 @@ class UILinksWidget
|
||||
protected $m_sLinkedClass;
|
||||
protected $m_sRemoteClass;
|
||||
protected $m_bDuplicatesAllowed;
|
||||
|
||||
|
||||
/**
|
||||
* UILinksWidget constructor.
|
||||
* @param string $sClass
|
||||
* @param string $sAttCode
|
||||
* @param int $iInputId
|
||||
* @param string $sNameSuffix
|
||||
* @param bool $bDuplicatesAllowed
|
||||
*/
|
||||
public function __construct($sClass, $sAttCode, $iInputId, $sNameSuffix = '', $bDuplicatesAllowed = false)
|
||||
{
|
||||
$this->m_sClass = $sClass;
|
||||
@@ -92,16 +100,19 @@ class UILinksWidget
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A one-row form for editing a link record
|
||||
* @param WebPage $oP Web page used for the ouput
|
||||
* @param DBObject $oLinkedObj The object to which all the elements of the linked set refer to
|
||||
* @param DBObject $oLinkedObj Remote object
|
||||
* @param mixed $linkObjOrId Either the object linked or a unique number for new link records to add
|
||||
* @param Hash $aArgs Extra context arguments
|
||||
* @return string The HTML fragment of the one-row form
|
||||
* @param array $aArgs Extra context arguments
|
||||
* @param DBObject $oCurrentObj The object to which all the elements of the linked set refer to
|
||||
* @param int $iUniqueId A unique identifier of new links
|
||||
* @param boolean $bReadOnly Display link as editable or read-only. Default is false (editable)
|
||||
* @return array The HTML fragment of the one-row form
|
||||
*/
|
||||
protected function GetFormRow(WebPage $oP, DBObject $oLinkedObj, $linkObjOrId = null, $aArgs = array(), $oCurrentObj )
|
||||
protected function GetFormRow(WebPage $oP, DBObject $oLinkedObj, $linkObjOrId, $aArgs, $oCurrentObj, $iUniqueId, $bReadOnly = false)
|
||||
{
|
||||
$sPrefix = "$this->m_sAttCode{$this->m_sNameSuffix}";
|
||||
$aRow = array();
|
||||
@@ -115,16 +126,33 @@ class UILinksWidget
|
||||
$aArgs['prefix'] = $sPrefix;
|
||||
$aArgs['wizHelper'] = "oWizardHelper{$this->m_iInputId}{$key}";
|
||||
$aArgs['this'] = $linkObjOrId;
|
||||
$aRow['form::checkbox'] = "<input class=\"selection\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"$key\">";
|
||||
$aRow['form::checkbox'] .= "<input type=\"hidden\" name=\"attr_{$sPrefix}id{$sNameSuffix}\" value=\"$key\">";
|
||||
foreach($this->m_aEditableFields as $sFieldCode)
|
||||
{
|
||||
$sFieldId = $this->m_iInputId.'_'.$sFieldCode.'['.$linkObjOrId->GetKey().']';
|
||||
$sSafeId = utils::GetSafeId($sFieldId);
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $sFieldCode);
|
||||
$aRow[$sFieldCode] = cmdbAbstractObject::GetFormElementForField($oP, $this->m_sLinkedClass, $sFieldCode, $oAttDef, $linkObjOrId->Get($sFieldCode), '' /* DisplayValue */, $sSafeId, $sNameSuffix, 0, $aArgs);
|
||||
$aFieldsMap[$sFieldCode] = $sSafeId;
|
||||
}
|
||||
|
||||
if($bReadOnly)
|
||||
{
|
||||
$aRow['form::checkbox'] = "";
|
||||
foreach($this->m_aEditableFields as $sFieldCode)
|
||||
{
|
||||
$sDisplayValue = $linkObjOrId->GetEditValue($sFieldCode);
|
||||
$aRow[$sFieldCode] = $sDisplayValue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$aRow['form::checkbox'] = "<input class=\"selection\" data-remote-id=\"$iRemoteObjKey\" data-link-id=\"$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().']';
|
||||
$sSafeId = utils::GetSafeId($sFieldId);
|
||||
$sValue = $linkObjOrId->Get($sFieldCode);
|
||||
$sDisplayValue = $linkObjOrId->GetEditValue($sFieldCode);
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $sFieldCode);
|
||||
$aRow[$sFieldCode] = '<div class="field_container" style="border:none;"><div class="field_data"><div class="field_value">'.
|
||||
cmdbAbstractObject::GetFormElementForField($oP, $this->m_sLinkedClass, $sFieldCode, $oAttDef, $sValue, $sDisplayValue, $sSafeId, $sNameSuffix, 0, $aArgs).
|
||||
'</div></div></div>';
|
||||
$aFieldsMap[$sFieldCode] = $sSafeId;
|
||||
}
|
||||
}
|
||||
|
||||
$sState = $linkObjOrId->GetState();
|
||||
}
|
||||
else
|
||||
@@ -135,78 +163,60 @@ class UILinksWidget
|
||||
// New link existing only in memory
|
||||
$oNewLinkObj = $linkObjOrId;
|
||||
$iRemoteObjKey = $oNewLinkObj->Get($this->m_sExtKeyToRemote);
|
||||
$oRemoteObj = MetaModel::GetObject($this->m_sRemoteClass, $iRemoteObjKey);
|
||||
$oNewLinkObj->Set($this->m_sExtKeyToMe, $oCurrentObj); // Setting the extkey with the object also fills the related external fields
|
||||
$linkObjOrId = -$iRemoteObjKey;
|
||||
}
|
||||
else
|
||||
{
|
||||
$iRemoteObjKey = -$linkObjOrId;
|
||||
$iRemoteObjKey = $linkObjOrId;
|
||||
$oNewLinkObj = MetaModel::NewObject($this->m_sLinkedClass);
|
||||
$oRemoteObj = MetaModel::GetObject($this->m_sRemoteClass, -$linkObjOrId);
|
||||
$oRemoteObj = MetaModel::GetObject($this->m_sRemoteClass, $iRemoteObjKey);
|
||||
$oNewLinkObj->Set($this->m_sExtKeyToRemote, $oRemoteObj); // Setting the extkey with the object alsoo fills the related external fields
|
||||
$oNewLinkObj->Set($this->m_sExtKeyToMe, $oCurrentObj); // Setting the extkey with the object also fills the related external fields
|
||||
}
|
||||
$sPrefix .= "[$linkObjOrId][";
|
||||
$sPrefix .= "[-$iUniqueId][";
|
||||
$sNameSuffix = "]"; // To make a tabular form
|
||||
$aArgs['prefix'] = $sPrefix;
|
||||
$aArgs['wizHelper'] = "oWizardHelper{$this->m_iInputId}_".(-$linkObjOrId);
|
||||
$aArgs['wizHelper'] = "oWizardHelper{$this->m_iInputId}_".($iUniqueId < 0 ? -$iUniqueId : $iUniqueId);
|
||||
$aArgs['this'] = $oNewLinkObj;
|
||||
$aRow['form::checkbox'] = "<input class=\"selection\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"$linkObjOrId\">";
|
||||
$aRow['form::checkbox'] .= "<input type=\"hidden\" name=\"attr_{$sPrefix}id{$sNameSuffix}\" value=\"\">";
|
||||
$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=\"-$iUniqueId\">";
|
||||
foreach($this->m_aEditableFields as $sFieldCode)
|
||||
{
|
||||
$sFieldId = $this->m_iInputId.'_'.$sFieldCode.'['.$linkObjOrId.']';
|
||||
$sFieldId = $this->m_iInputId.'_'.$sFieldCode.'['.-$iUniqueId.']';
|
||||
$sSafeId = utils::GetSafeId($sFieldId);
|
||||
$sValue = $oNewLinkObj->Get($sFieldCode);
|
||||
$sDisplayValue = $oNewLinkObj->GetEditValue($sFieldCode);
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $sFieldCode);
|
||||
$aRow[$sFieldCode] = cmdbAbstractObject::GetFormElementForField($oP, $this->m_sLinkedClass, $sFieldCode, $oAttDef, $oNewLinkObj->Get($sFieldCode) /* TO DO/ call GetDefaultValue($oObject->ToArgs()) */, '' /* DisplayValue */, $sSafeId /* id */, $sNameSuffix, 0, $aArgs);
|
||||
$aRow[$sFieldCode] = '<div class="field_container" style="border:none;"><div class="field_data"><div class="field_value">'.
|
||||
cmdbAbstractObject::GetFormElementForField($oP, $this->m_sLinkedClass, $sFieldCode, $oAttDef, $sValue, $sDisplayValue, $sSafeId /* id */, $sNameSuffix, 0, $aArgs).
|
||||
'</div></div></div>';
|
||||
$aFieldsMap[$sFieldCode] = $sSafeId;
|
||||
}
|
||||
$sState = '';
|
||||
$sJSDaysMin = json_encode(array(Dict::S('DayOfWeek-Sunday-Min'), Dict::S('DayOfWeek-Monday-Min'), Dict::S('DayOfWeek-Tuesday-Min'), Dict::S('DayOfWeek-Wednesday-Min'),
|
||||
Dict::S('DayOfWeek-Thursday-Min'), Dict::S('DayOfWeek-Friday-Min'), Dict::S('DayOfWeek-Saturday-Min')));
|
||||
$sJSMonthsShort = json_encode(array(Dict::S('Month-01-Short'), Dict::S('Month-02-Short'), Dict::S('Month-03-Short'), Dict::S('Month-04-Short'), Dict::S('Month-05-Short'), Dict::S('Month-06-Short'),
|
||||
Dict::S('Month-07-Short'), Dict::S('Month-08-Short'), Dict::S('Month-09-Short'), Dict::S('Month-10-Short'), Dict::S('Month-11-Short'), Dict::S('Month-12-Short')));
|
||||
$iFirstDayOfWeek = (int) Dict::S('Calendar-FirstDayOfWeek');
|
||||
|
||||
$oP->add_script(
|
||||
<<<EOF
|
||||
$(".date-pick").datepicker({
|
||||
showOn: 'button',
|
||||
buttonImage: '../images/calendar.png',
|
||||
buttonImageOnly: true,
|
||||
dateFormat: 'yy-mm-dd',
|
||||
constrainInput: false,
|
||||
changeMonth: true,
|
||||
changeYear: true,
|
||||
dayNamesMin: $sJSDaysMin,
|
||||
monthNamesShort: $sJSMonthsShort,
|
||||
firstDay: $iFirstDayOfWeek
|
||||
});
|
||||
$(".datetime-pick").datepicker({
|
||||
showOn: 'button',
|
||||
buttonImage: '../images/calendar.png',
|
||||
buttonImageOnly: true,
|
||||
dateFormat: 'yy-mm-dd 00:00:00',
|
||||
constrainInput: false,
|
||||
changeMonth: true,
|
||||
changeYear: true,
|
||||
dayNamesMin: $sJSDaysMin,
|
||||
monthNamesShort: $sJSMonthsShort,
|
||||
firstDay: $iFirstDayOfWeek
|
||||
});
|
||||
// Rows created with ajax call need OnLinkAdded call.
|
||||
// Rows added before loading the form cannot call OnLinkAdded.
|
||||
if ($iUniqueId > 0)
|
||||
{
|
||||
$oP->add_script(
|
||||
<<<EOF
|
||||
PrepareWidgets();
|
||||
oWidget{$this->m_iInputId}.OnLinkAdded($iUniqueId, $iRemoteObjKey);
|
||||
EOF
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$sExtKeyToMeId = utils::GetSafeId($sPrefix.$this->m_sExtKeyToMe);
|
||||
$aFieldsMap[$this->m_sExtKeyToMe] = $sExtKeyToMeId;
|
||||
$aRow['form::checkbox'] .= "<input type=\"hidden\" id=\"$sExtKeyToMeId\" value=\"".$oCurrentObj->GetKey()."\">";
|
||||
|
||||
$sExtKeyToRemoteId = utils::GetSafeId($sPrefix.$this->m_sExtKeyToRemote);
|
||||
$aFieldsMap[$this->m_sExtKeyToRemote] = $sExtKeyToRemoteId;
|
||||
$aRow['form::checkbox'] .= "<input type=\"hidden\" id=\"$sExtKeyToRemoteId\" value=\"$iRemoteObjKey\">";
|
||||
|
||||
if(!$bReadOnly)
|
||||
{
|
||||
$sExtKeyToMeId = utils::GetSafeId($sPrefix.$this->m_sExtKeyToMe);
|
||||
$aFieldsMap[$this->m_sExtKeyToMe] = $sExtKeyToMeId;
|
||||
$aRow['form::checkbox'] .= "<input type=\"hidden\" id=\"$sExtKeyToMeId\" value=\"".$oCurrentObj->GetKey()."\">";
|
||||
|
||||
$sExtKeyToRemoteId = utils::GetSafeId($sPrefix.$this->m_sExtKeyToRemote);
|
||||
$aFieldsMap[$this->m_sExtKeyToRemote] = $sExtKeyToRemoteId;
|
||||
$aRow['form::checkbox'] .= "<input type=\"hidden\" id=\"$sExtKeyToRemoteId\" value=\"$iRemoteObjKey\">";
|
||||
}
|
||||
|
||||
$iFieldsCount = count($aFieldsMap);
|
||||
$sJsonFieldsMap = json_encode($aFieldsMap);
|
||||
|
||||
@@ -227,7 +237,11 @@ EOF
|
||||
|
||||
/**
|
||||
* Display one row of the whole form
|
||||
* @return none
|
||||
* @param WebPage $oP
|
||||
* @param array $aConfig
|
||||
* @param array $aRow
|
||||
* @param int $iRowId
|
||||
* @return string
|
||||
*/
|
||||
protected function DisplayFormRow(WebPage $oP, $aConfig, $aRow, $iRowId)
|
||||
{
|
||||
@@ -245,8 +259,8 @@ EOF
|
||||
/**
|
||||
* Display the table with the form for editing all the links at once
|
||||
* @param WebPage $oP The web page used for the output
|
||||
* @param Hash $aConfig The table's header configuration
|
||||
* @param Hash $aData The tabular data to be displayed
|
||||
* @param array $aConfig The table's header configuration
|
||||
* @param array $aData The tabular data to be displayed
|
||||
* @return string Html fragment representing the form table
|
||||
*/
|
||||
protected function DisplayFormTable(WebPage $oP, $aConfig, $aData)
|
||||
@@ -283,48 +297,60 @@ EOF
|
||||
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get the HTML fragment corresponding to the linkset editing widget
|
||||
* @param WebPage $oP The web page used for all the output
|
||||
* @param DBObjectSet The initial value of the linked set
|
||||
* @param Hash $aArgs Extra context arguments
|
||||
* @param WebPage $oPage
|
||||
* @param DBObject|ormLinkSet $oValue
|
||||
* @param array $aArgs Extra context arguments
|
||||
* @param string $sFormPrefix prefix of the fields in the current form
|
||||
* @param DBObject $oCurrentObj the current object to which the linkset is related
|
||||
* @return string The HTML fragment to be inserted into the page
|
||||
*/
|
||||
public function Display(WebPage $oPage, DBObjectSet $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj)
|
||||
public function Display(WebPage $oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj)
|
||||
{
|
||||
$sHtmlValue = '';
|
||||
$sTargetClass = self::GetTargetClass($this->m_sClass, $this->m_sAttCode);
|
||||
$sHtmlValue .= "<div id=\"linkedset_{$this->m_sAttCode}{$this->m_sNameSuffix}\">\n";
|
||||
$sHtmlValue .= "<input type=\"hidden\" id=\"{$sFormPrefix}{$this->m_iInputId}\">\n";
|
||||
$oValue->Rewind();
|
||||
$aForm = array();
|
||||
$iAddedId = 1; // Unique id for new links
|
||||
while($oCurrentLink = $oValue->Fetch())
|
||||
{
|
||||
$aRow = array();
|
||||
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $oCurrentLink->Get($this->m_sExtKeyToRemote));
|
||||
if ($oCurrentLink->IsNew())
|
||||
{
|
||||
$key = -$oLinkedObj->GetKey();
|
||||
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs, $oCurrentObj);
|
||||
}
|
||||
else
|
||||
{
|
||||
$key = $oCurrentLink->GetKey();
|
||||
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs, $oCurrentObj);
|
||||
}
|
||||
// We try to retrieve the remote object as usual
|
||||
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $oCurrentLink->Get($this->m_sExtKeyToRemote), false /* Must not be found */);
|
||||
// If successful, it means that we can edit its link
|
||||
if($oLinkedObj !== null)
|
||||
{
|
||||
$bReadOnly = false;
|
||||
}
|
||||
// Else we retrieve it without restrictions (silos) and will display its link as readonly
|
||||
else
|
||||
{
|
||||
$bReadOnly = true;
|
||||
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $oCurrentLink->Get($this->m_sExtKeyToRemote), false /* Must not be found */, true);
|
||||
}
|
||||
|
||||
if ($oCurrentLink->IsNew())
|
||||
{
|
||||
$key = -($iAddedId++);
|
||||
}
|
||||
else
|
||||
{
|
||||
$key = $oCurrentLink->GetKey();
|
||||
}
|
||||
$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}');
|
||||
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();
|
||||
$('#{$this->m_iInputId}').bind('update_value', function() { $(this).val(oWidget{$this->m_iInputId}.GetUpdatedValue()); })
|
||||
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();\" >";
|
||||
@@ -332,13 +358,19 @@ EOF
|
||||
$sHtmlValue .= "<span style=\"clear:both;\"><p> </p></span>\n";
|
||||
$sHtmlValue .= "</div>\n";
|
||||
$oPage->add_at_the_end("<div id=\"dlg_{$this->m_sAttCode}{$this->m_sNameSuffix}\"></div>"); // To prevent adding forms inside the main form
|
||||
return $sHtmlValue;
|
||||
return $sHtmlValue;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $sClass
|
||||
* @param string $sAttCode
|
||||
* @return string
|
||||
*/
|
||||
protected static function GetTargetClass($sClass, $sAttCode)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
$sLinkedClass = $oAttDef->GetLinkedClass();
|
||||
$sTargetClass = '';
|
||||
switch(get_class($oAttDef))
|
||||
{
|
||||
case 'AttributeLinkedSetIndirect':
|
||||
@@ -353,10 +385,14 @@ EOF
|
||||
|
||||
return $sTargetClass;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param DBObject $oCurrentObj
|
||||
*/
|
||||
public function GetObjectPickerDialog($oPage, $oCurrentObj)
|
||||
{
|
||||
$bOpen = MetaModel::GetConfig()->Get('legacy_search_drawer_open');
|
||||
$bOpen = MetaModel::GetConfig()->Get('legacy_search_drawer_open') || utils::IsHighCardinality($this->m_sRemoteClass);
|
||||
$sHtml = "<div class=\"wizContainer\" style=\"vertical-align:top;\">\n";
|
||||
$oFilter = new DBObjectSearch($this->m_sRemoteClass);
|
||||
$this->SetSearchDefaultFromContext($oCurrentObj, $oFilter);
|
||||
@@ -397,51 +433,31 @@ EOF
|
||||
}
|
||||
if (!$this->m_bDuplicatesAllowed && count($aAlreadyLinkedIds) > 0)
|
||||
{
|
||||
// Positive IDs correspond to existing link records
|
||||
// negative IDs correspond to "remote" objects to be linked
|
||||
$aLinkIds = array();
|
||||
$aRemoteObjIds = array();
|
||||
foreach($aAlreadyLinkedIds as $iId)
|
||||
{
|
||||
if ($iId > 0)
|
||||
{
|
||||
$aLinkIds[] = $iId;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aRemoteObjIds[] = -$iId;
|
||||
}
|
||||
}
|
||||
|
||||
if (count($aLinkIds) >0)
|
||||
{
|
||||
// Search for the links to find to which "remote" object they are linked
|
||||
$oLinkFilter = new DBObjectSearch($this->m_sLinkedClass);
|
||||
$oLinkFilter->AddCondition('id', $aLinkIds, 'IN');
|
||||
$oLinkSet = new CMDBObjectSet($oLinkFilter);
|
||||
while($oLink = $oLinkSet->Fetch())
|
||||
{
|
||||
$aRemoteObjIds[] = $oLink->Get($this->m_sExtKeyToRemote);
|
||||
}
|
||||
}
|
||||
$oFilter->AddCondition('id', $aRemoteObjIds, 'NOTIN');
|
||||
$oFilter->AddCondition('id', $aAlreadyLinkedIds, 'NOTIN');
|
||||
}
|
||||
$oSet = new CMDBObjectSet($oFilter);
|
||||
$oBlock = new DisplayBlock($oFilter, 'list', false);
|
||||
$oBlock->Display($oP, "ResultsToAdd_{$this->m_sAttCode}", array('menu' => false, 'cssCount'=> '#count_'.$this->m_sAttCode.$this->m_sNameSuffix , 'selection_mode' => true, 'table_id' => 'add_'.$this->m_sAttCode)); // Don't display the 'Actions' menu on the results
|
||||
}
|
||||
|
||||
public function DoAddObjects(WebPage $oP, $oFullSetFilter, $oCurrentObj)
|
||||
|
||||
/**
|
||||
* @param WebPage $oP
|
||||
* @param int $iMaxAddedId
|
||||
* @param $oFullSetFilter
|
||||
* @param DBObject $oCurrentObj
|
||||
*/
|
||||
public function DoAddObjects(WebPage $oP, $iMaxAddedId, $oFullSetFilter, $oCurrentObj)
|
||||
{
|
||||
$aLinkedObjectIds = utils::ReadMultipleSelection($oFullSetFilter);
|
||||
|
||||
$iAdditionId = $iMaxAddedId + 1;
|
||||
foreach($aLinkedObjectIds as $iObjectId)
|
||||
{
|
||||
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $iObjectId);
|
||||
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $iObjectId, false);
|
||||
if (is_object($oLinkedObj))
|
||||
{
|
||||
$aRow = $this->GetFormRow($oP, $oLinkedObj, -$iObjectId, array(), $oCurrentObj ); // Not yet created link get negative Ids
|
||||
$oP->add($this->DisplayFormRow($oP, $this->m_aTableConfig, $aRow, -$iObjectId));
|
||||
$aRow = $this->GetFormRow($oP, $oLinkedObj, $iObjectId, array(), $oCurrentObj, $iAdditionId); // Not yet created link get negative Ids
|
||||
$oP->add($this->DisplayFormRow($oP, $this->m_aTableConfig, $aRow, -$iAdditionId));
|
||||
$iAdditionId++;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -453,7 +469,7 @@ EOF
|
||||
/**
|
||||
* Initializes the default search parameters based on 1) a 'current' object and 2) the silos defined by the context
|
||||
* @param DBObject $oSourceObj
|
||||
* @param DBSearch $oSearch
|
||||
* @param DBSearch|DBObjectSearch $oSearch
|
||||
*/
|
||||
protected function SetSearchDefaultFromContext($oSourceObj, &$oSearch)
|
||||
{
|
||||
@@ -472,7 +488,6 @@ EOF
|
||||
|
||||
if (MetaModel::IsValidAttCode($sSrcClass, $sAttCode))
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($sSrcClass, $sAttCode);
|
||||
$defaultValue = $oSourceObj->Get($sAttCode);
|
||||
|
||||
// Find the attcode for the same 'context' parameter in the destination class
|
||||
@@ -492,4 +507,3 @@ EOF
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -58,9 +58,15 @@ class UIPasswordWidget
|
||||
$sConfirmPasswordValue = $aPasswordValues ? $aPasswordValues['confirm'] : '*****';
|
||||
$sChangedValue = (($sPasswordValue != '*****') || ($sConfirmPasswordValue != '*****')) ? 1 : 0;
|
||||
$sHtmlValue = '';
|
||||
$sHtmlValue = '<input type="password" maxlength="255" name="attr_'.$sCode.'[value]" id="'.$this->iId.'" value="'.htmlentities($sPasswordValue, ENT_QUOTES, 'UTF-8').'"/> <span class="form_validation" id="v_'.$this->iId.'"></span><span id="fstatus_'.$this->iId.'"></span><br/>';
|
||||
$sHtmlValue .= '<input type="password" maxlength="255" id="'.$this->iId.'_confirm" value="'.htmlentities($sConfirmPasswordValue, ENT_QUOTES, 'UTF-8').'" name="attr_'.$sCode.'[confirm]"/> '.Dict::S('UI:PasswordConfirm').' <input id="'.$this->iId.'_reset" type="button" value="'.Dict::S('UI:Button:ResetPassword').'" onClick="ResetPwd(\''.$this->iId.'\');">';
|
||||
$sHtmlValue .= '<div class="field_input_zone field_input_onewaypassword">';
|
||||
$sHtmlValue .= '<input type="password" maxlength="255" name="attr_'.$sCode.'[value]" id="'.$this->iId.'" value="'.htmlentities($sPasswordValue, ENT_QUOTES, 'UTF-8').'"/>';
|
||||
$sHtmlValue .= '<input type="password" maxlength="255" id="'.$this->iId.'_confirm" value="'.htmlentities($sConfirmPasswordValue, ENT_QUOTES, 'UTF-8').'" name="attr_'.$sCode.'[confirm]"/>';
|
||||
$sHtmlValue .= '<span>'.Dict::S('UI:PasswordConfirm').'</span>';
|
||||
$sHtmlValue .= '<input id="'.$this->iId.'_reset" type="button" value="'.Dict::S('UI:Button:ResetPassword').'" onClick="ResetPwd(\''.$this->iId.'\');">';
|
||||
$sHtmlValue .= '<input type="hidden" id="'.$this->iId.'_changed" name="attr_'.$sCode.'[changed]" value="'.$sChangedValue.'"/>';
|
||||
$sHtmlValue .= '</div>';
|
||||
|
||||
$sHtmlValue .= '<span class="form_validation" id="v_'.$this->iId.'"></span><span class="field_status" id="fstatus_'.$this->iId.'"></span>';
|
||||
|
||||
$oPage->add_ready_script("$('#$this->iId').bind('keyup change', function(evt) { return PasswordFieldChanged('$this->iId') } );"); // Bind to a custom event: validate
|
||||
$oPage->add_ready_script("$('#$this->iId').bind('keyup change validate', function(evt, sFormId) { return ValidatePasswordField('$this->iId', sFormId) } );"); // Bind to a custom event: validate
|
||||
|
||||
@@ -1,423 +0,0 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 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/>
|
||||
|
||||
|
||||
/**
|
||||
* Class UILinksWizard
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
class UILinksWizard
|
||||
{
|
||||
protected $m_sClass;
|
||||
protected $m_sLinkageAttr;
|
||||
protected $m_iObjectId;
|
||||
protected $m_sLinkedClass;
|
||||
protected $m_sLinkingAttCode;
|
||||
protected $m_aEditableFields;
|
||||
protected $m_aTableConfig;
|
||||
|
||||
public function __construct($sClass, $sLinkageAttr, $iObjectId, $sLinkedClass = '')
|
||||
{
|
||||
$this->m_sClass = $sClass;
|
||||
$this->m_sLinkageAttr = $sLinkageAttr;
|
||||
$this->m_iObjectId = $iObjectId;
|
||||
$this->m_sLinkedClass = $sLinkedClass; // Will try to guess below, if it's empty
|
||||
$this->m_sLinkingAttCode = ''; // Will be filled once we've found the attribute corresponding to the linked class
|
||||
|
||||
$this->m_aEditableFields = array();
|
||||
$this->m_aTableConfig = array();
|
||||
$this->m_aTableConfig['form::checkbox'] = array( 'label' => "<input class=\"select_all\" type=\"checkbox\" value=\"1\" onChange=\"var value = this.checked; $('.selection').each( function() { this.checked = value; } );OnSelectChange();\">", 'description' => Dict::S('UI:SelectAllToggle+'));
|
||||
foreach(MetaModel::GetAttributesList($this->m_sClass) as $sAttCode)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
|
||||
if ($oAttDef->IsExternalKey() && ($sAttCode != $this->m_sLinkageAttr))
|
||||
{
|
||||
if (empty($this->m_sLinkedClass))
|
||||
{
|
||||
// This is a class of objects we can manage !
|
||||
// Since nothing was specify, any class will do !
|
||||
$this->m_sLinkedClass = $oAttDef->GetTargetClass();
|
||||
$this->m_sLinkingAttCode = $sAttCode;
|
||||
}
|
||||
else if ($this->m_sLinkedClass == $oAttDef->GetTargetClass())
|
||||
{
|
||||
// This is the class of objects we want to manage !
|
||||
$this->m_sLinkingAttCode = $sAttCode;
|
||||
}
|
||||
}
|
||||
else if ( (!$oAttDef->IsExternalKey()) && (!$oAttDef->IsExternalField()))
|
||||
{
|
||||
$this->m_aEditableFields[] = $sAttCode;
|
||||
$this->m_aTableConfig[$sAttCode] = array( 'label' => $oAttDef->GetLabel(), 'description' => $oAttDef->GetDescription());
|
||||
}
|
||||
}
|
||||
if (empty($this->m_sLinkedClass))
|
||||
{
|
||||
throw( new Exception(Dict::Format('UI:Error:IncorrectLinkDefinition_LinkedClass_Class', $sLinkedClass, $sClass)));
|
||||
}
|
||||
foreach(MetaModel::GetZListItems($this->m_sLinkedClass, 'list') as $sFieldCode)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $sFieldCode);
|
||||
$this->m_aTableConfig['static::'.$sFieldCode] = array( 'label' => $oAttDef->GetLabel(), 'description' => $oAttDef->GetDescription());
|
||||
}
|
||||
}
|
||||
|
||||
public function Display(WebPage $oP, $aExtraParams = array())
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sLinkageAttr);
|
||||
$sTargetClass = $oAttDef->GetTargetClass();
|
||||
$oTargetObj = MetaModel::GetObject($sTargetClass, $this->m_iObjectId);
|
||||
|
||||
$oP->set_title("iTop - ".MetaModel::GetName($this->m_sLinkedClass)." objects linked with ".MetaModel::GetName(get_class($oTargetObj)).": ".$oTargetObj->GetRawName());
|
||||
$oP->add("<div class=\"wizContainer\">\n");
|
||||
$oP->add("<form method=\"post\">\n");
|
||||
$oP->add("<div class=\"page_header\">\n");
|
||||
$oP->add("<input type=\"hidden\" id=\"linksToRemove\" name=\"linksToRemove\" value=\"\">\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"operation\" value=\"do_modify_links\">\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"class\" value=\"{$this->m_sClass}\">\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"linkage\" value=\"{$this->m_sLinkageAttr}\">\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"object_id\" value=\"{$this->m_iObjectId}\">\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"linking_attcode\" value=\"{$this->m_sLinkingAttCode}\">\n");
|
||||
$oP->add("<h1>".Dict::Format('UI:ManageObjectsOf_Class_LinkedWith_Class_Instance', MetaModel::GetName($this->m_sLinkedClass), MetaModel::GetName(get_class($oTargetObj)), "<span class=\"hilite\">".$oTargetObj->GetHyperlink()."</span>")."</h1>\n");
|
||||
$oP->add("</div>\n");
|
||||
$oP->add_script(
|
||||
<<<EOF
|
||||
function OnSelectChange()
|
||||
{
|
||||
var nbChecked = $('.selection:checked').length;
|
||||
if (nbChecked > 0)
|
||||
{
|
||||
$('#btnRemove').removeAttr('disabled');
|
||||
}
|
||||
else
|
||||
{
|
||||
$('#btnRemove').attr('disabled','disabled');
|
||||
}
|
||||
}
|
||||
|
||||
function RemoveSelected()
|
||||
{
|
||||
$('.selection:checked').each(
|
||||
function()
|
||||
{
|
||||
$('#linksToRemove').val($('#linksToRemove').val() + ' ' + this.value);
|
||||
$('#row_'+this.value).remove();
|
||||
}
|
||||
);
|
||||
// Disable the button since all the selected items have been removed
|
||||
$('#btnRemove').attr('disabled','disabled');
|
||||
// Re-run the zebra plugin to properly highlight the remaining lines
|
||||
$('.listResults').trigger('update');
|
||||
|
||||
}
|
||||
|
||||
function AddObjects()
|
||||
{
|
||||
// TO DO: compute the list of objects already linked with the current Object
|
||||
$.post( GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', { 'operation': 'addObjects',
|
||||
'class': '{$this->m_sClass}',
|
||||
'linkageAttr': '{$this->m_sLinkageAttr}',
|
||||
'linkedClass': '{$this->m_sLinkedClass}',
|
||||
'objectId': '{$this->m_iObjectId}'
|
||||
},
|
||||
function(data)
|
||||
{
|
||||
$('#ModalDlg').html(data);
|
||||
dlgWidth = $(document).width() - 100;
|
||||
$('#ModalDlg').css('width', dlgWidth);
|
||||
$('#ModalDlg').css('left', 50);
|
||||
$('#ModalDlg').css('top', 50);
|
||||
$('#ModalDlg').dialog( 'open' );
|
||||
},
|
||||
'html'
|
||||
);
|
||||
}
|
||||
|
||||
function SearchObjectsToAdd(currentFormId)
|
||||
{
|
||||
var theMap = { 'class': '{$this->m_sClass}',
|
||||
'linkageAttr': '{$this->m_sLinkageAttr}',
|
||||
'linkedClass': '{$this->m_sLinkedClass}',
|
||||
'objectId': '{$this->m_iObjectId}'
|
||||
}
|
||||
if ($('#'+currentFormId+' :input[name=class]').val() != undefined)
|
||||
{
|
||||
theMap.linkedClass = $('#'+currentFormId+' :input[name=class]').val();
|
||||
}
|
||||
// Gather the parameters from the search form
|
||||
$('#'+currentFormId+' :input').each(
|
||||
function(i)
|
||||
{
|
||||
if (this.name != '')
|
||||
{
|
||||
theMap[this.name] = this.value;
|
||||
}
|
||||
}
|
||||
);
|
||||
theMap['operation'] = 'searchObjectsToAdd';
|
||||
|
||||
// Run the query and display the results
|
||||
$.post( GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', theMap,
|
||||
function(data)
|
||||
{
|
||||
$('#SearchResultsToAdd').html(data);
|
||||
$('#SearchResultsToAdd .listResults').tablesorter( { headers: {0: false}}, widgets: ['myZebra', 'truncatedList']} ); // sortable and zebra tables
|
||||
|
||||
},
|
||||
'html'
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function DoAddObjects(currentFormId)
|
||||
{
|
||||
var theMap = { 'class': '{$this->m_sClass}',
|
||||
'linkageAttr': '{$this->m_sLinkageAttr}',
|
||||
'linkedClass': '{$this->m_sLinkedClass}',
|
||||
'objectId': '{$this->m_iObjectId}'
|
||||
}
|
||||
|
||||
// Gather the parameters from the search form
|
||||
$('#'+currentFormId+' :input').each(
|
||||
function(i)
|
||||
{
|
||||
if ( (this.name != '') && ((this.type != 'checkbox') || (this.checked)) )
|
||||
{
|
||||
//console.log(this.type);
|
||||
arrayExpr = /\[\]$/;
|
||||
if (arrayExpr.test(this.name))
|
||||
{
|
||||
// Array
|
||||
if (theMap[this.name] == undefined)
|
||||
{
|
||||
theMap[this.name] = new Array();
|
||||
}
|
||||
theMap[this.name].push(this.value);
|
||||
}
|
||||
else
|
||||
{
|
||||
theMap[this.name] = this.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
theMap['operation'] = 'doAddObjects';
|
||||
|
||||
// Run the query and display the results
|
||||
$.post( GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', theMap,
|
||||
function(data)
|
||||
{
|
||||
//console.log('Data: ' + data);
|
||||
if (data != '')
|
||||
{
|
||||
$('#empty_row').remove();
|
||||
}
|
||||
$('.listResults tbody').append(data);
|
||||
$('.listResults').trigger('update');
|
||||
$('.listResults').tablesorter( { headers: {0: false}}, widgets: ['myZebra', 'truncatedList']} ); // sortable and zebra tables
|
||||
},
|
||||
'html'
|
||||
);
|
||||
$('#ModalDlg').dialog('close');
|
||||
return false;
|
||||
}
|
||||
|
||||
function InitForm()
|
||||
{
|
||||
// make sure that the form is clean
|
||||
$('.selection').each( function() { this.checked = false; });
|
||||
$('#btnRemove').attr('disabled','disabled');
|
||||
$('#linksToRemove').val('');
|
||||
}
|
||||
|
||||
function SubmitHook()
|
||||
{
|
||||
var the_form = this;
|
||||
SearchObjectsToAdd(the_form.id);
|
||||
return false;
|
||||
}
|
||||
EOF
|
||||
);
|
||||
$oP->add_ready_script("InitForm();");
|
||||
$oFilter = new DBObjectSearch($this->m_sClass);
|
||||
$oFilter->AddCondition($this->m_sLinkageAttr, $this->m_iObjectId, '=');
|
||||
$oSet = new DBObjectSet($oFilter);
|
||||
$aForm = array();
|
||||
while($oCurrentLink = $oSet->Fetch())
|
||||
{
|
||||
$aRow = array();
|
||||
$key = $oCurrentLink->GetKey();
|
||||
$oLinkedObj = MetaModel::GetObject($this->m_sLinkedClass, $oCurrentLink->Get($this->m_sLinkingAttCode));
|
||||
|
||||
$aForm[$key] = $this->GetFormRow($oP, $oLinkedObj, $oCurrentLink);
|
||||
}
|
||||
//var_dump($aTableLabels);
|
||||
//var_dump($aForm);
|
||||
$this->DisplayFormTable($oP, $this->m_aTableConfig, $aForm);
|
||||
$oP->add("<span style=\"float:left;\"> <img src=\"../images/tv-item-last.gif\"> <input id=\"btnRemove\" type=\"button\" value=\"".Dict::S('UI:RemoveLinkedObjectsOf_Class')."\" onClick=\"RemoveSelected();\" >");
|
||||
$oP->add(" <input id=\"btnAdd\" type=\"button\" value=\"".Dict::Format('UI:AddLinkedObjectsOf_Class', MetaModel::GetName($this->m_sLinkedClass))."\" onClick=\"AddObjects();\"></span>\n");
|
||||
$oP->add("<span style=\"float:right;\"><input id=\"btnCancel\" type=\"button\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"BackToDetails('".$sTargetClass."', ".$this->m_iObjectId.", '', '');\">");
|
||||
$oP->add(" <input id=\"btnOk\" type=\"submit\" value=\"".Dict::S('UI:Button:Ok')."\"></span>\n");
|
||||
$oP->add("<span style=\"clear:both;\"><p> </p></span>\n");
|
||||
$oP->add("</div>\n");
|
||||
$oP->add("</form>\n");
|
||||
if (isset($aExtraParams['StartWithAdd']) && ($aExtraParams['StartWithAdd']))
|
||||
{
|
||||
$oP->add_ready_script("AddObjects();");
|
||||
}
|
||||
}
|
||||
|
||||
protected function GetFormRow($oP, $oLinkedObj, $currentLink = null )
|
||||
{
|
||||
$aRow = array();
|
||||
if(is_object($currentLink))
|
||||
{
|
||||
$key = $currentLink->GetKey();
|
||||
$sNameSuffix = "[$key]"; // To make a tabular form
|
||||
$aRow['form::checkbox'] = "<input class=\"selection\" type=\"checkbox\" onChange=\"OnSelectChange();\" value=\"$key\">";
|
||||
$aRow['form::checkbox'] .= "<input type=\"hidden\" name=\"linkId[$key]\" value=\"$key\">";
|
||||
foreach($this->m_aEditableFields as $sFieldCode)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sFieldCode);
|
||||
$aRow[$sFieldCode] = cmdbAbstractObject::GetFormElementForField($oP, $this->m_sClass, $sFieldCode, $oAttDef, $currentLink->Get($sFieldCode), '' /* DisplayValue */, $key, $sNameSuffix);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// form for creating a new record
|
||||
$sNameSuffix = "[$currentLink]"; // To make a tabular form
|
||||
$aRow['form::checkbox'] = "<input class=\"selection\" type=\"checkbox\" onChange=\"OnSelectChange();\" value=\"$currentLink\">";
|
||||
$aRow['form::checkbox'] .= "<input type=\"hidden\" name=\"linkId[$currentLink]\" value=\"$currentLink\">";
|
||||
foreach($this->m_aEditableFields as $sFieldCode)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sFieldCode);
|
||||
$aRow[$sFieldCode] = cmdbAbstractObject::GetFormElementForField($oP, $this->m_sClass, $sFieldCode, $oAttDef, '' /* TO DO/ call GetDefaultValue($oObject->ToArgs()) */, '' /* DisplayValue */, '' /* id */, $sNameSuffix);
|
||||
}
|
||||
}
|
||||
foreach(MetaModel::GetZListItems($this->m_sLinkedClass, 'list') as $sFieldCode)
|
||||
{
|
||||
$aRow['static::'.$sFieldCode] = $oLinkedObj->GetAsHTML($sFieldCode);
|
||||
}
|
||||
return $aRow;
|
||||
}
|
||||
|
||||
protected function DisplayFormTable(WebPage $oP, $aConfig, $aData)
|
||||
{
|
||||
$oP->add("<table class=\"listResults\">\n");
|
||||
// Header
|
||||
$oP->add("<thead>\n");
|
||||
$oP->add("<tr>\n");
|
||||
foreach($aConfig as $sName=>$aDef)
|
||||
{
|
||||
$oP->add("<th title=\"".$aDef['description']."\">".$aDef['label']."</th>\n");
|
||||
}
|
||||
$oP->add("</tr>\n");
|
||||
$oP->add("</thead>\n");
|
||||
|
||||
// Content
|
||||
$oP->add("</tbody>\n");
|
||||
if (count($aData) == 0)
|
||||
{
|
||||
$oP->add("<tr id=\"empty_row\"><td colspan=\"".count($aConfig)."\" style=\"text-align:center;\">".Dict::S('UI:Message:EmptyList:UseAdd')."</td></td>");
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach($aData as $iRowId => $aRow)
|
||||
{
|
||||
$this->DisplayFormRow($oP, $aConfig, $aRow, $iRowId);
|
||||
}
|
||||
}
|
||||
$oP->add("</tbody>\n");
|
||||
|
||||
// Footer
|
||||
$oP->add("</table>\n");
|
||||
}
|
||||
|
||||
protected function DisplayFormRow(WebPage $oP, $aConfig, $aRow, $iRowId)
|
||||
{
|
||||
$oP->add("<tr id=\"row_$iRowId\">\n");
|
||||
foreach($aConfig as $sName=>$void)
|
||||
{
|
||||
$oP->add("<td>".$aRow[$sName]."</td>\n");
|
||||
}
|
||||
$oP->add("</tr>\n");
|
||||
}
|
||||
|
||||
public function DisplayAddForm(WebPage $oP)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sLinkageAttr);
|
||||
$sTargetClass = $oAttDef->GetTargetClass();
|
||||
$oTargetObj = MetaModel::GetObject($sTargetClass, $this->m_iObjectId);
|
||||
$oP->add("<div class=\"wizContainer\">\n");
|
||||
//$oP->add("<div class=\"page_header\">\n");
|
||||
//$oP->add("<h1>".Dict::Format('UI:AddObjectsOf_Class_LinkedWith_Class_Instance', MetaModel::GetName($this->m_sLinkedClass), MetaModel::GetName(get_class($oTargetObj)), "<span class=\"hilite\">".$oTargetObj->GetHyperlink()."</span>")."</h1>\n");
|
||||
//$oP->add("</div>\n");
|
||||
|
||||
$oFilter = new DBObjectSearch($this->m_sLinkedClass);
|
||||
$oSet = new CMDBObjectSet($oFilter);
|
||||
$oBlock = new DisplayBlock($oFilter, 'search', false);
|
||||
$oBlock->Display($oP, 'SearchFormToAdd', array('open' => true));
|
||||
$oP->Add("<form id=\"ObjectsAddForm\" OnSubmit=\"return DoAddObjects(this.id);\">\n");
|
||||
$oP->Add("<div id=\"SearchResultsToAdd\">\n");
|
||||
$oP->Add("<div style=\"height: 100px; background: #fff;border-color:#F6F6F1 #E6E6E1 #E6E6E1 #F6F6F1; border-style:solid; border-width:3px; text-align: center; vertical-align: center;\"><p>".Dict::S('UI:Message:EmptyList:UseSearchForm')."</p></div>\n");
|
||||
$oP->Add("</div>\n");
|
||||
$oP->add("<input type=\"button\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"$('#ModalDlg').dialog('close');\"> <input type=\"submit\" value=\"".Dict::S('UI:Button:Add')."\">");
|
||||
$oP->Add("</div>\n");
|
||||
$oP->Add("</form>\n");
|
||||
$oP->add_ready_script("$('#ModalDlg').dialog('option', {title:'".Dict::Format('UI:AddObjectsOf_Class_LinkedWith_Class_Instance', MetaModel::GetName($this->m_sLinkedClass), MetaModel::GetName(get_class($oTargetObj)), "<span class=\"hilite\">".$oTargetObj->GetHyperlink()."</span>")."'});");
|
||||
$oP->add_ready_script("$('div#SearchFormToAdd form').bind('submit.uilinksWizard', SubmitHook);");
|
||||
}
|
||||
|
||||
public function SearchObjectsToAdd(WebPage $oP)
|
||||
{
|
||||
//$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sLinkageAttr);
|
||||
|
||||
$oFilter = new DBObjectSearch($this->m_sLinkedClass);
|
||||
$oSet = new CMDBObjectSet($oFilter);
|
||||
$oBlock = new DisplayBlock($oFilter, 'list', false);
|
||||
$oBlock->Display($oP, 'ResultsToAdd', array('menu' => false, 'selection_mode' => true)); // Don't display the 'Actions' menu on the results
|
||||
}
|
||||
|
||||
public function DoAddObjects(WebPage $oP, $aLinkedObjectIds = array())
|
||||
{
|
||||
//$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sLinkageAttr);
|
||||
//$sTargetClass = $oAttDef->GetTargetClass();
|
||||
//$oP->Add("<!-- nb of objects to add: ".count($aLinkedObjectIds)." -->\n"); // Just to make sure it's not empty
|
||||
$aTable = array();
|
||||
foreach($aLinkedObjectIds as $iObjectId)
|
||||
{
|
||||
$oLinkedObj = MetaModel::GetObject($this->m_sLinkedClass, $iObjectId);
|
||||
if (is_object($oLinkedObj))
|
||||
{
|
||||
$aRow = $this->GetFormRow($oP, $oLinkedObj, -$iObjectId ); // Not yet created link get negative Ids
|
||||
$this->DisplayFormRow($oP, $this->m_aTableConfig, $aRow, -$iObjectId);
|
||||
}
|
||||
else
|
||||
{
|
||||
echo Dict::Format('UI:Error:Object_Class_Id_NotFound', $this->m_sLinkedClass, $iObjectId);
|
||||
}
|
||||
}
|
||||
//var_dump($aTable);
|
||||
//$oP->Add("<!-- end of added list -->\n"); // Just to make sure it's not empty
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -19,7 +19,7 @@
|
||||
/**
|
||||
* Store and retrieve user's preferences (i.e persistent per user settings)
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
require_once(APPROOT.'/core/dbobject.class.php');
|
||||
@@ -50,7 +50,7 @@ class appUserPreferences extends DBObject
|
||||
self::Load();
|
||||
}
|
||||
$aPrefs = self::$oUserPrefs->Get('preferences');
|
||||
if (isset($aPrefs[$sCode]))
|
||||
if (array_key_exists($sCode, $aPrefs))
|
||||
{
|
||||
return $aPrefs[$sCode];
|
||||
}
|
||||
@@ -72,9 +72,16 @@ class appUserPreferences extends DBObject
|
||||
self::Load();
|
||||
}
|
||||
$aPrefs = self::$oUserPrefs->Get('preferences');
|
||||
$aPrefs[$sCode] = $sValue;
|
||||
self::$oUserPrefs->Set('preferences', $aPrefs);
|
||||
self::Save();
|
||||
if (array_key_exists($sCode, $aPrefs) && ($aPrefs[$sCode] === $sValue))
|
||||
{
|
||||
// Do not write it again
|
||||
}
|
||||
else
|
||||
{
|
||||
$aPrefs[$sCode] = $sValue;
|
||||
self::$oUserPrefs->Set('preferences', $aPrefs);
|
||||
self::Save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -148,7 +155,9 @@ class appUserPreferences extends DBObject
|
||||
{
|
||||
if (self::$oUserPrefs->IsModified())
|
||||
{
|
||||
utils::PushArchiveMode(false);
|
||||
self::$oUserPrefs->DBUpdate();
|
||||
utils::PopArchiveMode();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -172,7 +181,9 @@ class appUserPreferences extends DBObject
|
||||
$oObj->Set('preferences', array()); // Default preferences: an empty array
|
||||
try
|
||||
{
|
||||
utils::PushArchiveMode(false);
|
||||
$oObj->DBInsert();
|
||||
utils::PopArchiveMode();
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
@@ -207,7 +218,8 @@ class appUserPreferences extends DBObject
|
||||
*/
|
||||
public function DBDeleteTracked(CMDBChange $oChange, $bSkipStrongSecurity = null, &$oDeletionPlan = null)
|
||||
{
|
||||
utils::PushArchiveMode(false);
|
||||
$this->DBDelete($oDeletionPlan);
|
||||
utils::PopArchiveMode();
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
use Html2Text\Html2Text;
|
||||
use Leafo\ScssPhp\Compiler;
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -22,7 +22,7 @@ use Leafo\ScssPhp\Compiler;
|
||||
/**
|
||||
* Static class utils
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -140,6 +140,81 @@ class utils
|
||||
}
|
||||
}
|
||||
|
||||
protected static $bPageMode = null;
|
||||
/**
|
||||
* @var boolean[]
|
||||
*/
|
||||
protected static $aModes = array();
|
||||
|
||||
public static function InitArchiveMode()
|
||||
{
|
||||
if (isset($_SESSION['archive_mode']))
|
||||
{
|
||||
$iDefault = $_SESSION['archive_mode'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$iDefault = 0;
|
||||
}
|
||||
// Read and record the value for switching the archive mode
|
||||
$iCurrent = self::ReadParam('with-archive', $iDefault);
|
||||
if (isset($_SESSION))
|
||||
{
|
||||
$_SESSION['archive_mode'] = $iCurrent;
|
||||
}
|
||||
// Read and use the value for the current page (web services)
|
||||
$iCurrent = self::ReadParam('with_archive', $iCurrent, true);
|
||||
self::$bPageMode = ($iCurrent == 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param boolean $bMode if true then activate archive mode (archived objects are visible), otherwise archived objects are
|
||||
* hidden (archive = "soft deletion")
|
||||
*/
|
||||
public static function PushArchiveMode($bMode)
|
||||
{
|
||||
array_push(self::$aModes, $bMode);
|
||||
}
|
||||
|
||||
public static function PopArchiveMode()
|
||||
{
|
||||
array_pop(self::$aModes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean true if archive mode is enabled
|
||||
*/
|
||||
public static function IsArchiveMode()
|
||||
{
|
||||
if (count(self::$aModes) > 0)
|
||||
{
|
||||
$bRet = end(self::$aModes);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (self::$bPageMode === null)
|
||||
{
|
||||
self::InitArchiveMode();
|
||||
}
|
||||
$bRet = self::$bPageMode;
|
||||
}
|
||||
return $bRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to be called by the GUI and define if the user will see obsolete data (otherwise, the user will have to dig further)
|
||||
* @return bool
|
||||
*/
|
||||
public static function ShowObsoleteData()
|
||||
{
|
||||
$bDefault = MetaModel::GetConfig()->Get('obsolescence.show_obsolete_data'); // default is false
|
||||
$bShow = appUserPreferences::GetPref('show_obsolete_data', $bDefault);
|
||||
if (static::IsArchiveMode())
|
||||
{
|
||||
$bShow = true;
|
||||
}
|
||||
return $bShow;
|
||||
}
|
||||
|
||||
public static function ReadParam($sName, $defaultValue = "", $bAllowCLI = false, $sSanitizationFilter = 'parameter')
|
||||
{
|
||||
@@ -437,6 +512,45 @@ class utils
|
||||
return $iReturn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a value into a more friendly format (KB, MB, GB, TB) instead a juste a Bytes amount.
|
||||
*
|
||||
* @param type $value
|
||||
* @return string
|
||||
*/
|
||||
public static function BytesToFriendlyFormat($value)
|
||||
{
|
||||
$sReturn = '';
|
||||
// Kilobytes
|
||||
if ($value >= 1024)
|
||||
{
|
||||
$sReturn = 'K';
|
||||
$value = $value / 1024;
|
||||
}
|
||||
// Megabytes
|
||||
if ($value >= 1024)
|
||||
{
|
||||
$sReturn = 'M';
|
||||
$value = $value / 1024;
|
||||
}
|
||||
// Gigabytes
|
||||
if ($value >= 1024)
|
||||
{
|
||||
$sReturn = 'G';
|
||||
$value = $value / 1024;
|
||||
}
|
||||
// Terabytes
|
||||
if ($value >= 1024)
|
||||
{
|
||||
$sReturn = 'T';
|
||||
$value = $value / 1024;
|
||||
}
|
||||
|
||||
$value = round($value, 1);
|
||||
|
||||
return $value . '' . $sReturn . 'B';
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to convert a string to a date, given a format specification. It replaces strtotime which does not allow for specifying a date in a french format (for instance)
|
||||
* Example: StringToTime('01/05/11 12:03:45', '%d/%m/%y %H:%i:%s')
|
||||
@@ -501,16 +615,20 @@ class utils
|
||||
{
|
||||
if (self::$oConfig == null)
|
||||
{
|
||||
$sConfigFile = self::GetConfigFilePath();
|
||||
if (file_exists($sConfigFile))
|
||||
{
|
||||
self::$oConfig = new Config($sConfigFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
// When executing the setup, the config file may be still missing
|
||||
self::$oConfig = new Config();
|
||||
}
|
||||
self::$oConfig = MetaModel::GetConfig();
|
||||
if (self::$oConfig == null)
|
||||
{
|
||||
$sConfigFile = self::GetConfigFilePath();
|
||||
if (file_exists($sConfigFile))
|
||||
{
|
||||
self::$oConfig = new Config($sConfigFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
// When executing the setup, the config file may be still missing
|
||||
self::$oConfig = new Config();
|
||||
}
|
||||
}
|
||||
}
|
||||
return self::$oConfig;
|
||||
}
|
||||
@@ -661,7 +779,7 @@ class utils
|
||||
}
|
||||
return $bResult;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the CAS client
|
||||
*/
|
||||
@@ -813,7 +931,7 @@ class utils
|
||||
*/
|
||||
public static function GetCachePath()
|
||||
{
|
||||
return APPROOT.'data/cache-'.self::GetCurrentEnvironment().'/';
|
||||
return APPROOT.'data/cache-'.MetaModel::GetEnvironment().'/';
|
||||
}
|
||||
/**
|
||||
* Merge standard menu items with plugin provided menus items
|
||||
@@ -848,9 +966,13 @@ class utils
|
||||
// Bulk export actions
|
||||
$aResult[] = new JSPopupMenuItem('UI:Menu:CSVExport', Dict::S('UI:Menu:CSVExport'), "ExportListDlg('$sOQL', '$sDataTableId', 'csv', ".json_encode(Dict::S('UI:Menu:CSVExport')).")");
|
||||
$aResult[] = new JSPopupMenuItem('UI:Menu:ExportXLSX', Dict::S('ExcelExporter:ExportMenu'), "ExportListDlg('$sOQL', '$sDataTableId', 'xlsx', ".json_encode(Dict::S('ExcelExporter:ExportMenu')).")");
|
||||
$aResult[] = new JSPopupMenuItem('UI:Menu:ExportPDF', Dict::S('UI:Menu:ExportPDF'), "ExportListDlg('$sOQL', '$sDataTableId', 'pdf', ".json_encode(Dict::S('UI:Menu:ExportPDF')).")");
|
||||
}
|
||||
$aResult[] = new JSPopupMenuItem('UI:Menu:AddToDashboard', Dict::S('UI:Menu:AddToDashboard'), "DashletCreationDlg('$sOQL')");
|
||||
if (extension_loaded('gd'))
|
||||
{
|
||||
// PDF export requires GD
|
||||
$aResult[] = new JSPopupMenuItem('UI:Menu:ExportPDF', Dict::S('UI:Menu:ExportPDF'), "ExportListDlg('$sOQL', '$sDataTableId', 'pdf', ".json_encode(Dict::S('UI:Menu:ExportPDF')).")");
|
||||
}
|
||||
}
|
||||
$aResult[] = new JSPopupMenuItem('UI:Menu:AddToDashboard', Dict::S('UI:Menu:AddToDashboard'), "DashletCreationDlg('$sOQL', '$sContext')");
|
||||
$aResult[] = new JSPopupMenuItem('UI:Menu:ShortcutList', Dict::S('UI:Menu:ShortcutList'), "ShortcutListDlg('$sOQL', '$sDataTableId', '$sContext')");
|
||||
|
||||
break;
|
||||
@@ -946,8 +1068,7 @@ class utils
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the absolute URL to the modules root path
|
||||
* @return string ...
|
||||
* @return string the absolute URL to the modules root path
|
||||
*/
|
||||
static public function GetAbsoluteUrlModulesRoot()
|
||||
{
|
||||
@@ -955,33 +1076,67 @@ class utils
|
||||
return $sUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URL to a page that will execute the requested module page
|
||||
*
|
||||
* To be compatible with this mechanism, the called page must include approot
|
||||
* with an absolute path OR not include it at all (losing the direct access to the page)
|
||||
* if (!defined('__DIR__')) define('__DIR__', dirname(__FILE__));
|
||||
* require_once(__DIR__.'/../../approot.inc.php');
|
||||
*
|
||||
* @return string ...
|
||||
*/
|
||||
/**
|
||||
* To be compatible with this mechanism, the called page must include approot with an absolute path OR not include
|
||||
* it at all (losing the direct access to the page) :
|
||||
*
|
||||
* ```php
|
||||
* if (!defined('__DIR__')) define('__DIR__', dirname(__FILE__));
|
||||
* require_once(__DIR__.'/../../approot.inc.php');
|
||||
* ```
|
||||
*
|
||||
* @param string $sModule
|
||||
* @param string $sPage
|
||||
* @param string[] $aArguments
|
||||
* @param string $sEnvironment
|
||||
*
|
||||
* @return string the URL to a page that will execute the requested module page, with query string values url encoded
|
||||
*
|
||||
* @see GetExecPageArguments can be used to submit using the GET method (see bug in N.1108)
|
||||
* @see GetAbsoluteUrlExecPage
|
||||
*/
|
||||
static public function GetAbsoluteUrlModulePage($sModule, $sPage, $aArguments = array(), $sEnvironment = null)
|
||||
{
|
||||
$aArgs = self::GetExecPageArguments($sModule, $sPage, $aArguments, $sEnvironment);
|
||||
$sArgs = http_build_query($aArgs);
|
||||
|
||||
return self::GetAbsoluteUrlExecPage()."?".$sArgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sModule
|
||||
* @param string $sPage
|
||||
* @param string[] $aArguments
|
||||
* @param string $sEnvironment
|
||||
*
|
||||
* @return string[] key/value pair for the exec page query string. <b>Warning</b> : values are not url encoded !
|
||||
* @throws \Exception if one of the argument has a reserved name
|
||||
*/
|
||||
static public function GetExecPageArguments($sModule, $sPage, $aArguments = array(), $sEnvironment = null)
|
||||
{
|
||||
$sEnvironment = is_null($sEnvironment) ? self::GetCurrentEnvironment() : $sEnvironment;
|
||||
$aArgs = array();
|
||||
$aArgs[] = 'exec_module='.$sModule;
|
||||
$aArgs[] = 'exec_page='.$sPage;
|
||||
$aArgs[] = 'exec_env='.$sEnvironment;
|
||||
$aArgs['exec_module'] = $sModule;
|
||||
$aArgs['exec_page'] = $sPage;
|
||||
$aArgs['exec_env'] = $sEnvironment;
|
||||
foreach($aArguments as $sName => $sValue)
|
||||
{
|
||||
if (($sName == 'exec_module')||($sName == 'exec_page')||($sName == 'exec_env'))
|
||||
if (($sName == 'exec_module') || ($sName == 'exec_page') || ($sName == 'exec_env'))
|
||||
{
|
||||
throw new Exception("Module page: $sName is a reserved page argument name");
|
||||
}
|
||||
$aArgs[] = $sName.'='.urlencode($sValue);
|
||||
$aArgs[$sName] = $sValue;
|
||||
}
|
||||
$sArgs = implode('&', $aArgs);
|
||||
return self::GetAbsoluteUrlAppRoot().'pages/exec.php?'.$sArgs;
|
||||
|
||||
return $aArgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
static public function GetAbsoluteUrlExecPage()
|
||||
{
|
||||
return self::GetAbsoluteUrlAppRoot().'pages/exec.php';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1275,10 +1430,21 @@ class utils
|
||||
*/
|
||||
public static function ResizeImageToFit(ormDocument $oImage, $iWidth, $iHeight, $iMaxImageWidth, $iMaxImageHeight)
|
||||
{
|
||||
// If image size smaller than maximums, we do nothing
|
||||
if (($iWidth <= $iMaxImageWidth) && ($iHeight <= $iMaxImageHeight))
|
||||
{
|
||||
return $oImage;
|
||||
}
|
||||
|
||||
|
||||
// If gd extension is not loaded, we put a warning in the log and return the image as is
|
||||
if (extension_loaded('gd') === false)
|
||||
{
|
||||
IssueLog::Warning('Image could not be resized as the "gd" extension does not seem to be loaded. It will remain as ' . $iWidth . 'x' . $iHeight . ' instead of ' . $iMaxImageWidth . 'x' . $iMaxImageHeight);
|
||||
return $oImage;
|
||||
}
|
||||
|
||||
|
||||
switch($oImage->GetMimeType())
|
||||
{
|
||||
case 'image/gif':
|
||||
@@ -1342,5 +1508,349 @@ class utils
|
||||
return $oResampledImage;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a 128 bit UUID in the format: {########-####-####-####-############}
|
||||
*
|
||||
* Note: this method can be run from the command line as well as from the web server.
|
||||
* Note2: this method is not cryptographically secure! If you need a cryptographically secure value
|
||||
* consider using open_ssl or PHP 7 methods.
|
||||
* @param string $sPrefix
|
||||
* @return string
|
||||
*/
|
||||
static public function CreateUUID($sPrefix = '')
|
||||
{
|
||||
$uid = uniqid("", true);
|
||||
$data = $sPrefix;
|
||||
$data .= __FILE__;
|
||||
$data .= mt_rand();
|
||||
$hash = strtoupper(hash('ripemd128', $uid . md5($data)));
|
||||
$sUUID = '{' .
|
||||
substr($hash, 0, 8) .
|
||||
'-' .
|
||||
substr($hash, 8, 4) .
|
||||
'-' .
|
||||
substr($hash, 12, 4) .
|
||||
'-' .
|
||||
substr($hash, 16, 4) .
|
||||
'-' .
|
||||
substr($hash, 20, 12) .
|
||||
'}';
|
||||
return $sUUID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the module containing the file where the call to this function is made
|
||||
* or an empty string if no such module is found (or not called within a module file)
|
||||
* @param number $iCallDepth The depth of the module in the callstack. Zero when called directly from within the module
|
||||
* @return string
|
||||
*/
|
||||
static public function GetCurrentModuleName($iCallDepth = 0)
|
||||
{
|
||||
$sCurrentModuleName = '';
|
||||
$aCallStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
|
||||
$sCallerFile = realpath($aCallStack[$iCallDepth]['file']);
|
||||
|
||||
foreach(GetModulesInfo() as $sModuleName => $aInfo)
|
||||
{
|
||||
if ($aInfo['root_dir'] !== '')
|
||||
{
|
||||
$sRootDir = realpath(APPROOT.$aInfo['root_dir']);
|
||||
|
||||
if(substr($sCallerFile, 0, strlen($sRootDir)) === $sRootDir)
|
||||
{
|
||||
$sCurrentModuleName = $sModuleName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $sCurrentModuleName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the relative (to APPROOT) path of the root directory of the module containing the file where the call to this function is made
|
||||
* or an empty string if no such module is found (or not called within a module file)
|
||||
* @param number $iCallDepth The depth of the module in the callstack. Zero when called directly from within the module
|
||||
* @return string
|
||||
*/
|
||||
static public function GetCurrentModuleDir($iCallDepth)
|
||||
{
|
||||
$sCurrentModuleDir = '';
|
||||
$aCallStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
|
||||
$sCallerFile = realpath($aCallStack[$iCallDepth]['file']);
|
||||
|
||||
foreach(GetModulesInfo() as $sModuleName => $aInfo)
|
||||
{
|
||||
if ($aInfo['root_dir'] !== '')
|
||||
{
|
||||
$sRootDir = realpath(APPROOT.$aInfo['root_dir']);
|
||||
|
||||
if(substr($sCallerFile, 0, strlen($sRootDir)) === $sRootDir)
|
||||
{
|
||||
$sCurrentModuleDir = basename($sRootDir);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $sCurrentModuleDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the base URL for all files in the current module from which this method is called
|
||||
* or an empty string if no such module is found (or not called within a module file)
|
||||
* @return string
|
||||
*/
|
||||
static public function GetCurrentModuleUrl()
|
||||
{
|
||||
$sDir = static::GetCurrentModuleDir(1);
|
||||
if ( $sDir !== '')
|
||||
{
|
||||
return static::GetAbsoluteUrlModulesRoot().'/'.$sDir;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of a given setting for the current module
|
||||
* @param string $sProperty The name of the property to retrieve
|
||||
* @param mixed $defaultvalue
|
||||
* @return mixed
|
||||
*/
|
||||
static public function GetCurrentModuleSetting($sProperty, $defaultvalue = null)
|
||||
{
|
||||
$sModuleName = static::GetCurrentModuleName(1);
|
||||
return MetaModel::GetModuleSetting($sModuleName, $sProperty, $defaultvalue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the compiled version of a given module, as it was seen by the compiler
|
||||
* @param string $sModuleName
|
||||
* @return string|NULL
|
||||
*/
|
||||
static public function GetCompiledModuleVersion($sModuleName)
|
||||
{
|
||||
$aModulesInfo = GetModulesInfo();
|
||||
if (array_key_exists($sModuleName, $aModulesInfo))
|
||||
{
|
||||
return $aModulesInfo[$sModuleName]['version'];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given path/url is an http(s) URL
|
||||
* @param string $sPath
|
||||
* @return boolean
|
||||
*/
|
||||
public static function IsURL($sPath)
|
||||
{
|
||||
$bRet = false;
|
||||
if ((substr($sPath, 0, 7) == 'http://') || (substr($sPath, 0, 8) == 'https://') || (substr($sPath, 0, 8) == 'ftp://'))
|
||||
{
|
||||
$bRet = true;
|
||||
}
|
||||
return $bRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given URL is a link to download a document/image on the CURRENT iTop
|
||||
* In such a case we can read the content of the file directly in the database (if the users rights allow) and return the ormDocument
|
||||
* @param string $sPath
|
||||
* @return false|ormDocument
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function IsSelfURL($sPath)
|
||||
{
|
||||
$result = false;
|
||||
$sPageUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.document.php';
|
||||
if (substr($sPath, 0, strlen($sPageUrl)) == $sPageUrl)
|
||||
{
|
||||
// If the URL is an URL pointing to this instance of iTop, then
|
||||
// extract the "query" part of the URL and analyze it
|
||||
$sQuery = parse_url($sPath, PHP_URL_QUERY);
|
||||
if ($sQuery !== null)
|
||||
{
|
||||
$aParams = array();
|
||||
foreach(explode('&', $sQuery) as $sChunk)
|
||||
{
|
||||
$aParts = explode('=', $sChunk);
|
||||
if (count($aParts) != 2) continue;
|
||||
$aParams[$aParts[0]] = urldecode($aParts[1]);
|
||||
}
|
||||
$result = array_key_exists('operation', $aParams) && array_key_exists('class', $aParams) && array_key_exists('id', $aParams) && array_key_exists('field', $aParams) && ($aParams['operation'] == 'download_document');
|
||||
if ($result)
|
||||
{
|
||||
// This is a 'download_document' operation, let's retrieve the document directly from the database
|
||||
$sClass = $aParams['class'];
|
||||
$iKey = $aParams['id'];
|
||||
$sAttCode = $aParams['field'];
|
||||
|
||||
$oObj = MetaModel::GetObject($sClass, $iKey, false /* must exist */); // Users rights apply here !!
|
||||
if ($oObj)
|
||||
{
|
||||
/**
|
||||
* @var ormDocument $result
|
||||
*/
|
||||
$result = clone $oObj->Get($sAttCode);
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new Exception('Invalid URL. This iTop URL is not pointing to a valid Document/Image.');
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the content of a file (and retrieve its MIME type) from either:
|
||||
* - an URL pointing to a blob (image/document) on the current iTop server
|
||||
* - an http(s) URL
|
||||
* - the local file system (but only if you are an administrator)
|
||||
* @param string $sPath
|
||||
* @return ormDocument|null
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function FileGetContentsAndMIMEType($sPath)
|
||||
{
|
||||
$oUploadedDoc = null;
|
||||
$aKnownExtensions = array(
|
||||
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
|
||||
'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
|
||||
'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
|
||||
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||
'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
|
||||
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
|
||||
'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
|
||||
'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
|
||||
'jpg' => 'image/jpeg',
|
||||
'jpeg' => 'image/jpeg',
|
||||
'gif' => 'image/gif',
|
||||
'png' => 'image/png',
|
||||
'pdf' => 'application/pdf',
|
||||
'doc' => 'application/msword',
|
||||
'dot' => 'application/msword',
|
||||
'xls' => 'application/vnd.ms-excel',
|
||||
'ppt' => 'application/vnd.ms-powerpoint',
|
||||
'vsd' => 'application/x-visio',
|
||||
'vdx' => 'application/visio.drawing',
|
||||
'odt' => 'application/vnd.oasis.opendocument.text',
|
||||
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
|
||||
'odp' => 'application/vnd.oasis.opendocument.presentation',
|
||||
'zip' => 'application/zip',
|
||||
'txt' => 'text/plain',
|
||||
'htm' => 'text/html',
|
||||
'html' => 'text/html',
|
||||
'exe' => 'application/octet-stream'
|
||||
);
|
||||
|
||||
$sData = null;
|
||||
$sMimeType = 'text/plain'; // Default MIME Type: treat the file as a bunch a characters...
|
||||
$sFileName = 'uploaded-file'; // Default name for downloaded-files
|
||||
$sExtension = '.txt'; // Default file extension in case we don't know the MIME Type
|
||||
|
||||
if(empty($sPath))
|
||||
{
|
||||
// Empty path (NULL or '') means that there is no input, making an empty document.
|
||||
$oUploadedDoc = new ormDocument('', '', '');
|
||||
}
|
||||
elseif (static::IsURL($sPath))
|
||||
{
|
||||
if ($oUploadedDoc = static::IsSelfURL($sPath))
|
||||
{
|
||||
// Nothing more to do, we've got it !!
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remote file, let's use the HTTP headers to find the MIME Type
|
||||
$sData = @file_get_contents($sPath);
|
||||
if ($sData === false)
|
||||
{
|
||||
throw new Exception("Failed to load the file from the URL '$sPath'.");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isset($http_response_header))
|
||||
{
|
||||
$aHeaders = static::ParseHeaders($http_response_header);
|
||||
$sMimeType = array_key_exists('Content-Type', $aHeaders) ? strtolower($aHeaders['Content-Type']) : 'application/x-octet-stream';
|
||||
// Compute the file extension from the MIME Type
|
||||
foreach($aKnownExtensions as $sExtValue => $sMime)
|
||||
{
|
||||
if ($sMime === $sMimeType)
|
||||
{
|
||||
$sExtension = '.'.$sExtValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
$sFileName .= $sExtension;
|
||||
}
|
||||
$oUploadedDoc = new ormDocument($sData, $sMimeType, $sFileName);
|
||||
}
|
||||
}
|
||||
else if (UserRights::IsAdministrator())
|
||||
{
|
||||
// Only administrators are allowed to read local files
|
||||
$sData = @file_get_contents($sPath);
|
||||
if ($sData === false)
|
||||
{
|
||||
throw new Exception("Failed to load the file '$sPath'. The file does not exist or the current process is not allowed to access it.");
|
||||
}
|
||||
$sExtension = strtolower(pathinfo($sPath, PATHINFO_EXTENSION));
|
||||
$sFileName = basename($sPath);
|
||||
|
||||
if (array_key_exists($sExtension, $aKnownExtensions))
|
||||
{
|
||||
$sMimeType = $aKnownExtensions[$sExtension];
|
||||
}
|
||||
else if (extension_loaded('fileinfo'))
|
||||
{
|
||||
$finfo = new finfo(FILEINFO_MIME);
|
||||
$sMimeType = $finfo->file($sPath);
|
||||
}
|
||||
$oUploadedDoc = new ormDocument($sData, $sMimeType, $sFileName);
|
||||
}
|
||||
return $oUploadedDoc;
|
||||
}
|
||||
|
||||
protected static function ParseHeaders($aHeaders)
|
||||
{
|
||||
$aCleanHeaders = array();
|
||||
foreach( $aHeaders as $sKey => $sValue )
|
||||
{
|
||||
$aTokens = explode(':', $sValue, 2);
|
||||
if(isset($aTokens[1]))
|
||||
{
|
||||
$aCleanHeaders[trim($aTokens[0])] = trim($aTokens[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The header is not in the form Header-Code: Value
|
||||
$aCleanHeaders[] = $sValue; // Store the value as-is
|
||||
$aMatches = array();
|
||||
// Check if it's not the HTTP response code
|
||||
if( preg_match("|HTTP/[0-9\.]+\s+([0-9]+)|", $sValue, $aMatches) )
|
||||
{
|
||||
$aCleanHeaders['reponse_code'] = intval($aMatches[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $aCleanHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given class if configured as a high cardinality class.
|
||||
*
|
||||
* @param $sClass
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function IsHighCardinality($sClass)
|
||||
{
|
||||
$aHugeClasses = MetaModel::GetConfig()->Get('high_cardinality_classes');
|
||||
return in_array($sClass, $aHugeClasses);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -345,27 +345,40 @@ class WebPage implements Page
|
||||
*/
|
||||
public function GetDetails($aFields)
|
||||
{
|
||||
$sHtml = "<table class=\"details\">\n";
|
||||
$sHtml .= "<tbody>\n";
|
||||
$sHtml = "<div class=\"details\">\n";
|
||||
foreach($aFields as $aAttrib)
|
||||
{
|
||||
$sHtml .= "<tr>\n";
|
||||
$sDataAttCode = isset($aAttrib['attcode']) ? "data-attcode=\"{$aAttrib['attcode']}\"" : '';
|
||||
$sLayout = isset($aAttrib['layout']) ? $aAttrib['layout'] : 'small';
|
||||
$sHtml .= "<div class=\"field_container field_{$sLayout}\" $sDataAttCode>\n";
|
||||
$sHtml .= "<div class=\"field_label label\">{$aAttrib['label']}</div>\n";
|
||||
|
||||
$sHtml .= "<div class=\"field_data\">\n";
|
||||
// By Rom, for csv import, proposed to show several values for column selection
|
||||
if (is_array($aAttrib['value']))
|
||||
{
|
||||
$sHtml .= "<td class=\"label\">".$aAttrib['label']."</td><td>".implode("</td><td>", $aAttrib['value'])."</td>\n";
|
||||
$sHtml .= "<div class=\"field_value\">".implode("</div><div>", $aAttrib['value'])."</div>\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sHtml .= "<td class=\"label\">".$aAttrib['label']."</td><td>".$aAttrib['value']."</td>\n";
|
||||
$sHtml .= "<div class=\"field_value\">".$aAttrib['value']."</div>\n";
|
||||
}
|
||||
$sComment = (isset($aAttrib['comments'])) ? $aAttrib['comments'] : ' ';
|
||||
$sInfo = (isset($aAttrib['infos'])) ? $aAttrib['infos'] : ' ';
|
||||
$sHtml .= "<td>$sComment</td><td>$sInfo</td>\n";
|
||||
$sHtml .= "</tr>\n";
|
||||
// Checking if we should add comments & infos
|
||||
$sComment = (isset($aAttrib['comments'])) ? $aAttrib['comments'] : '';
|
||||
$sInfo = (isset($aAttrib['infos'])) ? $aAttrib['infos'] : '';
|
||||
if($sComment !== '')
|
||||
{
|
||||
$sHtml .= "<div class=\"field_comments\">$sComment</div>\n";
|
||||
}
|
||||
if($sInfo !== '')
|
||||
{
|
||||
$sHtml .= "<div class=\"field_infos\">$sInfo</div>\n";
|
||||
}
|
||||
$sHtml .= "</div>\n";
|
||||
|
||||
$sHtml .= "</div>\n";
|
||||
}
|
||||
$sHtml .= "</tbody>\n";
|
||||
$sHtml .= "</table>\n";
|
||||
$sHtml .= "</div>\n";
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
@@ -734,7 +747,7 @@ class WebPage implements Page
|
||||
{
|
||||
foreach ($aActions as $aAction)
|
||||
{
|
||||
$sClass = isset($aAction['class']) ? " class=\"{$aAction['class']}\"" : "";
|
||||
$sClass = isset($aAction['css_classes']) ? ' class="'.implode(' ', $aAction['css_classes']).'"' : '';
|
||||
$sOnClick = isset($aAction['onclick']) ? ' onclick="'.htmlspecialchars($aAction['onclick'], ENT_QUOTES, "UTF-8").'"' : '';
|
||||
$sTarget = isset($aAction['target']) ? " target=\"{$aAction['target']}\"" : "";
|
||||
if (empty($aAction['url']))
|
||||
|
||||
@@ -84,6 +84,18 @@ class WizardHelper
|
||||
$oTargetObj = MetaModel::GetObject($sLinkedAttDef->GetTargetClass(), $aLinkedObject[$sLinkedAttCode]);
|
||||
$oLinkedObj->Set($sLinkedAttCode, $oTargetObj);
|
||||
}
|
||||
elseif($sLinkedAttDef instanceof AttributeDateTime)
|
||||
{
|
||||
$sDate = $aLinkedObject[$sLinkedAttCode];
|
||||
if($sDate !== null && $sDate !== '')
|
||||
{
|
||||
$oDateTimeFormat = AttributeDateTime::GetFormat();
|
||||
$oDate = $oDateTimeFormat->Parse($sDate);
|
||||
$sDate = $oDate->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
$oLinkedObj->Set($sLinkedAttCode, $sDate);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oLinkedObj->Set($sLinkedAttCode, $aLinkedObject[$sLinkedAttCode]);
|
||||
@@ -138,6 +150,22 @@ class WizardHelper
|
||||
$oObj->Set($sAttCode, $value);
|
||||
}
|
||||
}
|
||||
else if ($oAttDef instanceof AttributeDateTime) // AttributeDate is derived from AttributeDateTime
|
||||
{
|
||||
if ($value != null)
|
||||
{
|
||||
$oDate = $oAttDef->GetFormat()->Parse($value);
|
||||
if ($oDate instanceof DateTime)
|
||||
{
|
||||
$value = $oDate->format($oAttDef->GetInternalFormat());
|
||||
}
|
||||
else
|
||||
{
|
||||
$value = null;
|
||||
}
|
||||
}
|
||||
$oObj->Set($sAttCode, $value);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oObj->Set($sAttCode, $value);
|
||||
@@ -254,11 +282,21 @@ class WizardHelper
|
||||
{
|
||||
return $this->m_aData['m_sClass'];
|
||||
}
|
||||
|
||||
public function GetFormPrefix()
|
||||
{
|
||||
return $this->m_aData['m_sFormPrefix'];
|
||||
}
|
||||
|
||||
public function GetFormPrefix()
|
||||
{
|
||||
return $this->m_aData['m_sFormPrefix'];
|
||||
}
|
||||
|
||||
public function GetInitialState()
|
||||
{
|
||||
return isset($this->m_aData['m_sInitialState']) ? $this->m_aData['m_sInitialState'] : null;
|
||||
}
|
||||
|
||||
public function GetStimulus()
|
||||
{
|
||||
return isset($this->m_aData['m_sStimulus']) ? $this->m_aData['m_sStimulus'] : null;
|
||||
}
|
||||
|
||||
public function GetIdForField($sFieldName)
|
||||
{
|
||||
|
||||
10
composer.json
Normal file
10
composer.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"require": {
|
||||
"php": ">=5.3.6 <7.2.0"
|
||||
},
|
||||
"config": {
|
||||
"platform": {
|
||||
"php": "5.3.6"
|
||||
}
|
||||
}
|
||||
}
|
||||
98
core/apc-compat.php
Normal file
98
core/apc-compat.php
Normal file
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
// Copyright (C) 2016-2017 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/>
|
||||
|
||||
// Emulate the API of APC, over APCU
|
||||
// Note: for PHP < 7, this compatibility used to be provided by APCU itself (if compiled with some options)
|
||||
// for PHP 7+, it can be provided by the mean of apcu_bc, which is not so simple to install
|
||||
// The current emulation aims at skipping this complexity
|
||||
if (!function_exists('apc_store') && function_exists('apcu_store'))
|
||||
{
|
||||
function apc_add($key, $var, $ttl = 0)
|
||||
{
|
||||
return apcu_add($key, $var, $ttl);
|
||||
}
|
||||
function apc_cache_info($cache_type = '', $limited = false)
|
||||
{
|
||||
return apcu_cache_info($limited);
|
||||
}
|
||||
function apc_cas($key, $old, $new)
|
||||
{
|
||||
return apcu_cas($key, $old, $new);
|
||||
}
|
||||
function apc_clear_cache($cache_type = '')
|
||||
{
|
||||
return apcu_clear_cache();
|
||||
}
|
||||
function apc_dec($key, $step = 1, &$success = null)
|
||||
{
|
||||
apcu_dec($key, $step, $success);
|
||||
}
|
||||
function apc_delete($key)
|
||||
{
|
||||
return apcu_delete($key);
|
||||
}
|
||||
function apc_exists($keys)
|
||||
{
|
||||
return apcu_exists($keys);
|
||||
}
|
||||
function apc_fetch($key)
|
||||
{
|
||||
return apcu_fetch($key);
|
||||
}
|
||||
function apc_inc($key, $step = 1, &$success = null)
|
||||
{
|
||||
apcu_inc($key, $step, $success);
|
||||
}
|
||||
function apc_sma_info($limited = false)
|
||||
{
|
||||
return apcu_sma_info($limited);
|
||||
}
|
||||
function apc_store($key, $var, $ttl = 0)
|
||||
{
|
||||
return apcu_store($key, $var, $ttl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns user cache info... beware of the format of the returned structure that may vary (See usages)
|
||||
* @return array
|
||||
*/
|
||||
function apc_cache_info_compat()
|
||||
{
|
||||
if (!function_exists('apc_cache_info')) return array();
|
||||
|
||||
$oFunction = new ReflectionFunction('apc_cache_info');
|
||||
if ($oFunction->getNumberOfParameters() != 2)
|
||||
{
|
||||
// Beware: APCu behaves slightly differently from APC !!
|
||||
// Worse: the compatibility layer integrated into APC differs from apcu-bc (testing the number of parameters is a must)
|
||||
// In CLI mode (PHP > 7) apc_cache_info returns null and outputs an error message.
|
||||
$aCacheUserData = @apc_cache_info();
|
||||
}
|
||||
else
|
||||
{
|
||||
$aCacheUserData = @apc_cache_info('user');
|
||||
}
|
||||
return $aCacheUserData;
|
||||
}
|
||||
|
||||
// Cache emulation
|
||||
if (!function_exists('apc_store'))
|
||||
{
|
||||
require_once(APPROOT.'core/apc-emulation.php');
|
||||
}
|
||||
301
core/apc-emulation.php
Normal file
301
core/apc-emulation.php
Normal file
@@ -0,0 +1,301 @@
|
||||
<?php
|
||||
// Copyright (c) 2010-2017 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/>
|
||||
//
|
||||
|
||||
/**
|
||||
* Date: 27/09/2017
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param string $cache_type
|
||||
* @param bool $limited
|
||||
* @return array|bool
|
||||
*/
|
||||
function apc_cache_info($cache_type = '', $limited = false)
|
||||
{
|
||||
$aInfo = array();
|
||||
$sRootCacheDir = apc_emul_get_cache_filename('');
|
||||
$aInfo['cache_list'] = apc_emul_get_cache_entries($sRootCacheDir);
|
||||
return $aInfo;
|
||||
}
|
||||
|
||||
function apc_emul_get_cache_entries($sEntry)
|
||||
{
|
||||
$aResult = array();
|
||||
if (is_dir($sEntry))
|
||||
{
|
||||
$aFiles = array_diff(scandir($sEntry), array('.', '..'));
|
||||
foreach($aFiles as $sFile)
|
||||
{
|
||||
$sSubFile = $sEntry.'/'.$sFile;
|
||||
$aResult = array_merge($aResult, apc_emul_get_cache_entries($sSubFile));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$sKey = basename($sEntry);
|
||||
if (strpos($sKey, '-') === 0)
|
||||
{
|
||||
$sKey = substr($sKey, 1);
|
||||
}
|
||||
$aResult[] = array('info' => $sKey);
|
||||
}
|
||||
return $aResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|string $key
|
||||
* @param $var
|
||||
* @param int $ttl
|
||||
* @return array|bool
|
||||
*/
|
||||
function apc_store($key, $var = NULL, $ttl = 0)
|
||||
{
|
||||
if (is_array($key))
|
||||
{
|
||||
$aResult = array();
|
||||
foreach($key as $sKey => $value)
|
||||
{
|
||||
$aResult[] = apc_emul_store_unit($sKey, $value, $ttl);
|
||||
}
|
||||
return $aResult;
|
||||
}
|
||||
return apc_emul_store_unit($key, $var, $ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sKey
|
||||
* @param $value
|
||||
* @param int $iTTL time to live
|
||||
* @return bool
|
||||
*/
|
||||
function apc_emul_store_unit($sKey, $value, $iTTL)
|
||||
{
|
||||
if ($iTTL > 0)
|
||||
{
|
||||
// hint for ttl management
|
||||
$sKey = '-'.$sKey;
|
||||
}
|
||||
|
||||
$sFilename = apc_emul_get_cache_filename($sKey);
|
||||
// try to create the folder
|
||||
$sDirname = dirname($sFilename);
|
||||
if (!file_exists($sDirname))
|
||||
{
|
||||
if (!@mkdir($sDirname, 0755, true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$bRes = !(@file_put_contents($sFilename, serialize($value), LOCK_EX) === false);
|
||||
apc_emul_manage_new_entry($sFilename);
|
||||
return $bRes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $key string|array
|
||||
* @return mixed
|
||||
*/
|
||||
function apc_fetch($key)
|
||||
{
|
||||
if (is_array($key))
|
||||
{
|
||||
$aResult = array();
|
||||
foreach($key as $sKey)
|
||||
{
|
||||
$aResult[$sKey] = apc_emul_fetch_unit($sKey);
|
||||
}
|
||||
return $aResult;
|
||||
}
|
||||
return apc_emul_fetch_unit($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sKey
|
||||
* @return bool|mixed
|
||||
*/
|
||||
function apc_emul_fetch_unit($sKey)
|
||||
{
|
||||
// Try the 'TTLed' version
|
||||
$sValue = apc_emul_readcache_locked(apc_emul_get_cache_filename('-'.$sKey));
|
||||
if ($sValue === false)
|
||||
{
|
||||
$sValue = apc_emul_readcache_locked(apc_emul_get_cache_filename($sKey));
|
||||
if ($sValue === false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$oRes = @unserialize($sValue);
|
||||
return $oRes;
|
||||
}
|
||||
|
||||
function apc_emul_readcache_locked($sFilename)
|
||||
{
|
||||
$file = @fopen($sFilename, 'r');
|
||||
if ($file === false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
flock($file, LOCK_SH);
|
||||
$sContent = @fread($file, @filesize($sFilename));
|
||||
flock($file, LOCK_UN);
|
||||
fclose($file);
|
||||
return $sContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $cache_type
|
||||
* @return bool
|
||||
*/
|
||||
function apc_clear_cache($cache_type = '')
|
||||
{
|
||||
$sRootCacheDir = apc_emul_get_cache_filename('');
|
||||
apc_emul_delete_entry($sRootCacheDir);
|
||||
return true;
|
||||
}
|
||||
|
||||
function apc_emul_delete_entry($sCache)
|
||||
{
|
||||
if (is_dir($sCache))
|
||||
{
|
||||
$aFiles = array_diff(scandir($sCache), array('.', '..'));
|
||||
foreach($aFiles as $sFile)
|
||||
{
|
||||
$sSubFile = $sCache.'/'.$sFile;
|
||||
if (!apc_emul_delete_entry($sSubFile))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!@rmdir($sCache))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!@unlink($sCache))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
* @return bool|string[]
|
||||
*/
|
||||
function apc_delete($key)
|
||||
{
|
||||
return apc_emul_delete_entry(apc_emul_get_cache_filename($key));
|
||||
}
|
||||
|
||||
|
||||
function apc_emul_get_cache_filename($sKey)
|
||||
{
|
||||
$sPath = str_replace(array(' ', '/', '\\', '.'), '-', $sKey);
|
||||
return utils::GetCachePath().'apc-emul/'.$sPath;
|
||||
}
|
||||
|
||||
|
||||
/** Manage the cache files when a new cache entry is added
|
||||
* @param string $sNewFilename new cache file added
|
||||
*/
|
||||
function apc_emul_manage_new_entry($sNewFilename)
|
||||
{
|
||||
// Check only once per request
|
||||
static $aFilesByTime = null;
|
||||
static $iFileCount = 0;
|
||||
$iMaxFiles = MetaModel::GetConfig()->Get('apc_cache_emulation.max_entries');
|
||||
if ($iMaxFiles == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!$aFilesByTime)
|
||||
{
|
||||
$sRootCacheDir = apc_emul_get_cache_filename('');
|
||||
$aFilesByTime = apc_emul_list_files_time($sRootCacheDir);
|
||||
$iFileCount = count($aFilesByTime);
|
||||
if ($iMaxFiles !== 0)
|
||||
{
|
||||
asort($aFilesByTime);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$aFilesByTime[$sNewFilename] = time();
|
||||
$iFileCount++;
|
||||
}
|
||||
if ($iFileCount > $iMaxFiles)
|
||||
{
|
||||
$iFileNbToRemove = $iFileCount - $iMaxFiles;
|
||||
foreach($aFilesByTime as $sFileToRemove => $iTime)
|
||||
{
|
||||
@unlink($sFileToRemove);
|
||||
if ($iFileNbToRemove-- === 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
$aFilesByTime = array_slice($aFilesByTime, $iFileCount - $iMaxFiles, null, true);
|
||||
$iFileCount = $iMaxFiles;
|
||||
}
|
||||
}
|
||||
|
||||
/** Get the list of files with their associated access time
|
||||
* @param string $sCheck Directory to scan
|
||||
* @param array $aFilesByTime used by recursion
|
||||
* @return array
|
||||
*/
|
||||
function apc_emul_list_files_time($sCheck, &$aFilesByTime = array())
|
||||
{
|
||||
// Garbage collection
|
||||
$aFiles = array_diff(@scandir($sCheck), array('.', '..'));
|
||||
foreach($aFiles as $sFile)
|
||||
{
|
||||
$sSubFile = $sCheck.'/'.$sFile;
|
||||
if (is_dir($sSubFile))
|
||||
{
|
||||
apc_emul_list_files_time($sSubFile, $aFilesByTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
$iTime = apc_emul_get_file_time($sSubFile);
|
||||
if ($iTime !== false)
|
||||
{
|
||||
$aFilesByTime[$sSubFile] = $iTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $aFilesByTime;
|
||||
}
|
||||
|
||||
/** Get the file access time if TTL is managed
|
||||
* @param string $sFilename
|
||||
* @return bool|int returns the file atime or false if not relevant
|
||||
*/
|
||||
function apc_emul_get_file_time($sFilename)
|
||||
{
|
||||
if (strpos(basename($sFilename), '-') === 0)
|
||||
{
|
||||
return @fileatime($sFilename);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,6 +22,7 @@ MetaModel::IncludeModule('application/user.preferences.class.inc.php');
|
||||
MetaModel::IncludeModule('application/user.dashboard.class.inc.php');
|
||||
MetaModel::IncludeModule('application/audit.rule.class.inc.php');
|
||||
MetaModel::IncludeModule('application/query.class.inc.php');
|
||||
MetaModel::IncludeModule('setup/moduleinstallation.class.inc.php');
|
||||
|
||||
MetaModel::IncludeModule('core/event.class.inc.php');
|
||||
MetaModel::IncludeModule('core/action.class.inc.php');
|
||||
|
||||
55
core/background.inc.php
Normal file
55
core/background.inc.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
// Copyright (C) 2017 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/>
|
||||
|
||||
|
||||
/**
|
||||
* Tasks performed in the background
|
||||
*
|
||||
* @copyright Copyright (C) 2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
class ObsolescenceDateUpdater implements iBackgroundProcess
|
||||
{
|
||||
public function GetPeriodicity()
|
||||
{
|
||||
return MetaModel::GetConfig()->Get('obsolescence.date_update_interval'); // 10 mn
|
||||
}
|
||||
|
||||
public function Process($iUnixTimeLimit)
|
||||
{
|
||||
$iCountSet = 0;
|
||||
$iCountReset = 0;
|
||||
$iClasses = 0;
|
||||
foreach (MetaModel::EnumObsoletableClasses() as $sClass)
|
||||
{
|
||||
$oObsoletedToday = new DBObjectSearch($sClass);
|
||||
$oObsoletedToday->AddCondition('obsolescence_flag', 1, '=');
|
||||
$oObsoletedToday->AddCondition('obsolescence_date', null, '=');
|
||||
$sToday = date(AttributeDate::GetSQLFormat());
|
||||
$iCountSet += MetaModel::BulkUpdate($oObsoletedToday, array('obsolescence_date' => $sToday));
|
||||
|
||||
$oObsoletedToday = new DBObjectSearch($sClass);
|
||||
$oObsoletedToday->AddCondition('obsolescence_flag', 1, '!=');
|
||||
$oObsoletedToday->AddCondition('obsolescence_date', null, '!=');
|
||||
$iCountReset += MetaModel::BulkUpdate($oObsoletedToday, array('obsolescence_date' => null));
|
||||
}
|
||||
echo "Obsolescence date updated (classes: $iClasses ; set: $iCountSet ; reset: $iCountReset)\n";
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -471,6 +471,11 @@ class BulkChange
|
||||
// skip the private key, if any
|
||||
if ($sAttCode == 'id') continue;
|
||||
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
|
||||
|
||||
// skip reconciliation keys
|
||||
if (!$oAttDef->IsWritable() && in_array($sAttCode, $this->m_aReconcilKeys)){ continue; }
|
||||
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
|
||||
$aReasons = array();
|
||||
$iFlags = $oTargetObj->GetAttributeFlags($sAttCode, $aReasons);
|
||||
@@ -828,8 +833,8 @@ class BulkChange
|
||||
$sFormat = $sDateFormat;
|
||||
}
|
||||
$oFormat = new DateTimeFormat($sFormat);
|
||||
$sRegExp = $oFormat->ToRegExpr();
|
||||
if (!preg_match('/'.$sRegExp.'/', $this->m_aData[$iRow][$iCol]))
|
||||
$sRegExp = $oFormat->ToRegExpr('/');
|
||||
if (!preg_match($sRegExp, $this->m_aData[$iRow][$iCol]))
|
||||
{
|
||||
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-DateFormat'));
|
||||
}
|
||||
@@ -1306,5 +1311,3 @@ EOF
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
|
||||
@@ -112,7 +112,9 @@ class BulkExportResultGC implements iBackgroundProcess
|
||||
}
|
||||
$iProcessed++;
|
||||
@unlink($oResult->Get('temp_file_path'));
|
||||
utils::PushArchiveMode(false);
|
||||
$oResult->DBDelete();
|
||||
utils::PopArchiveMode();
|
||||
}
|
||||
return "Cleaned $iProcessed old export results(s).";
|
||||
}
|
||||
@@ -306,7 +308,10 @@ abstract class BulkExport
|
||||
$this->oBulkExportResult->Set('temp_file_path', $this->sTmpFile);
|
||||
}
|
||||
$this->oBulkExportResult->Set('status_info', json_encode($this->GetStatusInfo()));
|
||||
return $this->oBulkExportResult->DBWrite();
|
||||
utils::PushArchiveMode(false);
|
||||
$ret = $this->oBulkExportResult->DBWrite();
|
||||
utils::PopArchiveMode();
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function Cleanup()
|
||||
@@ -318,7 +323,9 @@ abstract class BulkExport
|
||||
{
|
||||
@unlink($sFilename);
|
||||
}
|
||||
utils::PushArchiveMode(false);
|
||||
$this->oBulkExportResult->DBDelete();
|
||||
utils::PopArchiveMode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -412,7 +419,11 @@ abstract class BulkExport
|
||||
// The built-in exports
|
||||
require_once(APPROOT.'core/tabularbulkexport.class.inc.php');
|
||||
require_once(APPROOT.'core/htmlbulkexport.class.inc.php');
|
||||
require_once(APPROOT.'core/pdfbulkexport.class.inc.php');
|
||||
if (extension_loaded('gd'))
|
||||
{
|
||||
// PDF export - via TCPDF - requires GD
|
||||
require_once(APPROOT.'core/pdfbulkexport.class.inc.php');
|
||||
}
|
||||
require_once(APPROOT.'core/csvbulkexport.class.inc.php');
|
||||
require_once(APPROOT.'core/excelbulkexport.class.inc.php');
|
||||
require_once(APPROOT.'core/spreadsheetbulkexport.class.inc.php');
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* Class cmdbObject
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -231,6 +231,202 @@ abstract class CMDBObject extends DBObject
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sAttCode
|
||||
* @param $original Original value
|
||||
* @param $value Current value
|
||||
*/
|
||||
protected function RecordAttChange($sAttCode, $original, $value)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
if ($oAttDef->IsExternalField()) return;
|
||||
if ($oAttDef->IsLinkSet()) return;
|
||||
if ($oAttDef->GetTrackingLevel() == ATTRIBUTE_TRACKING_NONE) return;
|
||||
|
||||
if ($oAttDef instanceOf AttributeOneWayPassword)
|
||||
{
|
||||
// One Way encrypted passwords' history is stored -one way- encrypted
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeOneWayPassword");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
|
||||
if (is_null($original))
|
||||
{
|
||||
$original = '';
|
||||
}
|
||||
$oMyChangeOp->Set("prev_pwd", $original);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeEncryptedString)
|
||||
{
|
||||
// Encrypted string history is stored encrypted
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeEncrypted");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
|
||||
if (is_null($original))
|
||||
{
|
||||
$original = '';
|
||||
}
|
||||
$oMyChangeOp->Set("prevstring", $original);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeBlob)
|
||||
{
|
||||
// Data blobs
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeBlob");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
|
||||
if (is_null($original))
|
||||
{
|
||||
$original = new ormDocument();
|
||||
}
|
||||
$oMyChangeOp->Set("prevdata", $original);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeStopWatch)
|
||||
{
|
||||
// Stop watches - record changes for sub items only (they are visible, the rest is not visible)
|
||||
//
|
||||
foreach ($oAttDef->ListSubItems() as $sSubItemAttCode => $oSubItemAttDef)
|
||||
{
|
||||
$item_value = $oAttDef->GetSubItemValue($oSubItemAttDef->Get('item_code'), $value, $this);
|
||||
$item_original = $oAttDef->GetSubItemValue($oSubItemAttDef->Get('item_code'), $original, $this);
|
||||
|
||||
if ($item_value != $item_original)
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sSubItemAttCode);
|
||||
|
||||
$oMyChangeOp->Set("oldvalue", $item_original);
|
||||
$oMyChangeOp->Set("newvalue", $item_value);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeCaseLog)
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeCaseLog");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
|
||||
$oMyChangeOp->Set("lastentry", $value->GetLatestEntryIndex());
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeLongText)
|
||||
{
|
||||
// Data blobs
|
||||
if ($oAttDef->GetFormat() == 'html')
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeHTML");
|
||||
}
|
||||
else
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeLongText");
|
||||
}
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
|
||||
if (!is_null($original) && ($original instanceof ormCaseLog))
|
||||
{
|
||||
$original = $original->GetText();
|
||||
}
|
||||
$oMyChangeOp->Set("prevdata", $original);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeText)
|
||||
{
|
||||
// Data blobs
|
||||
if ($oAttDef->GetFormat() == 'html')
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeHTML");
|
||||
}
|
||||
else
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeText");
|
||||
}
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
|
||||
if (!is_null($original) && ($original instanceof ormCaseLog))
|
||||
{
|
||||
$original = $original->GetText();
|
||||
}
|
||||
$oMyChangeOp->Set("prevdata", $original);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeBoolean)
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
$oMyChangeOp->Set("oldvalue", $original ? 1 : 0);
|
||||
$oMyChangeOp->Set("newvalue", $value ? 1 : 0);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeHierarchicalKey)
|
||||
{
|
||||
// Hierarchical keys
|
||||
//
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
$oMyChangeOp->Set("oldvalue", $original);
|
||||
$oMyChangeOp->Set("newvalue", $value[$sAttCode]);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeCustomFields)
|
||||
{
|
||||
// Custom fields
|
||||
//
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeCustomFields");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
$oMyChangeOp->Set("prevdata", json_encode($original->GetValues()));
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeURL)
|
||||
{
|
||||
// URLs
|
||||
//
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeURL");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
$oMyChangeOp->Set("oldvalue", $original);
|
||||
$oMyChangeOp->Set("newvalue", $value);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Scalars
|
||||
//
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
$oMyChangeOp->Set("oldvalue", $original);
|
||||
$oMyChangeOp->Set("newvalue", $value);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $aValues
|
||||
* @param array $aOrigValues
|
||||
*/
|
||||
protected function RecordAttChanges(array $aValues, array $aOrigValues)
|
||||
{
|
||||
parent::RecordAttChanges($aValues, $aOrigValues);
|
||||
@@ -239,11 +435,6 @@ abstract class CMDBObject extends DBObject
|
||||
//
|
||||
foreach ($aValues as $sAttCode=> $value)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
if ($oAttDef->IsExternalField()) continue;
|
||||
if ($oAttDef->IsLinkSet()) continue;
|
||||
if ($oAttDef->GetTrackingLevel() == ATTRIBUTE_TRACKING_NONE) continue;
|
||||
|
||||
if (array_key_exists($sAttCode, $aOrigValues))
|
||||
{
|
||||
$original = $aOrigValues[$sAttCode];
|
||||
@@ -252,185 +443,7 @@ abstract class CMDBObject extends DBObject
|
||||
{
|
||||
$original = null;
|
||||
}
|
||||
|
||||
if ($oAttDef instanceOf AttributeOneWayPassword)
|
||||
{
|
||||
// One Way encrypted passwords' history is stored -one way- encrypted
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeOneWayPassword");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
|
||||
if (is_null($original))
|
||||
{
|
||||
$original = '';
|
||||
}
|
||||
$oMyChangeOp->Set("prev_pwd", $original);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeEncryptedString)
|
||||
{
|
||||
// Encrypted string history is stored encrypted
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeEncrypted");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
|
||||
if (is_null($original))
|
||||
{
|
||||
$original = '';
|
||||
}
|
||||
$oMyChangeOp->Set("prevstring", $original);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeBlob)
|
||||
{
|
||||
// Data blobs
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeBlob");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
|
||||
if (is_null($original))
|
||||
{
|
||||
$original = new ormDocument();
|
||||
}
|
||||
$oMyChangeOp->Set("prevdata", $original);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeStopWatch)
|
||||
{
|
||||
// Stop watches - record changes for sub items only (they are visible, the rest is not visible)
|
||||
//
|
||||
foreach ($oAttDef->ListSubItems() as $sSubItemAttCode => $oSubItemAttDef)
|
||||
{
|
||||
$item_value = $oAttDef->GetSubItemValue($oSubItemAttDef->Get('item_code'), $value, $this);
|
||||
$item_original = $oAttDef->GetSubItemValue($oSubItemAttDef->Get('item_code'), $original, $this);
|
||||
|
||||
if ($item_value != $item_original)
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sSubItemAttCode);
|
||||
|
||||
$oMyChangeOp->Set("oldvalue", $item_original);
|
||||
$oMyChangeOp->Set("newvalue", $item_value);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeCaseLog)
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeCaseLog");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
|
||||
$oMyChangeOp->Set("lastentry", $value->GetLatestEntryIndex());
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeLongText)
|
||||
{
|
||||
// Data blobs
|
||||
if ($oAttDef->GetFormat() == 'html')
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeHTML");
|
||||
}
|
||||
else
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeLongText");
|
||||
}
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
|
||||
if (!is_null($original) && ($original instanceof ormCaseLog))
|
||||
{
|
||||
$original = $original->GetText();
|
||||
}
|
||||
$oMyChangeOp->Set("prevdata", $original);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeText)
|
||||
{
|
||||
// Data blobs
|
||||
if ($oAttDef->GetFormat() == 'html')
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeHTML");
|
||||
}
|
||||
else
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeText");
|
||||
}
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
|
||||
if (!is_null($original) && ($original instanceof ormCaseLog))
|
||||
{
|
||||
$original = $original->GetText();
|
||||
}
|
||||
$oMyChangeOp->Set("prevdata", $original);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeBoolean)
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
$oMyChangeOp->Set("oldvalue", $original ? 1 : 0);
|
||||
$oMyChangeOp->Set("newvalue", $value ? 1 : 0);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeHierarchicalKey)
|
||||
{
|
||||
// Hierarchical keys
|
||||
//
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
$oMyChangeOp->Set("oldvalue", $original);
|
||||
$oMyChangeOp->Set("newvalue", $value[$sAttCode]);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeCustomFields)
|
||||
{
|
||||
// Custom fields
|
||||
//
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeCustomFields");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
$oMyChangeOp->Set("prevdata", json_encode($original->GetValues()));
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeURL)
|
||||
{
|
||||
// URLs
|
||||
//
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeURL");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
$oMyChangeOp->Set("oldvalue", $original);
|
||||
$oMyChangeOp->Set("newvalue", $value);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Scalars
|
||||
//
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
$oMyChangeOp->Set("oldvalue", $original);
|
||||
$oMyChangeOp->Set("newvalue", $value);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
$this->RecordAttChange($sAttCode, $original, $value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -596,6 +609,34 @@ abstract class CMDBObject extends DBObject
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function DBArchive()
|
||||
{
|
||||
// Note: do the job anyway, so as to repair any DB discrepancy
|
||||
$bOriginal = $this->Get('archive_flag');
|
||||
parent::DBArchive();
|
||||
|
||||
if (!$bOriginal)
|
||||
{
|
||||
utils::PushArchiveMode(false);
|
||||
$this->RecordAttChange('archive_flag', false, true);
|
||||
utils::PopArchiveMode();
|
||||
}
|
||||
}
|
||||
|
||||
public function DBUnarchive()
|
||||
{
|
||||
// Note: do the job anyway, so as to repair any DB discrepancy
|
||||
$bOriginal = $this->Get('archive_flag');
|
||||
parent::DBUnarchive();
|
||||
|
||||
if ($bOriginal)
|
||||
{
|
||||
utils::PushArchiveMode(false);
|
||||
$this->RecordAttChange('archive_flag', true, false);
|
||||
utils::PopArchiveMode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -649,5 +690,5 @@ class CMDBObjectSet extends DBObjectSet
|
||||
$oRetSet->AddObjectExtended($aObjectsByClassAlias);
|
||||
}
|
||||
return $oRetSet;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,13 +33,15 @@ class MySQLException extends CoreException
|
||||
{
|
||||
if ($oException != null)
|
||||
{
|
||||
$aContext['mysql_error'] = $oException->getCode();
|
||||
$aContext['mysql_errno'] = $oException->getMessage();
|
||||
$aContext['mysql_errno'] = $oException->getCode();
|
||||
$this->code = $oException->getCode();
|
||||
$aContext['mysql_error'] = $oException->getMessage();
|
||||
}
|
||||
else
|
||||
{
|
||||
$aContext['mysql_error'] = CMDBSource::GetError();
|
||||
$aContext['mysql_errno'] = CMDBSource::GetErrNo();
|
||||
$this->code = CMDBSource::GetErrNo();
|
||||
$aContext['mysql_error'] = CMDBSource::GetError();
|
||||
}
|
||||
parent::__construct($sIssue, $aContext);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -18,8 +18,9 @@
|
||||
|
||||
|
||||
define('ITOP_APPLICATION', 'iTop');
|
||||
define('ITOP_VERSION', '$ITOP_VERSION$');
|
||||
define('ITOP_REVISION', '$WCREV$');
|
||||
define('ITOP_APPLICATION_SHORT', 'iTop');
|
||||
define('ITOP_VERSION', '2.4.0');
|
||||
define('ITOP_REVISION', 'svn');
|
||||
define('ITOP_BUILD_DATE', '$WCNOW$');
|
||||
|
||||
define('ACCESS_USER_WRITE', 1);
|
||||
@@ -48,7 +49,6 @@ define ('DEFAULT_LOG_GLOBAL', true);
|
||||
define ('DEFAULT_LOG_NOTIFICATION', true);
|
||||
define ('DEFAULT_LOG_ISSUE', true);
|
||||
define ('DEFAULT_LOG_WEB_SERVICE', true);
|
||||
define ('DEFAULT_LOG_QUERIES', false);
|
||||
|
||||
define ('DEFAULT_QUERY_CACHE_ENABLED', true);
|
||||
|
||||
@@ -195,14 +195,22 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'allow_target_creation' => array(
|
||||
'type' => 'bool',
|
||||
'description' => 'Displays the + button on external keys to create target objects',
|
||||
'default' => true,
|
||||
'value' => true,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'allow_menu_on_linkset' => array(
|
||||
'type' => 'bool',
|
||||
'description' => 'Display Action menus in view mode on any LinkedSet with edit_mode != none',
|
||||
'default' => false,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'allow_target_creation' => array(
|
||||
'type' => 'bool',
|
||||
'description' => 'Displays the + button on external keys to create target objects',
|
||||
'default' => true,
|
||||
'value' => true,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
// Levels that trigger a confirmation in the CSV import/synchro wizard
|
||||
'csv_import_min_object_confirmation' => array(
|
||||
'type' => 'integer',
|
||||
@@ -246,7 +254,7 @@ class Config
|
||||
),
|
||||
'access_mode' => array(
|
||||
'type' => 'integer',
|
||||
'description' => 'Combination of flags (ACCESS_USER_WRITE | ACCESS_ADMIN_WRITE, or ACCESS_FULL)',
|
||||
'description' => 'Access mode: ACCESS_READONLY = 0, ACCESS_ADMIN_WRITE = 2, ACCESS_FULL = 3',
|
||||
'default' => ACCESS_FULL,
|
||||
'value' => ACCESS_FULL,
|
||||
'source_of_value' => '',
|
||||
@@ -428,6 +436,14 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
),
|
||||
'apc_cache_emulation.max_entries' => array(
|
||||
'type' => 'integer',
|
||||
'description' => 'Maximum number of cache entries (0 means no limit)',
|
||||
'default' => 1000,
|
||||
'value' => 1000,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'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',
|
||||
@@ -930,6 +946,46 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'obsolescence.show_obsolete_data' => array(
|
||||
'type' => 'bool',
|
||||
'description' => 'Default value for the user preference "show obsolete data"',
|
||||
'default' => false,
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'obsolescence.date_update_interval' => array(
|
||||
'type' => 'integer',
|
||||
'description' => 'Delay in seconds between two refreshes of the obsolescence dates.',
|
||||
'default' => 600,
|
||||
'value' => 600,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'disable_attachments_download_legacy_portal' => array(
|
||||
'type' => 'bool',
|
||||
'description' => 'Disable attachments download from legacy portal',
|
||||
'default' => true,
|
||||
'value' => true,
|
||||
'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 auto-complete mode)',
|
||||
'default' => array(),
|
||||
'value' => array(),
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
),
|
||||
);
|
||||
|
||||
public function IsProperty($sPropCode)
|
||||
@@ -990,7 +1046,6 @@ class Config
|
||||
protected $m_bLogNotification;
|
||||
protected $m_bLogIssue;
|
||||
protected $m_bLogWebService;
|
||||
protected $m_bLogQueries; // private setting
|
||||
protected $m_bQueryCacheEnabled; // private setting
|
||||
|
||||
/**
|
||||
@@ -1085,7 +1140,6 @@ class Config
|
||||
$this->m_sExtAuthVariable = DEFAULT_EXT_AUTH_VARIABLE;
|
||||
$this->m_sEncryptionKey = DEFAULT_ENCRYPTION_KEY;
|
||||
$this->m_aCharsets = array();
|
||||
$this->m_bLogQueries = DEFAULT_LOG_QUERIES;
|
||||
$this->m_bQueryCacheEnabled = DEFAULT_QUERY_CACHE_ENABLED;
|
||||
|
||||
$this->m_aModuleSettings = array();
|
||||
@@ -1198,7 +1252,6 @@ class Config
|
||||
$this->m_bLogNotification = isset($MySettings['log_notification']) ? (bool) trim($MySettings['log_notification']) : DEFAULT_LOG_NOTIFICATION;
|
||||
$this->m_bLogIssue = isset($MySettings['log_issue']) ? (bool) trim($MySettings['log_issue']) : DEFAULT_LOG_ISSUE;
|
||||
$this->m_bLogWebService = isset($MySettings['log_web_service']) ? (bool) trim($MySettings['log_web_service']) : DEFAULT_LOG_WEB_SERVICE;
|
||||
$this->m_bLogQueries = isset($MySettings['log_queries']) ? (bool) trim($MySettings['log_queries']) : DEFAULT_LOG_QUERIES;
|
||||
$this->m_bQueryCacheEnabled = isset($MySettings['query_cache_enabled']) ? (bool) trim($MySettings['query_cache_enabled']) : DEFAULT_QUERY_CACHE_ENABLED;
|
||||
|
||||
$this->m_iMinDisplayLimit = isset($MySettings['min_display_limit']) ? trim($MySettings['min_display_limit']) : DEFAULT_MIN_DISPLAY_LIMIT;
|
||||
@@ -1317,7 +1370,7 @@ class Config
|
||||
|
||||
public function GetLogQueries()
|
||||
{
|
||||
return $this->m_bLogQueries;
|
||||
return false;
|
||||
}
|
||||
|
||||
public function GetQueryCacheEnabled()
|
||||
@@ -1519,7 +1572,6 @@ class Config
|
||||
$aSettings['log_notification'] = $this->m_bLogNotification;
|
||||
$aSettings['log_issue'] = $this->m_bLogIssue;
|
||||
$aSettings['log_web_service'] = $this->m_bLogWebService;
|
||||
$aSettings['log_queries'] = $this->m_bLogQueries;
|
||||
$aSettings['query_cache_enabled'] = $this->m_bQueryCacheEnabled;
|
||||
$aSettings['min_display_limit'] = $this->m_iMinDisplayLimit;
|
||||
$aSettings['max_display_limit'] = $this->m_iMaxDisplayLimit;
|
||||
@@ -1578,7 +1630,6 @@ class Config
|
||||
'log_notification' => $this->m_bLogNotification,
|
||||
'log_issue' => $this->m_bLogIssue,
|
||||
'log_web_service' => $this->m_bLogWebService,
|
||||
'log_queries' => $this->m_bLogQueries,
|
||||
'query_cache_enabled' => $this->m_bQueryCacheEnabled,
|
||||
'secure_connection_required' => $this->m_bSecureConnectionRequired,
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2016 Combodo SARL
|
||||
// Copyright (C) 2016-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -29,13 +29,13 @@
|
||||
*
|
||||
* if (ContextTag::Check('GUI:Portal'))
|
||||
*
|
||||
* @copyright Copyright (C) 2016 Combodo SARL
|
||||
* @copyright Copyright (C) 2016-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
class ContextTag
|
||||
{
|
||||
protected static $aStack;
|
||||
protected static $aStack = array();
|
||||
|
||||
/**
|
||||
* Store a context tag on the stack
|
||||
|
||||
@@ -112,4 +112,13 @@ class SecurityException extends CoreException
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Throwned when querying on an object that exists in the database but is archived
|
||||
*
|
||||
* @see N.1108
|
||||
*/
|
||||
class ArchivedObjectException extends CoreException
|
||||
{
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
@@ -413,10 +413,16 @@ EOF
|
||||
/**
|
||||
* Get the regular expression to (approximately) validate a date/time for the current format
|
||||
* The validation does not take into account the number of days in a month (i.e. June 31st will pass, as well as Feb 30th!)
|
||||
* @param string $sDelimiter Surround the regexp (and escape) if needed
|
||||
* @return string The regular expression in PCRE syntax
|
||||
*/
|
||||
public function ToRegExpr()
|
||||
public function ToRegExpr($sDelimiter = null)
|
||||
{
|
||||
return '^'.$this->Transform('regexpr', "\\%s", false /* escape all */, '.?*$^()[]/:').'$';
|
||||
$sRet = '^'.$this->Transform('regexpr', "\\%s", false /* escape all */, '.?*$^()[]:').'$';
|
||||
if ($sDelimiter !== null)
|
||||
{
|
||||
$sRet = $sDelimiter.str_replace($sDelimiter, '\\'.$sDelimiter, $sRet).$sDelimiter;
|
||||
}
|
||||
return $sRet;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -116,7 +116,7 @@ abstract class DBObject implements iDisplay
|
||||
// set default values
|
||||
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef)
|
||||
{
|
||||
$this->m_aCurrValues[$sAttCode] = $oAttDef->GetDefaultValue($this);
|
||||
$this->m_aCurrValues[$sAttCode] = $this->GetDefaultValue($sAttCode);
|
||||
$this->m_aOrigValues[$sAttCode] = null;
|
||||
if ($oAttDef->IsExternalField() || ($oAttDef instanceof AttributeFriendlyName))
|
||||
{
|
||||
@@ -192,10 +192,14 @@ abstract class DBObject implements iDisplay
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $bAllowAllData DEPRECATED: the reload must never fail!
|
||||
* @throws CoreException
|
||||
*/
|
||||
public function Reload($bAllowAllData = false)
|
||||
{
|
||||
assert($this->m_bIsInDB);
|
||||
$aRow = MetaModel::MakeSingleRow(get_class($this), $this->m_iKey, false /* must be found */, $bAllowAllData/* in the future $this->m_bAllowAllData ??*/);
|
||||
$aRow = MetaModel::MakeSingleRow(get_class($this), $this->m_iKey, false /* must be found */, true /* AllowAllData */);
|
||||
if (empty($aRow))
|
||||
{
|
||||
throw new CoreException("Failed to reload object of class '".get_class($this)."', id = ".$this->m_iKey);
|
||||
@@ -208,23 +212,7 @@ abstract class DBObject implements iDisplay
|
||||
{
|
||||
if (!$oAttDef->IsLinkSet()) continue;
|
||||
|
||||
// Load the link information
|
||||
$sLinkClass = $oAttDef->GetLinkedClass();
|
||||
$sExtKeyToMe = $oAttDef->GetExtKeyToMe();
|
||||
|
||||
// The class to target is not the current class, because if this is a derived class,
|
||||
// it may differ from the target class, then things start to become confusing
|
||||
$oRemoteExtKeyAtt = MetaModel::GetAttributeDef($sLinkClass, $sExtKeyToMe);
|
||||
$sMyClass = $oRemoteExtKeyAtt->GetTargetClass();
|
||||
|
||||
$oMyselfSearch = new DBObjectSearch($sMyClass);
|
||||
$oMyselfSearch->AddCondition('id', $this->m_iKey, '=');
|
||||
|
||||
$oLinkSearch = new DBObjectSearch($sLinkClass);
|
||||
$oLinkSearch->AddCondition_PointingTo($oMyselfSearch, $sExtKeyToMe);
|
||||
$oLinks = new DBObjectSet($oLinkSearch);
|
||||
|
||||
$this->m_aCurrValues[$sAttCode] = $oLinks;
|
||||
$this->m_aCurrValues[$sAttCode] = $oAttDef->GetDefaultValue($this);
|
||||
$this->m_aOrigValues[$sAttCode] = clone $this->m_aCurrValues[$sAttCode];
|
||||
$this->m_aLoadedAtt[$sAttCode] = true;
|
||||
}
|
||||
@@ -348,7 +336,14 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
return $bFullyLoaded;
|
||||
}
|
||||
|
||||
|
||||
protected function _Set($sAttCode, $value)
|
||||
{
|
||||
$this->m_aCurrValues[$sAttCode] = $value;
|
||||
$this->m_aTouchedAtt[$sAttCode] = true;
|
||||
unset($this->m_aModifiedAtt[$sAttCode]);
|
||||
}
|
||||
|
||||
public function Set($sAttCode, $value)
|
||||
{
|
||||
if ($sAttCode == 'finalclass')
|
||||
@@ -356,8 +351,15 @@ abstract class DBObject implements iDisplay
|
||||
// Ignore it - this attribute is set upon object creation and that's it
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
|
||||
if (!$oAttDef->IsWritable())
|
||||
{
|
||||
$sClass = get_class($this);
|
||||
throw new Exception("Attempting to set the value on the read-only attribute $sClass::$sAttCode");
|
||||
}
|
||||
|
||||
if ($this->m_bIsInDB && !$this->m_bFullyLoaded && !$this->m_bDirty)
|
||||
{
|
||||
// First time Set is called... ensure that the object gets fully loaded
|
||||
@@ -380,7 +382,7 @@ abstract class DBObject implements iDisplay
|
||||
|
||||
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sCode => $oDef)
|
||||
{
|
||||
if (($oDef->IsExternalField() || ($oDef instanceof AttributeFriendlyName)) && ($oDef->GetKeyAttCode() == $sAttCode))
|
||||
if ($oDef->IsExternalField() && ($oDef->GetKeyAttCode() == $sAttCode))
|
||||
{
|
||||
$this->m_aCurrValues[$sCode] = $value->Get($oDef->GetExtAttCode());
|
||||
$this->m_aLoadedAtt[$sCode] = true;
|
||||
@@ -393,48 +395,28 @@ abstract class DBObject implements iDisplay
|
||||
// Invalidate the corresponding fields so that they get reloaded in case they are needed (See Get())
|
||||
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sCode => $oDef)
|
||||
{
|
||||
if (($oDef->IsExternalField() || ($oDef instanceof AttributeFriendlyName)) && ($oDef->GetKeyAttCode() == $sAttCode))
|
||||
if ($oDef->IsExternalField() && ($oDef->GetKeyAttCode() == $sAttCode))
|
||||
{
|
||||
$this->m_aCurrValues[$sCode] = $oDef->GetDefaultValue($this);
|
||||
$this->m_aCurrValues[$sCode] = $this->GetDefaultValue($sCode);
|
||||
unset($this->m_aLoadedAtt[$sCode]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if($oAttDef->IsLinkSet())
|
||||
if ($oAttDef->IsLinkSet() && ($value != null))
|
||||
{
|
||||
if (is_null($value))
|
||||
{
|
||||
// Normalize
|
||||
$value = DBObjectSet::FromScratch($oAttDef->GetLinkedClass());
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((get_class($value) != 'DBObjectSet') && !is_subclass_of($value, 'DBObjectSet'))
|
||||
{
|
||||
throw new CoreUnexpectedValue("expecting a set of persistent objects (found a '".get_class($value)."'), setting default value (empty list)");
|
||||
}
|
||||
}
|
||||
|
||||
$oObjectSet = $value;
|
||||
$sSetClass = $oObjectSet->GetClass();
|
||||
$sLinkClass = $oAttDef->GetLinkedClass();
|
||||
// not working fine :-( if (!is_subclass_of($sSetClass, $sLinkClass))
|
||||
if ($sSetClass != $sLinkClass)
|
||||
{
|
||||
throw new CoreUnexpectedValue("expecting a set of '$sLinkClass' objects (found a set of '$sSetClass'), setting default value (empty list)");
|
||||
}
|
||||
$realvalue = clone $this->m_aCurrValues[$sAttCode];
|
||||
$realvalue->UpdateFromCompleteList($value);
|
||||
}
|
||||
|
||||
$realvalue = $oAttDef->MakeRealValue($value, $this);
|
||||
|
||||
$this->m_aCurrValues[$sAttCode] = $realvalue;
|
||||
$this->m_aTouchedAtt[$sAttCode] = true;
|
||||
unset($this->m_aModifiedAtt[$sAttCode]);
|
||||
else
|
||||
{
|
||||
$realvalue = $oAttDef->MakeRealValue($value, $this);
|
||||
}
|
||||
$this->_Set($sAttCode, $realvalue);
|
||||
|
||||
foreach (MetaModel::ListMetaAttributes(get_class($this), $sAttCode) as $sMetaAttCode => $oMetaAttDef)
|
||||
{
|
||||
$this->Set($sMetaAttCode, $oMetaAttDef->MapValue($this));
|
||||
$this->_Set($sMetaAttCode, $oMetaAttDef->MapValue($this));
|
||||
}
|
||||
|
||||
// The object has changed, reset caches
|
||||
@@ -538,7 +520,7 @@ abstract class DBObject implements iDisplay
|
||||
else
|
||||
{
|
||||
// Not loaded... is it related to an external key?
|
||||
if ($oAttDef->IsExternalField() || ($oAttDef instanceof AttributeFriendlyName))
|
||||
if ($oAttDef->IsExternalField())
|
||||
{
|
||||
// Let's get the object and compute all of the corresponding attributes
|
||||
// (i.e not only the requested attribute)
|
||||
@@ -560,7 +542,7 @@ abstract class DBObject implements iDisplay
|
||||
|
||||
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sCode => $oDef)
|
||||
{
|
||||
if (($oDef->IsExternalField() || ($oDef instanceof AttributeFriendlyName)) && ($oDef->GetKeyAttCode() == $sExtKeyAttCode))
|
||||
if ($oDef->IsExternalField() && ($oDef->GetKeyAttCode() == $sExtKeyAttCode))
|
||||
{
|
||||
if ($oRemote)
|
||||
{
|
||||
@@ -568,7 +550,7 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->m_aCurrValues[$sCode] = $oDef->GetDefaultValue($this);
|
||||
$this->m_aCurrValues[$sCode] = $this->GetDefaultValue($sCode);
|
||||
}
|
||||
$this->m_aLoadedAtt[$sCode] = true;
|
||||
}
|
||||
@@ -578,7 +560,7 @@ abstract class DBObject implements iDisplay
|
||||
$value = $this->m_aCurrValues[$sAttCode];
|
||||
}
|
||||
|
||||
if ($value instanceof DBObjectSet)
|
||||
if ($value instanceof ormLinkSet)
|
||||
{
|
||||
$value->Rewind();
|
||||
}
|
||||
@@ -594,6 +576,19 @@ abstract class DBObject implements iDisplay
|
||||
return $this->m_aOrigValues[$sAttCode];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default value of the $sAttCode. By default, returns the default value of the AttributeDefinition.
|
||||
* Overridable.
|
||||
*
|
||||
* @param $sAttCode
|
||||
* @return mixed
|
||||
*/
|
||||
public function GetDefaultValue($sAttCode)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
return $oAttDef->GetDefaultValue($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns data loaded by the mean of a dynamic and explicit JOIN
|
||||
*/
|
||||
@@ -738,7 +733,9 @@ abstract class DBObject implements iDisplay
|
||||
else
|
||||
{
|
||||
$sHtmlLabel = htmlentities($this->Get($sAttCode.'_friendlyname'), ENT_QUOTES, 'UTF-8');
|
||||
return $this->MakeHyperLink($sTargetClass, $iTargetKey, $sHtmlLabel);
|
||||
$bArchived = $this->IsArchived($sAttCode);
|
||||
$bObsolete = $this->IsObsolete($sAttCode);
|
||||
return $this->MakeHyperLink($sTargetClass, $iTargetKey, $sHtmlLabel, null, true, $bArchived, $bObsolete);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -817,10 +814,12 @@ abstract class DBObject implements iDisplay
|
||||
* @param string $sHtmlLabel Label with HTML entities escaped (< escaped as <)
|
||||
* @param null $sUrlMakerClass
|
||||
* @param bool|true $bWithNavigationContext
|
||||
* @param bool|false $bArchived
|
||||
* @param bool|false $bObsolete
|
||||
* @return string
|
||||
* @throws DictExceptionMissingString
|
||||
*/
|
||||
public static function MakeHyperLink($sObjClass, $sObjKey, $sHtmlLabel = '', $sUrlMakerClass = null, $bWithNavigationContext = true)
|
||||
public static function MakeHyperLink($sObjClass, $sObjKey, $sHtmlLabel = '', $sUrlMakerClass = null, $bWithNavigationContext = true, $bArchived = false, $bObsolete = false)
|
||||
{
|
||||
if ($sObjKey <= 0) return '<em>'.Dict::S('UI:UndefinedObject').'</em>'; // Objects built in memory have negative IDs
|
||||
|
||||
@@ -843,19 +842,58 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
$sHint = MetaModel::GetName($sObjClass)."::$sObjKey";
|
||||
$sUrl = ApplicationContext::MakeObjectUrl($sObjClass, $sObjKey, $sUrlMakerClass, $bWithNavigationContext);
|
||||
if (strlen($sUrl) > 0)
|
||||
|
||||
$bClickable = !$bArchived || utils::IsArchiveMode();
|
||||
if ($bArchived)
|
||||
{
|
||||
return "<a href=\"$sUrl\" title=\"$sHint\">$sHtmlLabel</a>";
|
||||
$sSpanClass = 'archived';
|
||||
$sFA = 'fa-archive object-archived';
|
||||
$sHint = Dict::S('ObjectRef:Archived');
|
||||
}
|
||||
elseif ($bObsolete)
|
||||
{
|
||||
$sSpanClass = 'obsolete';
|
||||
$sFA = 'fa-eye-slash object-obsolete';
|
||||
$sHint = Dict::S('ObjectRef:Obsolete');
|
||||
}
|
||||
else
|
||||
{
|
||||
return $sHtmlLabel;
|
||||
$sSpanClass = '';
|
||||
$sFA = '';
|
||||
}
|
||||
if ($sFA == '')
|
||||
{
|
||||
$sIcon = '';
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($bClickable)
|
||||
{
|
||||
$sIcon = "<span class=\"object-ref-icon fa $sFA fa-1x fa-fw\"></span>";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sIcon = "<span class=\"object-ref-icon-disabled fa $sFA fa-1x fa-fw\"></span>";
|
||||
}
|
||||
}
|
||||
|
||||
if ($bClickable && (strlen($sUrl) > 0))
|
||||
{
|
||||
$sHLink = "<a class=\"object-ref-link\" href=\"$sUrl\">$sIcon$sHtmlLabel</a>";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sHLink = $sIcon.$sHtmlLabel;
|
||||
}
|
||||
$sRet = "<span class=\"object-ref $sSpanClass\" title=\"$sHint\">$sHLink</span>";
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
public function GetHyperlink($sUrlMakerClass = null, $bWithNavigationContext = true)
|
||||
{
|
||||
return self::MakeHyperLink(get_class($this), $this->GetKey(), $this->GetName(), $sUrlMakerClass, $bWithNavigationContext);
|
||||
$bArchived = $this->IsArchived();
|
||||
$bObsolete = $this->IsObsolete();
|
||||
return self::MakeHyperLink(get_class($this), $this->GetKey(), $this->GetName(), $sUrlMakerClass, $bWithNavigationContext, $bArchived, $bObsolete);
|
||||
}
|
||||
|
||||
public static function ComputeStandardUIPage($sClass)
|
||||
@@ -1056,6 +1094,67 @@ abstract class DBObject implements iDisplay
|
||||
return $iFlags | $iSynchroFlags; // Combine both sets of flags
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of flags (OPT_ATT_HIDDEN, OPT_ATT_READONLY, OPT_ATT_MANDATORY...)
|
||||
* for the given attribute in a transition
|
||||
* @param $sAttCode string $sAttCode The code of the attribute
|
||||
* @param $sStimulus string The stimulus code to apply
|
||||
* @param $aReasons array To store the reasons why the attribute is read-only (info about the synchro replicas)
|
||||
* @param $sOriginState string The state from which to apply $sStimulus, if empty current state will be used
|
||||
* @return integer Flags: the binary combination of the flags applicable to this attribute
|
||||
*/
|
||||
public function GetTransitionFlags($sAttCode, $sStimulus, &$aReasons = array(), $sOriginState = '')
|
||||
{
|
||||
$iFlags = 0; // By default (if no lifecycle) no flag at all
|
||||
|
||||
$sStateAttCode = MetaModel::GetStateAttributeCode(get_class($this));
|
||||
// If no state attribute, there is no lifecycle
|
||||
if (empty($sStateAttCode))
|
||||
{
|
||||
return $iFlags;
|
||||
}
|
||||
|
||||
// Retrieving current state if necessary
|
||||
if ($sOriginState === '')
|
||||
{
|
||||
$sOriginState = $this->Get($sStateAttCode);
|
||||
}
|
||||
|
||||
// Retrieving attribute flags
|
||||
$iAttributeFlags = $this->GetAttributeFlags($sAttCode, $aReasons, $sOriginState);
|
||||
|
||||
// Retrieving transition flags
|
||||
$iTransitionFlags = MetaModel::GetTransitionFlags(get_class($this), $sOriginState, $sStimulus, $sAttCode);
|
||||
|
||||
// Merging transition flags with attribute flags
|
||||
$iFlags = $iTransitionFlags | $iAttributeFlags;
|
||||
|
||||
return $iFlags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of attribute codes (with their flags) when $sStimulus is applied on the object in the $sOriginState state.
|
||||
* Note: Attributes (and flags) from the target state and the transition are combined.
|
||||
*
|
||||
* @param $sStimulus string
|
||||
* @param $sOriginState string Default is current state
|
||||
* @return array
|
||||
*/
|
||||
public function GetTransitionAttributes($sStimulus, $sOriginState = null)
|
||||
{
|
||||
$sObjClass = get_class($this);
|
||||
|
||||
// Defining current state as origin state if not specified
|
||||
if($sOriginState === null)
|
||||
{
|
||||
$sOriginState = $this->GetState();
|
||||
}
|
||||
|
||||
$aAttributes = MetaModel::GetTransitionAttributes($sObjClass, $sStimulus, $sOriginState);
|
||||
|
||||
return $aAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of flags (OPT_ATT_HIDDEN, OPT_ATT_READONLY, OPT_ATT_MANDATORY...)
|
||||
* for the given attribute for the current state of the object considered as an INITIAL state
|
||||
@@ -1167,7 +1266,7 @@ abstract class DBObject implements iDisplay
|
||||
|
||||
$aChanges = $this->ListChanges();
|
||||
|
||||
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef)
|
||||
foreach($aChanges as $sAttCode => $value)
|
||||
{
|
||||
$res = $this->CheckValue($sAttCode);
|
||||
if ($res !== true)
|
||||
@@ -1407,42 +1506,23 @@ abstract class DBObject implements iDisplay
|
||||
return true;
|
||||
}
|
||||
|
||||
// used only by insert
|
||||
protected function OnObjectKeyReady()
|
||||
{
|
||||
// Meant to be overloaded
|
||||
}
|
||||
|
||||
// used both by insert/update
|
||||
private function DBWriteLinks()
|
||||
{
|
||||
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef)
|
||||
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode => $oAttDef)
|
||||
{
|
||||
if (!$oAttDef->IsLinkSet()) continue;
|
||||
if (!array_key_exists($sAttCode, $this->m_aTouchedAtt)) continue;
|
||||
if (array_key_exists($sAttCode, $this->m_aModifiedAtt) && ($this->m_aModifiedAtt[$sAttCode] == false)) continue;
|
||||
|
||||
// Note: any change to this algorithm must be reproduced into the implementation of AttributeLinkSet::Equals()
|
||||
$sExtKeyToMe = $oAttDef->GetExtKeyToMe();
|
||||
$sAdditionalKey = null;
|
||||
if ($oAttDef->IsIndirect() && !$oAttDef->DuplicatesAllowed())
|
||||
{
|
||||
$sAdditionalKey = $oAttDef->GetExtKeyToRemote();
|
||||
}
|
||||
$oComparator = new DBObjectSetComparator($this->m_aOrigValues[$sAttCode], $this->Get($sAttCode), array($sExtKeyToMe), $sAdditionalKey);
|
||||
$aChanges = $oComparator->GetDifferences();
|
||||
|
||||
foreach($aChanges['added'] as $oLink)
|
||||
{
|
||||
// Make sure that the objects in the set point to "this"
|
||||
$oLink->Set($oAttDef->GetExtKeyToMe(), $this->m_iKey);
|
||||
$id = $oLink->DBWrite();
|
||||
}
|
||||
|
||||
foreach($aChanges['modified'] as $oLink)
|
||||
{
|
||||
// Objects in the set either remain attached or have been detached -> leave the link as is
|
||||
$oLink->DBWrite();
|
||||
}
|
||||
|
||||
foreach($aChanges['removed'] as $oLink)
|
||||
{
|
||||
$oLink->DBDelete();
|
||||
}
|
||||
|
||||
$oLinkSet = $this->m_aCurrValues[$sAttCode];
|
||||
$oLinkSet->DBWrite($this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1644,11 +1724,21 @@ abstract class DBObject implements iDisplay
|
||||
$this->DBInsertSingleTable($sParentClass);
|
||||
}
|
||||
|
||||
$this->DBWriteLinks();
|
||||
$this->OnObjectKeyReady();
|
||||
|
||||
$this->DBWriteLinks();
|
||||
$this->WriteExternalAttributes();
|
||||
|
||||
$this->m_bIsInDB = true;
|
||||
$this->m_bDirty = false;
|
||||
foreach ($this->m_aCurrValues as $sAttCode => $value)
|
||||
{
|
||||
if (is_object($value))
|
||||
{
|
||||
$value = clone $value;
|
||||
}
|
||||
$this->m_aOrigValues[$sAttCode] = $value;
|
||||
}
|
||||
|
||||
$this->AfterInsert();
|
||||
|
||||
@@ -1873,7 +1963,7 @@ abstract class DBObject implements iDisplay
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
if ($oAttDef->IsExternalKey()) $bHasANewExternalKeyValue = true;
|
||||
if ($oAttDef->IsDirectField())
|
||||
if ($oAttDef->IsBasedOnDBColumns())
|
||||
{
|
||||
$aDBChanges[$sAttCode] = $aChanges[$sAttCode];
|
||||
}
|
||||
@@ -1931,6 +2021,7 @@ abstract class DBObject implements iDisplay
|
||||
{
|
||||
$oFilter = new DBObjectSearch(get_class($this));
|
||||
$oFilter->AddCondition('id', $this->m_iKey, '=');
|
||||
$oFilter->AllowAllData();
|
||||
|
||||
$sSQL = $oFilter->MakeUpdateQuery($aDBChanges);
|
||||
CMDBSource::Query($sSQL);
|
||||
@@ -2303,8 +2394,7 @@ abstract class DBObject implements iDisplay
|
||||
*/
|
||||
public function Reset($sAttCode)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
$this->Set($sAttCode, $oAttDef->GetDefaultValue($this));
|
||||
$this->Set($sAttCode, $this->GetDefaultValue($sAttCode));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2840,7 +2930,7 @@ abstract class DBObject implements iDisplay
|
||||
$oSearch->AllowAllData();
|
||||
}
|
||||
$oSet = new CMDBObjectSet($oSearch);
|
||||
if ($oSet->Count() > 0)
|
||||
if ($oSet->Count(1) > 0)
|
||||
{
|
||||
$aDependentObjects[$sRemoteClass][$sExtKeyAttCode] = array(
|
||||
'attribute' => $oExtKeyAttDef,
|
||||
@@ -3308,8 +3398,7 @@ abstract class DBObject implements iDisplay
|
||||
{
|
||||
throw new Exception("Unknown attribute ".get_class($this)."::".$sAttCode);
|
||||
}
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
$this->Set($sAttCode, $oAttDef->GetDefaultValue());
|
||||
$this->Set($sAttCode, $this->GetDefaultValue($sAttCode));
|
||||
break;
|
||||
|
||||
case 'nullify':
|
||||
@@ -3393,7 +3482,7 @@ abstract class DBObject implements iDisplay
|
||||
throw new Exception('Missing argument #1: source attribute');
|
||||
}
|
||||
$sSourceKeyAttCode = $aParams[0];
|
||||
if (!MetaModel::IsValidAttCode(get_class($oObjectToRead), $sSourceKeyAttCode))
|
||||
if (($sSourceKeyAttCode != 'id') && !MetaModel::IsValidAttCode(get_class($oObjectToRead), $sSourceKeyAttCode))
|
||||
{
|
||||
throw new Exception("Unknown attribute ".get_class($oObjectToRead)."::".$sSourceKeyAttCode);
|
||||
}
|
||||
@@ -3470,5 +3559,94 @@ abstract class DBObject implements iDisplay
|
||||
throw new Exception("Invalid verb");
|
||||
}
|
||||
}
|
||||
|
||||
public function IsArchived($sKeyAttCode = null)
|
||||
{
|
||||
$bRet = false;
|
||||
$sFlagAttCode = is_null($sKeyAttCode) ? 'archive_flag' : $sKeyAttCode.'_archive_flag';
|
||||
if (MetaModel::IsValidAttCode(get_class($this), $sFlagAttCode) && $this->Get($sFlagAttCode))
|
||||
{
|
||||
$bRet = true;
|
||||
}
|
||||
return $bRet;
|
||||
}
|
||||
|
||||
public function IsObsolete($sKeyAttCode = null)
|
||||
{
|
||||
$bRet = false;
|
||||
$sFlagAttCode = is_null($sKeyAttCode) ? 'obsolescence_flag' : $sKeyAttCode.'_obsolescence_flag';
|
||||
if (MetaModel::IsValidAttCode(get_class($this), $sFlagAttCode) && $this->Get($sFlagAttCode))
|
||||
{
|
||||
$bRet = true;
|
||||
}
|
||||
return $bRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $bArchive
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function DBWriteArchiveFlag($bArchive)
|
||||
{
|
||||
if (!MetaModel::IsArchivable(get_class($this)))
|
||||
{
|
||||
throw new Exception(get_class($this).' is not an archivable class');
|
||||
}
|
||||
|
||||
$iFlag = $bArchive ? 1 : 0;
|
||||
$sDate = $bArchive ? '"'.date(AttributeDate::GetSQLFormat()).'"' : 'null';
|
||||
|
||||
$sClass = get_class($this);
|
||||
$sArchiveRoot = MetaModel::GetAttributeOrigin($sClass, 'archive_flag');
|
||||
$sRootTable = MetaModel::DBGetTable($sArchiveRoot);
|
||||
$sRootKey = MetaModel::DBGetKey($sArchiveRoot);
|
||||
$aJoins = array("`$sRootTable`");
|
||||
$aUpdates = array();
|
||||
foreach (MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL) as $sParentClass)
|
||||
{
|
||||
if (!MetaModel::IsValidAttCode($sParentClass, 'archive_flag')) continue;
|
||||
|
||||
$sTable = MetaModel::DBGetTable($sParentClass);
|
||||
$aUpdates[] = "`$sTable`.`archive_flag` = $iFlag";
|
||||
if ($sParentClass == $sArchiveRoot)
|
||||
{
|
||||
if (!$bArchive || $this->Get('archive_date') == '')
|
||||
{
|
||||
// Erase or set the date (do not change it)
|
||||
$aUpdates[] = "`$sTable`.`archive_date` = $sDate";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$sKey = MetaModel::DBGetKey($sParentClass);
|
||||
$aJoins[] = "`$sTable` ON `$sTable`.`$sKey` = `$sRootTable`.`$sRootKey`";
|
||||
}
|
||||
}
|
||||
$sJoins = implode(' INNER JOIN ', $aJoins);
|
||||
$sValues = implode(', ', $aUpdates);
|
||||
$sUpdateQuery = "UPDATE $sJoins SET $sValues WHERE `$sRootTable`.`$sRootKey` = ".$this->GetKey();
|
||||
CMDBSource::Query($sUpdateQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be called to repair the database (tables consistency)
|
||||
* The archive_date will be preserved
|
||||
* @throws Exception
|
||||
*/
|
||||
public function DBArchive()
|
||||
{
|
||||
$this->DBWriteArchiveFlag(true);
|
||||
$this->m_aCurrValues['archive_flag'] = true;
|
||||
$this->m_aOrigValues['archive_flag'] = true;
|
||||
}
|
||||
|
||||
public function DBUnarchive()
|
||||
{
|
||||
$this->DBWriteArchiveFlag(false);
|
||||
$this->m_aCurrValues['archive_flag'] = false;
|
||||
$this->m_aOrigValues['archive_flag'] = false;
|
||||
$this->m_aCurrValues['archive_date'] = null;
|
||||
$this->m_aOrigValues['archive_date'] = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
63
core/dbobjectiterator.php
Normal file
63
core/dbobjectiterator.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2017 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 set of persistent objects, could be heterogeneous as long as the objects in the set have a common ancestor class
|
||||
*
|
||||
* @package iTopORM
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
interface iDBObjectSetIterator extends Countable
|
||||
{
|
||||
/**
|
||||
* The class of the objects of the collection (at least a common ancestor)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function GetClass();
|
||||
|
||||
/**
|
||||
* The total number of objects in the collection
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function Count();
|
||||
|
||||
/**
|
||||
* Reset the cursor to the first item in the collection. Equivalent to Seek(0)
|
||||
*
|
||||
* @return DBObject The fetched object or null when at the end
|
||||
*/
|
||||
public function Rewind();
|
||||
|
||||
/**
|
||||
* Position the cursor to the given 0-based position
|
||||
*
|
||||
* @param int $iRow
|
||||
*/
|
||||
public function Seek($iPosition);
|
||||
|
||||
/**
|
||||
* Fetch the object at the current position in the collection and move the cursor to the next position.
|
||||
*
|
||||
* @return DBObject The fetched object or null when at the end
|
||||
*/
|
||||
public function Fetch();
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -16,11 +16,12 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
require_once('dbobjectiterator.php');
|
||||
|
||||
/**
|
||||
* Object set management
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -30,31 +31,66 @@
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class DBObjectSet
|
||||
class DBObjectSet implements iDBObjectSetIterator
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
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"
|
||||
*/
|
||||
protected $m_aAddedObjects;
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $m_aArgs;
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $m_aAttToLoad;
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $m_aOrderBy;
|
||||
/**
|
||||
* @var bool True when the filter has been used OR the set is built step by step (AddObject...)
|
||||
*/
|
||||
public $m_bLoaded;
|
||||
/**
|
||||
* @var int Total number of rows for the query without LIMIT. null if unknown yet
|
||||
*/
|
||||
protected $m_iNumTotalDBRows;
|
||||
/**
|
||||
* @var int Total number of rows LOADED in $this->m_oSQLResult via a SQL query. 0 by default
|
||||
*/
|
||||
protected $m_iNumLoadedDBRows;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $m_iCurrRow;
|
||||
/**
|
||||
* @var DBSearch
|
||||
*/
|
||||
protected $m_oFilter;
|
||||
/**
|
||||
* @var mysqli_result
|
||||
*/
|
||||
protected $m_oSQLResult;
|
||||
protected $m_bSort;
|
||||
|
||||
/**
|
||||
* Create a new set based on a Search definition.
|
||||
*
|
||||
*
|
||||
* @param DBSearch $oFilter The search filter defining the objects which are part of the set (multiple columns/objects per row are supported)
|
||||
* @param hash $aOrderBy Array of '[<classalias>.]attcode' => bAscending
|
||||
* @param hash $aArgs Values to substitute for the search/query parameters (if any). Format: param_name => value
|
||||
* @param array $aOrderBy Array of '[<classalias>.]attcode' => bAscending
|
||||
* @param array $aArgs Values to substitute for the search/query parameters (if any). Format: param_name => value
|
||||
* @param hash $aExtendedDataSpec
|
||||
* @param int $iLimitCount Maximum number of rows to load (i.e. equivalent to MySQL's LIMIT start, count)
|
||||
* @param int $iLimitStart Index of the first row to load (i.e. equivalent to MySQL's LIMIT start, count)
|
||||
* @param bool $bSort if false no order by is done
|
||||
*/
|
||||
public function __construct(DBSearch $oFilter, $aOrderBy = array(), $aArgs = array(), $aExtendedDataSpec = null, $iLimitCount = 0, $iLimitStart = 0)
|
||||
public function __construct(DBSearch $oFilter, $aOrderBy = array(), $aArgs = array(), $aExtendedDataSpec = null, $iLimitCount = 0, $iLimitStart = 0, $bSort = true)
|
||||
{
|
||||
$this->m_oFilter = $oFilter->DeepClone();
|
||||
$this->m_aAddedIds = array();
|
||||
@@ -64,11 +100,12 @@ class DBObjectSet
|
||||
$this->m_aExtendedDataSpec = $aExtendedDataSpec;
|
||||
$this->m_iLimitCount = $iLimitCount;
|
||||
$this->m_iLimitStart = $iLimitStart;
|
||||
$this->m_bSort = $bSort;
|
||||
|
||||
$this->m_iNumTotalDBRows = null; // Total number of rows for the query without LIMIT. null if unknown yet
|
||||
$this->m_iNumLoadedDBRows = 0; // Total number of rows LOADED in $this->m_oSQLResult via a SQL query. 0 by default
|
||||
$this->m_bLoaded = false; // true when the filter has been used OR the set is built step by step (AddObject...)
|
||||
$this->m_aAddedObjects = array(); // array of (row => array of (classalias) => object/null) storing the objects added "in memory"
|
||||
$this->m_iNumTotalDBRows = null;
|
||||
$this->m_iNumLoadedDBRows = 0;
|
||||
$this->m_bLoaded = false;
|
||||
$this->m_aAddedObjects = array();
|
||||
$this->m_iCurrRow = 0;
|
||||
$this->m_oSQLResult = null;
|
||||
}
|
||||
@@ -123,6 +160,17 @@ class DBObjectSet
|
||||
$this->m_iCurrRow = 0;
|
||||
$this->m_oSQLResult = null;
|
||||
}
|
||||
|
||||
public function SetShowObsoleteData($bShow)
|
||||
{
|
||||
$this->m_oFilter->SetShowObsoleteData($bShow);
|
||||
}
|
||||
|
||||
public function GetShowObsoleteData()
|
||||
{
|
||||
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
|
||||
*
|
||||
@@ -150,11 +198,25 @@ class DBObjectSet
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttToLoad);
|
||||
$aAttToLoadWithAttDef[$sClassAlias][$sAttToLoad] = $oAttDef;
|
||||
if ($oAttDef->IsExternalKey())
|
||||
if ($oAttDef->IsExternalKey(EXTKEY_ABSOLUTE))
|
||||
{
|
||||
// Add the external key friendly name anytime
|
||||
$oFriendlyNameAttDef = MetaModel::GetAttributeDef($sClass, $sAttToLoad.'_friendlyname');
|
||||
$aAttToLoadWithAttDef[$sClassAlias][$sAttToLoad.'_friendlyname'] = $oFriendlyNameAttDef;
|
||||
|
||||
if (MetaModel::IsArchivable($oAttDef->GetTargetClass(EXTKEY_ABSOLUTE)))
|
||||
{
|
||||
// Add the archive flag if necessary
|
||||
$oArchiveFlagAttDef = MetaModel::GetAttributeDef($sClass, $sAttToLoad.'_archive_flag');
|
||||
$aAttToLoadWithAttDef[$sClassAlias][$sAttToLoad.'_archive_flag'] = $oArchiveFlagAttDef;
|
||||
}
|
||||
|
||||
if (MetaModel::IsObsoletable($oAttDef->GetTargetClass(EXTKEY_ABSOLUTE)))
|
||||
{
|
||||
// Add the obsolescence flag if necessary
|
||||
$oObsoleteFlagAttDef = MetaModel::GetAttributeDef($sClass, $sAttToLoad.'_obsolescence_flag');
|
||||
$aAttToLoadWithAttDef[$sClassAlias][$sAttToLoad.'_obsolescence_flag'] = $oObsoleteFlagAttDef;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -162,6 +224,20 @@ class DBObjectSet
|
||||
$oFriendlyNameAttDef = MetaModel::GetAttributeDef($sClass, 'friendlyname');
|
||||
$aAttToLoadWithAttDef[$sClassAlias]['friendlyname'] = $oFriendlyNameAttDef;
|
||||
|
||||
if (MetaModel::IsArchivable($sClass))
|
||||
{
|
||||
// Add the archive flag if necessary
|
||||
$oArchiveFlagAttDef = MetaModel::GetAttributeDef($sClass, 'archive_flag');
|
||||
$aAttToLoadWithAttDef[$sClassAlias]['archive_flag'] = $oArchiveFlagAttDef;
|
||||
}
|
||||
|
||||
if (MetaModel::IsObsoletable($sClass))
|
||||
{
|
||||
// Add the obsolescence flag if necessary
|
||||
$oObsoleteFlagAttDef = MetaModel::GetAttributeDef($sClass, 'obsolescence_flag');
|
||||
$aAttToLoadWithAttDef[$sClassAlias]['obsolescence_flag'] = $oObsoleteFlagAttDef;
|
||||
}
|
||||
|
||||
// Make sure that the final class is requested anytime, whatever the specification (needed for object construction!)
|
||||
if (!MetaModel::IsStandaloneClass($sClass) && !array_key_exists('finalclass', $aAttToLoadWithAttDef[$sClassAlias]))
|
||||
{
|
||||
@@ -192,7 +268,7 @@ class DBObjectSet
|
||||
*
|
||||
* @param string $sClass The class (or an ancestor) for the objects to be added in this set
|
||||
*
|
||||
* @return DBObject The empty set
|
||||
* @return DBObjectSet The empty set
|
||||
*/
|
||||
static public function FromScratch($sClass)
|
||||
{
|
||||
@@ -264,8 +340,14 @@ class DBObjectSet
|
||||
}
|
||||
|
||||
return self::FromArray($sTargetClass, $aTargets);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function ToArray($bWithId = true)
|
||||
{
|
||||
$aRet = array();
|
||||
@@ -332,8 +414,15 @@ class DBObjectSet
|
||||
$iRow++;
|
||||
}
|
||||
return $aRet;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: After calling this method, the set cursor will be at the end of the set. You might want to rewind it.
|
||||
*
|
||||
* @param string $sAttCode
|
||||
* @param bool $bWithId
|
||||
* @return array
|
||||
*/
|
||||
public function GetColumnAsArray($sAttCode, $bWithId = true)
|
||||
{
|
||||
$aRet = array();
|
||||
@@ -364,6 +453,7 @@ class DBObjectSet
|
||||
{
|
||||
// Make sure that we carry on the parameters of the set with the filter
|
||||
$oFilter = $this->m_oFilter->DeepClone();
|
||||
$oFilter->SetShowObsoleteData(true);
|
||||
// Note: the arguments found within a set can be object (but not in a filter)
|
||||
// That's why PrepareQueryArguments must be invoked there
|
||||
$oFilter->SetInternalParams(array_merge($oFilter->GetInternalParams(), $this->m_aArgs));
|
||||
@@ -445,8 +535,8 @@ class DBObjectSet
|
||||
|
||||
/**
|
||||
* Sets the sort order for loading the rows from the DB. Changing the order by causes a Reload.
|
||||
*
|
||||
* @param hash $aOrderBy Format: field_code => boolean (true = ascending, false = descending)
|
||||
*
|
||||
* @param hash $aOrderBy Format: [alias.]attcode => boolean (true = ascending, false = descending)
|
||||
*/
|
||||
public function SetOrderBy($aOrderBy)
|
||||
{
|
||||
@@ -461,6 +551,34 @@ class DBObjectSet
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function SetOrderByClasses($aAliases = null)
|
||||
{
|
||||
if ($aAliases === null)
|
||||
{
|
||||
$aAliases = array();
|
||||
foreach ($this->GetSelectedClasses() as $sAlias => $sClass)
|
||||
{
|
||||
$aAliases[$sAlias] = true;
|
||||
}
|
||||
}
|
||||
|
||||
$aAttributes = array();
|
||||
foreach ($aAliases as $sAlias => $bClassDirection)
|
||||
{
|
||||
foreach (MetaModel::GetOrderByDefault($this->m_oFilter->GetClass($sAlias)) as $sAttCode => $bAttributeDirection)
|
||||
{
|
||||
$bDirection = $bClassDirection ? $bAttributeDirection : !$bAttributeDirection;
|
||||
$aAttributes[$sAlias.'.'.$sAttCode] = $bDirection;
|
||||
}
|
||||
}
|
||||
$this->SetOrderBy($aAttributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the 'count' limit for loading the rows from the DB
|
||||
*
|
||||
@@ -486,10 +604,15 @@ class DBObjectSet
|
||||
*
|
||||
* Limitation: the sort order has no effect on objects added in-memory
|
||||
*
|
||||
* @return hash Format: field_code => boolean (true = ascending, false = descending)
|
||||
* @return array Format: field_code => boolean (true = ascending, false = descending)
|
||||
*/
|
||||
public function GetRealSortOrder()
|
||||
{
|
||||
if (!$this->m_bSort)
|
||||
{
|
||||
// No order by
|
||||
return array();
|
||||
}
|
||||
// Get the class default sort order if not specified with the API
|
||||
//
|
||||
if (empty($this->m_aOrderBy))
|
||||
@@ -511,14 +634,7 @@ class DBObjectSet
|
||||
// Note: it is mandatory to set this value now, to protect against reentrance
|
||||
$this->m_bLoaded = true;
|
||||
|
||||
if ($this->m_iLimitCount > 0)
|
||||
{
|
||||
$sSQL = $this->m_oFilter->MakeSelectQuery($this->GetRealSortOrder(), $this->m_aArgs, $this->m_aAttToLoad, $this->m_aExtendedDataSpec, $this->m_iLimitCount, $this->m_iLimitStart);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sSQL = $this->m_oFilter->MakeSelectQuery($this->GetRealSortOrder(), $this->m_aArgs, $this->m_aAttToLoad, $this->m_aExtendedDataSpec);
|
||||
}
|
||||
$sSQL = $this->_makeSelectQuery($this->m_aAttToLoad);
|
||||
|
||||
if (is_object($this->m_oSQLResult))
|
||||
{
|
||||
@@ -527,8 +643,36 @@ class DBObjectSet
|
||||
$this->m_oSQLResult = null;
|
||||
}
|
||||
$this->m_iNumTotalDBRows = null;
|
||||
|
||||
$this->m_oSQLResult = CMDBSource::Query($sSQL);
|
||||
|
||||
try
|
||||
{
|
||||
$this->m_oSQLResult = CMDBSource::Query($sSQL);
|
||||
} catch (MySQLException $e)
|
||||
{
|
||||
// 1116 = ER_TOO_MANY_TABLES
|
||||
// https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html#error_er_too_many_tables
|
||||
if ($e->getCode() != 1116)
|
||||
{
|
||||
throw $e;
|
||||
}
|
||||
|
||||
// N.689 Workaround for the 61 max joins in MySQL : full lazy load !
|
||||
$aAttToLoad = array();
|
||||
foreach($this->m_oFilter->GetSelectedClasses() as $sClassAlias => $sClass)
|
||||
{
|
||||
$aAttToLoad[$sClassAlias] = array();
|
||||
$bIsAbstractClass = MetaModel::IsAbstract($sClass);
|
||||
$bIsClassWithChildren = MetaModel::HasChildrenClasses($sClass);
|
||||
if ($bIsAbstractClass || $bIsClassWithChildren)
|
||||
{
|
||||
// we need finalClass field at least to be able to instantiate the real corresponding object !
|
||||
$aAttToLoad[$sClassAlias]['finalclass'] = MetaModel::GetAttributeDef($sClass, 'finalclass');
|
||||
}
|
||||
}
|
||||
$sSQL = $this->_makeSelectQuery($aAttToLoad);
|
||||
$this->m_oSQLResult = CMDBSource::Query($sSQL); // may fail again
|
||||
}
|
||||
|
||||
if ($this->m_oSQLResult === false) return;
|
||||
|
||||
if (($this->m_iLimitCount == 0) && ($this->m_iLimitStart == 0))
|
||||
@@ -539,24 +683,54 @@ class DBObjectSet
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param string[] $aAttToLoad
|
||||
*
|
||||
* @return string SQL query
|
||||
*/
|
||||
public function Count()
|
||||
private function _makeSelectQuery($aAttToLoad)
|
||||
{
|
||||
if ($this->m_iLimitCount > 0)
|
||||
{
|
||||
$sSQL = $this->m_oFilter->MakeSelectQuery($this->GetRealSortOrder(), $this->m_aArgs, $aAttToLoad,
|
||||
$this->m_aExtendedDataSpec, $this->m_iLimitCount, $this->m_iLimitStart);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sSQL = $this->m_oFilter->MakeSelectQuery($this->GetRealSortOrder(), $this->m_aArgs, $aAttToLoad,
|
||||
$this->m_aExtendedDataSpec);
|
||||
}
|
||||
|
||||
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
|
||||
*
|
||||
* @param int $iLimit used for autocomplete: the count is only used to know if the number of entries exceed
|
||||
* a certain amount or not
|
||||
*
|
||||
* @return int The total number of rows for this set.
|
||||
* @throws \CoreException
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
*/
|
||||
public function Count($iLimit = 0)
|
||||
{
|
||||
if (is_null($this->m_iNumTotalDBRows))
|
||||
{
|
||||
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, 0, 0, true);
|
||||
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, $iLimit, 0, true);
|
||||
$resQuery = CMDBSource::Query($sSQL);
|
||||
if (!$resQuery) return 0;
|
||||
|
||||
|
||||
$aRow = CMDBSource::FetchArray($resQuery);
|
||||
CMDBSource::FreeResult($resQuery);
|
||||
$this->m_iNumTotalDBRows = $aRow['COUNT'];
|
||||
}
|
||||
|
||||
return $this->m_iNumTotalDBRows + count($this->m_aAddedObjects); // Does it fix Trac #887 ??
|
||||
}
|
||||
|
||||
@@ -849,22 +1023,6 @@ class DBObjectSet
|
||||
return $oComparator->SetsAreEquivalent();
|
||||
}
|
||||
|
||||
protected function GetObjectAt($iIndex)
|
||||
{
|
||||
if (!$this->m_bLoaded) $this->Load();
|
||||
|
||||
// Save the current position for iteration
|
||||
$iCurrPos = $this->m_iCurrRow;
|
||||
|
||||
$this->Seek($iIndex);
|
||||
$oObject = $this->Fetch();
|
||||
|
||||
// Restore the current position for iteration
|
||||
$this->Seek($this->m_iCurrRow);
|
||||
|
||||
return $oObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a new set (in memory) made of objects of the given set which are NOT present in the current set
|
||||
*
|
||||
@@ -1126,19 +1284,27 @@ class DBObjectSetComparator
|
||||
protected $aIDs1;
|
||||
protected $aIDs2;
|
||||
protected $aExcludedColumns;
|
||||
|
||||
/**
|
||||
* @var iDBObjectSetIterator
|
||||
*/
|
||||
protected $oSet1;
|
||||
/**
|
||||
* @var iDBObjectSetIterator
|
||||
*/
|
||||
protected $oSet2;
|
||||
|
||||
protected $sAdditionalKeyColumn;
|
||||
protected $aAdditionalKeys;
|
||||
|
||||
/**
|
||||
* Initializes the comparator
|
||||
* @param DBObjectSet $oSet1 The first set of objects to compare, or null
|
||||
* @param DBObjectSet $oSet2 The second set of objects to compare, or null
|
||||
* @param iDBObjectSetIterator $oSet1 The first set of objects to compare, or null
|
||||
* @param iDBObjectSetIterator $oSet2 The second set of objects to compare, or null
|
||||
* @param array $aExcludedColumns The list of columns (= attribute codes) to exclude from the comparison
|
||||
* @param string $sAdditionalKeyColumn The attribute code of an additional column to be considered as a key indentifying the object (useful for n:n links)
|
||||
*/
|
||||
public function __construct($oSet1, $oSet2, $aExcludedColumns = array(), $sAdditionalKeyColumn = null)
|
||||
public function __construct(iDBObjectSetIterator $oSet1, iDBObjectSetIterator $oSet2, $aExcludedColumns = array(), $sAdditionalKeyColumn = null)
|
||||
{
|
||||
$this->aFingerprints1 = null;
|
||||
$this->aFingerprints2 = null;
|
||||
@@ -1164,9 +1330,6 @@ class DBObjectSetComparator
|
||||
|
||||
if ($this->oSet1 !== null)
|
||||
{
|
||||
$aAliases = $this->oSet1->GetSelectedClasses();
|
||||
if (count($aAliases) > 1) throw new Exception('DBObjectSetComparator does not support Sets with more than one column. $oSet1: ('.print_r($aAliases, true).')');
|
||||
|
||||
$this->oSet1->Rewind();
|
||||
while($oObj = $this->oSet1->Fetch())
|
||||
{
|
||||
@@ -1182,9 +1345,6 @@ class DBObjectSetComparator
|
||||
|
||||
if ($this->oSet2 !== null)
|
||||
{
|
||||
$aAliases = $this->oSet2->GetSelectedClasses();
|
||||
if (count($aAliases) > 1) throw new Exception('DBObjectSetComparator does not support Sets with more than one column. $oSet2: ('.print_r($aAliases, true).')');
|
||||
|
||||
$this->oSet2->Rewind();
|
||||
while($oObj = $this->oSet2->Fetch())
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2015-2016 Combodo SARL
|
||||
// Copyright (C) 2015-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* A union of DBObjectSearches
|
||||
*
|
||||
* @copyright Copyright (C) 2015-2016 Combodo SARL
|
||||
* @copyright Copyright (C) 2015-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -55,6 +55,40 @@ class DBUnionSearch extends DBSearch
|
||||
$this->ComputeSelectedClasses();
|
||||
}
|
||||
|
||||
public function AllowAllData()
|
||||
{
|
||||
foreach ($this->aSearches as $oSearch)
|
||||
{
|
||||
$oSearch->AllowAllData();
|
||||
}
|
||||
}
|
||||
public function IsAllDataAllowed()
|
||||
{
|
||||
foreach ($this->aSearches as $oSearch)
|
||||
{
|
||||
if ($oSearch->IsAllDataAllowed() === false) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function SetArchiveMode($bEnable)
|
||||
{
|
||||
foreach ($this->aSearches as $oSearch)
|
||||
{
|
||||
$oSearch->SetArchiveMode($bEnable);
|
||||
}
|
||||
parent::SetArchiveMode($bEnable);
|
||||
}
|
||||
|
||||
public function SetShowObsoleteData($bShow)
|
||||
{
|
||||
foreach ($this->aSearches as $oSearch)
|
||||
{
|
||||
$oSearch->SetShowObsoleteData($bShow);
|
||||
}
|
||||
parent::SetShowObsoleteData($bShow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the lowest common ancestor for each of the selected class
|
||||
*/
|
||||
@@ -187,6 +221,23 @@ class DBUnionSearch extends DBSearch
|
||||
$this->ComputeSelectedClasses();
|
||||
}
|
||||
|
||||
/**
|
||||
* Change any alias of the query tree
|
||||
*
|
||||
* @param $sOldName
|
||||
* @param $sNewName
|
||||
* @return bool True if the alias has been found and changed
|
||||
*/
|
||||
public function RenameAlias($sOldName, $sNewName)
|
||||
{
|
||||
$bRet = false;
|
||||
foreach ($this->aSearches as $oSearch)
|
||||
{
|
||||
$bRet = $oSearch->RenameAlias($sOldName, $sNewName) || $bRet;
|
||||
}
|
||||
return $bRet;
|
||||
}
|
||||
|
||||
public function IsAny()
|
||||
{
|
||||
$bIsAny = true;
|
||||
@@ -282,19 +333,33 @@ class DBUnionSearch extends DBSearch
|
||||
}
|
||||
}
|
||||
|
||||
public function AddCondition_PointingTo(DBObjectSearch $oFilter, $sExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS)
|
||||
/**
|
||||
* @param DBObjectSearch $oFilter
|
||||
* @param $sExtKeyAttCode
|
||||
* @param int $iOperatorCode
|
||||
* @param null $aRealiasingMap array of <old-alias> => <new-alias>, for each alias that has changed
|
||||
* @throws CoreException
|
||||
* @throws CoreWarning
|
||||
*/
|
||||
public function AddCondition_PointingTo(DBObjectSearch $oFilter, $sExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS, &$aRealiasingMap = null)
|
||||
{
|
||||
foreach ($this->aSearches as $oSearch)
|
||||
{
|
||||
$oSearch->AddCondition_PointingTo($oFilter, $sExtKeyAttCode, $iOperatorCode);
|
||||
$oSearch->AddCondition_PointingTo($oFilter, $sExtKeyAttCode, $iOperatorCode, $aRealiasingMap);
|
||||
}
|
||||
}
|
||||
|
||||
public function AddCondition_ReferencedBy(DBObjectSearch $oFilter, $sForeignExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS)
|
||||
/**
|
||||
* @param DBObjectSearch $oFilter
|
||||
* @param $sForeignExtKeyAttCode
|
||||
* @param int $iOperatorCode
|
||||
* @param null $aRealiasingMap array of <old-alias> => <new-alias>, for each alias that has changed
|
||||
*/
|
||||
public function AddCondition_ReferencedBy(DBObjectSearch $oFilter, $sForeignExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS, &$aRealiasingMap = null)
|
||||
{
|
||||
foreach ($this->aSearches as $oSearch)
|
||||
{
|
||||
$oSearch->AddCondition_ReferencedBy($oFilter, $sForeignExtKeyAttCode, $iOperatorCode);
|
||||
$oSearch->AddCondition_ReferencedBy($oFilter, $sForeignExtKeyAttCode, $iOperatorCode, $aRealiasingMap);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -357,12 +422,12 @@ class DBUnionSearch extends DBSearch
|
||||
/**
|
||||
* Overloads for query building
|
||||
*/
|
||||
public function ToOQL($bDevelopParams = false, $aContextParams = null)
|
||||
public function ToOQL($bDevelopParams = false, $aContextParams = null, $bWithAllowAllFlag = false)
|
||||
{
|
||||
$aSubQueries = array();
|
||||
foreach ($this->aSearches as $oSearch)
|
||||
{
|
||||
$aSubQueries[] = $oSearch->ToOQL($bDevelopParams, $aContextParams);
|
||||
$aSubQueries[] = $oSearch->ToOQL($bDevelopParams, $aContextParams, $bWithAllowAllFlag);
|
||||
}
|
||||
$sRet = implode(' UNION ', $aSubQueries);
|
||||
return $sRet;
|
||||
@@ -409,11 +474,11 @@ class DBUnionSearch extends DBSearch
|
||||
throw new Exception('MakeUpdateQuery is not implemented for the unions!');
|
||||
}
|
||||
|
||||
protected function MakeSQLQuery($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr = null, $aSelectedClasses = null)
|
||||
public function GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr = null, $aSelectedClasses = null)
|
||||
{
|
||||
if (count($this->aSearches) == 1)
|
||||
{
|
||||
return $this->aSearches[0]->MakeSQLQuery($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr);
|
||||
return $this->aSearches[0]->GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr);
|
||||
}
|
||||
|
||||
$aSQLQueries = array();
|
||||
@@ -431,19 +496,30 @@ class DBUnionSearch extends DBSearch
|
||||
$aSearchSelectedClasses[$sSearchAlias] = $this->aSelectedClasses[$sAlias];
|
||||
}
|
||||
|
||||
if (is_null($aAttToLoad))
|
||||
if ($bGetCount)
|
||||
{
|
||||
$aQueryAttToLoad = null;
|
||||
// Select only ids for the count to allow optimization of joins
|
||||
foreach($aSearchAliases as $sSearchAlias)
|
||||
{
|
||||
$aQueryAttToLoad[$sSearchAlias] = array();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// (Eventually) Transform the aliases
|
||||
$aQueryAttToLoad = array();
|
||||
foreach ($aAttToLoad as $sAlias => $aAttributes)
|
||||
if (is_null($aAttToLoad))
|
||||
{
|
||||
$iColumn = array_search($sAlias, $aAliases);
|
||||
$sQueryAlias = ($iColumn === false) ? $sAlias : $aSearchAliases[$iColumn];
|
||||
$aQueryAttToLoad[$sQueryAlias] = $aAttributes;
|
||||
$aQueryAttToLoad = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
// (Eventually) Transform the aliases
|
||||
$aQueryAttToLoad = array();
|
||||
foreach($aAttToLoad as $sAlias => $aAttributes)
|
||||
{
|
||||
$iColumn = array_search($sAlias, $aAliases);
|
||||
$sQueryAlias = ($iColumn === false) ? $sAlias : $aSearchAliases[$iColumn];
|
||||
$aQueryAttToLoad[$sQueryAlias] = $aAttributes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -468,7 +544,13 @@ class DBUnionSearch extends DBSearch
|
||||
$aQueryGroupByExpr[$sExpressionAlias] = $oExpression->Translate($aTranslationData, false, false);
|
||||
}
|
||||
}
|
||||
$oSubQuery = $oSearch->MakeSQLQuery($aQueryAttToLoad, false, $aModifierProperties, $aQueryGroupByExpr, $aSearchSelectedClasses);
|
||||
$oSubQuery = $oSearch->GetSQLQueryStructure($aQueryAttToLoad, false, $aQueryGroupByExpr, $aSearchSelectedClasses);
|
||||
if (count($aSearchAliases) > 1)
|
||||
{
|
||||
// Necessary to make sure that selected columns will match throughout all the queries
|
||||
// (default order of selected fields depending on the order of JOINS)
|
||||
$oSubQuery->SortSelectedFields();
|
||||
}
|
||||
$aSQLQueries[] = $oSubQuery;
|
||||
}
|
||||
|
||||
|
||||
@@ -192,7 +192,7 @@ class DesignElement extends \DOMElement
|
||||
* Returns the node directly under the given node
|
||||
* @param $sTagName
|
||||
* @param bool|true $bMustExist
|
||||
* @return null
|
||||
* @return MFElement
|
||||
* @throws DOMFormatException
|
||||
*/
|
||||
public function GetUniqueElement($sTagName, $bMustExist = true)
|
||||
@@ -216,7 +216,7 @@ class DesignElement extends \DOMElement
|
||||
/**
|
||||
* Returns the node directly under the current node, or null if missing
|
||||
* @param $sTagName
|
||||
* @return null
|
||||
* @return MFElement
|
||||
* @throws DOMFormatException
|
||||
*/
|
||||
public function GetOptionalElement($sTagName)
|
||||
|
||||
@@ -195,8 +195,6 @@ class EMail
|
||||
|
||||
$aFailedRecipients = array();
|
||||
$this->m_oMessage->setMaxLineLength(0);
|
||||
IssueLog::Info(__METHOD__.' '.$this->m_oMessage->getMaxLineLength());
|
||||
IssueLog::Info(__METHOD__.' '.$this->m_oMessage->toString());
|
||||
$iSent = $oMailer->send($this->m_oMessage, $aFailedRecipients);
|
||||
if ($iSent === 0)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -264,8 +264,8 @@ class EventIssue extends Event
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not a string
|
||||
$aPost[$sKey] = (string) $sValue;
|
||||
// Not a string (avoid warnings in case the value cannot be easily casted into a string)
|
||||
$aPost[$sKey] = @(string) $sValue;
|
||||
}
|
||||
}
|
||||
$this->Set('arguments_post', $aPost);
|
||||
@@ -408,4 +408,30 @@ class EventLoginUsage extends Event
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
class EventOnObject extends Event
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb,view_in_gui",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_event_onobject",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"display_template" => "",
|
||||
"order_by_default" => array('date' => false)
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeString("obj_class", array("allowed_values"=>null, "sql"=>"obj_class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeInteger("obj_key", array("allowed_values"=>null, "sql"=>"obj_key", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'obj_class', 'obj_key', 'message')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'obj_class', 'obj_key', 'message')); // Attributes to be displayed for a list
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,10 +188,10 @@ EOF
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
|
||||
$sRet = $oAttDef->GetAsCSV($value, '', '', $oObj);
|
||||
}
|
||||
else if ($value instanceOf ormCustomFieldsValue)
|
||||
else if ($value instanceOf ormDocument)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
|
||||
$sRet = $oAttDef->GetAsCSV($value, "\n", '', $oObj);
|
||||
$sRet = $oAttDef->GetAsCSV($value, '', '', $oObj);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
110
core/expressioncache.class.inc.php
Normal file
110
core/expressioncache.class.inc.php
Normal file
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
// Copyright (c) 2010-2017 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/>
|
||||
//
|
||||
|
||||
class ExpressionCache
|
||||
{
|
||||
static private $aCache = array();
|
||||
|
||||
static public function GetCachedExpression($sClass, $sAttCode)
|
||||
{
|
||||
// read current cache
|
||||
@include_once (static::GetCacheFileName());
|
||||
|
||||
$oExpr = null;
|
||||
$sKey = static::GetKey($sClass, $sAttCode);
|
||||
if (array_key_exists($sKey, static::$aCache))
|
||||
{
|
||||
$oExpr = static::$aCache[$sKey];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (class_exists('ExpressionCacheData'))
|
||||
{
|
||||
if (array_key_exists($sKey, ExpressionCacheData::$aCache))
|
||||
{
|
||||
$sVal = ExpressionCacheData::$aCache[$sKey];
|
||||
$oExpr = unserialize($sVal);
|
||||
static::$aCache[$sKey] = $oExpr;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $oExpr;
|
||||
}
|
||||
|
||||
|
||||
static public function Warmup()
|
||||
{
|
||||
$sFilePath = static::GetCacheFileName();
|
||||
|
||||
if (!is_file($sFilePath))
|
||||
{
|
||||
$content = <<<EOF
|
||||
<?php
|
||||
// Copyright (c) 2010-2017 Combodo SARL
|
||||
// Generated Expression Cache file
|
||||
|
||||
class ExpressionCacheData
|
||||
{
|
||||
static \$aCache = array(
|
||||
EOF;
|
||||
|
||||
foreach(MetaModel::GetClasses() as $sClass)
|
||||
{
|
||||
$content .= static::GetSerializedExpression($sClass, 'friendlyname');
|
||||
if (MetaModel::IsObsoletable($sClass))
|
||||
{
|
||||
$content .= static::GetSerializedExpression($sClass, 'obsolescence_flag');
|
||||
}
|
||||
}
|
||||
|
||||
$content .= <<<EOF
|
||||
);
|
||||
}
|
||||
EOF;
|
||||
|
||||
file_put_contents($sFilePath, $content);
|
||||
}
|
||||
}
|
||||
|
||||
static private function GetSerializedExpression($sClass, $sAttCode)
|
||||
{
|
||||
$sKey = static::GetKey($sClass, $sAttCode);
|
||||
$oExpr = DBObjectSearch::GetPolymorphicExpression($sClass, $sAttCode);
|
||||
return "'".$sKey."' => '".serialize($oExpr)."',\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sClass
|
||||
* @param $sAttCode
|
||||
* @return string
|
||||
*/
|
||||
static private function GetKey($sClass, $sAttCode)
|
||||
{
|
||||
return $sClass.'::'.$sAttCode;
|
||||
}
|
||||
|
||||
public static function GetCacheFileName()
|
||||
{
|
||||
return utils::GetCachePath().'expressioncache.php';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,20 @@
|
||||
<?php
|
||||
// Copyright (C) 2016-2017 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/>
|
||||
/**
|
||||
* Base class for all possible implementations of HTML Sanitization
|
||||
*/
|
||||
@@ -138,8 +154,9 @@ class HTMLDOMSanitizer extends HTMLSanitizer
|
||||
protected static $aTagsWhiteList = array(
|
||||
'html' => array(),
|
||||
'body' => array(),
|
||||
'a' => array('href', 'name', 'style'),
|
||||
'a' => array('href', 'name', 'style', 'target', 'title'),
|
||||
'p' => array('style'),
|
||||
'blockquote' => array('style'),
|
||||
'br' => array(),
|
||||
'span' => array('style'),
|
||||
'div' => array('style'),
|
||||
@@ -148,7 +165,7 @@ class HTMLDOMSanitizer extends HTMLSanitizer
|
||||
'u' => array(),
|
||||
'em' => array(),
|
||||
'strong' => array(),
|
||||
'img' => array('src','style'),
|
||||
'img' => array('src', 'style', 'alt', 'title'),
|
||||
'ul' => array('style'),
|
||||
'ol' => array('style'),
|
||||
'li' => array('style'),
|
||||
@@ -159,7 +176,7 @@ class HTMLDOMSanitizer extends HTMLSanitizer
|
||||
'nav' => array('style'),
|
||||
'section' => array('style'),
|
||||
'code' => array('style'),
|
||||
'table' => array('style', 'width'),
|
||||
'table' => array('style', 'width', 'summary', 'align', 'border', 'cellpadding', 'cellspacing'),
|
||||
'thead' => array('style'),
|
||||
'tbody' => array('style'),
|
||||
'tr' => array('style'),
|
||||
@@ -183,21 +200,45 @@ class HTMLDOMSanitizer extends HTMLSanitizer
|
||||
'hr' => array('style'),
|
||||
'pre' => array(),
|
||||
'center' => array(),
|
||||
'caption' => array(),
|
||||
);
|
||||
|
||||
protected static $aAttrsWhiteList = array(
|
||||
'href' => '/^(http:|https:)/i',
|
||||
'src' => '/^(http:|https:|data:)/i',
|
||||
);
|
||||
|
||||
protected static $aStylesWhiteList = array(
|
||||
'background-color', 'color', 'float', 'font', 'font-style', 'font-size', 'font-family', 'padding', 'margin', 'border', 'cellpadding', 'cellspacing', 'bordercolor', 'border-collapse', 'width', 'height',
|
||||
'background-color', 'color', 'float', 'font', 'font-style', 'font-size', 'font-family', 'padding', 'margin', 'border', 'cellpadding', 'cellspacing', 'bordercolor', 'border-collapse', 'width', 'height', 'text-align',
|
||||
);
|
||||
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
// Building href validation pattern from url and email validation patterns as the patterns are not used the same way in HTML content than in standard attributes value.
|
||||
// eg. "foo@bar.com" vs "mailto:foo@bar.com?subject=Title&body=Hello%20world"
|
||||
if (!array_key_exists('href', self::$aAttrsWhiteList))
|
||||
{
|
||||
// Regular urls
|
||||
$sUrlPattern = utils::GetConfig()->Get('url_validation_pattern');
|
||||
// Mailto urls
|
||||
$sMailtoPattern = '(mailto:(' . utils::GetConfig()->Get('email_validation_pattern') . ')(?:\?(?:subject|body)=([a-zA-Z0-9+\$_.-]*)(?:&(?:subject|body)=([a-zA-Z0-9+\$_.-]*))?)?)';
|
||||
|
||||
$sPattern = $sUrlPattern . '|' . $sMailtoPattern;
|
||||
$sPattern = '/'.str_replace('/', '\/', $sPattern).'/i';
|
||||
self::$aAttrsWhiteList['href'] = $sPattern;
|
||||
}
|
||||
}
|
||||
|
||||
public function DoSanitize($sHTML)
|
||||
{
|
||||
$this->oDoc = new DOMDocument();
|
||||
$this->oDoc->preserveWhitespace = true;
|
||||
|
||||
// MS outlook implements empty lines by the mean of <p><o:p></o:p></p>
|
||||
// We have to transform that into <p><br></p> (which is how Thunderbird implements empty lines)
|
||||
// Unfortunately, DOMDocument::loadHTML does not take the tag namespaces into account (once loaded there is no way to know if the tag did have a namespace)
|
||||
// therefore we have to do the transformation upfront
|
||||
$sHTML = preg_replace('@<o:p>\s*</o:p>@', '<br>', $sHTML);
|
||||
|
||||
@$this->oDoc->loadHTML('<?xml encoding="UTF-8"?>'.$sHTML); // For loading HTML chunks where the character set is not specified
|
||||
|
||||
$this->CleanNode($this->oDoc);
|
||||
|
||||
@@ -464,7 +464,7 @@ EOF
|
||||
oEditor.on( 'instanceReady', function() {
|
||||
if(!CKEDITOR.env.iOS && $('#'+oEditor.id+'_toolbox .editor_magnifier').length == 0)
|
||||
{
|
||||
$('#'+oEditor.id+'_toolbox').append('<span class="editor_magnifier" title="$sToggleFullScreen" style="display:block;width:12px;height:11px;border:1px #A6A6A6 solid;cursor:pointer; background-image:url($sAppRootUrl/images/full-screen.png)"> </span>');
|
||||
$('#'+oEditor.id+'_toolbox').append('<span class="editor_magnifier" title="$sToggleFullScreen" style="display:block;width:12px;height:11px;border:1px #A6A6A6 solid;cursor:pointer; background-image:url(\\'$sAppRootUrl/images/full-screen.png\\')"> </span>');
|
||||
$('#'+oEditor.id+'_toolbox .editor_magnifier').on('click', function() {
|
||||
oEditor.execCommand('maximize');
|
||||
if ($(this).closest('.cke_maximized').length != 0)
|
||||
@@ -473,12 +473,15 @@ EOF
|
||||
}
|
||||
});
|
||||
}
|
||||
oEditor.widgets.registered.uploadimage.onUploaded = function( upload ) {
|
||||
var oData = JSON.parse(upload.xhr.responseText);
|
||||
this.replaceWith( '<img src="' + upload.url + '" ' +
|
||||
'width="' + oData.width + '" ' +
|
||||
'height="' + oData.height + '">' );
|
||||
}
|
||||
if (oEditor.widgets.registered.uploadimage)
|
||||
{
|
||||
oEditor.widgets.registered.uploadimage.onUploaded = function( upload ) {
|
||||
var oData = JSON.parse(upload.xhr.responseText);
|
||||
this.replaceWith( '<img src="' + upload.url + '" ' +
|
||||
'width="' + oData.width + '" ' +
|
||||
'height="' + oData.height + '">' );
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
EOF
|
||||
|
||||
93
core/introspection.class.inc.php
Normal file
93
core/introspection.class.inc.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
// Copyright (C) 2017 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/>
|
||||
|
||||
/**
|
||||
* Usage:
|
||||
* require_once(...'introspection.class.inc.php');
|
||||
*/
|
||||
|
||||
require_once('attributedef.class.inc.php');
|
||||
|
||||
class Introspection
|
||||
{
|
||||
protected $aAttributeHierarchy = array(); // class => child classes
|
||||
protected $aAttributes = array();
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->InitAttributes();
|
||||
}
|
||||
|
||||
protected function InitAttributes()
|
||||
{
|
||||
foreach(get_declared_classes() as $sPHPClass)
|
||||
{
|
||||
$oRefClass = new ReflectionClass($sPHPClass);
|
||||
if ($sPHPClass == 'AttributeDefinition' || $oRefClass->isSubclassOf('AttributeDefinition'))
|
||||
{
|
||||
if ($oParentClass = $oRefClass->getParentClass())
|
||||
{
|
||||
$sParentClass = $oParentClass->getName();
|
||||
if (!array_key_exists($sParentClass, $this->aAttributeHierarchy))
|
||||
{
|
||||
$this->aAttributeHierarchy[$sParentClass] = array();
|
||||
}
|
||||
$this->aAttributeHierarchy[$sParentClass][] = $sPHPClass;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sParentClass = null;
|
||||
}
|
||||
$this->aAttributes[$sPHPClass] = array(
|
||||
'parent' => $sParentClass,
|
||||
'LoadInObject' => $sPHPClass::LoadInObject(),
|
||||
'LoadFromDB' => $sPHPClass::LoadFromDB(),
|
||||
'IsBasedOnDBColumns' => $sPHPClass::IsBasedOnDBColumns(),
|
||||
'IsBasedOnOQLExpression' => $sPHPClass::IsBasedOnOQLExpression(),
|
||||
'IsExternalField' => $sPHPClass::IsExternalField(),
|
||||
'IsScalar' => $sPHPClass::IsScalar(),
|
||||
'IsLinkset' => $sPHPClass::IsLinkset(),
|
||||
'IsHierarchicalKey' => $sPHPClass::IsHierarchicalKey(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
public function GetAttributes()
|
||||
{
|
||||
return $this->aAttributes;
|
||||
}
|
||||
public function GetAttributeHierarchy()
|
||||
{
|
||||
return $this->aAttributeHierarchy;
|
||||
}
|
||||
public function EnumAttributeCharacteristics()
|
||||
{
|
||||
return array(
|
||||
'LoadInObject' => 'Is the value stored in the object itself?',
|
||||
'LoadFromDB' => 'Is the value read from the DB?',
|
||||
'IsBasedOnDBColumns' => 'Is this a value stored within one or several columns?',
|
||||
'IsBasedOnOQLExpression' => 'Is this a value computed after other attributes, by the mean of an OQL expression?',
|
||||
'IsExternalField' => 'Is this a value stored on a related object (external key)?',
|
||||
'IsScalar' => 'Is this a value that makes sense in a SQL/OQL expression?',
|
||||
'IsLinkset' => 'Is this a collection (1-N or N-N)?',
|
||||
'IsHierarchicalKey' => 'Is this attribute an external key pointing to the host class?',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -19,7 +19,7 @@
|
||||
/**
|
||||
* File logging
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -69,81 +69,54 @@ class FileLog
|
||||
}
|
||||
}
|
||||
|
||||
class SetupLog
|
||||
abstract class LogAPI
|
||||
{
|
||||
protected static $m_oFileLog;
|
||||
|
||||
public static function Enable($sTargetFile)
|
||||
{
|
||||
self::$m_oFileLog = new FileLog($sTargetFile);
|
||||
static::$m_oFileLog = new FileLog($sTargetFile);
|
||||
}
|
||||
|
||||
public static function Error($sText)
|
||||
{
|
||||
self::$m_oFileLog->Error($sText);
|
||||
if (static::$m_oFileLog)
|
||||
{
|
||||
static::$m_oFileLog->Error($sText);
|
||||
}
|
||||
}
|
||||
public static function Warning($sText)
|
||||
{
|
||||
self::$m_oFileLog->Warning($sText);
|
||||
if (static::$m_oFileLog)
|
||||
{
|
||||
static::$m_oFileLog->Warning($sText);
|
||||
}
|
||||
}
|
||||
public static function Info($sText)
|
||||
{
|
||||
self::$m_oFileLog->Info($sText);
|
||||
if (static::$m_oFileLog)
|
||||
{
|
||||
static::$m_oFileLog->Info($sText);
|
||||
}
|
||||
}
|
||||
public static function Ok($sText)
|
||||
{
|
||||
self::$m_oFileLog->Ok($sText);
|
||||
if (static::$m_oFileLog)
|
||||
{
|
||||
static::$m_oFileLog->Ok($sText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class IssueLog
|
||||
class SetupLog extends LogAPI
|
||||
{
|
||||
protected static $m_oFileLog;
|
||||
|
||||
public static function Enable($sTargetFile)
|
||||
{
|
||||
self::$m_oFileLog = new FileLog($sTargetFile);
|
||||
}
|
||||
public static function Error($sText)
|
||||
{
|
||||
self::$m_oFileLog->Error($sText);
|
||||
}
|
||||
public static function Warning($sText)
|
||||
{
|
||||
self::$m_oFileLog->Warning($sText);
|
||||
}
|
||||
public static function Info($sText)
|
||||
{
|
||||
self::$m_oFileLog->Info($sText);
|
||||
}
|
||||
public static function Ok($sText)
|
||||
{
|
||||
self::$m_oFileLog->Ok($sText);
|
||||
}
|
||||
protected static $m_oFileLog = null;
|
||||
}
|
||||
|
||||
class ToolsLog
|
||||
class IssueLog extends LogAPI
|
||||
{
|
||||
protected static $m_oFileLog;
|
||||
|
||||
public static function Enable($sTargetFile)
|
||||
{
|
||||
self::$m_oFileLog = new FileLog($sTargetFile);
|
||||
}
|
||||
public static function Error($sText)
|
||||
{
|
||||
self::$m_oFileLog->Error($sText);
|
||||
}
|
||||
public static function Warning($sText)
|
||||
{
|
||||
self::$m_oFileLog->Warning($sText);
|
||||
}
|
||||
public static function Info($sText)
|
||||
{
|
||||
self::$m_oFileLog->Info($sText);
|
||||
}
|
||||
public static function Ok($sText)
|
||||
{
|
||||
self::$m_oFileLog->Ok($sText);
|
||||
}
|
||||
protected static $m_oFileLog = null;
|
||||
}
|
||||
|
||||
class ToolsLog extends LogAPI
|
||||
{
|
||||
protected static $m_oFileLog = null;
|
||||
}
|
||||
?>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -89,8 +89,7 @@ class ModuleDesign extends \Combodo\iTop\DesignDocument
|
||||
}
|
||||
else
|
||||
{
|
||||
var_dump($aFiles);
|
||||
$aAvailable = array();
|
||||
$aAvailable = array();
|
||||
foreach ($aFiles as $sFile)
|
||||
{
|
||||
$aAvailable[] = "'".basename($sFile, '.xml')."'";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2013-2016 Combodo SARL
|
||||
// Copyright (C) 2013-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -24,7 +24,7 @@
|
||||
* Relies on MySQL locks because the API sem_get is not always present in the
|
||||
* installed PHP.
|
||||
*
|
||||
* @copyright Copyright (C) 2013-2016 Combodo SARL
|
||||
* @copyright Copyright (C) 2013-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
class iTopMutex
|
||||
@@ -38,10 +38,14 @@ class iTopMutex
|
||||
{
|
||||
// Compute the name of a lock for mysql
|
||||
// Note: names are server-wide!!! So let's make the name specific to this iTop instance
|
||||
$oConfig = utils::GetConfig(); // Will return an empty config when called during the setup
|
||||
$oConfig = MetaModel::GetConfig();
|
||||
if ($oConfig === null)
|
||||
{
|
||||
$oConfig = utils::GetConfig(); // Will return an empty config when called during the setup
|
||||
}
|
||||
$sDBName = $oConfig->GetDBName();
|
||||
$sDBSubname = $oConfig->GetDBSubname();
|
||||
$this->sName = 'itop.'.$sName;
|
||||
$this->sName = $sName;
|
||||
if (substr($sName, -strlen($sDBName.$sDBSubname)) != $sDBName.$sDBSubname)
|
||||
{
|
||||
// If the name supplied already ends with the expected suffix
|
||||
@@ -49,7 +53,10 @@ class iTopMutex
|
||||
// running cron job by its mutex, without knowing if the config already exists or not
|
||||
$this->sName .= $sDBName.$sDBSubname;
|
||||
}
|
||||
|
||||
|
||||
// Limit the length of the name for MySQL > 5.7.5
|
||||
$this->sName = 'itop.'.md5($this->sName);
|
||||
|
||||
$this->bLocked = false; // Not yet locked
|
||||
|
||||
if (!array_key_exists($this->sName, self::$aAcquiredLocks))
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
// Copyright (c) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// 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.
|
||||
@@ -15,14 +15,7 @@
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* General definition of an expression tree (could be OQL, SQL or whatever)
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
//
|
||||
|
||||
class MissingQueryArgument extends CoreException
|
||||
{
|
||||
@@ -45,6 +38,13 @@ abstract class Expression
|
||||
// recursive rendering (aArgs used as input by default, or used as output if bRetrofitParams set to True
|
||||
abstract public function Render(&$aArgs = null, $bRetrofitParams = false);
|
||||
|
||||
/**
|
||||
* Recursively browse the expression tree
|
||||
* @param Closure $callback
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function Browse(Closure $callback);
|
||||
|
||||
abstract public function ApplyParameters($aArgs);
|
||||
|
||||
// recursively builds an array of class => fieldname
|
||||
@@ -76,11 +76,21 @@ abstract class Expression
|
||||
return self::FromOQL(base64_decode($sValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sConditionExpr
|
||||
* @return Expression
|
||||
*/
|
||||
static public function FromOQL($sConditionExpr)
|
||||
{
|
||||
static $aCache = array();
|
||||
if (array_key_exists($sConditionExpr, $aCache))
|
||||
{
|
||||
return unserialize($aCache[$sConditionExpr]);
|
||||
}
|
||||
$oOql = new OqlInterpreter($sConditionExpr);
|
||||
$oExpression = $oOql->ParseExpression();
|
||||
|
||||
$aCache[$sConditionExpr] = serialize($oExpression);
|
||||
|
||||
return $oExpression;
|
||||
}
|
||||
|
||||
@@ -90,14 +100,22 @@ abstract class Expression
|
||||
return $oSql;
|
||||
}
|
||||
|
||||
public function LogAnd($oExpr)
|
||||
/**
|
||||
* @param Expression $oExpr
|
||||
* @return Expression
|
||||
*/
|
||||
public function LogAnd(Expression $oExpr)
|
||||
{
|
||||
if ($this->IsTrue()) return clone $oExpr;
|
||||
if ($oExpr->IsTrue()) return clone $this;
|
||||
return new BinaryExpression($this, 'AND', $oExpr);
|
||||
}
|
||||
|
||||
public function LogOr($oExpr)
|
||||
/**
|
||||
* @param Expression $oExpr
|
||||
* @return Expression
|
||||
*/
|
||||
public function LogOr(Expression $oExpr)
|
||||
{
|
||||
return new BinaryExpression($this, 'OR', $oExpr);
|
||||
}
|
||||
@@ -139,6 +157,11 @@ class SQLExpression extends Expression
|
||||
return $this->m_sSQL;
|
||||
}
|
||||
|
||||
public function Browse(Closure $callback)
|
||||
{
|
||||
$callback($this);
|
||||
}
|
||||
|
||||
public function ApplyParameters($aArgs)
|
||||
{
|
||||
}
|
||||
@@ -203,6 +226,10 @@ 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)
|
||||
{
|
||||
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;
|
||||
@@ -245,7 +272,14 @@ class BinaryExpression extends Expression
|
||||
$sRight = $this->GetRightExpr()->Render($aArgs, $bRetrofitParams);
|
||||
return "($sLeft $sOperator $sRight)";
|
||||
}
|
||||
|
||||
|
||||
public function Browse(Closure $callback)
|
||||
{
|
||||
$callback($this);
|
||||
$this->m_oLeftExpr->Browse($callback);
|
||||
$this->m_oRightExpr->Browse($callback);
|
||||
}
|
||||
|
||||
public function ApplyParameters($aArgs)
|
||||
{
|
||||
if ($this->m_oLeftExpr instanceof VariableExpression)
|
||||
@@ -358,7 +392,7 @@ class UnaryExpression extends Expression
|
||||
public function GetValue()
|
||||
{
|
||||
return $this->m_value;
|
||||
}
|
||||
}
|
||||
|
||||
// recursive rendering
|
||||
public function Render(&$aArgs = null, $bRetrofitParams = false)
|
||||
@@ -366,6 +400,11 @@ class UnaryExpression extends Expression
|
||||
return CMDBSource::Quote($this->m_value);
|
||||
}
|
||||
|
||||
public function Browse(Closure $callback)
|
||||
{
|
||||
$callback($this);
|
||||
}
|
||||
|
||||
public function ApplyParameters($aArgs)
|
||||
{
|
||||
}
|
||||
@@ -484,6 +523,12 @@ class FieldExpression extends UnaryExpression
|
||||
public function GetParent() {return $this->m_sParent;}
|
||||
public function GetName() {return $this->m_sName;}
|
||||
|
||||
public function SetParent($sParent)
|
||||
{
|
||||
$this->m_sParent = $sParent;
|
||||
$this->m_value = $sParent.'.'.$this->m_sName;
|
||||
}
|
||||
|
||||
// recursive rendering
|
||||
public function Render(&$aArgs = null, $bRetrofitParams = false)
|
||||
{
|
||||
@@ -579,7 +624,7 @@ class FieldExpression extends UnaryExpression
|
||||
$iObjKey = (int)$sValue;
|
||||
if ($iObjKey > 0)
|
||||
{
|
||||
$oObject = MetaModel::GetObject($sObjClass, $iObjKey);
|
||||
$oObject = MetaModel::GetObjectWithArchive($sObjClass, $iObjKey);
|
||||
$sRes = $oObject->GetHyperlink();
|
||||
}
|
||||
else
|
||||
@@ -680,7 +725,7 @@ class VariableExpression extends UnaryExpression
|
||||
throw new MissingQueryArgument('Missing query argument', array('expecting'=>$this->m_sName, 'available'=>array_keys($aArgs)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function RenameParam($sOldName, $sNewName)
|
||||
{
|
||||
if ($this->m_sName == $sOldName)
|
||||
@@ -768,6 +813,15 @@ class ListExpression extends Expression
|
||||
return '('.implode(', ', $aRes).')';
|
||||
}
|
||||
|
||||
public function Browse(Closure $callback)
|
||||
{
|
||||
$callback($this);
|
||||
foreach ($this->m_aExpressions as $oExpr)
|
||||
{
|
||||
$oExpr->Browse($callback);
|
||||
}
|
||||
}
|
||||
|
||||
public function ApplyParameters($aArgs)
|
||||
{
|
||||
$aRes = array();
|
||||
@@ -888,6 +942,15 @@ class FunctionExpression extends Expression
|
||||
return $this->m_sVerb.'('.implode(', ', $aRes).')';
|
||||
}
|
||||
|
||||
public function Browse(Closure $callback)
|
||||
{
|
||||
$callback($this);
|
||||
foreach ($this->m_aArgs as $iPos => $oExpr)
|
||||
{
|
||||
$oExpr->Browse($callback);
|
||||
}
|
||||
}
|
||||
|
||||
public function ApplyParameters($aArgs)
|
||||
{
|
||||
$aRes = array();
|
||||
@@ -1071,6 +1134,12 @@ class IntervalExpression extends Expression
|
||||
return 'INTERVAL '.$this->m_oValue->Render($aArgs, $bRetrofitParams).' '.$this->m_sUnit;
|
||||
}
|
||||
|
||||
public function Browse(Closure $callback)
|
||||
{
|
||||
$callback($this);
|
||||
$this->m_oValue->Browse($callback);
|
||||
}
|
||||
|
||||
public function ApplyParameters($aArgs)
|
||||
{
|
||||
if ($this->m_oValue instanceof VariableExpression)
|
||||
@@ -1151,6 +1220,15 @@ class CharConcatExpression extends Expression
|
||||
return "CAST(CONCAT(".implode(', ', $aRes).") AS CHAR)";
|
||||
}
|
||||
|
||||
public function Browse(Closure $callback)
|
||||
{
|
||||
$callback($this);
|
||||
foreach ($this->m_aExpressions as $oExpr)
|
||||
{
|
||||
$oExpr->Browse($callback);
|
||||
}
|
||||
}
|
||||
|
||||
public function ApplyParameters($aArgs)
|
||||
{
|
||||
$aRes = array();
|
||||
@@ -1255,6 +1333,15 @@ class CharConcatWSExpression extends CharConcatExpression
|
||||
return "CAST(CONCAT_WS($sSep, ".implode(', ', $aRes).") AS CHAR)";
|
||||
}
|
||||
|
||||
public function Browse(Closure $callback)
|
||||
{
|
||||
$callback($this);
|
||||
foreach ($this->m_aExpressions as $oExpr)
|
||||
{
|
||||
$oExpr->Browse($callback);
|
||||
}
|
||||
}
|
||||
|
||||
public function Translate($aTranslationData, $bMatchAll = true, $bMarkFieldsAsResolved = true)
|
||||
{
|
||||
$aRes = array();
|
||||
@@ -1269,15 +1356,41 @@ class CharConcatWSExpression extends CharConcatExpression
|
||||
|
||||
class QueryBuilderExpressions
|
||||
{
|
||||
/**
|
||||
* @var Expression
|
||||
*/
|
||||
protected $m_oConditionExpr;
|
||||
/**
|
||||
* @var Expression[]
|
||||
*/
|
||||
protected $m_aSelectExpr;
|
||||
/**
|
||||
* @var Expression[]
|
||||
*/
|
||||
protected $m_aGroupByExpr;
|
||||
/**
|
||||
* @var Expression[]
|
||||
*/
|
||||
protected $m_aJoinFields;
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected $m_aClassIds;
|
||||
|
||||
public function __construct($oSearch, $aGroupByExpr = null)
|
||||
public function __construct(DBObjectSearch $oSearch, $aGroupByExpr = null)
|
||||
{
|
||||
$this->m_oConditionExpr = $oSearch->GetCriteria();
|
||||
if (!$oSearch->GetShowObsoleteData())
|
||||
{
|
||||
foreach ($oSearch->GetSelectedClasses() as $sAlias => $sClass)
|
||||
{
|
||||
if (MetaModel::IsObsoletable($sClass))
|
||||
{
|
||||
$oNotObsolete = new BinaryExpression(new FieldExpression('obsolescence_flag', $sAlias), '=', new ScalarExpression(0));
|
||||
$this->m_oConditionExpr = $this->m_oConditionExpr->LogAnd($oNotObsolete);
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->m_aSelectExpr = array();
|
||||
$this->m_aGroupByExpr = $aGroupByExpr;
|
||||
$this->m_aJoinFields = array();
|
||||
@@ -1304,23 +1417,35 @@ class QueryBuilderExpressions
|
||||
return $this->m_oConditionExpr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Expression|mixed
|
||||
*/
|
||||
public function PopJoinField()
|
||||
{
|
||||
return array_pop($this->m_aJoinFields);
|
||||
}
|
||||
|
||||
public function AddSelect($sAttAlias, $oExpression)
|
||||
/**
|
||||
* @param string $sAttAlias
|
||||
* @param Expression $oExpression
|
||||
*/
|
||||
public function AddSelect($sAttAlias, Expression $oExpression)
|
||||
{
|
||||
$this->m_aSelectExpr[$sAttAlias] = $oExpression;
|
||||
}
|
||||
|
||||
//$oConditionTree = $oConditionTree->LogAnd($oFinalClassRestriction);
|
||||
public function AddCondition($oExpression)
|
||||
/**
|
||||
* @param Expression $oExpression
|
||||
*/
|
||||
public function AddCondition(Expression $oExpression)
|
||||
{
|
||||
$this->m_oConditionExpr = $this->m_oConditionExpr->LogAnd($oExpression);
|
||||
}
|
||||
|
||||
public function PushJoinField($oExpression)
|
||||
/**
|
||||
* @param Expression $oExpression
|
||||
*/
|
||||
public function PushJoinField(Expression $oExpression)
|
||||
{
|
||||
array_push($this->m_aJoinFields, $oExpression);
|
||||
}
|
||||
@@ -1403,6 +1528,4 @@ class QueryBuilderExpressions
|
||||
$this->m_aJoinFields[$index] = $oExpression->RenameParam($sOldName, $sNewName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* Wrapper to execute the parser, lexical analyzer and normalization of an OQL query
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -83,6 +83,9 @@ class OqlInterpreter
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return OqlQuery
|
||||
*/
|
||||
public function ParseQuery()
|
||||
{
|
||||
$oRes = $this->Parse();
|
||||
@@ -93,6 +96,9 @@ class OqlInterpreter
|
||||
return $oRes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Expression
|
||||
*/
|
||||
public function ParseExpression()
|
||||
{
|
||||
$oRes = $this->Parse();
|
||||
@@ -103,5 +109,3 @@ class OqlInterpreter
|
||||
return $oRes;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -23,7 +23,7 @@ define('CASELOG_SEPARATOR', "\n".'========== %1$s : %2$s (%3$d) ============'."\
|
||||
/**
|
||||
* Class to store a "case log" in a structured way, keeping track of its successive entries
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
class ormCaseLog {
|
||||
@@ -191,8 +191,15 @@ class ormCaseLog {
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->m_sLog;
|
||||
if($this->IsEmpty()) return '';
|
||||
|
||||
return $this->m_sLog;
|
||||
}
|
||||
|
||||
public function IsEmpty()
|
||||
{
|
||||
return ($this->m_sLog === null);
|
||||
}
|
||||
|
||||
public function ClearModifiedFlag()
|
||||
{
|
||||
@@ -388,8 +395,9 @@ class ormCaseLog {
|
||||
if (($bEditMode) && (count($aIndex) > 0) && $this->m_bModified)
|
||||
{
|
||||
// Don't display the first element, that is still considered as editable
|
||||
$iPos = $aIndex[0]['separator_length'] + $aIndex[0]['text_length'];
|
||||
array_shift($aIndex);
|
||||
$aLastEntry = end($aIndex);
|
||||
$iPos = $aLastEntry['separator_length'] + $aLastEntry['text_length'];
|
||||
array_pop($aIndex);
|
||||
}
|
||||
for($index=count($aIndex)-1 ; $index >= 0 ; $index--)
|
||||
{
|
||||
@@ -519,57 +527,36 @@ class ormCaseLog {
|
||||
if ($this->m_bModified)
|
||||
{
|
||||
$aLatestEntry = end($this->m_aIndex);
|
||||
if ($aLatestEntry['user_name'] != $sOnBehalfOf)
|
||||
if ($aLatestEntry['user_name'] == $sOnBehalfOf)
|
||||
{
|
||||
$bMergeEntries = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
$bMergeEntries = true;
|
||||
// Append the new text to the previous one
|
||||
$sPreviousText = substr($this->m_sLog, $aLatestEntry['separator_length'], $aLatestEntry['text_length']);
|
||||
$sText = $sPreviousText."\n".$sText;
|
||||
|
||||
// Cleanup the previous entry
|
||||
array_pop($this->m_aIndex);
|
||||
$this->m_sLog = substr($this->m_sLog, $aLatestEntry['separator_length'] + $aLatestEntry['text_length']);
|
||||
}
|
||||
}
|
||||
|
||||
if ($bMergeEntries)
|
||||
{
|
||||
$aLatestEntry = end($this->m_aIndex);
|
||||
$this->m_sLog = substr($this->m_sLog, $aLatestEntry['separator_length']);
|
||||
$sSeparator = sprintf(CASELOG_SEPARATOR, $sDate, $sOnBehalfOf, $iUserId);
|
||||
$iSepLength = strlen($sSeparator);
|
||||
$iTextlength = strlen($sText."\n");
|
||||
$this->m_sLog = $sSeparator.$sText.$this->m_sLog; // Latest entry printed first
|
||||
$this->m_aIndex[] = array(
|
||||
'user_name' => $sOnBehalfOf,
|
||||
'user_id' => $iUserId,
|
||||
'date' => time(),
|
||||
'text_length' => $aLatestEntry['text_length'] + $iTextlength,
|
||||
'separator_length' => $iSepLength,
|
||||
'format' => 'html',
|
||||
);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
$sSeparator = sprintf(CASELOG_SEPARATOR, $sDate, $sOnBehalfOf, $iUserId);
|
||||
$iSepLength = strlen($sSeparator);
|
||||
$iTextlength = strlen($sText);
|
||||
$this->m_sLog = $sSeparator.$sText.$this->m_sLog; // Latest entry printed first
|
||||
$this->m_aIndex[] = array(
|
||||
'user_name' => $sOnBehalfOf,
|
||||
'user_id' => $iUserId,
|
||||
'date' => time(),
|
||||
'text_length' => $iTextlength,
|
||||
'separator_length' => $iSepLength,
|
||||
'format' => 'html',
|
||||
);
|
||||
}
|
||||
|
||||
$sSeparator = sprintf(CASELOG_SEPARATOR, $sDate, $sOnBehalfOf, $iUserId);
|
||||
$iSepLength = strlen($sSeparator);
|
||||
$iTextlength = strlen($sText);
|
||||
$this->m_sLog = $sSeparator.$sText.$this->m_sLog; // Latest entry printed first
|
||||
$this->m_aIndex[] = array(
|
||||
'user_name' => $sOnBehalfOf,
|
||||
'user_id' => $iUserId,
|
||||
'date' => time(),
|
||||
'text_length' => $iTextlength,
|
||||
'separator_length' => $iSepLength,
|
||||
'format' => 'html',
|
||||
);
|
||||
$this->m_bModified = true;
|
||||
}
|
||||
|
||||
|
||||
public function AddLogEntryFromJSON($oJson, $bCheckUserId = true)
|
||||
{
|
||||
$sText = HTMLSanitizer::Sanitize(isset($oJson->message) ? $oJson->message : '');
|
||||
|
||||
if (isset($oJson->user_id))
|
||||
{
|
||||
if (!UserRights::IsAdministrator())
|
||||
@@ -616,10 +603,16 @@ class ormCaseLog {
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: what is the default format ? text ?
|
||||
// The default is HTML
|
||||
$sFormat = 'html';
|
||||
}
|
||||
|
||||
|
||||
$sText = isset($oJson->message) ? $oJson->message : '';
|
||||
if ($sFormat == 'html')
|
||||
{
|
||||
$sText = HTMLSanitizer::Sanitize($sText);
|
||||
}
|
||||
|
||||
$sDate = date(AttributeDateTime::GetInternalFormat(), $iDate);
|
||||
|
||||
$sSeparator = sprintf(CASELOG_SEPARATOR, $sDate, $sOnBehalfOf, $iUserId);
|
||||
@@ -639,12 +632,12 @@ class ormCaseLog {
|
||||
}
|
||||
|
||||
|
||||
public function GetModifiedEntry()
|
||||
public function GetModifiedEntry($sFormat = 'text')
|
||||
{
|
||||
$sModifiedEntry = '';
|
||||
if ($this->m_bModified)
|
||||
{
|
||||
$sModifiedEntry = $this->GetLatestEntry();
|
||||
$sModifiedEntry = $this->GetLatestEntry($sFormat);
|
||||
}
|
||||
return $sModifiedEntry;
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ class ormCustomFieldsValue
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this->oHostObject), $this->sAttCode);
|
||||
$oHandler = $oAttDef->GetHandler($this->GetValues());
|
||||
return 'template...verb='.$sVerb.' sur "'.json_encode($this->aCurrentValues).'"';
|
||||
return $oHandler->GetForTemplate($this->aCurrentValues, $sVerb, $bLocalize);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -51,6 +51,8 @@ class ormDocument
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
if($this->IsEmpty()) return '';
|
||||
|
||||
return MyHelpers::beautifulstr($this->m_data, 100, true);
|
||||
}
|
||||
|
||||
@@ -115,21 +117,29 @@ class ormDocument
|
||||
*/
|
||||
public function GetDownloadLink($sClass, $Id, $sAttCode)
|
||||
{
|
||||
return "<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=download_document&class=$sClass&id=$Id&field=$sAttCode\">".htmlentities($this->GetFileName(), ENT_QUOTES, 'UTF-8')."</a>\n";
|
||||
return "<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/ajax.document.php?operation=download_document&class=$sClass&id=$Id&field=$sAttCode\">".htmlentities($this->GetFileName(), ENT_QUOTES, 'UTF-8')."</a>\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an URL to display a document like an image
|
||||
* @return string
|
||||
*/
|
||||
public function GetDisplayURL($sClass, $Id, $sAttCode)
|
||||
{
|
||||
return utils::GetAbsoluteUrlAppRoot() . "pages/ajax.render.php?operation=display_document&class=$sClass&id=$Id&field=$sAttCode";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an URL to download a document like an image (uses HTTP caching)
|
||||
* @return string
|
||||
*/
|
||||
*/
|
||||
public function GetDownloadURL($sClass, $Id, $sAttCode)
|
||||
{
|
||||
// Compute a signature to reset the cache anytime the data changes (this is acceptable if used only with icon files)
|
||||
$sSignature = md5($this->GetData());
|
||||
return utils::GetAbsoluteUrlAppRoot()."pages/ajax.document.php?operation=download_document&class=$sClass&id=$Id&field=$sAttCode&s=$sSignature&cache=86400";
|
||||
return utils::GetAbsoluteUrlAppRoot() . "pages/ajax.document.php?operation=download_document&class=$sClass&id=$Id&field=$sAttCode&s=$sSignature&cache=86400";
|
||||
}
|
||||
|
||||
|
||||
public function IsPreviewAvailable()
|
||||
{
|
||||
$bRet = false;
|
||||
@@ -176,7 +186,7 @@ class ormDocument
|
||||
{
|
||||
$oPage->TrashUnexpectedOutput();
|
||||
$oPage->SetContentType($oDocument->GetMimeType());
|
||||
//$oPage->SetContentDisposition($sContentDisposition,$oDocument->GetFileName());
|
||||
$oPage->SetContentDisposition($sContentDisposition,$oDocument->GetFileName());
|
||||
$oPage->add($oDocument->GetData());
|
||||
}
|
||||
}
|
||||
|
||||
729
core/ormlinkset.class.inc.php
Normal file
729
core/ormlinkset.class.inc.php
Normal file
@@ -0,0 +1,729 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2017 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/>
|
||||
|
||||
require_once('dbobjectiterator.php');
|
||||
|
||||
|
||||
/**
|
||||
* The value for an attribute representing a set of links between the host object and "remote" objects
|
||||
*
|
||||
* @package iTopORM
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
||||
{
|
||||
protected $sHostClass; // subclass of DBObject
|
||||
protected $sAttCode; // xxxxxx_list
|
||||
protected $sClass; // class of the links
|
||||
|
||||
/**
|
||||
* @var DBObjectSet
|
||||
*/
|
||||
protected $oOriginalSet;
|
||||
|
||||
/**
|
||||
* @var DBObject[] array of iObjectId => DBObject
|
||||
*/
|
||||
protected $aOriginalObjects = null;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $bHasDelta = false;
|
||||
|
||||
/**
|
||||
* Object from the original set, minus the removed objects
|
||||
* @var DBObject[] array of iObjectId => DBObject
|
||||
*/
|
||||
protected $aPreserved = array();
|
||||
|
||||
/**
|
||||
* @var DBObject[] New items
|
||||
*/
|
||||
protected $aAdded = array();
|
||||
|
||||
/**
|
||||
* @var DBObject[] Modified items (could also be found in aPreserved)
|
||||
*/
|
||||
protected $aModified = array();
|
||||
|
||||
/**
|
||||
* @var int[] Removed items
|
||||
*/
|
||||
protected $aRemoved = array();
|
||||
|
||||
/**
|
||||
* @var int Position in the collection
|
||||
*/
|
||||
protected $iCursor = 0;
|
||||
|
||||
/**
|
||||
* __toString magical function overload.
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* ormLinkSet constructor.
|
||||
* @param $sHostClass
|
||||
* @param $sAttCode
|
||||
* @param DBObjectSet|null $oOriginalSet
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __construct($sHostClass, $sAttCode, DBObjectSet $oOriginalSet = null)
|
||||
{
|
||||
$this->sHostClass = $sHostClass;
|
||||
$this->sAttCode = $sAttCode;
|
||||
$this->oOriginalSet = $oOriginalSet ? clone $oOriginalSet : null;
|
||||
|
||||
$oAttDef = MetaModel::GetAttributeDef($sHostClass, $sAttCode);
|
||||
if (!$oAttDef instanceof AttributeLinkedSet)
|
||||
{
|
||||
throw new Exception("ormLinkSet: $sAttCode is not a link set");
|
||||
}
|
||||
$this->sClass = $oAttDef->GetLinkedClass();
|
||||
if ($oOriginalSet && ($oOriginalSet->GetClass() != $this->sClass))
|
||||
{
|
||||
throw new Exception("ormLinkSet: wrong class for the original set, found {$oOriginalSet->GetClass()} while expecting {$oAttDef->GetLinkedClass()}");
|
||||
}
|
||||
}
|
||||
|
||||
public function GetFilter()
|
||||
{
|
||||
return clone $this->oOriginalSet->GetFilter();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 hash $aAttToLoad Format: alias => array of attribute_codes
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function OptimizeColumnLoad($aAttToLoad)
|
||||
{
|
||||
$this->oOriginalSet->OptimizeColumnLoad($aAttToLoad);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DBObject $oLink
|
||||
*/
|
||||
public function AddItem(DBObject $oLink)
|
||||
{
|
||||
assert($oLink instanceof $this->sClass);
|
||||
// No impact on the iteration algorithm
|
||||
$iObjectId = $oLink->GetKey();
|
||||
$this->aAdded[$iObjectId] = $oLink;
|
||||
$this->bHasDelta = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DBObject $oObject
|
||||
* @param string $sClassAlias
|
||||
* @deprecated Since iTop 2.4, use ormLinkset->AddItem() instead.
|
||||
*/
|
||||
public function AddObject(DBObject $oObject, $sClassAlias = '')
|
||||
{
|
||||
$this->AddItem($oObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $iObjectId
|
||||
*/
|
||||
public function RemoveItem($iObjectId)
|
||||
{
|
||||
if (array_key_exists($iObjectId, $this->aPreserved))
|
||||
{
|
||||
unset($this->aPreserved[$iObjectId]);
|
||||
$this->aRemoved[$iObjectId] = $iObjectId;
|
||||
$this->bHasDelta = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (array_key_exists($iObjectId, $this->aAdded))
|
||||
{
|
||||
unset($this->aAdded[$iObjectId]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DBObject $oLink
|
||||
*/
|
||||
public function ModifyItem(DBObject $oLink)
|
||||
{
|
||||
assert($oLink instanceof $this->sClass);
|
||||
|
||||
$iObjectId = $oLink->GetKey();
|
||||
if (array_key_exists($iObjectId, $this->aPreserved))
|
||||
{
|
||||
unset($this->aPreserved[$iObjectId]);
|
||||
$this->aModified[$iObjectId] = $oLink;
|
||||
$this->bHasDelta = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected function LoadOriginalIds()
|
||||
{
|
||||
if ($this->aOriginalObjects === null)
|
||||
{
|
||||
if ($this->oOriginalSet)
|
||||
{
|
||||
$this->aOriginalObjects = $this->GetArrayOfIndex();
|
||||
$this->aPreserved = $this->aOriginalObjects; // Copy (not effective until aPreserved gets modified)
|
||||
foreach ($this->aRemoved as $iObjectId)
|
||||
{
|
||||
if (array_key_exists($iObjectId, $this->aPreserved))
|
||||
{
|
||||
unset($this->aPreserved[$iObjectId]);
|
||||
}
|
||||
}
|
||||
foreach ($this->aModified as $iObjectId => $oLink)
|
||||
{
|
||||
if (array_key_exists($iObjectId, $this->aPreserved))
|
||||
{
|
||||
unset($this->aPreserved[$iObjectId]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// Nothing to load
|
||||
$this->aOriginalObjects = array();
|
||||
$this->aPreserved = array();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: After calling this method, the set cursor will be at the end of the set. You might want to rewind it.
|
||||
* @return array
|
||||
*/
|
||||
protected function GetArrayOfIndex()
|
||||
{
|
||||
$aRet = array();
|
||||
$this->oOriginalSet->Rewind();
|
||||
$iRow = 0;
|
||||
while ($oObject = $this->oOriginalSet->Fetch())
|
||||
{
|
||||
$aRet[$oObject->GetKey()] = $iRow++;
|
||||
}
|
||||
return $aRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $bWithId
|
||||
* @return array
|
||||
* @deprecated Since iTop 2.4, use foreach($this as $oItem){} instead
|
||||
*/
|
||||
public function ToArray($bWithId = true)
|
||||
{
|
||||
$aRet = array();
|
||||
foreach($this as $oItem)
|
||||
{
|
||||
if ($bWithId)
|
||||
{
|
||||
$aRet[$oItem->GetKey()] = $oItem;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aRet[] = $oItem;
|
||||
}
|
||||
}
|
||||
return $aRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sAttCode
|
||||
* @param bool $bWithId
|
||||
* @return array
|
||||
*/
|
||||
public function GetColumnAsArray($sAttCode, $bWithId = true)
|
||||
{
|
||||
$aRet = array();
|
||||
foreach($this as $oItem)
|
||||
{
|
||||
if ($bWithId)
|
||||
{
|
||||
$aRet[$oItem->GetKey()] = $oItem->Get($sAttCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
$aRet[] = $oItem->Get($sAttCode);
|
||||
}
|
||||
}
|
||||
return $aRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* The class of the objects of the collection (at least a common ancestor)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function GetClass()
|
||||
{
|
||||
return $this->sClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* The total number of objects in the collection
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function Count()
|
||||
{
|
||||
$this->LoadOriginalIds();
|
||||
$iRet = count($this->aPreserved) + count($this->aAdded) + count($this->aModified);
|
||||
return $iRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Position the cursor to the given 0-based position
|
||||
*
|
||||
* @param $iPosition
|
||||
* @throws Exception
|
||||
* @internal param int $iRow
|
||||
*/
|
||||
public function Seek($iPosition)
|
||||
{
|
||||
$this->LoadOriginalIds();
|
||||
|
||||
$iCount = $this->Count();
|
||||
if ($iPosition >= $iCount)
|
||||
{
|
||||
throw new Exception("Invalid position $iPosition: the link set is made of $iCount items.");
|
||||
}
|
||||
$this->rewind();
|
||||
for($iPos = 0 ; $iPos < $iPosition ; $iPos++)
|
||||
{
|
||||
$this->next();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the object at the current position in the collection and move the cursor to the next position.
|
||||
*
|
||||
* @return DBObject|null The fetched object or null when at the end
|
||||
*/
|
||||
public function Fetch()
|
||||
{
|
||||
$this->LoadOriginalIds();
|
||||
|
||||
$ret = $this->current();
|
||||
if ($ret === false)
|
||||
{
|
||||
$ret = null;
|
||||
}
|
||||
$this->next();
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current element
|
||||
* @link http://php.net/manual/en/iterator.current.php
|
||||
* @return mixed Can return any type.
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
$this->LoadOriginalIds();
|
||||
|
||||
$iPreservedCount = count($this->aPreserved);
|
||||
if ($this->iCursor < $iPreservedCount)
|
||||
{
|
||||
$iRet = current($this->aPreserved);
|
||||
$this->oOriginalSet->Seek($iRet);
|
||||
$oRet = $this->oOriginalSet->Fetch();
|
||||
}
|
||||
else
|
||||
{
|
||||
$iModifiedCount = count($this->aModified);
|
||||
if($this->iCursor < $iPreservedCount + $iModifiedCount)
|
||||
{
|
||||
$oRet = current($this->aModified);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oRet = current($this->aAdded);
|
||||
}
|
||||
}
|
||||
return $oRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move forward to next element
|
||||
* @link http://php.net/manual/en/iterator.next.php
|
||||
* @return void Any returned value is ignored.
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
$this->LoadOriginalIds();
|
||||
|
||||
$iPreservedCount = count($this->aPreserved);
|
||||
if ($this->iCursor < $iPreservedCount)
|
||||
{
|
||||
next($this->aPreserved);
|
||||
}
|
||||
else
|
||||
{
|
||||
$iModifiedCount = count($this->aModified);
|
||||
if($this->iCursor < $iPreservedCount + $iModifiedCount)
|
||||
{
|
||||
next($this->aModified);
|
||||
}
|
||||
else
|
||||
{
|
||||
next($this->aAdded);
|
||||
}
|
||||
}
|
||||
// Increment AFTER moving the internal cursors because when starting aModified / aAdded, we must leave it intact
|
||||
$this->iCursor++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the key of the current element
|
||||
* @link http://php.net/manual/en/iterator.key.php
|
||||
* @return mixed scalar on success, or null on failure.
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return $this->iCursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if current position is valid
|
||||
* @link http://php.net/manual/en/iterator.valid.php
|
||||
* @return boolean The return value will be casted to boolean and then evaluated.
|
||||
* Returns true on success or false on failure.
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
$this->LoadOriginalIds();
|
||||
|
||||
$iCount = $this->Count();
|
||||
$bRet = ($this->iCursor < $iCount);
|
||||
return $bRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewind the Iterator to the first element
|
||||
* @link http://php.net/manual/en/iterator.rewind.php
|
||||
* @return void Any returned value is ignored.
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
$this->LoadOriginalIds();
|
||||
|
||||
$this->iCursor = 0;
|
||||
reset($this->aPreserved);
|
||||
reset($this->aAdded);
|
||||
reset($this->aModified);
|
||||
}
|
||||
|
||||
public function HasDelta()
|
||||
{
|
||||
return $this->bHasDelta;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method has been designed specifically for AttributeLinkedSet:Equals and as such it assumes that the passed argument is a clone of this.
|
||||
* @param ormLinkSet $oFellow
|
||||
* @return bool|null
|
||||
* @throws Exception
|
||||
*/
|
||||
public function Equals(ormLinkSet $oFellow)
|
||||
{
|
||||
$bRet = null;
|
||||
if ($this === $oFellow)
|
||||
{
|
||||
$bRet = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( ($this->oOriginalSet !== $oFellow->oOriginalSet)
|
||||
&& ($this->oOriginalSet->GetFilter()->ToOQL() != $oFellow->oOriginalSet->GetFilter()->ToOQL()) )
|
||||
{
|
||||
throw new Exception('ormLinkSet::Equals assumes that compared link sets have the same original scope');
|
||||
}
|
||||
if ($this->HasDelta())
|
||||
{
|
||||
throw new Exception('ormLinkSet::Equals assumes that left link set had no delta');
|
||||
}
|
||||
$bRet = !$oFellow->HasDelta();
|
||||
}
|
||||
return $bRet;
|
||||
}
|
||||
|
||||
public function UpdateFromCompleteList(iDBObjectSetIterator $oFellow)
|
||||
{
|
||||
if ($oFellow === $this)
|
||||
{
|
||||
throw new Exception('ormLinkSet::UpdateFromCompleteList assumes that the passed link set is at least a clone of the current one');
|
||||
}
|
||||
$bUpdateFromDelta = false;
|
||||
if ($oFellow instanceof ormLinkSet)
|
||||
{
|
||||
if ( ($this->oOriginalSet === $oFellow->oOriginalSet)
|
||||
|| ($this->oOriginalSet->GetFilter()->ToOQL() == $oFellow->oOriginalSet->GetFilter()->ToOQL()) )
|
||||
{
|
||||
$bUpdateFromDelta = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($bUpdateFromDelta)
|
||||
{
|
||||
// Same original set -> simply update the delta
|
||||
$this->iCursor = 0;
|
||||
$this->aAdded = $oFellow->aAdded;
|
||||
$this->aRemoved = $oFellow->aRemoved;
|
||||
$this->aModified = $oFellow->aModified;
|
||||
$this->aPreserved = $oFellow->aPreserved;
|
||||
$this->bHasDelta = $oFellow->bHasDelta;
|
||||
}
|
||||
else
|
||||
{
|
||||
// For backward compatibility reasons, let's rebuild a delta...
|
||||
|
||||
// Reset the delta
|
||||
$this->iCursor = 0;
|
||||
$this->aAdded = array();
|
||||
$this->aRemoved = array();
|
||||
$this->aModified = array();
|
||||
$this->aPreserved = ($this->aOriginalObjects === null) ? array() : $this->aOriginalObjects;
|
||||
$this->bHasDelta = false;
|
||||
|
||||
/** @var AttributeLinkedSet $oAttDef */
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->sHostClass, $this->sAttCode);
|
||||
$sExtKeyToMe = $oAttDef->GetExtKeyToMe();
|
||||
$sAdditionalKey = null;
|
||||
if ($oAttDef->IsIndirect() && !$oAttDef->DuplicatesAllowed())
|
||||
{
|
||||
$sAdditionalKey = $oAttDef->GetExtKeyToRemote();
|
||||
}
|
||||
// Compare both collections by iterating the whole sets, order them, a build a fingerprint based on meaningful data (what make the difference)
|
||||
$oComparator = new DBObjectSetComparator($this, $oFellow, array($sExtKeyToMe), $sAdditionalKey);
|
||||
$aChanges = $oComparator->GetDifferences();
|
||||
foreach ($aChanges['added'] as $oLink)
|
||||
{
|
||||
$this->AddItem($oLink);
|
||||
}
|
||||
|
||||
foreach ($aChanges['modified'] as $oLink)
|
||||
{
|
||||
$this->ModifyItem($oLink);
|
||||
}
|
||||
|
||||
foreach ($aChanges['removed'] as $oLink)
|
||||
{
|
||||
$this->RemoveItem($oLink->GetKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DBObject $oHostObject
|
||||
*/
|
||||
public function DBWrite(DBObject $oHostObject)
|
||||
{
|
||||
/** @var AttributeLinkedSet $oAttDef */
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($oHostObject), $this->sAttCode);
|
||||
$sExtKeyToMe = $oAttDef->GetExtKeyToMe();
|
||||
$sExtKeyToRemote = $oAttDef->IsIndirect() ? $oAttDef->GetExtKeyToRemote() : 'n/a';
|
||||
|
||||
$aCheckLinks = array();
|
||||
$aCheckRemote = array();
|
||||
foreach ($this->aAdded as $oLink)
|
||||
{
|
||||
if ($oLink->IsNew())
|
||||
{
|
||||
if ($oAttDef->IsIndirect() && !$oAttDef->DuplicatesAllowed())
|
||||
{
|
||||
//todo: faire un test qui passe dans cette branche !
|
||||
$aCheckRemote[] = $oLink->Get($sExtKeyToRemote);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//todo: faire un test qui passe dans cette branche !
|
||||
$aCheckLinks[] = $oLink->GetKey();
|
||||
}
|
||||
}
|
||||
foreach ($this->aRemoved as $iLinkId)
|
||||
{
|
||||
$aCheckLinks[] = $iLinkId;
|
||||
}
|
||||
foreach ($this->aModified as $iLinkId => $oLink)
|
||||
{
|
||||
$aCheckLinks[] = $oLink->GetKey();
|
||||
}
|
||||
|
||||
// Critical section : serialize any write access to these links
|
||||
//
|
||||
$oMtx = new iTopMutex('Write-'.$this->sClass);
|
||||
$oMtx->Lock();
|
||||
|
||||
// Check for the existing links
|
||||
//
|
||||
/** @var DBObject[] $aExistingLinks */
|
||||
$aExistingLinks = array();
|
||||
/** @var Int[] $aExistingRemote */
|
||||
$aExistingRemote = array();
|
||||
if (count($aCheckLinks) > 0)
|
||||
{
|
||||
$oSearch = new DBObjectSearch($this->sClass);
|
||||
$oSearch->AddCondition('id', $aCheckLinks, 'IN');
|
||||
$oSet = new DBObjectSet($oSearch);
|
||||
$aExistingLinks = $oSet->ToArray();
|
||||
}
|
||||
|
||||
// Check for the existing remote objects
|
||||
//
|
||||
if (count($aCheckRemote) > 0)
|
||||
{
|
||||
$oSearch = new DBObjectSearch($this->sClass);
|
||||
$oSearch->AddCondition($sExtKeyToMe, $oHostObject->GetKey(), '=');
|
||||
$oSearch->AddCondition($sExtKeyToRemote, $aCheckRemote, 'IN');
|
||||
$oSet = new DBObjectSet($oSearch);
|
||||
$aExistingRemote = $oSet->GetColumnAsArray($sExtKeyToRemote, true);
|
||||
}
|
||||
|
||||
// Write the links according to the existing links
|
||||
//
|
||||
foreach ($this->aAdded as $oLink)
|
||||
{
|
||||
// Make sure that the objects in the set point to "this"
|
||||
$oLink->Set($sExtKeyToMe, $oHostObject->GetKey());
|
||||
|
||||
if ($oLink->IsNew())
|
||||
{
|
||||
if (count($aCheckRemote) > 0)
|
||||
{
|
||||
$bIsDuplicate = false;
|
||||
foreach($aExistingRemote as $sLinkKey => $sExtKey)
|
||||
{
|
||||
if ($sExtKey == $oLink->Get($sExtKeyToRemote))
|
||||
{
|
||||
// Do not create a duplicate
|
||||
// + In the case of a remove action followed by an add action
|
||||
// of an existing link,
|
||||
// the final state to consider is add action,
|
||||
// so suppress the entry in the removed list.
|
||||
if (array_key_exists($sLinkKey, $this->aRemoved))
|
||||
{
|
||||
unset($this->aRemoved[$sLinkKey]);
|
||||
}
|
||||
$bIsDuplicate = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($bIsDuplicate)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!array_key_exists($oLink->GetKey(), $aExistingLinks))
|
||||
{
|
||||
$oLink->DBClone();
|
||||
}
|
||||
}
|
||||
$oLink->DBWrite();
|
||||
}
|
||||
foreach ($this->aRemoved as $iLinkId)
|
||||
{
|
||||
if (array_key_exists($iLinkId, $aExistingLinks))
|
||||
{
|
||||
$oLink = $aExistingLinks[$iLinkId];
|
||||
if ($oAttDef->IsIndirect())
|
||||
{
|
||||
$oLink->DBDelete();
|
||||
}
|
||||
else
|
||||
{
|
||||
$oExtKeyToRemote = MetaModel::GetAttributeDef($this->sClass, $sExtKeyToMe);
|
||||
if ($oExtKeyToRemote->IsNullAllowed())
|
||||
{
|
||||
if ($oLink->Get($sExtKeyToMe) == $oHostObject->GetKey())
|
||||
{
|
||||
// Detach the link object from this
|
||||
$oLink->Set($sExtKeyToMe, 0);
|
||||
$oLink->DBUpdate();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$oLink->DBDelete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Note: process modifications at the end: if a link to remove has also been listed as modified, then it will be gracefully ignored
|
||||
foreach ($this->aModified as $iLinkId => $oLink)
|
||||
{
|
||||
if (array_key_exists($oLink->GetKey(), $aExistingLinks))
|
||||
{
|
||||
$oLink->DBUpdate();
|
||||
}
|
||||
else
|
||||
{
|
||||
$oLink->DBClone();
|
||||
}
|
||||
}
|
||||
|
||||
// End of the critical section
|
||||
//
|
||||
$oMtx->Unlock();
|
||||
}
|
||||
|
||||
public function ToDBObjectSet($bShowObsolete = true)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->sHostClass, $this->sAttCode);
|
||||
$oLinkSearch = $this->GetFilter();
|
||||
if ($oAttDef->IsIndirect())
|
||||
{
|
||||
$sExtKeyToRemote = $oAttDef->GetExtKeyToRemote();
|
||||
$oLinkingAttDef = MetaModel::GetAttributeDef($this->sClass, $sExtKeyToRemote);
|
||||
$sTargetClass = $oLinkingAttDef->GetTargetClass();
|
||||
if (!$bShowObsolete && MetaModel::IsObsoletable($sTargetClass))
|
||||
{
|
||||
$oNotObsolete = new BinaryExpression(
|
||||
new FieldExpression('obsolescence_flag', $sTargetClass),
|
||||
'=',
|
||||
new ScalarExpression(0)
|
||||
);
|
||||
$oNotObsoleteRemote = new DBObjectSearch($sTargetClass);
|
||||
$oNotObsoleteRemote->AddConditionExpression($oNotObsolete);
|
||||
$oLinkSearch->AddCondition_PointingTo($oNotObsoleteRemote, $sExtKeyToRemote);
|
||||
}
|
||||
}
|
||||
$oLinkSet = new DBObjectSet($oLinkSearch);
|
||||
$oLinkSet->SetShowObsoleteData($bShowObsolete);
|
||||
if ($this->HasDelta())
|
||||
{
|
||||
$oLinkSet->AddObjectArray($this->aAdded);
|
||||
}
|
||||
return $oLinkSet;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2014 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -22,7 +22,7 @@ require_once('backgroundprocess.inc.php');
|
||||
* ormStopWatch
|
||||
* encapsulate the behavior of a stop watch that will be stored as an attribute of class AttributeStopWatch
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -260,7 +260,7 @@ class ormStopWatch
|
||||
return $iRet;
|
||||
}
|
||||
|
||||
protected function ComputeDeadline($oObject, $oAttDef, $iStartTime, $iDurationSec)
|
||||
protected function ComputeDeadline($oObject, $oAttDef, $iPercent, $iStartTime, $iDurationSec)
|
||||
{
|
||||
$sWorkingTimeComputer = $oAttDef->Get('working_time_computing');
|
||||
if ($sWorkingTimeComputer == '')
|
||||
@@ -280,7 +280,7 @@ class ormStopWatch
|
||||
}
|
||||
// GetDeadline($oObject, $iDuration, DateTime $oStartDate)
|
||||
$oStartDate = new DateTime('@'.$iStartTime); // setTimestamp not available in PHP 5.2
|
||||
$oDeadline = call_user_func($aCallSpec, $oObject, $iDurationSec, $oStartDate);
|
||||
$oDeadline = call_user_func($aCallSpec, $oObject, $iDurationSec, $oStartDate, $iPercent);
|
||||
$iRet = $oDeadline->format('U');
|
||||
return $iRet;
|
||||
}
|
||||
@@ -384,8 +384,8 @@ 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, $this->iLastStart, $iThresholdDuration - $this->iTimeSpent);
|
||||
// OR $aThresholdData['deadline'] = $this->ComputeDeadline($oObject, $oAttDef, $this->iStarted, $iThresholdDuration);
|
||||
$aThresholdData['deadline'] = $this->ComputeDeadline($oObject, $oAttDef, $iPercent, $this->iLastStart, $iThresholdDuration - $this->iTimeSpent);
|
||||
// OR $aThresholdData['deadline'] = $this->ComputeDeadline($oObject, $oAttDef, $iPercent, $this->iStarted, $iThresholdDuration);
|
||||
|
||||
if (class_exists('WorkingTimeRecorder'))
|
||||
{
|
||||
@@ -494,6 +494,7 @@ class CheckStopWatchThresholds implements iBackgroundProcess
|
||||
$sExpression = "SELECT $sClass WHERE {$sAttCode}_laststart AND {$sAttCode}_{$iThreshold}_triggered = 0 AND {$sAttCode}_{$iThreshold}_deadline < '$sNow'";
|
||||
$oFilter = DBObjectSearch::FromOQL($sExpression);
|
||||
$oSet = new DBObjectSet($oFilter);
|
||||
$oSet->OptimizeColumnLoad(array($sAttCode));
|
||||
while ((time() < $iTimeLimit) && ($oObj = $oSet->Fetch()))
|
||||
{
|
||||
$sClass = get_class($oObj);
|
||||
|
||||
@@ -226,9 +226,9 @@ EOF
|
||||
$iNewWidth = $iWidth * $fScale;
|
||||
$iNewHeight = $iHeight * $fScale;
|
||||
|
||||
$sUrl = 'data:' . $value->GetMimeType() . ';base64,' . base64_encode($value->GetData());
|
||||
$sUrl = 'data:'.$value->GetMimeType().';base64,'.base64_encode($value->GetData());
|
||||
}
|
||||
$sRet = '<img src="' . $sUrl . '" style="width: ' . $iNewWidth . 'px; height: ' . $iNewHeight . 'px">';
|
||||
$sRet = ($sUrl !== null) ? '<img src="'.$sUrl.'" style="width: '.$iNewWidth.'px; height: '.$iNewHeight.'px">' : '';
|
||||
$sRet = '<div class="view-image">'.$sRet.'</div>';
|
||||
}
|
||||
else
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -19,7 +19,7 @@
|
||||
/**
|
||||
* Associated with the metamodel -> MakeQuery/MakeQuerySingleTable
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -30,6 +30,7 @@ class QueryBuilderContext
|
||||
protected $m_aTableAliases;
|
||||
protected $m_aModifierProperties;
|
||||
protected $m_aSelectedClasses;
|
||||
protected $m_aFilteredTables;
|
||||
|
||||
public $m_oQBExpressions;
|
||||
|
||||
@@ -40,6 +41,7 @@ class QueryBuilderContext
|
||||
|
||||
$this->m_aClassAliases = $oFilter->GetJoinedClasses();
|
||||
$this->m_aTableAliases = array();
|
||||
$this->m_aFilteredTables = array();
|
||||
|
||||
$this->m_aModifierProperties = $aModifierProperties;
|
||||
if (is_null($aSelectedClasses))
|
||||
@@ -84,4 +86,21 @@ class QueryBuilderContext
|
||||
{
|
||||
return $this->m_aSelectedClasses[$sAlias];
|
||||
}
|
||||
|
||||
public function AddFilteredTable($sTableAlias, $oCondition)
|
||||
{
|
||||
if (array_key_exists($sTableAlias, $this->m_aFilteredTables))
|
||||
{
|
||||
$this->m_aFilteredTables[$sTableAlias][] = $oCondition;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->m_aFilteredTables[$sTableAlias] = array($oCondition);
|
||||
}
|
||||
}
|
||||
|
||||
public function GetFilteredTables()
|
||||
{
|
||||
return $this->m_aFilteredTables;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2015 Combodo SARL
|
||||
// Copyright (C) 2015-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -18,7 +18,7 @@
|
||||
/**
|
||||
* Data structures (i.e. PHP classes) to build and use relation graphs
|
||||
*
|
||||
* @copyright Copyright (C) 2015 Combodo SARL
|
||||
* @copyright Copyright (C) 2015-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*
|
||||
*/
|
||||
@@ -106,7 +106,7 @@ class RelationRedundancyNode extends GraphNode
|
||||
/**
|
||||
* Make a normalized ID to ensure the uniqueness of such a node
|
||||
*/
|
||||
public static function MakeId($sRelCode, $sNeighbourId, $oSinkObject)
|
||||
public static function MakeId($sRelCode, $sNeighbourId, $oSourceObject, $oSinkObject)
|
||||
{
|
||||
return 'redundancy-'.$sRelCode.'-'.$sNeighbourId.'-'.get_class($oSinkObject).'::'.$oSinkObject->GetKey();
|
||||
}
|
||||
@@ -209,7 +209,7 @@ class RelationGraph extends SimpleGraph
|
||||
{
|
||||
if ($sOQL === '') return;
|
||||
|
||||
$oSearch = DBObjectSearch::FromOQL($sOQL);
|
||||
$oSearch = static::MakeSearch($sOQL);
|
||||
$aAliases = $oSearch->GetSelectedClasses();
|
||||
if (count($aAliases) < 2 )
|
||||
{
|
||||
@@ -326,7 +326,7 @@ class RelationGraph extends SimpleGraph
|
||||
$this->AddRelatedObjects($sRelCode, false, $oSinkNode, $iMaxDepth, $bEnableRedundancy);
|
||||
//echo "<h5>After processing of {$oSinkNode->GetId()}</h5>\n".$this->DumpAsHtmlImage()."<br/>\n";
|
||||
}
|
||||
|
||||
|
||||
// Mark also the "context" nodes as reached and record the "root causes" for each node
|
||||
$oIterator = new RelationTypeIterator($this, 'Node');
|
||||
foreach($oIterator as $oNode)
|
||||
@@ -393,7 +393,7 @@ class RelationGraph extends SimpleGraph
|
||||
$sQuery = $bDown ? $aQueryInfo['sQueryDown'] : $aQueryInfo['sQueryUp'];
|
||||
try
|
||||
{
|
||||
$oFlt = DBObjectSearch::FromOQL($sQuery);
|
||||
$oFlt = static::MakeSearch($sQuery);
|
||||
$oObjSet = new DBObjectSet($oFlt, array(), $oObject->ToArgsForQuery());
|
||||
$oRelatedObj = $oObjSet->Fetch();
|
||||
}
|
||||
@@ -407,11 +407,11 @@ class RelationGraph extends SimpleGraph
|
||||
do
|
||||
{
|
||||
set_time_limit($iLoopTimeLimit);
|
||||
|
||||
|
||||
$sObjectRef = RelationObjectNode::MakeId($oRelatedObj);
|
||||
$oRelatedNode = $this->GetNode($sObjectRef);
|
||||
if (is_null($oRelatedNode))
|
||||
{
|
||||
{
|
||||
$oRelatedNode = new RelationObjectNode($this, $oRelatedObj);
|
||||
}
|
||||
$oSourceNode = $bDown ? $oObjectNode : $oRelatedNode;
|
||||
@@ -449,17 +449,21 @@ class RelationGraph extends SimpleGraph
|
||||
$oObject = $oToNode->GetProperty('object');
|
||||
if ($this->IsRedundancyEnabled($sRelCode, $aQueryInfo, $oToNode))
|
||||
{
|
||||
|
||||
$sId = RelationRedundancyNode::MakeId($sRelCode, $aQueryInfo['sNeighbour'], $oToNode->GetProperty('object'));
|
||||
$sUniqueNeighbourId = $aQueryInfo['sDefinedInClass'].'-'.$aQueryInfo['sNeighbour'];
|
||||
$sId = RelationRedundancyNode::MakeId($sRelCode, $sUniqueNeighbourId, $oFromNode->GetProperty('object'), $oToNode->GetProperty('object'));
|
||||
|
||||
$oRedundancyNode = $this->GetNode($sId);
|
||||
if (is_null($oRedundancyNode))
|
||||
{
|
||||
// Get the upper neighbours
|
||||
$sQuery = $aQueryInfo['sQueryUp'];
|
||||
if (!$sQuery)
|
||||
{
|
||||
throw new Exception("Redundancy cannot be enabled on the relation $sRelCode/{$aQueryInfo['sDefinedInClass']}/{$aQueryInfo['sNeighbour']}: its direction is \"{$aQueryInfo['sDirection']}\"");
|
||||
}
|
||||
try
|
||||
{
|
||||
$oFlt = DBObjectSearch::FromOQL($sQuery);
|
||||
$oFlt = static::MakeSearch($sQuery);
|
||||
$oObjSet = new DBObjectSet($oFlt, array(), $oObject->ToArgsForQuery());
|
||||
$iCount = $oObjSet->Count();
|
||||
}
|
||||
@@ -540,10 +544,13 @@ class RelationGraph extends SimpleGraph
|
||||
{
|
||||
if ($oAttDef->Get('relation_code') == $sRelCode)
|
||||
{
|
||||
if ($oAttDef->Get('neighbour_id') == $aQueryInfo['sNeighbour'])
|
||||
if ($oAttDef->Get('from_class') == $aQueryInfo['sFromClass'])
|
||||
{
|
||||
$oRet = $oAttDef;
|
||||
break;
|
||||
if ($oAttDef->Get('neighbour_id') == $aQueryInfo['sNeighbour'])
|
||||
{
|
||||
$oRet = $oAttDef;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -574,4 +581,17 @@ class RelationGraph extends SimpleGraph
|
||||
}
|
||||
return $aResults;
|
||||
}
|
||||
|
||||
protected static function MakeSearch($sOQL)
|
||||
{
|
||||
$oSearch = DBSearch::FromOQL($sOQL);
|
||||
if (MetaModel::IsObsoletable($oSearch->GetClass()))
|
||||
{
|
||||
// Exclude obsolete objects anytime
|
||||
$oSearch->AddCondition('obsolescence_flag', 0);
|
||||
}
|
||||
// Exclude archived objects anytime
|
||||
$oSearch->SetArchiveMode(false);
|
||||
return $oSearch;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2015 Combodo SARL
|
||||
// Copyright (C) 2015-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -18,7 +18,7 @@
|
||||
/**
|
||||
* Data structures (i.e. PHP classes) to manage "graphs"
|
||||
*
|
||||
* @copyright Copyright (C) 2015 Combodo SARL
|
||||
* @copyright Copyright (C) 2015-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*
|
||||
* Example:
|
||||
@@ -346,14 +346,15 @@ class SimpleGraph
|
||||
}
|
||||
unset($this->aNodes[$oNode->GetId()]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes the given node but preserves the connectivity of the graph
|
||||
* all "source" nodes are connected to all "sink" nodes
|
||||
* @param GraphNode $oNode
|
||||
* @param bool $bAllowLoopingEdge
|
||||
* @throws SimpleGraphException
|
||||
*/
|
||||
public function FilterNode(GraphNode $oNode)
|
||||
public function FilterNode(GraphNode $oNode, $bAllowLoopingEdge = false)
|
||||
{
|
||||
if (!array_key_exists($oNode->GetId(), $this->aNodes)) throw new SimpleGraphException('Cannot filter the node (id='.$oNode->GetId().') from the graph. The node was not found in the graph.');
|
||||
|
||||
@@ -362,13 +363,19 @@ class SimpleGraph
|
||||
foreach($oNode->GetOutgoingEdges() as $oEdge)
|
||||
{
|
||||
$sSinkId = $oEdge->GetSinkNode()->GetId();
|
||||
$aSinkNodes[$sSinkId] = $oEdge->GetSinkNode();
|
||||
if ($sSinkId != $oNode->GetId())
|
||||
{
|
||||
$aSinkNodes[$sSinkId] = $oEdge->GetSinkNode();
|
||||
}
|
||||
$this->_RemoveEdge($oEdge);
|
||||
}
|
||||
foreach($oNode->GetIncomingEdges() as $oEdge)
|
||||
{
|
||||
$sSourceId = $oEdge->GetSourceNode()->GetId();
|
||||
$aSourceNodes[$sSourceId] = $oEdge->GetSourceNode();
|
||||
if ($sSourceId != $oNode->GetId())
|
||||
{
|
||||
$aSourceNodes[$sSourceId] = $oEdge->GetSourceNode();
|
||||
}
|
||||
$this->_RemoveEdge($oEdge);
|
||||
}
|
||||
unset($this->aNodes[$oNode->GetId()]);
|
||||
@@ -377,7 +384,10 @@ class SimpleGraph
|
||||
{
|
||||
foreach($aSinkNodes as $sSinkId => $oSinkNode)
|
||||
{
|
||||
$oEdge = new RelationEdge($this, $oSourceNode, $oSinkNode);
|
||||
if ($bAllowLoopingEdge || ($oSourceNode->GetId() != $oSinkNode->GetId()))
|
||||
{
|
||||
$oEdge = new RelationEdge($this, $oSourceNode, $oSinkNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ class SpreadsheetBulkExport extends TabularBulkExport
|
||||
$oP->p(" *\tfields: (mandatory) the comma separated list of field codes to export (e.g: name,org_id,service_name...).");
|
||||
$oP->p(" *\tno_localize: (optional) pass 1 to retrieve the raw (untranslated) values for enumerated fields. Default: 0.");
|
||||
$oP->p(" *\tdate_format: the format to use when exporting date and time fields (default = the SQL format). e.g. 'Y-m-d H:i:s'");
|
||||
$oP->p(" *\tformatted_text: set to 1 to formatted text fields with their HTML markup, 0 to remove formatting. Default is 1 (= formatted text)");
|
||||
}
|
||||
|
||||
public function EnumFormParts()
|
||||
@@ -51,12 +52,19 @@ class SpreadsheetBulkExport extends TabularBulkExport
|
||||
$oP->add('<fieldset><legend>'.Dict::S('Core:BulkExport:SpreadsheetOptions').'</legend>');
|
||||
$oP->add('<table>');
|
||||
$oP->add('<tr>');
|
||||
$oP->add('<td><input type="checkbox" id="spreadsheet_no_localize" name="no_localize" value="1"'.$sChecked.'><label for="spreadsheet_no_localize"> '.Dict::S('Core:BulkExport:OptionNoLocalize').'</label></td>');
|
||||
|
||||
|
||||
$oP->add('<td style="vertical-align:top">');
|
||||
$sChecked = (utils::ReadParam('formatted_text', 1) == 1) ? ' checked ' : '';
|
||||
$oP->add('<h3>'.Dict::S('Core:BulkExport:TextFormat').'</h3>');
|
||||
$oP->add('<input type="hidden" name="formatted_text" value="0">'); // Trick to pass the zero value if the checkbox below is unchecked, since we want the default value to be "1"
|
||||
$oP->add('<input type="checkbox" id="spreadsheet_formatted_text" name="formatted_text" value="1"'.$sChecked.'><label for="spreadsheet_formatted_text"> '.Dict::S('Core:BulkExport:OptionFormattedText').'</label><br/><br/>');
|
||||
$oP->add('<input type="checkbox" id="spreadsheet_no_localize" name="no_localize" value="1"'.$sChecked.'><label for="spreadsheet_no_localize"> '.Dict::S('Core:BulkExport:OptionNoLocalize').'</label>');
|
||||
$oP->add('</td>');
|
||||
|
||||
$sDateTimeFormat = utils::ReadParam('date_format', (string)AttributeDateTime::GetFormat(), true, 'raw_data');
|
||||
$sDefaultChecked = ($sDateTimeFormat == (string)AttributeDateTime::GetFormat()) ? ' checked' : '';
|
||||
$sCustomChecked = ($sDateTimeFormat !== (string)AttributeDateTime::GetFormat()) ? ' checked' : '';
|
||||
|
||||
|
||||
$oP->add('<td>');
|
||||
$oP->add('<h3>'.Dict::S('Core:BulkExport:DateTimeFormat').'</h3>');
|
||||
$sDefaultFormat = htmlentities((string)AttributeDateTime::GetFormat(), ENT_QUOTES, 'UTF-8');
|
||||
@@ -65,23 +73,23 @@ class SpreadsheetBulkExport extends TabularBulkExport
|
||||
$sFormatInput = '<input type="text" size="15" name="date_format" id="spreadsheet_custom_date_time_format" title="" value="'.htmlentities($sDateTimeFormat, ENT_QUOTES, 'UTF-8').'"/>';
|
||||
$oP->add('<input type="radio" id="spreadsheet_date_time_format_custom" name="spreadsheet_date_format_radio" value="custom"'.$sCustomChecked.'><label for="spreadsheet_date_time_format_custom"> '.Dict::Format('Core:BulkExport:DateTimeFormatCustom_Format', $sFormatInput).'</label>');
|
||||
$oP->add('</td>');
|
||||
|
||||
|
||||
$oP->add('</tr>');
|
||||
$oP->add('</table>');
|
||||
$oP->add('</fieldset>');
|
||||
$sJSTooltip = json_encode('<div class="date_format_tooltip">'.Dict::S('UI:CSVImport:CustomDateTimeFormatTooltip').'</div>');
|
||||
$oP->add_ready_script(
|
||||
<<<EOF
|
||||
<<<EOF
|
||||
$('#spreadsheet_custom_date_time_format').tooltip({content: function() { return $sJSTooltip; } });
|
||||
$('#form_part_spreadsheet_options').on('preview_updated', function() { FormatDatesInPreview('spreadsheet', 'spreadsheet'); });
|
||||
$('#spreadsheet_date_time_format_default').on('click', function() { FormatDatesInPreview('spreadsheet', 'spreadsheet'); });
|
||||
$('#spreadsheet_date_time_format_custom').on('click', function() { FormatDatesInPreview('spreadsheet', 'spreadsheet'); });
|
||||
$('#spreadsheet_custom_date_time_format').on('click', function() { $('#spreadsheet_date_time_format_custom').prop('checked', true); });
|
||||
$('#spreadsheet_custom_date_time_format').on('click', function() { $('#spreadsheet_date_time_format_custom').prop('checked', true); FormatDatesInPreview('spreadsheet', 'spreadsheet'); }).on('keyup', function() { FormatDatesInPreview('spreadsheet', 'spreadsheet'); });
|
||||
$('#spreadsheet_custom_date_time_format').on('click', function() { $('#spreadsheet_date_time_format_custom').prop('checked', true); FormatDatesInPreview('spreadsheet', 'spreadsheet'); }).on('keyup', function() { FormatDatesInPreview('spreadsheet', 'spreadsheet'); });
|
||||
EOF
|
||||
);
|
||||
);
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
return parent:: DisplayFormPart($oP, $sPartId);
|
||||
}
|
||||
@@ -90,26 +98,27 @@ EOF
|
||||
public function ReadParameters()
|
||||
{
|
||||
parent::ReadParameters();
|
||||
$this->aStatusInfo['formatted_text'] = (bool)utils::ReadParam('formatted_text', 1, true);
|
||||
|
||||
$sDateFormatRadio = utils::ReadParam('spreadsheet_date_format_radio', '');
|
||||
switch($sDateFormatRadio)
|
||||
{
|
||||
case 'default':
|
||||
// Export from the UI => format = same as is the UI
|
||||
$this->aStatusInfo['date_format'] = (string)AttributeDateTime::GetFormat();
|
||||
break;
|
||||
|
||||
// Export from the UI => format = same as is the UI
|
||||
$this->aStatusInfo['date_format'] = (string)AttributeDateTime::GetFormat();
|
||||
break;
|
||||
|
||||
case 'custom':
|
||||
// Custom format specified from the UI
|
||||
$this->aStatusInfo['date_format'] = utils::ReadParam('date_format', (string)AttributeDateTime::GetFormat(), true, 'raw_data');
|
||||
break;
|
||||
|
||||
// Custom format specified from the UI
|
||||
$this->aStatusInfo['date_format'] = utils::ReadParam('date_format', (string)AttributeDateTime::GetFormat(), true, 'raw_data');
|
||||
break;
|
||||
|
||||
default:
|
||||
// Export from the command line (or scripted) => default format is SQL, as in previous versions of iTop, unless specified otherwise
|
||||
$this->aStatusInfo['date_format'] = utils::ReadParam('date_format', (string)AttributeDateTime::GetSQLFormat(), true, 'raw_data');
|
||||
// Export from the command line (or scripted) => default format is SQL, as in previous versions of iTop, unless specified otherwise
|
||||
$this->aStatusInfo['date_format'] = utils::ReadParam('date_format', (string)AttributeDateTime::GetSQLFormat(), true, 'raw_data');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function GetSampleData($oObj, $sAttCode)
|
||||
{
|
||||
if ($sAttCode != 'id')
|
||||
@@ -126,6 +135,7 @@ EOF
|
||||
|
||||
protected function GetValue($oObj, $sAttCode)
|
||||
{
|
||||
$bFormattedText = (array_key_exists('formatted_text', $this->aStatusInfo) ? $this->aStatusInfo['formatted_text'] : false);
|
||||
switch($sAttCode)
|
||||
{
|
||||
case 'id':
|
||||
@@ -147,13 +157,26 @@ EOF
|
||||
{
|
||||
$sRet = '';
|
||||
}
|
||||
elseif ($oAttDef instanceof AttributeText)
|
||||
{
|
||||
if ($bFormattedText)
|
||||
{
|
||||
// Replace paragraphs (<p...>...</p>, etc) by line breaks (<br/>) since Excel (pre-2016) splits the cells when there is a paragraph
|
||||
$sRet = static::HtmlToSpreadsheet($oObj->GetAsHTML($sAttCode));
|
||||
}
|
||||
else
|
||||
{
|
||||
$sRet = utils::HtmlToText($oObj->GetAsHTML($sAttCode));
|
||||
}
|
||||
}
|
||||
elseif ($oAttDef instanceof AttributeString)
|
||||
{
|
||||
$sRet = $oObj->GetAsHTML($sAttCode);
|
||||
}
|
||||
elseif ($oAttDef instanceof AttributeCustomFields)
|
||||
{
|
||||
$sRet = $oObj->GetAsHTML($sAttCode);
|
||||
// Stick to the weird implementation made in GetNextChunk
|
||||
$sRet = utils::TextToHtml($oObj->GetEditValue($sAttCode));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -175,7 +198,7 @@ EOF
|
||||
{
|
||||
// Integration within MS-Excel web queries + HTTPS + IIS:
|
||||
// MS-IIS set these header values with no-cache... while Excel fails to do the job if using HTTPS
|
||||
// Then the fix is to force the reset of header values Pragma and Cache-control
|
||||
// Then the fix is to force the reset of header values Pragma and Cache-control
|
||||
$oPage->add_header("Pragma:", true);
|
||||
$oPage->add_header("Cache-control:", true);
|
||||
}
|
||||
@@ -230,13 +253,14 @@ EOF
|
||||
$oSet = new DBObjectSet($this->oSearch);
|
||||
$oSet->SetLimit($this->iChunkSize, $this->aStatusInfo['position']);
|
||||
$this->OptimizeColumnLoad($oSet);
|
||||
|
||||
|
||||
$sExportDateTimeFormat = $this->aStatusInfo['date_format'];
|
||||
$bFormattedText = (array_key_exists('formatted_text', $this->aStatusInfo) ? $this->aStatusInfo['formatted_text'] : false);
|
||||
// Date & time formats
|
||||
$oDateTimeFormat = new DateTimeFormat($sExportDateTimeFormat);
|
||||
$oDateFormat = new DateTimeFormat($oDateTimeFormat->ToDateFormat());
|
||||
$oTimeFormat = new DateTimeFormat($oDateTimeFormat->ToTimeFormat());
|
||||
|
||||
|
||||
$iCount = 0;
|
||||
$sData = '';
|
||||
$iPreviousTimeLimit = ini_get('max_execution_time');
|
||||
@@ -258,55 +282,75 @@ EOF
|
||||
$sData .= "<td x:str></td>";
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
switch($sAttCode)
|
||||
{
|
||||
case 'id':
|
||||
$sField = $oObj->GetKey();
|
||||
$sData .= "<td>$sField</td>";
|
||||
break;
|
||||
$sField = $oObj->GetKey();
|
||||
$sData .= "<td>$sField</td>";
|
||||
break;
|
||||
|
||||
default:
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
|
||||
$oFinalAttDef = $oAttDef->GetFinalAttDef();
|
||||
if (get_class($oFinalAttDef) == 'AttributeDateTime')
|
||||
{
|
||||
// Split the date and time in two columns
|
||||
$sDate = $oDateFormat->Format($oObj->Get($sAttCode));
|
||||
$sTime = $oTimeFormat->Format($oObj->Get($sAttCode));
|
||||
$sData .= "<td>$sDate</td>";
|
||||
$sData .= "<td>$sTime</td>";
|
||||
}
|
||||
else if (get_class($oFinalAttDef) == 'AttributeDate')
|
||||
{
|
||||
$sDate = $oDateFormat->Format($oObj->Get($sAttCode));
|
||||
$sData .= "<td>$sDate</td>";
|
||||
}
|
||||
else if($oAttDef instanceof AttributeCaseLog)
|
||||
{
|
||||
$rawValue = $oObj->Get($sAttCode);
|
||||
$sField = str_replace("\n", "<br/>", htmlentities($rawValue->__toString(), ENT_QUOTES, 'UTF-8'));
|
||||
// Trick for Excel: treat the content as text even if it begins with an equal sign
|
||||
$sData .= "<td x:str>$sField</td>";
|
||||
}
|
||||
else if($oAttDef instanceof AttributeString)
|
||||
{
|
||||
$sField = $oObj->GetAsHTML($sAttCode, $this->bLocalizeOutput);
|
||||
$sData .= "<td x:str>$sField</td>";
|
||||
}
|
||||
else
|
||||
{
|
||||
$rawValue = $oObj->Get($sAttCode);
|
||||
if ($this->bLocalizeOutput)
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
|
||||
$oFinalAttDef = $oAttDef->GetFinalAttDef();
|
||||
if (get_class($oFinalAttDef) == 'AttributeDateTime')
|
||||
{
|
||||
$sField = htmlentities($oFinalAttDef->GetEditValue($rawValue), ENT_QUOTES, 'UTF-8');
|
||||
// Split the date and time in two columns
|
||||
$sDate = $oDateFormat->Format($oObj->Get($sAttCode));
|
||||
$sTime = $oTimeFormat->Format($oObj->Get($sAttCode));
|
||||
$sData .= "<td>$sDate</td>";
|
||||
$sData .= "<td>$sTime</td>";
|
||||
}
|
||||
else if (get_class($oFinalAttDef) == 'AttributeDate')
|
||||
{
|
||||
$sDate = $oDateFormat->Format($oObj->Get($sAttCode));
|
||||
$sData .= "<td>$sDate</td>";
|
||||
}
|
||||
else if($oAttDef instanceof AttributeCaseLog)
|
||||
{
|
||||
$rawValue = $oObj->Get($sAttCode);
|
||||
$sField = str_replace("\n", "<br/>", htmlentities($rawValue->__toString(), ENT_QUOTES, 'UTF-8'));
|
||||
// Trick for Excel: treat the content as text even if it begins with an equal sign
|
||||
$sData .= "<td x:str>$sField</td>";
|
||||
}
|
||||
elseif ($oAttDef instanceof AttributeText)
|
||||
{
|
||||
if ($bFormattedText)
|
||||
{
|
||||
// Replace paragraphs (<p...>...</p>, etc) by line breaks (<br/>) since Excel (pre-2016) splits the cells when there is a paragraph
|
||||
$sField = static::HtmlToSpreadsheet($oObj->GetAsHTML($sAttCode));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Convert to plain text
|
||||
$sField = utils::HtmlToText($oObj->GetAsHTML($sAttCode));
|
||||
}
|
||||
$sData .= "<td x:str>$sField</td>";
|
||||
}
|
||||
elseif ($oAttDef instanceof AttributeCustomFields)
|
||||
{
|
||||
// GetAsHTML returns a table that would not fit
|
||||
$sField = utils::TextToHtml($oObj->GetEditValue($sAttCode));
|
||||
$sData .= "<td x:str>$sField</td>";
|
||||
}
|
||||
else if($oAttDef instanceof AttributeString)
|
||||
{
|
||||
$sField = $oObj->GetAsHTML($sAttCode, $this->bLocalizeOutput);
|
||||
$sData .= "<td x:str>$sField</td>";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sField = htmlentities($rawValue, ENT_QUOTES, 'UTF-8');
|
||||
$rawValue = $oObj->Get($sAttCode);
|
||||
if ($this->bLocalizeOutput)
|
||||
{
|
||||
$sField = htmlentities($oFinalAttDef->GetEditValue($rawValue), ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
else
|
||||
{
|
||||
$sField = htmlentities($rawValue, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
$sData .= "<td>$sField</td>";
|
||||
}
|
||||
$sData .= "<td>$sField</td>";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -354,4 +398,51 @@ EOF
|
||||
{
|
||||
return 'html';
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup all markup displayed as line breaks (except <br> tags) since this
|
||||
* causes Excel (pre-2016) to generate extra lines in the table, thus breaking
|
||||
* the tabular disposition of the export
|
||||
* Note: Excel 2016 also refuses line breaks, so the only solution for this case is alas plain text
|
||||
* @param string $sHtml The HTML to cleanup
|
||||
* @return string The cleaned HTML
|
||||
*/
|
||||
public static function HtmlToSpreadsheet($sHtml)
|
||||
{
|
||||
if (trim(strip_tags($sHtml)) === '')
|
||||
{
|
||||
// Display this value as an empty cell in the table
|
||||
return ' ';
|
||||
}
|
||||
// The tags listed here are a subset of the whitelist defined in HTMLDOMSanitizer
|
||||
// Tags causing a visual "line break" in the displayed page (i.e. display: block) are to be replaced by a <span> followed by a <br/>
|
||||
// in order to preserve any inline style/attribute of the removed tag
|
||||
$aTagsToReplace = array(
|
||||
'pre', 'div', 'p', 'hr', 'center', 'h1', 'h2', 'h3', 'h4', 'li', 'fieldset', 'legend', 'nav', 'section', 'tr', 'caption',
|
||||
);
|
||||
// Tags to completely remove from the markup
|
||||
$aTagsToRemove = array(
|
||||
'table', 'thead', 'tbody', 'ul', 'ol', 'td', 'th',
|
||||
);
|
||||
|
||||
// Remove the englobing <div class="HTML" >...</div> to prevent an extra line break
|
||||
$sHtml = preg_replace('|^<div class="HTML" >(.*)</div>$|s', '$1', $sHtml); // Must use the "s" (. matches newline) modifier
|
||||
|
||||
foreach($aTagsToReplace as $sTag)
|
||||
{
|
||||
$sHtml = preg_replace("|<{$sTag} ?([^>]*)>|is", '<span $1>', $sHtml);
|
||||
$sHtml = preg_replace("|</{$sTag}>|i", '</span><br/>', $sHtml);
|
||||
}
|
||||
|
||||
foreach($aTagsToRemove as $sTag)
|
||||
{
|
||||
$sHtml = preg_replace("|<{$sTag} ?([^>]*)>|is", '', $sHtml);
|
||||
$sHtml = preg_replace("|</{$sTag}>|i", '', $sHtml);
|
||||
}
|
||||
|
||||
// Remove any trailing <br/>, if any, to prevent an extra line break
|
||||
$sHtml = preg_replace("|<br/>$|", '', $sHtml);
|
||||
|
||||
return $sHtml;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2015-2016 Combodo SARL
|
||||
// Copyright (C) 2015-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -21,7 +21,7 @@
|
||||
* SQLObjectQuery
|
||||
* build a mySQL compatible SQL query
|
||||
*
|
||||
* @copyright Copyright (C) 2015-2016 Combodo SARL
|
||||
* @copyright Copyright (C) 2015-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -36,7 +36,8 @@
|
||||
|
||||
class SQLObjectQuery extends SQLQuery
|
||||
{
|
||||
private $m_SourceOQL = '';
|
||||
public $m_aContextData = null;
|
||||
public $m_iOriginalTableCount = 0;
|
||||
private $m_sTable = '';
|
||||
private $m_sTableAlias = '';
|
||||
private $m_aFields = array();
|
||||
@@ -46,7 +47,7 @@ class SQLObjectQuery extends SQLQuery
|
||||
private $m_aValues = array(); // Values to set in case of an update query
|
||||
private $m_oSelectedIdField = null;
|
||||
private $m_aJoinSelects = array();
|
||||
private $m_bBeautifulQuery = false;
|
||||
protected $m_bBeautifulQuery = false;
|
||||
|
||||
// Data set by PrepareRendering()
|
||||
private $__aFrom;
|
||||
@@ -138,6 +139,11 @@ class SQLObjectQuery extends SQLQuery
|
||||
$this->m_aFields = $aExpressions;
|
||||
}
|
||||
|
||||
public function SortSelectedFields()
|
||||
{
|
||||
ksort($this->m_aFields);
|
||||
}
|
||||
|
||||
public function AddSelect($sAlias, $oExpression)
|
||||
{
|
||||
$this->m_aFields[$sAlias] = $oExpression;
|
||||
@@ -161,7 +167,7 @@ class SQLObjectQuery extends SQLQuery
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->m_oConditionExpr->LogAnd($oConditionExpr);
|
||||
$this->m_oConditionExpr = $this->m_oConditionExpr->LogAnd($oConditionExpr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,6 +312,14 @@ class SQLObjectQuery extends SQLQuery
|
||||
$this->PrepareRendering();
|
||||
$sFrom = self::ClauseFrom($this->__aFrom, $sIndent);
|
||||
$sWhere = self::ClauseWhere($this->m_oConditionExpr, $aArgs);
|
||||
if ($iLimitCount > 0)
|
||||
{
|
||||
$sLimit = 'LIMIT '.$iLimitStart.', '.$iLimitCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sLimit = '';
|
||||
}
|
||||
if ($bGetCount)
|
||||
{
|
||||
if (count($this->__aSelectedIdFields) > 0)
|
||||
@@ -316,11 +330,13 @@ class SQLObjectQuery extends SQLQuery
|
||||
$aCountFields[] = "COALESCE($sFieldExpr, 0)"; // Null values are excluded from the count
|
||||
}
|
||||
$sCountFields = implode(', ', $aCountFields);
|
||||
$sSQL = "SELECT$sLineSep COUNT(DISTINCT $sCountFields) AS COUNT$sLineSep FROM $sFrom$sLineSep WHERE $sWhere";
|
||||
// Count can be limited for performance reason, in this case the total amount is not important,
|
||||
// we only need to know if the number of entries is greater than a certain amount.
|
||||
$sSQL = "SELECT COUNT(*) AS COUNT FROM (SELECT$sLineSep DISTINCT $sCountFields $sLineSep FROM $sFrom$sLineSep WHERE $sWhere $sLimit) AS _tatooine_";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sSQL = "SELECT$sLineSep COUNT(*) AS COUNT$sLineSep FROM $sFrom$sLineSep WHERE $sWhere";
|
||||
$sSQL = "SELECT COUNT(*) AS COUNT FROM (SELECT$sLineSep 1 $sLineSep FROM $sFrom$sLineSep WHERE $sWhere $sLimit) AS _tatooine_";
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -331,14 +347,7 @@ class SQLObjectQuery extends SQLQuery
|
||||
{
|
||||
$sOrderBy = "ORDER BY $sOrderBy$sLineSep";
|
||||
}
|
||||
if ($iLimitCount > 0)
|
||||
{
|
||||
$sLimit = 'LIMIT '.$iLimitStart.', '.$iLimitCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sLimit = '';
|
||||
}
|
||||
|
||||
$sSQL = "SELECT$sLineSep DISTINCT $sSelect$sLineSep FROM $sFrom$sLineSep WHERE $sWhere$sLineSep $sOrderBy $sLimit";
|
||||
}
|
||||
return $sSQL;
|
||||
@@ -505,6 +514,7 @@ class SQLObjectQuery extends SQLQuery
|
||||
|
||||
public function OptimizeJoins($aUsedTables, $bTopCall = true)
|
||||
{
|
||||
$this->m_iOriginalTableCount = $this->CountTables();
|
||||
if ($bTopCall)
|
||||
{
|
||||
// Top call: complete the list of tables absolutely required to perform the right query
|
||||
@@ -529,7 +539,18 @@ class SQLObjectQuery extends SQLQuery
|
||||
return (count($this->m_aJoinSelects) == 0);
|
||||
}
|
||||
|
||||
protected function CollectUsedTables(&$aTables)
|
||||
public function CountTables()
|
||||
{
|
||||
$iRet = 1;
|
||||
foreach ($this->m_aJoinSelects as $i => $aJoinInfo)
|
||||
{
|
||||
$oSQLQuery = $aJoinInfo["select"];
|
||||
$iRet += $oSQLQuery->CountTables();
|
||||
}
|
||||
return $iRet;
|
||||
}
|
||||
|
||||
public function CollectUsedTables(&$aTables)
|
||||
{
|
||||
$this->m_oConditionExpr->CollectUsedParents($aTables);
|
||||
foreach($this->m_aFields as $sFieldAlias => $oField)
|
||||
@@ -596,4 +617,5 @@ class SQLObjectQuery extends SQLQuery
|
||||
// None of the tables is in the list of required tables
|
||||
return $bResult;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ require_once('cmdbsource.class.inc.php');
|
||||
abstract class SQLQuery
|
||||
{
|
||||
private $m_SourceOQL = '';
|
||||
private $m_bBeautifulQuery = false;
|
||||
protected $m_bBeautifulQuery = false;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
|
||||
@@ -58,7 +58,7 @@ class SQLUnionQuery extends SQLQuery
|
||||
{
|
||||
$aQueriesHtml[] = '<p>'.$oSQLQuery->DisplayHtml().'</p>';
|
||||
}
|
||||
echo implode('UNION', $aQueries);
|
||||
echo implode('UNION', $aQueriesHtml);
|
||||
}
|
||||
|
||||
public function AddInnerJoin($oSQLQuery, $sLeftField, $sRightField, $sRightTable = '')
|
||||
@@ -85,7 +85,6 @@ class SQLUnionQuery extends SQLQuery
|
||||
{
|
||||
$this->m_bBeautifulQuery = $bBeautifulQuery;
|
||||
$sLineSep = $this->m_bBeautifulQuery ? "\n" : '';
|
||||
$sIndent = $this->m_bBeautifulQuery ? " " : null;
|
||||
|
||||
$aSelects = array();
|
||||
foreach ($this->aQueries as $oSQLQuery)
|
||||
@@ -93,36 +92,33 @@ class SQLUnionQuery extends SQLQuery
|
||||
// Render SELECTS without orderby/limit/count
|
||||
$aSelects[] = $oSQLQuery->RenderSelect(array(), $aArgs, 0, 0, false, $bBeautifulQuery);
|
||||
}
|
||||
$sSelects = '('.implode(")$sLineSep UNION$sLineSep(", $aSelects).')';
|
||||
|
||||
if ($bGetCount)
|
||||
if ($iLimitCount > 0)
|
||||
{
|
||||
$sFrom = "($sLineSep$sSelects$sLineSep) as __selects__";
|
||||
$sSQL = "SELECT$sLineSep COUNT(*) AS COUNT$sLineSep FROM $sFrom$sLineSep";
|
||||
$sLimit = 'LIMIT '.$iLimitStart.', '.$iLimitCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sLimit = '';
|
||||
}
|
||||
|
||||
if ($bGetCount)
|
||||
{
|
||||
$sSelects = '('.implode(" $sLimit)$sLineSep UNION$sLineSep(", $aSelects)." $sLimit)";
|
||||
$sFrom = "($sLineSep$sSelects$sLineSep) as __selects__";
|
||||
$sSQL = "SELECT COUNT(*) AS COUNT FROM (SELECT$sLineSep 1 $sLineSep FROM $sFrom$sLineSep) AS _union_tatooine_";
|
||||
}
|
||||
else
|
||||
{
|
||||
$aSelects = array();
|
||||
foreach ($this->aQueries as $oSQLQuery)
|
||||
{
|
||||
// Render SELECT without orderby/limit/count
|
||||
$aSelects[] = $oSQLQuery->RenderSelect(array(), $aArgs, 0, 0, false, $bBeautifulQuery);
|
||||
}
|
||||
$sSelect = $this->aQueries[0]->RenderSelectClause();
|
||||
$sOrderBy = $this->aQueries[0]->RenderOrderByClause($aOrderBy);
|
||||
if (!empty($sOrderBy))
|
||||
{
|
||||
$sOrderBy = "ORDER BY $sOrderBy$sLineSep";
|
||||
}
|
||||
if ($iLimitCount > 0)
|
||||
{
|
||||
$sLimit = 'LIMIT '.$iLimitStart.', '.$iLimitCount;
|
||||
$sOrderBy = "ORDER BY $sOrderBy$sLineSep $sLimit";
|
||||
$sSQL = '('.implode(")$sLineSep UNION$sLineSep (", $aSelects).')'.$sLineSep.$sOrderBy;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sLimit = '';
|
||||
$sSQL = '('.implode(" $sLimit)$sLineSep UNION$sLineSep (", $aSelects)." $sLimit)";
|
||||
}
|
||||
$sSQL = $sSelects.$sLineSep.$sOrderBy.' '.$sLimit;
|
||||
}
|
||||
return $sSQL;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* User rights management API
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -161,6 +161,7 @@ abstract class UserRightsAddOnAPI
|
||||
}
|
||||
|
||||
|
||||
require_once(APPROOT.'/application/cmdbabstract.class.inc.php');
|
||||
abstract class User extends cmdbAbstractObject
|
||||
{
|
||||
public static function Init()
|
||||
@@ -245,7 +246,7 @@ abstract class User extends cmdbAbstractObject
|
||||
{
|
||||
if (is_null($this->oContactObject))
|
||||
{
|
||||
if ($this->Get('contactid') != 0)
|
||||
if (MetaModel::IsValidAttCode(get_class($this), 'contactid') && ($this->Get('contactid') != 0))
|
||||
{
|
||||
$this->oContactObject = MetaModel::GetObject('Contact', $this->Get('contactid'));
|
||||
}
|
||||
@@ -337,6 +338,8 @@ abstract class User extends cmdbAbstractObject
|
||||
'bulkread' => $this->GetGrantAsHtml($sClass, UR_ACTION_BULK_READ),
|
||||
'write' => $this->GetGrantAsHtml($sClass, UR_ACTION_MODIFY),
|
||||
'bulkwrite' => $this->GetGrantAsHtml($sClass, UR_ACTION_BULK_MODIFY),
|
||||
'delete' => $this->GetGrantAsHtml($sClass, UR_ACTION_DELETE),
|
||||
'bulkdelete' => $this->GetGrantAsHtml($sClass, UR_ACTION_BULK_DELETE),
|
||||
'stimuli' => $sStimuli,
|
||||
);
|
||||
}
|
||||
@@ -349,6 +352,8 @@ abstract class User extends cmdbAbstractObject
|
||||
$aDisplayConfig['bulkread'] = array('label' => Dict::S('UI:UserManagement:Action:BulkRead'), 'description' => Dict::S('UI:UserManagement:Action:BulkRead+'));
|
||||
$aDisplayConfig['write'] = array('label' => Dict::S('UI:UserManagement:Action:Modify'), 'description' => Dict::S('UI:UserManagement:Action:Modify+'));
|
||||
$aDisplayConfig['bulkwrite'] = array('label' => Dict::S('UI:UserManagement:Action:BulkModify'), 'description' => Dict::S('UI:UserManagement:Action:BulkModify+'));
|
||||
$aDisplayConfig['delete'] = array('label' => Dict::S('UI:UserManagement:Action:Delete'), 'description' => Dict::S('UI:UserManagement:Action:Delete+'));
|
||||
$aDisplayConfig['bulkdelete'] = array('label' => Dict::S('UI:UserManagement:Action:BulkDelete'), 'description' => Dict::S('UI:UserManagement:Action:BulkDelete+'));
|
||||
$aDisplayConfig['stimuli'] = array('label' => Dict::S('UI:UserManagement:Action:Stimuli'), 'description' => Dict::S('UI:UserManagement:Action:Stimuli+'));
|
||||
$oPage->table($aDisplayConfig, $aDisplayData);
|
||||
}
|
||||
@@ -583,6 +588,13 @@ class UserRights
|
||||
return false;
|
||||
}
|
||||
self::$m_oUser = $oUser;
|
||||
|
||||
if (isset($_SESSION['impersonate_user']))
|
||||
{
|
||||
self::$m_oRealUser = self::$m_oUser;
|
||||
self::$m_oUser = self::FindUser($_SESSION['impersonate_user']);
|
||||
}
|
||||
|
||||
Dict::SetUserLanguage(self::GetUserLanguage());
|
||||
return true;
|
||||
}
|
||||
@@ -641,6 +653,29 @@ class UserRights
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether or not the archive mode is allowed to the current user
|
||||
* @return boolean
|
||||
*/
|
||||
static function CanBrowseArchive()
|
||||
{
|
||||
if (is_null(self::$m_oUser))
|
||||
{
|
||||
$bRet = false;
|
||||
}
|
||||
elseif (isset($_SESSION['archive_allowed']))
|
||||
{
|
||||
$bRet = $_SESSION['archive_allowed'];
|
||||
}
|
||||
else
|
||||
{
|
||||
// As of now, anybody can switch to the archive mode as soon as there is an archivable class
|
||||
$bRet = (count(MetaModel::EnumArchivableClasses()) > 0);
|
||||
$_SESSION['archive_allowed'] = $bRet;
|
||||
}
|
||||
return $bRet;
|
||||
}
|
||||
|
||||
public static function CanChangePassword()
|
||||
{
|
||||
if (MetaModel::DBIsReadOnly())
|
||||
@@ -679,24 +714,50 @@ class UserRights
|
||||
}
|
||||
}
|
||||
|
||||
public static function Impersonate($sName, $sPassword)
|
||||
/**
|
||||
* @param string $sName Login identifier of the user to impersonate
|
||||
* @return bool True if an impersonation occurred
|
||||
*/
|
||||
public static function Impersonate($sName)
|
||||
{
|
||||
if (!self::CheckLogin()) return false;
|
||||
|
||||
$bRet = false;
|
||||
$oUser = self::FindUser($sName);
|
||||
if (is_null($oUser))
|
||||
if ($oUser)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!$oUser->CheckCredentials($sPassword))
|
||||
{
|
||||
return false;
|
||||
$bRet = true;
|
||||
if (is_null(self::$m_oRealUser))
|
||||
{
|
||||
// First impersonation
|
||||
self::$m_oRealUser = self::$m_oUser;
|
||||
}
|
||||
if (self::$m_oRealUser && (self::$m_oRealUser->GetKey() == $oUser->GetKey()))
|
||||
{
|
||||
// Equivalent to "Deimpersonate"
|
||||
self::Deimpersonate();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Do impersonate!
|
||||
self::$m_oUser = $oUser;
|
||||
Dict::SetUserLanguage(self::GetUserLanguage());
|
||||
$_SESSION['impersonate_user'] = $sName;
|
||||
self::_ResetSessionCache();
|
||||
}
|
||||
}
|
||||
return $bRet;
|
||||
}
|
||||
|
||||
self::$m_oRealUser = self::$m_oUser;
|
||||
self::$m_oUser = $oUser;
|
||||
Dict::SetUserLanguage(self::GetUserLanguage());
|
||||
return true;
|
||||
public static function Deimpersonate()
|
||||
{
|
||||
if (!is_null(self::$m_oRealUser))
|
||||
{
|
||||
self::$m_oUser = self::$m_oRealUser;
|
||||
Dict::SetUserLanguage(self::GetUserLanguage());
|
||||
unset($_SESSION['impersonate_user']);
|
||||
self::_ResetSessionCache();
|
||||
}
|
||||
}
|
||||
|
||||
public static function GetUser()
|
||||
@@ -828,6 +889,11 @@ class UserRights
|
||||
return self::$m_oRealUser->Get('login');
|
||||
}
|
||||
|
||||
public static function GetRealUserObject()
|
||||
{
|
||||
return self::$m_oRealUser;
|
||||
}
|
||||
|
||||
public static function GetRealUserId()
|
||||
{
|
||||
if (is_null(self::$m_oRealUser))
|
||||
@@ -1045,7 +1111,12 @@ class UserRights
|
||||
{
|
||||
$oUser = self::$m_oUser;
|
||||
}
|
||||
if ($oUser->GetKey() == self::$m_oUser->GetKey())
|
||||
if ($oUser === null)
|
||||
{
|
||||
// Not logged in: no profile at all
|
||||
$aProfiles = array();
|
||||
}
|
||||
elseif ((self::$m_oUser !== null) && ($oUser->GetKey() == self::$m_oUser->GetKey()))
|
||||
{
|
||||
// Data about the current user can be found into the session data
|
||||
if (array_key_exists('profile_list', $_SESSION))
|
||||
@@ -1086,8 +1157,16 @@ class UserRights
|
||||
self::$m_aAdmins = array();
|
||||
self::$m_aPortalUsers = array();
|
||||
}
|
||||
if (!isset($_SESSION) && !utils::IsModeCLI())
|
||||
{
|
||||
session_name('itop-'.md5(APPROOT));
|
||||
session_start();
|
||||
}
|
||||
self::_ResetSessionCache();
|
||||
return self::$m_oAddOn->FlushPrivileges();
|
||||
if (self::$m_oAddOn)
|
||||
{
|
||||
self::$m_oAddOn->FlushPrivileges();
|
||||
}
|
||||
}
|
||||
|
||||
static $m_aCacheUsers;
|
||||
@@ -1165,6 +1244,10 @@ class UserRights
|
||||
{
|
||||
unset($_SESSION['profile_list']);
|
||||
}
|
||||
if (isset($_SESSION['archive_allowed']))
|
||||
{
|
||||
unset($_SESSION['archive_allowed']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1670,5 +1753,4 @@ class CAS_SelfRegister implements iSelfRegister
|
||||
}
|
||||
|
||||
// By default enable the 'CAS_SelfRegister' defined above
|
||||
UserRights::SelectSelfRegister('CAS_SelfRegister');
|
||||
?>
|
||||
UserRights::SelectSelfRegister('CAS_SelfRegister');
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* Value set definitions (from a fixed list or from a query, etc.)
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -52,7 +52,7 @@ abstract class ValueSetDefinition
|
||||
}
|
||||
|
||||
|
||||
public function GetValues($aArgs, $sContains = '')
|
||||
public function GetValues($aArgs, $sContains = '', $sOperation = 'contains')
|
||||
{
|
||||
if (!$this->m_bIsLoaded)
|
||||
{
|
||||
@@ -93,12 +93,16 @@ abstract class ValueSetDefinition
|
||||
class ValueSetObjects extends ValueSetDefinition
|
||||
{
|
||||
protected $m_sContains;
|
||||
protected $m_sOperation;
|
||||
protected $m_sFilterExpr; // in OQL
|
||||
protected $m_sValueAttCode;
|
||||
protected $m_aOrderBy;
|
||||
protected $m_aExtraConditions;
|
||||
private $m_bAllowAllData;
|
||||
private $m_aModifierProperties;
|
||||
private $m_bSort;
|
||||
private $m_iLimit;
|
||||
|
||||
|
||||
/**
|
||||
* @param hash $aOrderBy Array of '[<classalias>.]attcode' => bAscending
|
||||
@@ -106,12 +110,15 @@ class ValueSetObjects extends ValueSetDefinition
|
||||
public function __construct($sFilterExp, $sValueAttCode = '', $aOrderBy = array(), $bAllowAllData = false, $aModifierProperties = array())
|
||||
{
|
||||
$this->m_sContains = '';
|
||||
$this->m_sOperation = '';
|
||||
$this->m_sFilterExpr = $sFilterExp;
|
||||
$this->m_sValueAttCode = $sValueAttCode;
|
||||
$this->m_aOrderBy = $aOrderBy;
|
||||
$this->m_bAllowAllData = $bAllowAllData;
|
||||
$this->m_aModifierProperties = $aModifierProperties;
|
||||
$this->m_aExtraConditions = array();
|
||||
$this->m_bSort = true;
|
||||
$this->m_iLimit = 0;
|
||||
}
|
||||
|
||||
public function SetModifierProperty($sPluginClass, $sProperty, $value)
|
||||
@@ -124,7 +131,7 @@ class ValueSetObjects extends ValueSetDefinition
|
||||
$this->m_aExtraConditions[] = $oFilter;
|
||||
}
|
||||
|
||||
public function ToObjectSet($aArgs = array(), $sContains = '')
|
||||
public function ToObjectSet($aArgs = array(), $sContains = '', $iAdditionalValue = null)
|
||||
{
|
||||
if ($this->m_bAllowAllData)
|
||||
{
|
||||
@@ -145,15 +152,29 @@ class ValueSetObjects extends ValueSetDefinition
|
||||
$oFilter->SetModifierProperty($sPluginClass, $sProperty, $value);
|
||||
}
|
||||
}
|
||||
if ($iAdditionalValue > 0)
|
||||
{
|
||||
$oSearchAdditionalValue = new DBObjectSearch($oFilter->GetClass());
|
||||
$oSearchAdditionalValue->AddConditionExpression( new BinaryExpression(
|
||||
new FieldExpression('id', $oSearchAdditionalValue->GetClassAlias()),
|
||||
'=',
|
||||
new VariableExpression('current_extkey_id'))
|
||||
);
|
||||
$oSearchAdditionalValue->AllowAllData();
|
||||
$oSearchAdditionalValue->SetArchiveMode(true);
|
||||
$oSearchAdditionalValue->SetInternalParams( array('current_extkey_id' => $iAdditionalValue) );
|
||||
|
||||
$oFilter = new DBUnionSearch(array($oFilter, $oSearchAdditionalValue));
|
||||
}
|
||||
|
||||
return new DBObjectSet($oFilter, $this->m_aOrderBy, $aArgs);
|
||||
}
|
||||
|
||||
public function GetValues($aArgs, $sContains = '')
|
||||
public function GetValues($aArgs, $sContains = '', $sOperation = 'contains')
|
||||
{
|
||||
if (!$this->m_bIsLoaded || ($sContains != $this->m_sContains))
|
||||
if (!$this->m_bIsLoaded || ($sContains != $this->m_sContains) || ($sOperation != $this->m_sOperation))
|
||||
{
|
||||
$this->LoadValues($aArgs, $sContains);
|
||||
$this->LoadValues($aArgs, $sContains, $sOperation);
|
||||
$this->m_bIsLoaded = true;
|
||||
}
|
||||
// The results are already filtered and sorted (on friendly name)
|
||||
@@ -161,9 +182,10 @@ class ValueSetObjects extends ValueSetDefinition
|
||||
return $aRet;
|
||||
}
|
||||
|
||||
protected function LoadValues($aArgs, $sContains = '')
|
||||
protected function LoadValues($aArgs, $sContains = '', $sOperation = 'contains')
|
||||
{
|
||||
$this->m_sContains = $sContains;
|
||||
$this->m_sOperation = $sOperation;
|
||||
|
||||
$this->m_aValues = array();
|
||||
|
||||
@@ -188,12 +210,67 @@ class ValueSetObjects extends ValueSetDefinition
|
||||
}
|
||||
}
|
||||
|
||||
$oValueExpr = new ScalarExpression('%'.$sContains.'%');
|
||||
$oNameExpr = new FieldExpression('friendlyname', $oFilter->GetClassAlias());
|
||||
$oNewCondition = new BinaryExpression($oNameExpr, 'LIKE', $oValueExpr);
|
||||
$oFilter->AddConditionExpression($oNewCondition);
|
||||
$oExpression = DBObjectSearch::GetPolymorphicExpression($oFilter->GetClass(), 'friendlyname');
|
||||
$aFields = $oExpression->ListRequiredFields();
|
||||
$sClass = $oFilter->GetClass();
|
||||
foreach($aFields as $sField)
|
||||
{
|
||||
$aFieldItems = explode('.', $sField);
|
||||
if ($aFieldItems[0] != $sClass)
|
||||
{
|
||||
$sOperation = 'contains';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$oObjects = new DBObjectSet($oFilter, $this->m_aOrderBy, $aArgs);
|
||||
switch ($sOperation)
|
||||
{
|
||||
case 'equals_start_with':
|
||||
$aAttributes = MetaModel::GetFriendlyNameAttributeCodeList($oFilter->GetClass());
|
||||
$sClassAlias = $oFilter->GetClassAlias();
|
||||
$aFilters = array();
|
||||
// Equals first
|
||||
$oValueExpr = new ScalarExpression($sContains);
|
||||
foreach($aAttributes as $sAttribute)
|
||||
{
|
||||
$oNewFilter = $oFilter->DeepClone();
|
||||
$oNameExpr = new FieldExpression($sAttribute, $sClassAlias);
|
||||
$oCondition = new BinaryExpression($oNameExpr, 'LIKE', $oValueExpr);
|
||||
$oNewFilter->AddConditionExpression($oCondition);
|
||||
$aFilters[] = $oNewFilter;
|
||||
}
|
||||
// start with next
|
||||
$oValueExpr = new ScalarExpression($sContains.'%');
|
||||
foreach($aAttributes as $sAttribute)
|
||||
{
|
||||
$oNewFilter = $oFilter->DeepClone();
|
||||
$oNameExpr = new FieldExpression($sAttribute, $sClassAlias);
|
||||
$oCondition = new BinaryExpression($oNameExpr, 'LIKE', $oValueExpr);
|
||||
$oNewFilter->AddConditionExpression($oCondition);
|
||||
$aFilters[] = $oNewFilter;
|
||||
}
|
||||
// Unions are much faster than OR conditions
|
||||
$oFilter = new DBUnionSearch($aFilters);
|
||||
break;
|
||||
|
||||
default:
|
||||
$oValueExpr = new ScalarExpression('%'.$sContains.'%');
|
||||
$oNameExpr = new FieldExpression('friendlyname', $oFilter->GetClassAlias());
|
||||
$oNewCondition = new BinaryExpression($oNameExpr, 'LIKE', $oValueExpr);
|
||||
$oFilter->AddConditionExpression($oNewCondition);
|
||||
break;
|
||||
}
|
||||
|
||||
$oObjects = new DBObjectSet($oFilter, $this->m_aOrderBy, $aArgs, null, $this->m_iLimit, 0, $this->m_bSort);
|
||||
if (empty($this->m_sValueAttCode))
|
||||
{
|
||||
$aAttToLoad = array($oFilter->GetClassAlias() => array('friendlyname'));
|
||||
}
|
||||
else
|
||||
{
|
||||
$aAttToLoad = array($oFilter->GetClassAlias() => array($this->m_sValueAttCode));
|
||||
}
|
||||
$oObjects->OptimizeColumnLoad($aAttToLoad);
|
||||
while ($oObject = $oObjects->Fetch())
|
||||
{
|
||||
if (empty($this->m_sValueAttCode))
|
||||
@@ -217,79 +294,21 @@ class ValueSetObjects extends ValueSetDefinition
|
||||
{
|
||||
return $this->m_sFilterExpr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set of existing values for a link set attribute, given a relation code
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class ValueSetRelatedObjectsFromLinkSet extends ValueSetDefinition
|
||||
{
|
||||
protected $m_sLinkSetAttCode;
|
||||
protected $m_sExtKeyToRemote;
|
||||
protected $m_sRelationCode;
|
||||
protected $m_iMaxDepth;
|
||||
protected $m_sTargetClass;
|
||||
protected $m_sTargetExtKey;
|
||||
// protected $m_aOrderBy;
|
||||
|
||||
public function __construct($sLinkSetAttCode, $sExtKeyToRemote, $sRelationCode, $iMaxDepth, $sTargetClass, $sTargetLinkClass, $sTargetExtKey)
|
||||
/**
|
||||
* @param $iLimit
|
||||
*/
|
||||
public function SetLimit($iLimit)
|
||||
{
|
||||
$this->m_sLinkSetAttCode = $sLinkSetAttCode;
|
||||
$this->m_sExtKeyToRemote = $sExtKeyToRemote;
|
||||
$this->m_sRelationCode = $sRelationCode;
|
||||
$this->m_iMaxDepth = $iMaxDepth;
|
||||
$this->m_sTargetClass = $sTargetClass;
|
||||
$this->m_sTargetLinkClass = $sTargetLinkClass;
|
||||
$this->m_sTargetExtKey = $sTargetExtKey;
|
||||
// $this->m_aOrderBy = $aOrderBy;
|
||||
$this->m_iLimit = $iLimit;
|
||||
}
|
||||
|
||||
protected function LoadValues($aArgs)
|
||||
/**
|
||||
* @param $bSort
|
||||
*/
|
||||
public function SetSort($bSort)
|
||||
{
|
||||
$this->m_aValues = array();
|
||||
|
||||
if (!array_key_exists('this', $aArgs))
|
||||
{
|
||||
throw new CoreException("Missing 'this' in arguments", array('args' => $aArgs));
|
||||
}
|
||||
|
||||
$oTarget = $aArgs['this->object()'];
|
||||
|
||||
// Nodes from which we will start the search for neighbourhood
|
||||
$oNodes = DBObjectSet::FromLinkSet($oTarget, $this->m_sLinkSetAttCode, $this->m_sExtKeyToRemote);
|
||||
|
||||
// Neighbours, whatever their class
|
||||
$aRelated = $oNodes->GetRelatedObjects($this->m_sRelationCode, $this->m_iMaxDepth);
|
||||
|
||||
$sRootClass = MetaModel::GetRootClass($this->m_sTargetClass);
|
||||
if (array_key_exists($sRootClass, $aRelated))
|
||||
{
|
||||
$aLinksToCreate = array();
|
||||
foreach($aRelated[$sRootClass] as $iKey => $oObject)
|
||||
{
|
||||
if (MetaModel::IsParentClass($this->m_sTargetClass, get_class($oObject)))
|
||||
{
|
||||
$oNewLink = MetaModel::NewObject($this->m_sTargetLinkClass);
|
||||
$oNewLink->Set($this->m_sTargetExtKey, $iKey);
|
||||
//$oNewLink->Set('role', 'concerned by an impacted CI');
|
||||
|
||||
$aLinksToCreate[] = $oNewLink;
|
||||
}
|
||||
}
|
||||
// #@# or AddObjectArray($aObjects) ?
|
||||
$oSetToCreate = DBObjectSet::FromArray($this->m_sTargetLinkClass, $aLinksToCreate);
|
||||
$this->m_aValues[$oObject->GetKey()] = $oObject->GetName();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function GetValuesDescription()
|
||||
{
|
||||
return 'Filter: '.$this->m_sFilterExpr;
|
||||
$this->m_bSort = $bSort;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2015 Combodo SARL
|
||||
// Copyright (C) 2015-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -19,7 +19,7 @@
|
||||
/**
|
||||
* Bulk export: XML export
|
||||
*
|
||||
* @copyright Copyright (C) 2015 Combodo SARL
|
||||
* @copyright Copyright (C) 2015-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -118,7 +118,7 @@ class XMLBulkExport extends BulkExport
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!$oAttDef->IsWritable())
|
||||
if ($oAttDef->IsExternalField())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -138,7 +138,7 @@ class XMLBulkExport extends BulkExport
|
||||
$aClass2Attributes[$sAlias] = $aAttributes;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$iPreviousTimeLimit = ini_get('max_execution_time');
|
||||
$iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop');
|
||||
|
||||
|
||||
@@ -5,4 +5,4 @@ $complement-light: #d6e8ef;
|
||||
$frame-background-color: #F1F1F1;
|
||||
$text-color: #000;
|
||||
// Beware the version number MUST be enclosed with quotes otherwise v2.3.0 becomes v2 0.3 .0
|
||||
$version: "v2.3.0";
|
||||
$version: "v2.4.0";
|
||||
File diff suppressed because one or more lines are too long
BIN
css/font-awesome/fonts/FontAwesome.otf
Normal file
BIN
css/font-awesome/fonts/FontAwesome.otf
Normal file
Binary file not shown.
BIN
css/font-awesome/fonts/fontawesome-webfont.eot
Normal file
BIN
css/font-awesome/fonts/fontawesome-webfont.eot
Normal file
Binary file not shown.
2671
css/font-awesome/fonts/fontawesome-webfont.svg
Normal file
2671
css/font-awesome/fonts/fontawesome-webfont.svg
Normal file
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 434 KiB |
Binary file not shown.
BIN
css/font-awesome/fonts/fontawesome-webfont.woff
Normal file
BIN
css/font-awesome/fonts/fontawesome-webfont.woff
Normal file
Binary file not shown.
BIN
css/font-awesome/fonts/fontawesome-webfont.woff2
Normal file
BIN
css/font-awesome/fonts/fontawesome-webfont.woff2
Normal file
Binary file not shown.
BIN
css/font-combodo/combodo-webfont.ttf
Normal file
BIN
css/font-combodo/combodo-webfont.ttf
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -21,7 +21,7 @@ OS2Version: 0
|
||||
OS2_WeightWidthSlopeOnly: 0
|
||||
OS2_UseTypoMetrics: 1
|
||||
CreationTime: 1463745065
|
||||
ModificationTime: 1464178421
|
||||
ModificationTime: 1506001058
|
||||
OS2TypoAscent: 0
|
||||
OS2TypoAOffset: 1
|
||||
OS2TypoDescent: 0
|
||||
@@ -35,7 +35,6 @@ HheadAscent: 0
|
||||
HheadAOffset: 1
|
||||
HheadDescent: 0
|
||||
HheadDOffset: 1
|
||||
OS2Vendor: 'PfEd'
|
||||
MarkAttachClasses: 1
|
||||
DEI: 91125
|
||||
Encoding: ISO8859-1
|
||||
@@ -47,7 +46,7 @@ FitToEm: 0
|
||||
WinInfo: 0 31 10
|
||||
BeginPrivate: 0
|
||||
EndPrivate
|
||||
BeginChars: 256 8
|
||||
BeginChars: 256 11
|
||||
|
||||
StartChar: zero
|
||||
Encoding: 48 48 0
|
||||
@@ -210,7 +209,7 @@ StartChar: three
|
||||
Encoding: 51 51 3
|
||||
Width: 1022
|
||||
VWidth: 0
|
||||
Flags: MO
|
||||
Flags: M
|
||||
LayerCount: 3
|
||||
Fore
|
||||
SplineSet
|
||||
@@ -261,11 +260,13 @@ StartChar: C
|
||||
Encoding: 67 67 4
|
||||
Width: 1080
|
||||
VWidth: 0
|
||||
Flags: HW
|
||||
Flags: W
|
||||
HStem: -112 36<398.67 444.211> 97 36<463.993 575.071> 116 37<411.524 459.906> 250 37<334.123 402.464> 335 37<749.246 821.773> 387 37<836.543 929.295> 396 37<873.093 933.545> 442 37<475 482 739.647 795.664> 621 37<286.042 389.13> 650 37<510.192 579.789>
|
||||
VStem: 53 37<216.048 298> 218 37<457.867 579.651> 272 36<60.6299 128.446> 439 37<479.452 577.67> 451 37<-73.2171 5.85426> 472 37<568.829 649.107> 553 37<359.872 417.574> 651 38<-34.3438 62.6665> 718 38<8.82031 164.606> 934 38<341.569 396>
|
||||
LayerCount: 3
|
||||
Fore
|
||||
SplineSet
|
||||
641 -116 m 4x9b20
|
||||
641 -116 m 4x9b39f0
|
||||
637 -116 633 -115 630 -113 c 4
|
||||
624 -110 616 -102 616 -88 c 4
|
||||
616 -84 617 -80 618 -75 c 4
|
||||
@@ -275,14 +276,14 @@ SplineSet
|
||||
651 31 641 52 624 75 c 4
|
||||
609 94 595 108 586 115 c 5
|
||||
562 107 523 97 500 97 c 6
|
||||
499 97 l 6xdb20
|
||||
499 97 l 6xdb39f0
|
||||
479 97 468 105 460 111 c 4
|
||||
457 114 455 115 453 116 c 4
|
||||
450 116 438 112 423 104 c 4
|
||||
412 98 405 94 401 90 c 5
|
||||
411 80 436 61 448 52 c 4
|
||||
460 43 468 37 473 32 c 4
|
||||
485 20 488 -4 488 -20 c 4
|
||||
485 20 488 -4 488 -20 c 4xb93af0
|
||||
488 -27 487 -33 487 -36 c 4
|
||||
485 -55 477 -90 452 -105 c 4
|
||||
444 -110 435 -112 426 -112 c 4
|
||||
@@ -321,10 +322,10 @@ SplineSet
|
||||
219 484 218 493 218 504 c 4
|
||||
218 526 222 550 231 575 c 4
|
||||
237 593 255 636 284 651 c 4
|
||||
293 656 305 658 320 658 c 4xb9a0
|
||||
293 656 305 658 320 658 c 4
|
||||
342 658 367 653 389 644 c 4
|
||||
416 633 437 617 452 597 c 4
|
||||
472 571 476 537 476 508 c 4
|
||||
472 571 476 537 476 508 c 4xb9bcf0
|
||||
476 497 476 487 475 479 c 5
|
||||
482 479 l 5
|
||||
505 499 l 5
|
||||
@@ -352,7 +353,7 @@ SplineSet
|
||||
766 473 767 473 768 473 c 4
|
||||
787 473 808 460 831 446 c 4
|
||||
845 437 865 424 873 424 c 4
|
||||
874 424 l 6x9d60
|
||||
874 424 l 6x9d79f0
|
||||
875 424 880 425 884 426 c 4
|
||||
897 429 915 433 930 433 c 4
|
||||
956 433 965 421 969 412 c 4
|
||||
@@ -362,7 +363,7 @@ SplineSet
|
||||
916 289 911 288 905 288 c 4
|
||||
888 288 863 299 835 311 c 4
|
||||
810 322 780 335 766 335 c 4
|
||||
765 335 764 335 764 335 c 4
|
||||
764 335 l 4
|
||||
763 335 757 331 748 315 c 4
|
||||
741 301 734 283 727 264 c 4
|
||||
721 248 715 231 708 216 c 5
|
||||
@@ -370,7 +371,7 @@ SplineSet
|
||||
756 69 755 58 753 47 c 4
|
||||
748 19 727 -22 708 -51 c 4
|
||||
697 -68 687 -82 677 -93 c 4
|
||||
663 -109 652 -116 641 -116 c 4x9b20
|
||||
663 -109 652 -116 641 -116 c 4x9b39f0
|
||||
308 85 m 5
|
||||
308 83 312 72 340 46 c 4
|
||||
357 30 374 18 374 18 c 6
|
||||
@@ -387,14 +388,14 @@ SplineSet
|
||||
383 55 362 72 362 90 c 4
|
||||
362 92 362 93 362 95 c 4
|
||||
363 100 366 113 402 134 c 4
|
||||
410 138 435 153 453 153 c 4xb9a0
|
||||
410 138 435 153 453 153 c 4xb9baf0
|
||||
455 153 458 152 460 152 c 4
|
||||
470 150 476 145 482 141 c 4
|
||||
488 136 491 133 499 133 c 6
|
||||
500 133 l 6xd920
|
||||
500 133 l 6xd93af0
|
||||
521 133 562 145 581 153 c 6
|
||||
589 156 l 5
|
||||
596 153 l 6xb920
|
||||
596 153 l 6xb93af0
|
||||
612 146 637 119 654 97 c 4
|
||||
670 75 689 44 689 16 c 4
|
||||
689 13 689 10 688 7 c 4
|
||||
@@ -413,7 +414,7 @@ SplineSet
|
||||
868 337 893 326 903 325 c 5
|
||||
907 329 916 340 924 359 c 4
|
||||
932 377 934 390 934 396 c 4
|
||||
933 396 932 396 930 396 c 4xdb20
|
||||
933 396 932 396 930 396 c 4xdb3af0
|
||||
919 396 902 392 892 390 c 4
|
||||
886 389 881 388 878 388 c 4
|
||||
876 388 875 387 873 387 c 4
|
||||
@@ -433,9 +434,9 @@ SplineSet
|
||||
629 559 619 566 619 566 c 6
|
||||
615 569 l 5
|
||||
580 633 l 5
|
||||
566 639 539 650 520 650 c 4x9d60
|
||||
566 639 539 650 520 650 c 4
|
||||
514 650 512 650 511 649 c 4
|
||||
510 648 509 644 509 637 c 4
|
||||
510 648 509 644 509 637 c 4x9d79f0
|
||||
509 629 510 618 514 602 c 4
|
||||
517 589 520 577 523 569 c 5
|
||||
562 561 l 5
|
||||
@@ -446,7 +447,7 @@ SplineSet
|
||||
435 465 l 6
|
||||
437 474 439 491 439 510 c 4
|
||||
439 533 436 558 423 575 c 4
|
||||
400 605 354 621 320 621 c 4x99a0
|
||||
400 605 354 621 320 621 c 4x99bcf0
|
||||
310 621 304 619 301 618 c 4
|
||||
281 608 255 548 255 504 c 4
|
||||
255 497 255 490 257 484 c 4
|
||||
@@ -475,22 +476,25 @@ SplineSet
|
||||
424 204 390 196 377 186 c 4
|
||||
330 149 310 105 308 85 c 5
|
||||
EndSplineSet
|
||||
Validated: 1
|
||||
EndChar
|
||||
|
||||
StartChar: I
|
||||
Encoding: 73 73 5
|
||||
Width: 1024
|
||||
VWidth: 0
|
||||
Flags: HW
|
||||
Flags: W
|
||||
HStem: -154 166<226 365 659 798> 126 26<498.267 525.733> 151 131<288 343 681 735> 313 132<288 343 681 735> 330 32<424.389 599.754> 443 26<498.267 525.733> 584 184<226 365 659 798>
|
||||
VStem: 51 175<12 151 445 584> 365 132<74 126 469 521> 366 31<282 313> 527 132<74 126 469 521> 627 31<282 313> 798 175<12 151 445 584>
|
||||
LayerCount: 3
|
||||
Fore
|
||||
SplineSet
|
||||
51 -154 m 1
|
||||
51 -154 m 1x8308
|
||||
51 768 l 1
|
||||
973 768 l 1
|
||||
973 -154 l 1
|
||||
51 -154 l 1
|
||||
497 469 m 2
|
||||
51 -154 l 1x8308
|
||||
497 469 m 1x87a8
|
||||
502 469 507 470 512 470 c 0
|
||||
517 470 522 469 527 469 c 1
|
||||
527 521 l 1
|
||||
@@ -498,35 +502,34 @@ SplineSet
|
||||
512 702 l 1
|
||||
414 604 l 1
|
||||
497 521 l 1
|
||||
497 469 l 1
|
||||
497 469 l 2
|
||||
497 469 l 1x87a8
|
||||
653 417 m 1
|
||||
681 445 l 1
|
||||
798 445 l 1
|
||||
798 445 l 1x9328
|
||||
798 584 l 1
|
||||
659 584 l 1
|
||||
659 467 l 1
|
||||
633 440 l 1
|
||||
643 433 649 425 653 417 c 1
|
||||
366 282 m 1
|
||||
366 282 m 1xb348
|
||||
366 313 l 1
|
||||
288 313 l 1
|
||||
206 396 l 1
|
||||
108 298 l 1
|
||||
206 200 l 1
|
||||
288 282 l 1
|
||||
366 282 l 1
|
||||
366 282 l 1xb348
|
||||
343 445 m 1
|
||||
371 417 l 1
|
||||
375 425 381 433 391 440 c 1
|
||||
365 467 l 1
|
||||
365 584 l 1
|
||||
365 584 l 1x9388
|
||||
226 584 l 1
|
||||
226 445 l 1
|
||||
343 445 l 1
|
||||
371 178 m 1
|
||||
343 151 l 1
|
||||
226 151 l 1
|
||||
226 151 l 1xa388
|
||||
226 12 l 1
|
||||
365 12 l 1
|
||||
365 129 l 1
|
||||
@@ -535,7 +538,7 @@ SplineSet
|
||||
818 396 m 1
|
||||
735 313 l 1
|
||||
658 313 l 1
|
||||
658 282 l 1
|
||||
658 282 l 1xb318
|
||||
735 282 l 1
|
||||
818 200 l 1
|
||||
916 298 l 1
|
||||
@@ -543,12 +546,12 @@ SplineSet
|
||||
653 178 m 1
|
||||
649 170 643 162 633 155 c 1
|
||||
659 129 l 1
|
||||
659 12 l 1
|
||||
659 12 l 1xa328
|
||||
798 12 l 1
|
||||
798 151 l 1
|
||||
681 151 l 1
|
||||
653 178 l 1
|
||||
527 126 m 1
|
||||
527 126 m 1xc3a8
|
||||
522 126 517 126 512 126 c 0
|
||||
507 126 502 126 497 126 c 1
|
||||
497 74 l 1
|
||||
@@ -556,8 +559,7 @@ SplineSet
|
||||
512 -106 l 1
|
||||
610 -8 l 1
|
||||
527 74 l 1
|
||||
527 126 l 1
|
||||
527 126 l 1
|
||||
527 126 l 1xc3a8
|
||||
610 348 m 0
|
||||
584 337 549 330 512 330 c 0
|
||||
475 330 441 337 414 348 c 0
|
||||
@@ -567,9 +569,9 @@ SplineSet
|
||||
449 157 479 152 512 152 c 0
|
||||
545 152 575 157 598 167 c 0
|
||||
616 174 627 184 627 192 c 2
|
||||
627 356 l 1
|
||||
627 356 l 1xcb58
|
||||
622 353 616 351 610 348 c 0
|
||||
512 443 m 1
|
||||
512 443 m 1x8f58
|
||||
479 443 449 438 426 428 c 0
|
||||
408 421 397 410 397 402 c 0
|
||||
397 394 408 384 426 377 c 0
|
||||
@@ -577,8 +579,7 @@ SplineSet
|
||||
545 362 575 367 598 377 c 0
|
||||
616 384 627 394 627 402 c 0
|
||||
627 410 616 421 598 428 c 0
|
||||
575 438 545 443 512 443 c 1
|
||||
512 443 l 1
|
||||
575 438 545 443 512 443 c 1x8f58
|
||||
EndSplineSet
|
||||
Validated: 5
|
||||
EndChar
|
||||
@@ -587,7 +588,8 @@ StartChar: four
|
||||
Encoding: 52 52 6
|
||||
Width: 1024
|
||||
VWidth: 0
|
||||
Flags: H
|
||||
HStem: -2 41<389.544 635.489> 292 109<316 441 550 675> 639 41<389.544 636.396>
|
||||
VStem: 117 41<241.556 436.857> 441 109<167 292 401 525> 868 41<249.643 435.223>
|
||||
LayerCount: 3
|
||||
Fore
|
||||
SplineSet
|
||||
@@ -607,7 +609,6 @@ SplineSet
|
||||
909 288 892 238 861 189 c 0
|
||||
835 149 801 111 757 77 c 1
|
||||
801 -67 l 1
|
||||
801 -67 l 1
|
||||
649 63 m 1
|
||||
733 17 l 1
|
||||
710 93 l 1
|
||||
@@ -618,7 +619,6 @@ SplineSet
|
||||
158 174 317 39 513 39 c 0
|
||||
552 39 592 49 634 59 c 2
|
||||
649 63 l 1
|
||||
649 63 l 1
|
||||
675 306 m 1
|
||||
675 298 667 292 657 292 c 2
|
||||
550 292 l 1
|
||||
@@ -640,7 +640,6 @@ SplineSet
|
||||
657 401 l 2
|
||||
667 401 675 394 675 386 c 2
|
||||
675 306 l 1
|
||||
675 306 l 1
|
||||
EndSplineSet
|
||||
Validated: 5
|
||||
EndChar
|
||||
@@ -649,15 +648,17 @@ StartChar: D
|
||||
Encoding: 68 68 7
|
||||
Width: 1080
|
||||
VWidth: 0
|
||||
Flags: HW
|
||||
Flags: W
|
||||
HStem: 198 131<79.9424 131.341> 306 98<835.102 921.871> 353 100<731.982 810.871>
|
||||
VStem: 238 219<460.138 583.719> 292 88<67.6511 114.863> 369 100<-72.2559 6.9753> 667 66<3.74069 76.8123>
|
||||
LayerCount: 3
|
||||
Fore
|
||||
SplineSet
|
||||
469 -14 m 4
|
||||
469 -14 m 4x26
|
||||
469 -45 459 -89 425 -89 c 4
|
||||
397 -89 372 -53 369 -41 c 4
|
||||
397 -89 372 -53 369 -41 c 4x26
|
||||
365 -27 363 7 363 7 c 5
|
||||
363 7 292 63 292 88 c 4
|
||||
363 7 292 63 292 88 c 4x2a
|
||||
292 113 317 164 366 202 c 4
|
||||
388 219 439 225 439 225 c 5
|
||||
439 225 414 269 373 269 c 4
|
||||
@@ -669,7 +670,7 @@ SplineSet
|
||||
119 206 108 198 97 198 c 4
|
||||
78 198 76 234 76 261 c 4
|
||||
76 268 76 274 76 279 c 4
|
||||
76 288 76 295 76 295 c 6
|
||||
76 295 l 6
|
||||
76 295 82 320 96 328 c 4
|
||||
97 328 98 329 99 329 c 4
|
||||
121 329 217 268 217 268 c 6
|
||||
@@ -696,12 +697,12 @@ SplineSet
|
||||
570 401 569 394 569 388 c 4
|
||||
569 355 614 330 614 330 c 5
|
||||
614 330 646 364 662 384 c 4
|
||||
678 404 734 453 763 453 c 4
|
||||
678 404 734 453 763 453 c 4xb2
|
||||
790 453 841 404 867 404 c 4
|
||||
878 404 902 413 924 413 c 4
|
||||
938 413 945 409 945 396 c 4
|
||||
945 371 922 318 905 307 c 4
|
||||
903 306 902 306 899 306 c 4
|
||||
903 306 902 306 899 306 c 4x52
|
||||
872 306 795 353 761 353 c 4
|
||||
758 353 756 353 754 352 c 4
|
||||
723 342 708 263 682 213 c 5
|
||||
@@ -714,11 +715,232 @@ SplineSet
|
||||
667 64 605 129 586 138 c 5
|
||||
565 130 522 118 499 118 c 4
|
||||
475 118 470 137 453 137 c 4
|
||||
437 137 380 108 380 93 c 4
|
||||
437 137 380 108 380 93 c 4x2a
|
||||
380 77 446 36 460 23 c 4
|
||||
465 18 469 3 469 -14 c 4
|
||||
465 18 469 3 469 -14 c 4x26
|
||||
EndSplineSet
|
||||
Validated: 1
|
||||
EndChar
|
||||
|
||||
StartChar: E
|
||||
Encoding: 69 69 8
|
||||
Width: 1024
|
||||
VWidth: 0
|
||||
HStem: -65 248<767.992 903.557> 62.9598 56.9516<226.56 349.224 705.189 710> 143 26.9554<256.859 324.845> 192.776 27.2245<289.957 314.952> 295.071 27.9295<269.575 354.789> 344 66<376.038 439.962> 433 25<407.015 431.988> 469 248<767.951 904.873>
|
||||
VStem: 66.5097 115.469<193.203 298.14> 204 32.9583<187.285 267.499> 257.966 57.0345<194.835 224.243> 335.831 30.8205<177.895 239.59> 388.713 44.4108<157.773 270.37> 407 25<433.012 457.985> 442 55.4336<365.791 447.874> 711 249<-8.06335 62 583 662.557>
|
||||
LayerCount: 3
|
||||
Fore
|
||||
SplineSet
|
||||
279 573 m 0x7ffb
|
||||
292 573 313 570 343 563 c 0
|
||||
462 535 489 420 496 389 c 0
|
||||
496.963 384.737 497.434 380.795 497.434 377.167 c 0
|
||||
497.434 354.414 478.912 344 447 344 c 0
|
||||
422.333 344 394.111 344.889 359.074 344.889 c 0
|
||||
341.556 344.889 322.333 344.667 301 344 c 0
|
||||
237 342 192 300 183 241 c 0
|
||||
182.31 236.477 181.979 231.948 181.979 227.44 c 0
|
||||
181.979 173.142 230.063 121.847 279 120 c 0
|
||||
280.57 119.941 282.134 119.911 283.69 119.911 c 0
|
||||
334.636 119.911 377.296 151.452 387 199 c 0
|
||||
388.159 204.678 388.713 210.303 388.713 215.813 c 0
|
||||
388.713 257.852 356.437 293.232 314 295 c 0
|
||||
312.865 295.047 311.742 295.071 310.63 295.071 c 0
|
||||
264.267 295.071 236.958 255.02 236.958 229.128 c 0
|
||||
236.958 206.914 253.918 170.972 286 170 c 0
|
||||
287.107 169.97 288.186 169.955 289.238 169.955 c 0
|
||||
323.358 169.955 329.15 185.419 334 199 c 0
|
||||
335.206 202.378 335.831 206.221 335.831 210.207 c 0
|
||||
335.831 222.743 329.657 236.689 316 242 c 0
|
||||
298 249 291 238 290 234 c 0
|
||||
289 228 294 220 299 220 c 0
|
||||
306 220 316 220 315 206 c 0
|
||||
314.143 195.714 302.265 192.776 292.592 192.776 c 0
|
||||
290.98 192.776 289.429 192.857 288 193 c 0
|
||||
278.231 193.977 257.966 202.588 257.966 228.155 c 0
|
||||
257.966 248.46 275.443 271.256 306.289 271.256 c 0
|
||||
308.143 271.256 310.047 271.172 312 271 c 0
|
||||
351.28 267.429 366.651 246.325 366.651 219.783 c 0
|
||||
366.651 216.594 366.429 213.326 366 210 c 0
|
||||
362 179 343 143 284 143 c 0
|
||||
225 143 204 192 204 229 c 0
|
||||
204 266 232 322 318 323 c 0
|
||||
318.502 323.006 319.002 323.009 319.501 323.009 c 0
|
||||
394.354 323.009 433.124 257.543 433.124 205.254 c 0
|
||||
433.124 198.198 432.418 191.383 431 185 c 0
|
||||
420.148 134.686 398.59 62.9598 273.187 62.9598 c 0
|
||||
271.478 62.9598 269.749 62.9731 268 63 c 0
|
||||
134.539 65.8599 66.5097 196.855 66.5097 295.718 c 0
|
||||
66.5097 300.562 66.673 305.329 67 310 c 0
|
||||
74 410 112 447 146 480 c 0
|
||||
204.987 537.114 264.85 531.109 264.85 543.245 c 0
|
||||
264.85 544.071 264.573 544.981 264 546 c 0
|
||||
260.913 551.732 257.827 557.659 257.827 562.495 c 0
|
||||
257.827 568.625 262.788 573 279 573 c 0x7ffb
|
||||
408 478 m 0
|
||||
389 478 374 463 374 444 c 0
|
||||
374 425 389 410 408 410 c 0
|
||||
427 410 442 425 442 444 c 0
|
||||
442 463 427 478 408 478 c 0
|
||||
835 717 m 0
|
||||
904 717 960 662 960 593 c 0
|
||||
960 524 904 469 835 469 c 0
|
||||
798 469 751 495 731 526 c 1
|
||||
515 456 l 1
|
||||
503 483 491 498 481 511 c 1
|
||||
712 583 l 2
|
||||
712 586 711 590 711 593 c 0
|
||||
711 662 766 717 835 717 c 0
|
||||
462 207 m 1
|
||||
727 121 l 1
|
||||
747 155 795 183 834 183 c 0
|
||||
903 183 958 128 958 59 c 0
|
||||
958 -10 903 -65 834 -65 c 0x9ff3
|
||||
765 -65 710 -10 710 59 c 0
|
||||
710 60 710 61 710 62 c 2
|
||||
456 143 l 1
|
||||
458.679 159.077 462.157 171.165 462.157 197.076 c 0
|
||||
462.157 200.176 462.107 203.474 462 207 c 1
|
||||
407 445 m 0x1ff7
|
||||
407 453 412 458 420 458 c 0
|
||||
428 458 432 453 432 445 c 0
|
||||
432 437 428 433 420 433 c 0
|
||||
412 433 407 437 407 445 c 0x1ff7
|
||||
EndSplineSet
|
||||
Validated: 1
|
||||
EndChar
|
||||
|
||||
StartChar: F
|
||||
Encoding: 70 70 9
|
||||
Width: 1024
|
||||
VWidth: 0
|
||||
HStem: -36 87<438.038 591.036> 87 39<470.027 570.5> 163 41<517.914 555.989> 241 41<519.089 577.135> 317 43<482.813 612.995> 394 100<654.807 741.193> 528 39<697.004 734.996>
|
||||
VStem: 177 177<195.689 296.938> 387 50<156.758 272.234> 469 87<171.273 203.985> 588 47<139.283 231.811> 669 67<122.953 266.087> 697 38<528.004 566.995> 750 85<426.5 559.5>
|
||||
LayerCount: 3
|
||||
Fore
|
||||
SplineSet
|
||||
502 742 m 0xfff4
|
||||
522 742 553 738 599 727 c 0
|
||||
780 684 822 509 833 462 c 0
|
||||
835 456 835 450 835 444 c 0
|
||||
835 409 806 392 758 392 c 0
|
||||
721 392 678 394 624 394 c 0
|
||||
597 394 568 393 535 392 c 0
|
||||
437 388 368 325 355 235 c 0
|
||||
354 228 354 222 354 215 c 0
|
||||
354 132 426 55 501 51 c 0
|
||||
504 51 507 51 510 51 c 0
|
||||
587 51 652 99 666 171 c 0
|
||||
668 180 669 188 669 196 c 0
|
||||
669 260 620 313 555 317 c 0
|
||||
553 317 550 317 548 317 c 0
|
||||
478 317 437 256 437 218 c 0
|
||||
437 184 463 127 512 126 c 0
|
||||
514 126 516 126 518 126 c 0
|
||||
569 126 578 150 586 171 c 0
|
||||
588 176 588 181 588 187 c 0
|
||||
588 207 579 228 557 237 c 0
|
||||
550 240 545 241 540 241 c 0
|
||||
525 241 520 230 518 225 c 0
|
||||
516 216 524 204 532 204 c 0
|
||||
543 204 556 202 556 183 c 0
|
||||
556 166 541 163 525 163 c 0
|
||||
522 163 518 163 515 163 c 0
|
||||
499 164 469 177 469 217 c 0
|
||||
469 247 497 282 543 282 c 0
|
||||
546 282 549 282 552 282 c 0
|
||||
611 277 635 243 635 202 c 0
|
||||
635 197 635 193 634 188 c 0
|
||||
628 141 598 87 508 87 c 0
|
||||
418 87 387 162 387 218 c 0
|
||||
387 274 430 358 561 360 c 0
|
||||
562 360 563 360 564 360 c 0
|
||||
677 360 736 262 736 182 c 0
|
||||
736 171 735 160 733 150 c 0
|
||||
716 74 683 -36 494 -36 c 0
|
||||
491 -36 487 -36 484 -36 c 0
|
||||
280 -32 177 170 177 321 c 0
|
||||
177 328 178 335 178 342 c 0
|
||||
188 495 246 549 298 600 c 0
|
||||
388 687 479 677 479 696 c 0
|
||||
479 697 479 698 478 700 c 0
|
||||
473 709 469 718 469 725 c 0
|
||||
469 735 477 742 502 742 c 0xfff4
|
||||
698 597 m 0
|
||||
669 597 646 574 646 545 c 0
|
||||
646 516 669 494 698 494 c 0
|
||||
727 494 750 516 750 545 c 0
|
||||
750 574 727 597 698 597 c 0
|
||||
697 547 m 0xffec
|
||||
697 560 703 567 716 567 c 0
|
||||
729 567 735 560 735 547 c 0
|
||||
735 534 729 528 716 528 c 0
|
||||
703 528 697 534 697 547 c 0xffec
|
||||
EndSplineSet
|
||||
Validated: 1
|
||||
EndChar
|
||||
|
||||
StartChar: O
|
||||
Encoding: 79 79 10
|
||||
Width: 1024
|
||||
VWidth: 0
|
||||
HStem: 1.59961 8<801.97 829.007> 20 35.2002<801.975 810.774 823.574 824.837> 33.5996 5.60059<810.774 817.806> 48.7998 6.40039<810.774 818.736> 61.5996 8.7998<801.043 829.91> 434.399 245.601<441.712 567.228>
|
||||
VStem: 780.375 8.7998<21.8033 49.6942> 801.975 8.7998<20 33.5996 39.2002 48.7998> 819.574 8.80078<40.0451 47.9513> 841.175 8.7998<21.5374 50.3356>
|
||||
LayerCount: 3
|
||||
Fore
|
||||
SplineSet
|
||||
504.375 680 m 0x8fc0
|
||||
672.375 680 816.375 560 846.774 395.2 c 0
|
||||
877.175 230.399 785.975 67.2002 629.175 7.2002 c 0
|
||||
621.975 4.7998 613.975 8 611.574 15.2002 c 2
|
||||
533.175 218.399 l 2
|
||||
530.774 225.6 533.975 234.399 541.175 236.8 c 0
|
||||
587.574 254.399 613.975 301.6 605.175 350.399 c 0
|
||||
596.375 399.2 553.975 434.399 504.375 434.399 c 0
|
||||
454.774 434.399 413.175 399.2 404.375 350.399 c 0
|
||||
395.574 301.6 421.175 254.399 467.574 236.8 c 0
|
||||
474.774 234.399 477.975 225.6 475.574 218.399 c 2
|
||||
397.975 15.2002 l 2
|
||||
395.574 8 386.774 4.7998 379.574 7.2002 c 0
|
||||
222.774 67.2002 131.574 230.399 161.975 395.2 c 0
|
||||
192.375 560 336.375 680 504.375 680 c 0x8fc0
|
||||
815.574 70.3994 m 0
|
||||
834.774 70.3994 849.975 55.2002 849.975 36 c 0
|
||||
849.975 16.7998 834.774 1.59961 815.574 1.59961 c 0
|
||||
796.375 1.59961 780.375 16.7998 780.375 36 c 0
|
||||
780.375 55.2002 796.375 70.3994 815.574 70.3994 c 0
|
||||
815.574 61.5996 m 0
|
||||
801.175 61.5996 789.175 50.3994 789.175 36 c 0
|
||||
789.175 21.5996 801.175 9.59961 815.574 9.59961 c 0
|
||||
829.975 9.59961 841.175 21.5996 841.175 36 c 0
|
||||
841.175 50.3994 829.975 61.5996 815.574 61.5996 c 0
|
||||
814.774 39.2002 m 2xbfc0
|
||||
816.375 39.2002 817.975 40 818.774 40.7998 c 0
|
||||
819.574 41.5996 819.574 42.3994 819.574 44 c 0
|
||||
819.574 45.5996 819.574 46.3994 818.774 47.2002 c 0
|
||||
817.975 48 816.375 48.7998 814.774 48.7998 c 2
|
||||
810.774 48.7998 l 1
|
||||
810.774 39.2002 l 1
|
||||
814.774 39.2002 l 2xbfc0
|
||||
810.774 33.5996 m 1
|
||||
810.774 20 l 1
|
||||
801.975 20 l 1
|
||||
801.975 55.2002 l 1xcfc0
|
||||
815.574 55.2002 l 2x9fc0
|
||||
820.375 55.2002 822.774 54.3994 825.175 52.7998 c 0
|
||||
827.574 51.2002 828.375 48.7998 828.375 45.5996 c 0
|
||||
828.375 43.2002 827.574 41.5996 826.774 40 c 0
|
||||
825.975 38.3994 824.375 36.7998 821.975 36 c 1
|
||||
823.574 36 824.375 35.2002 825.175 34.3994 c 0
|
||||
825.975 33.5996 827.574 32 828.375 29.5996 c 2
|
||||
833.175 20 l 1
|
||||
823.574 20 l 1xcfc0
|
||||
819.574 28.7998 l 2
|
||||
818.774 30.3994 817.175 31.2002 816.375 32 c 0
|
||||
815.574 32.7998 814.774 33.5996 813.175 33.5996 c 2
|
||||
810.774 33.5996 l 1
|
||||
EndSplineSet
|
||||
Validated: 524321
|
||||
EndChar
|
||||
EndChars
|
||||
EndSplineFont
|
||||
|
||||
Binary file not shown.
@@ -1,8 +1,8 @@
|
||||
@font-face {
|
||||
font-family: 'CombodoRegular';
|
||||
src: url('combodo-webfont.woff2?v=1.0') format('woff2'),
|
||||
url('combodo-webfont.woff?v=1.0') format('woff'),
|
||||
url('combodo-webfont.ttf?v=1.0') format('truetype');
|
||||
src: url('combodo-webfont.woff2?v=2.0') format('woff2'),
|
||||
url('combodo-webfont.woff?v=2.0') format('woff'),
|
||||
url('combodo-webfont.ttf?v=2.0') format('truetype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
|
||||
@@ -193,7 +193,16 @@
|
||||
.fc-combodo-icon:before {
|
||||
content: "D";
|
||||
}
|
||||
.fc-itophub-icon:before {
|
||||
content: "E";
|
||||
}
|
||||
.fc-chameleon-icon:before {
|
||||
content: "F";
|
||||
}
|
||||
.fc-itop-icon:before {
|
||||
content: "I";
|
||||
}
|
||||
.fc-opensource-icon:before {
|
||||
content: "O";
|
||||
}
|
||||
|
||||
|
||||
50
css/font-combodo/glyphs/E.svg
Executable file
50
css/font-combodo/glyphs/E.svg
Executable file
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1000"
|
||||
height="1000"
|
||||
viewBox="0 0 264.58333 264.58334"
|
||||
version="1.1"
|
||||
id="svg909">
|
||||
<defs
|
||||
id="defs903" />
|
||||
<metadata
|
||||
id="metadata906">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
style="fill:#000000;fill-opacity:1"
|
||||
transform="matrix(2.6189878,0,0,2.6189878,-138.11438,-66.566639)"
|
||||
id="layer1">
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 77.776825,48.628779 c -3.105953,0.0165 -2.465143,1.56905 -1.672249,3.00189 0.969595,1.75214 -6.046384,0.53457 -12.856064,7.17372 -3.69523,3.60269 -7.8302,7.46233 -8.56485,18.39112 -0.73465,10.9288 6.57401,26.601051 21.834325,26.890351 14.098468,0.26727 16.523001,-7.762201 17.760671,-13.268961 1.323287,-5.88768 -2.939907,-15.10068 -12.294338,-14.96704 -9.354428,0.13364 -12.428178,6.14692 -12.428178,10.15597 0,4.00904 2.27182,9.35447 8.686288,9.35447 6.414468,0 8.552552,-3.87524 8.953457,-7.2161 0.400905,-3.34087 -1.12866,-6.23284 -5.863206,-6.68176 -3.664545,-0.34747 -5.823431,2.30622 -5.896281,4.44314 -0.100228,2.93996 2.194808,3.98392 3.307291,4.04264 1.112483,0.0587 2.766575,-0.003 2.856155,-1.33635 0.10023,-1.49182 -0.935023,-1.63676 -1.686717,-1.62005 -0.534409,0.0119 -1.147985,-0.88438 -0.985986,-1.50379 0.121989,-0.46643 0.78549,-1.62036 2.790009,-0.81855 2.004523,0.8018 2.544982,3.13823 2.021065,4.67723 -0.534538,1.57021 -1.18617,3.2909 -5.228621,3.17397 -3.536694,-0.1023 -5.411874,-4.25964 -5.328354,-6.68177 0.0939,-2.724 3.190312,-7.1826 8.402068,-6.91534 5.211754,0.26728 8.953352,5.07775 7.884274,10.42314 -1.069079,5.34539 -6.013196,8.82024 -11.759488,8.55297 -5.746294,-0.26727 -11.359094,-6.68189 -10.423654,-13.09636 0.93544,-6.41447 5.87967,-10.95789 12.828675,-11.22516 6.949005,-0.26727 11.885592,0.016 15.894636,0.016 4.009047,0 6.155487,-1.61955 5.353677,-4.96042 -0.8018,-3.34086 -3.786135,-15.85543 -16.689419,-18.91977 -3.29736,-0.78307 -5.483391,-1.09268 -6.895186,-1.0852 z m 13.978992,10.35854 a 3.685268,3.685268 0 0 1 3.685563,3.68505 3.685268,3.685268 0 0 1 -3.685563,3.68556 3.685268,3.685268 0 0 1 -3.685048,-3.68556 3.685268,3.685268 0 0 1 3.685048,-3.68505 z"
|
||||
id="path882" />
|
||||
<path
|
||||
style="opacity:1;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 138.24445,32.953249 a 13.49711,13.49711 0 0 0 -13.49685,13.49685 13.49711,13.49711 0 0 0 0.0465,1.06195 l -25.043431,7.84087 c 1.104251,1.4075 2.257151,3.10658 3.611151,6.01255 l 23.55566,-7.5799 a 13.49711,13.49711 0 0 0 11.32696,6.1619 13.49711,13.49711 0 0 0 13.49737,-13.49737 13.49711,13.49711 0 0 0 -13.49737,-13.49685 z"
|
||||
id="rect827" />
|
||||
<path
|
||||
style="opacity:1;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 97.624193,88.491979 c 0.07421,3.57897 -0.303877,5.04652 -0.587044,6.95772 l 27.570411,8.739001 a 13.49711,13.49711 0 0 0 -0.0134,0.37258 13.49711,13.49711 0 0 0 13.49737,13.49737 13.49711,13.49711 0 0 0 13.49685,-13.49737 13.49711,13.49711 0 0 0 -13.49685,-13.496851 13.49711,13.49711 0 0 0 -11.65201,6.71794 z"
|
||||
id="rect827-1" />
|
||||
<circle
|
||||
style="opacity:1;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path888"
|
||||
cx="93.031555"
|
||||
cy="62.530666"
|
||||
r="1.3701637" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
41
css/font-combodo/glyphs/F.svg
Executable file
41
css/font-combodo/glyphs/F.svg
Executable file
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1000"
|
||||
height="1000"
|
||||
viewBox="0 0 264.58333 264.58334"
|
||||
version="1.1"
|
||||
id="svg909">
|
||||
<defs
|
||||
id="defs903" />
|
||||
<metadata
|
||||
id="metadata906">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
transform="translate(0,-32.41665)"
|
||||
id="layer1">
|
||||
<path
|
||||
style="fill:#1b1b1b;fill-opacity:1;stroke:#000000;stroke-width:1.71210456;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 128.74663,48.188011 c -13.29429,0.07062 -10.55146,6.715944 -7.15766,12.848874 4.15012,7.499617 -25.880108,2.288099 -55.027316,30.705397 -15.81655,15.420458 -33.515303,31.940728 -36.659797,78.718798 -3.144494,46.77813 28.138482,113.85946 93.456613,115.09774 60.34513,1.14398 70.72277,-33.22425 76.02032,-56.79463 5.66401,-25.20081 -12.58357,-64.63485 -52.62298,-64.06284 -40.0394,0.57201 -53.195848,26.31042 -53.195848,43.47021 0,17.15974 9.723978,40.03957 37.179578,40.03957 27.4556,0 36.60716,-16.58704 38.32314,-30.88679 1.71598,-14.2998 -4.83096,-26.67818 -25.09606,-28.59968 -15.68521,-1.48726 -24.9258,9.87122 -25.23762,19.0178 -0.429,12.5838 9.39435,17.05222 14.15607,17.30356 4.76172,0.25125 11.84166,-0.0128 12.22509,-5.71993 0.42901,-6.38538 -4.00214,-7.00576 -7.21959,-6.93424 -2.28741,0.0509 -4.91368,-3.78538 -4.22028,-6.43661 0.52215,-1.99644 3.3621,-6.93557 11.94197,-3.50361 8.57988,3.43191 10.89319,13.43244 8.65069,20.01977 -2.28797,6.72091 -5.07712,14.08591 -22.37987,13.58542 -15.13797,-0.43787 -23.16423,-18.23237 -22.80675,-28.59972 0.40192,-11.65944 13.65537,-30.74341 35.96305,-29.59947 22.30767,1.14403 38.32269,21.7341 33.74675,44.61377 -4.57593,22.87966 -25.73805,37.75293 -50.33368,36.60894 -24.59564,-1.14398 -48.619888,-28.60023 -44.615958,-56.05584 4.00393,-27.45561 25.166518,-46.90263 54.910078,-48.04662 29.74356,-1.14398 50.87344,0.0685 68.0332,0.0685 17.15977,0 26.34709,-6.93209 22.91514,-21.23189 -3.43192,-14.29975 -16.20565,-67.865386 -71.43508,-80.981563 -14.11356,-3.351744 -23.47035,-4.676956 -29.5132,-4.644939 z m 59.83374,44.337259 a 15.77391,15.77391 0 0 1 15.77517,15.77298 15.77391,15.77391 0 0 1 -15.77517,15.77516 15.77391,15.77391 0 0 1 -15.77297,-15.77516 15.77391,15.77391 0 0 1 15.77297,-15.77298 z"
|
||||
id="path882" />
|
||||
<circle
|
||||
style="opacity:1;fill:#101010;fill-opacity:1;stroke:#000000;stroke-width:1.71210456;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path888"
|
||||
cx="194.04086"
|
||||
cy="107.69173"
|
||||
r="5.8646588" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
87
css/font-combodo/glyphs/O.svg
Executable file
87
css/font-combodo/glyphs/O.svg
Executable file
@@ -0,0 +1,87 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="1000"
|
||||
height="1000"
|
||||
viewBox="0 0 264.58333 264.58334"
|
||||
version="1.1"
|
||||
id="svg876"
|
||||
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
|
||||
sodipodi:docname="O.svg">
|
||||
<defs
|
||||
id="defs870" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.35"
|
||||
inkscape:cx="-272.85714"
|
||||
inkscape:cy="560"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
units="px"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1005"
|
||||
inkscape:window-x="1911"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata873">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Calque 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-32.41664)">
|
||||
<path
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:middle;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:8.99672318;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 131.53572,50.179132 c -54.174274,0 -100.702568,38.6765 -110.575582,91.938598 -9.873159,53.26282 19.698102,106.06258 70.277674,125.4761 2.319548,0.89024 4.921621,-0.26836 5.811987,-2.58786 L 122.30183,199.2106 c 0.89023,-2.31957 -0.26838,-4.92164 -2.58789,-5.812 -14.87072,-5.70891 -23.515673,-21.14784 -20.612431,-36.80689 2.903191,-15.6588 16.504681,-26.96534 32.434211,-26.96534 15.92952,0 29.53338,11.30654 32.43657,26.96534 2.90326,15.65905 -5.74171,31.09798 -20.61242,36.80689 -2.31951,0.89036 -3.47812,3.49245 -2.58788,5.812 l 25.25202,65.79537 c 0.89038,2.31821 3.49074,3.47652 5.8096,2.58786 50.57958,-19.41352 80.15083,-72.21328 70.27768,-125.4761 -9.87301,-53.262098 -56.4013,-91.938598 -110.57557,-91.938598 z"
|
||||
id="path3773"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ssccscsssccccss" />
|
||||
<g
|
||||
style="display:inline"
|
||||
id="g3797"
|
||||
transform="matrix(1.2200654,0,0,1.2200654,455.86197,1099.5367)"
|
||||
inkscape:export-filename="/home/rafael/workspace/logo-osi/3/png/logo396x412.png"
|
||||
inkscape:export-xdpi="48.18"
|
||||
inkscape:export-ydpi="48.18">
|
||||
<path
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.25831962;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m -183.5617,-698.57555 c -5.02556,0 -9.12392,4.09113 -9.12392,9.10782 0,5.01668 4.09836,9.10781 9.12392,9.10781 5.02556,0 9.12392,-4.09113 9.12392,-9.10781 0,-5.01669 -4.09836,-9.10782 -9.12392,-9.10782 z m 0,2.25596 c 3.80399,0 6.8633,3.05458 6.8633,6.85186 0,3.79727 -3.05931,6.85119 -6.8633,6.85119 -3.80399,0 -6.86397,-3.05392 -6.86397,-6.85119 0,-3.79728 3.05998,-6.85186 6.86397,-6.85186 z"
|
||||
id="path3015"
|
||||
inkscape:connector-curvature="0" />
|
||||
<g
|
||||
aria-label="R"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:OpenSymbol;-inkscape-font-specification:'OpenSymbol Bold';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
|
||||
id="text3793">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
d="m -183.7552,-690.46513 q 0.74769,0 1.06902,-0.27807 0.3275,-0.27807 0.3275,-0.91454 0,-0.63028 -0.3275,-0.90217 -0.32133,-0.27189 -1.06902,-0.27189 h -1.00105 v 2.36667 z m -1.00105,1.64369 v 3.4913 h -2.37902 v -9.22569 h 3.63342 q 1.82289,0 2.66946,0.61175 0.85274,0.61175 0.85274,1.93412 0,0.91454 -0.44491,1.50157 -0.43873,0.58703 -1.32855,0.8651 0.48817,0.11123 0.87128,0.50671 0.3893,0.38929 0.78477,1.18642 l 1.29147,2.62002 h -2.53351 l -1.12463,-2.29252 q -0.33986,-0.69208 -0.69208,-0.94543 -0.34604,-0.25335 -0.92689,-0.25335 z"
|
||||
style="font-size:12.65519524px;line-height:1.25"
|
||||
id="path863" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.6 KiB |
@@ -18,6 +18,9 @@ aIcons = {
|
||||
'combodo-icon': 'Combodo icon',
|
||||
'combodo-icon-o': 'Combodo icon (outline)',
|
||||
'itop-icon': 'iTop icon',
|
||||
'itophub-icon': 'iTop Hub icon',
|
||||
'chameleon-icon': 'Hub\'s Chameleon icon',
|
||||
'opensource-icon': 'Open Source Logo',
|
||||
}
|
||||
|
||||
function GenerateTable() {
|
||||
|
||||
BIN
css/font-open-sans/OpenSans-Bold-cyrillic-ext.woff2
Normal file
BIN
css/font-open-sans/OpenSans-Bold-cyrillic-ext.woff2
Normal file
Binary file not shown.
BIN
css/font-open-sans/OpenSans-Bold-cyrillic.woff2
Normal file
BIN
css/font-open-sans/OpenSans-Bold-cyrillic.woff2
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user