mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-16 17:04:11 +01:00
Compare commits
895 Commits
support/2.
...
2.0.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f844cef03e | ||
|
|
4c0e176f3e | ||
|
|
f0f519e351 | ||
|
|
9a68890660 | ||
|
|
806ed2102c | ||
|
|
14b9778c50 | ||
|
|
0142910c26 | ||
|
|
0068d7c8be | ||
|
|
302811986e | ||
|
|
f773433a93 | ||
|
|
880653f191 | ||
|
|
02ee41ef09 | ||
|
|
d6344568c7 | ||
|
|
38a2fd75c0 | ||
|
|
7ca8c3e834 | ||
|
|
7d3a93c374 | ||
|
|
a978bb9c17 | ||
|
|
3ed01cde51 | ||
|
|
67471e2636 | ||
|
|
4c44159a88 | ||
|
|
5f5125a131 | ||
|
|
f6d0bda737 | ||
|
|
a64b299644 | ||
|
|
e022bf03db | ||
|
|
bab6ca7954 | ||
|
|
bb70884041 | ||
|
|
58cb67ecd3 | ||
|
|
4920cc4aee | ||
|
|
12c587896e | ||
|
|
eb74288c85 | ||
|
|
922049e8f2 | ||
|
|
d83edb803b | ||
|
|
9f41c7e577 | ||
|
|
1335a15c30 | ||
|
|
d847272264 | ||
|
|
8054d7b17a | ||
|
|
1ba2ed809c | ||
|
|
b2feca5eb5 | ||
|
|
87ce86880e | ||
|
|
d4de0d2ee3 | ||
|
|
730f522ab2 | ||
|
|
008cac25b4 | ||
|
|
ede552968b | ||
|
|
686848a0ae | ||
|
|
61b5b5cc71 | ||
|
|
ec16c5f86f | ||
|
|
d602bb2190 | ||
|
|
1b9d3d3304 | ||
|
|
02e5d57998 | ||
|
|
6236d96a65 | ||
|
|
6473a8f245 | ||
|
|
ab93eaefff | ||
|
|
037803c033 | ||
|
|
06582cfe35 | ||
|
|
a72e15023f | ||
|
|
7ddd74a95e | ||
|
|
4fccf5c815 | ||
|
|
a6d984e23f | ||
|
|
7bf3f97a72 | ||
|
|
e78743d309 | ||
|
|
6b86ac5090 | ||
|
|
14c78cb543 | ||
|
|
5007e6ffc3 | ||
|
|
1d4784b0c7 | ||
|
|
d497020733 | ||
|
|
98afb2dd49 | ||
|
|
f89792e171 | ||
|
|
8141f8c619 | ||
|
|
2009a10204 | ||
|
|
d2961c585e | ||
|
|
9b5fc043cb | ||
|
|
67ef671632 | ||
|
|
d07ca49e53 | ||
|
|
44aff84dfa | ||
|
|
c9ffdb9342 | ||
|
|
65343a485f | ||
|
|
74cda2831f | ||
|
|
c4632cda1a | ||
|
|
d7ba4166e5 | ||
|
|
ee3795ef6d | ||
|
|
e8a7695353 | ||
|
|
e4bb0acd94 | ||
|
|
1572d9da5a | ||
|
|
2d0ca37f27 | ||
|
|
df09182878 | ||
|
|
20bb9a62bb | ||
|
|
a6f5436d07 | ||
|
|
53e58d5887 | ||
|
|
ef1178c78d | ||
|
|
5456d9d20a | ||
|
|
7c8ff071d2 | ||
|
|
e4010b4b13 | ||
|
|
dbb9558b45 | ||
|
|
8f83970239 | ||
|
|
102b6c248b | ||
|
|
946b4212d3 | ||
|
|
e2e6861b03 | ||
|
|
7b7e69a890 | ||
|
|
92b2131d3b | ||
|
|
43130aef71 | ||
|
|
ba4e5ec786 | ||
|
|
3e79dad435 | ||
|
|
b0a84f96f1 | ||
|
|
85fa578f2a | ||
|
|
364bce90e4 | ||
|
|
fcfc1e7307 | ||
|
|
85cb04a3f3 | ||
|
|
ec77e58276 | ||
|
|
196fba7d81 | ||
|
|
317344da2e | ||
|
|
cd37a78800 | ||
|
|
9ed5ceb11e | ||
|
|
f47327fdd4 | ||
|
|
5ec37c8060 | ||
|
|
bb65153351 | ||
|
|
389b97dc50 | ||
|
|
31e30810f6 | ||
|
|
4bffe7aec9 | ||
|
|
fafa442b08 | ||
|
|
b095c6b1a3 | ||
|
|
d950422912 | ||
|
|
b504830f45 | ||
|
|
4dabb566a8 | ||
|
|
7459ec4844 | ||
|
|
678f982024 | ||
|
|
d628c4f670 | ||
|
|
e1336d7ebc | ||
|
|
01496f9595 | ||
|
|
a60d60bfab | ||
|
|
31cb0065b5 | ||
|
|
4dbc5d97b8 | ||
|
|
4800da1653 | ||
|
|
8f25fb8e64 | ||
|
|
b43884a760 | ||
|
|
caef02720c | ||
|
|
ce9806b01c | ||
|
|
7a193d1c24 | ||
|
|
31ea53435e | ||
|
|
dcb48d0f35 | ||
|
|
40aa78bb3d | ||
|
|
82e9e42939 | ||
|
|
1ecec1dd6d | ||
|
|
8dc5b05ac4 | ||
|
|
3b257eeb3a | ||
|
|
2574a0c8a4 | ||
|
|
404f6772fd | ||
|
|
d6dbe0fce7 | ||
|
|
8e26ca763b | ||
|
|
b4bc3ad716 | ||
|
|
80fac28106 | ||
|
|
5f11c97aef | ||
|
|
d36a03bfc3 | ||
|
|
8e0c57fce0 | ||
|
|
cb4c2a8e84 | ||
|
|
f993f07751 | ||
|
|
0167a66973 | ||
|
|
b7d8953ecb | ||
|
|
cba75527b3 | ||
|
|
bc0f48721b | ||
|
|
a6693d9535 | ||
|
|
6f2c404415 | ||
|
|
b00aae2536 | ||
|
|
6334370ef0 | ||
|
|
868748efb3 | ||
|
|
c67e7e18e6 | ||
|
|
16f1fd56ec | ||
|
|
1cab84e793 | ||
|
|
991fe9ccc1 | ||
|
|
0a53f8ec3f | ||
|
|
cd7af7a2ce | ||
|
|
319b3b81ce | ||
|
|
4235eae9b3 | ||
|
|
32ec19e09d | ||
|
|
63ea142168 | ||
|
|
23ec4faa65 | ||
|
|
da36fc673e | ||
|
|
3b65f33325 | ||
|
|
e980b051b1 | ||
|
|
c84a22c503 | ||
|
|
5c2578169e | ||
|
|
645731a76d | ||
|
|
3de2d654a0 | ||
|
|
934e500253 | ||
|
|
cfd2a7baff | ||
|
|
1867195c25 | ||
|
|
d4bcb9dff8 | ||
|
|
4172cb2023 | ||
|
|
ebff827013 | ||
|
|
1afcf46970 | ||
|
|
2e37ccc4c2 | ||
|
|
3b188524ca | ||
|
|
bd1096b0fc | ||
|
|
d42443697c | ||
|
|
8509237084 | ||
|
|
f16d1ee1e4 | ||
|
|
5672bee85f | ||
|
|
4d6ddb8586 | ||
|
|
a71b3bc231 | ||
|
|
723d51a871 | ||
|
|
4e1c3f321f | ||
|
|
2b58bca313 | ||
|
|
9b1d383848 | ||
|
|
96c1ec42ed | ||
|
|
7cb2fb9b02 | ||
|
|
6f90d626fc | ||
|
|
62302f9138 | ||
|
|
278cb653db | ||
|
|
9f9baf9caa | ||
|
|
09ebce2587 | ||
|
|
3c8cf0e8fb | ||
|
|
5542cfd79e | ||
|
|
60e7c22ab4 | ||
|
|
92502a7a88 | ||
|
|
47c65b161d | ||
|
|
5f98c0dcab | ||
|
|
9e4b25e833 | ||
|
|
4b095738d5 | ||
|
|
fa615638d9 | ||
|
|
a24b4437aa | ||
|
|
9f95d951d4 | ||
|
|
7400bd7dca | ||
|
|
258b4be167 | ||
|
|
4a849ee4db | ||
|
|
18664c8151 | ||
|
|
85472fe67a | ||
|
|
e1087d3f87 | ||
|
|
e789c6baec | ||
|
|
817cc0476a | ||
|
|
ea5908ac41 | ||
|
|
f5d42b95b8 | ||
|
|
d7093a9a6f | ||
|
|
7636b987b1 | ||
|
|
55f1763b60 | ||
|
|
87e33c72b5 | ||
|
|
99695a0fc1 | ||
|
|
0aa0de9f1c | ||
|
|
ebe89b0af7 | ||
|
|
74f895b5f4 | ||
|
|
9bc5406abb | ||
|
|
dd1cf43d41 | ||
|
|
b62b9caaf2 | ||
|
|
36149df584 | ||
|
|
e48716753d | ||
|
|
8c702a42e9 | ||
|
|
494e559748 | ||
|
|
a1801e53a2 | ||
|
|
f856859f83 | ||
|
|
7ebce0a841 | ||
|
|
3f50d3ea59 | ||
|
|
898c235c0d | ||
|
|
85e261a5fa | ||
|
|
d912e7f4fb | ||
|
|
bc14ad9e80 | ||
|
|
e81d872306 | ||
|
|
f83bb7fa90 | ||
|
|
032947ff03 | ||
|
|
9e39013d4c | ||
|
|
5d02db5440 | ||
|
|
7300698240 | ||
|
|
a47bbb3a9a | ||
|
|
a333bcb084 | ||
|
|
e92d193347 | ||
|
|
d5a0808118 | ||
|
|
864ce74cbc | ||
|
|
f684cb1745 | ||
|
|
54769aa2d1 | ||
|
|
272a249d14 | ||
|
|
02e6658439 | ||
|
|
0c327f2c36 | ||
|
|
dcb5a7208a | ||
|
|
94de069963 | ||
|
|
f0c66be7cd | ||
|
|
0b7ed90e18 | ||
|
|
20ba6242e7 | ||
|
|
015919702b | ||
|
|
ae8ff6b675 | ||
|
|
0ea6657610 | ||
|
|
9d6d93d42f | ||
|
|
4f845ec98d | ||
|
|
f65c84300f | ||
|
|
d8b9679346 | ||
|
|
e090b866e1 | ||
|
|
d30f34afc1 | ||
|
|
3b7aa49ca3 | ||
|
|
441bd44f97 | ||
|
|
7a18730949 | ||
|
|
0e27be0aca | ||
|
|
1c16365881 | ||
|
|
585e06f096 | ||
|
|
edce93282b | ||
|
|
26dca89b19 | ||
|
|
9b58e736ff | ||
|
|
36e6a6106b | ||
|
|
bbb31e2b7f | ||
|
|
afa3c40c3e | ||
|
|
401d61aa76 | ||
|
|
eda203af26 | ||
|
|
3022ba9b1a | ||
|
|
440f50259b | ||
|
|
5d402a5f9d | ||
|
|
2d83f331e2 | ||
|
|
677cc2b19e | ||
|
|
caa621eb04 | ||
|
|
5ea2ac3fef | ||
|
|
09318b81c0 | ||
|
|
d5be250640 | ||
|
|
fca3bb2a73 | ||
|
|
bf9cb67226 | ||
|
|
e54d6ecc12 | ||
|
|
30de6a1e39 | ||
|
|
6de4d93ef2 | ||
|
|
3c3d4a073d | ||
|
|
c2efdfa0bb | ||
|
|
2218003bec | ||
|
|
3ffd289a5e | ||
|
|
fe4d55fbf6 | ||
|
|
b5d9e5a8b6 | ||
|
|
046a7b0e2d | ||
|
|
32ca9727f7 | ||
|
|
08fc696f94 | ||
|
|
39ef3d13e6 | ||
|
|
151b300856 | ||
|
|
c5bf962095 | ||
|
|
a6a4cf5d00 | ||
|
|
90f7aa04bb | ||
|
|
bb9f074670 | ||
|
|
ef26f395bd | ||
|
|
e34516745c | ||
|
|
8474b423fe | ||
|
|
30b2d93bdf | ||
|
|
7162db0487 | ||
|
|
e08fa6b43b | ||
|
|
4b9e6edab8 | ||
|
|
7017bbf88b | ||
|
|
4f4ceeadc6 | ||
|
|
b0ecb2f6c6 | ||
|
|
4be0837ead | ||
|
|
e3832a13a6 | ||
|
|
169f576ccf | ||
|
|
894b59eee1 | ||
|
|
fe41d09acb | ||
|
|
387e4c6f0b | ||
|
|
6f8be14711 | ||
|
|
7d824dd03c | ||
|
|
899a7c1ba0 | ||
|
|
a84eff5c3b | ||
|
|
e0ae6484d3 | ||
|
|
552e90f674 | ||
|
|
898ee016c9 | ||
|
|
7d87aad0bb | ||
|
|
8d068b6a93 | ||
|
|
ea36d6b147 | ||
|
|
90e024b2bb | ||
|
|
955beb70e4 | ||
|
|
1a60b7005b | ||
|
|
fde3808cdf | ||
|
|
76e0ee66ae | ||
|
|
a2a0ee5194 | ||
|
|
0bced2f9ae | ||
|
|
ccc9729cc5 | ||
|
|
cf383bcf6b | ||
|
|
9292d5fa33 | ||
|
|
3b6646f1b9 | ||
|
|
ca1d4d8936 | ||
|
|
afa6399dce | ||
|
|
f93b1e1c1c | ||
|
|
fd7adb2202 | ||
|
|
05f50c285c | ||
|
|
0aa2dc9ce3 | ||
|
|
607236a7cb | ||
|
|
564ba105eb | ||
|
|
73b492e892 | ||
|
|
e99d96e081 | ||
|
|
abae2129ad | ||
|
|
f8c3e0ddea | ||
|
|
a28a0aba7d | ||
|
|
358911604b | ||
|
|
75eb44912f | ||
|
|
2893d16d58 | ||
|
|
2dbcb6d416 | ||
|
|
2b4ad2c50b | ||
|
|
7e4b69d272 | ||
|
|
d8c9044e15 | ||
|
|
b2e4cf2c09 | ||
|
|
08fa8362e3 | ||
|
|
447736f585 | ||
|
|
5ed91c2223 | ||
|
|
4fa07536d5 | ||
|
|
8881450d59 | ||
|
|
58af5528be | ||
|
|
98a1242050 | ||
|
|
9536c99422 | ||
|
|
7cfd5ad2a3 | ||
|
|
86ba340204 | ||
|
|
b32a142e14 | ||
|
|
7e45f34a86 | ||
|
|
1064feaa8e | ||
|
|
17658d1b6a | ||
|
|
ce643d9086 | ||
|
|
481515b419 | ||
|
|
6d60d92b03 | ||
|
|
3edbdf76f3 | ||
|
|
80bac5275c | ||
|
|
59fc9e24d9 | ||
|
|
7db7c0781f | ||
|
|
358ddf6019 | ||
|
|
ebf08345af | ||
|
|
a9ad236439 | ||
|
|
3066240ca0 | ||
|
|
d82326bfd4 | ||
|
|
f99ecb40d0 | ||
|
|
d7124123e9 | ||
|
|
e517f2b6f5 | ||
|
|
ea686059b6 | ||
|
|
3898371d44 | ||
|
|
f0a5a0a948 | ||
|
|
24ab96769a | ||
|
|
5e3a34d425 | ||
|
|
76724225e0 | ||
|
|
3ab539e2ba | ||
|
|
721a654152 | ||
|
|
57e51e44f1 | ||
|
|
f7642283f3 | ||
|
|
e7897b9139 | ||
|
|
59ce84f7cb | ||
|
|
bb6d87e8ed | ||
|
|
46dae2f06f | ||
|
|
0c1a366c07 | ||
|
|
71cc6f7e6b | ||
|
|
ba9a50b6fb | ||
|
|
9ef41a37b8 | ||
|
|
26db86beb2 | ||
|
|
69c37b07de | ||
|
|
7844db0719 | ||
|
|
6edb1e3482 | ||
|
|
7b887f3ea5 | ||
|
|
2fe407967b | ||
|
|
703be73c95 | ||
|
|
3060462edc | ||
|
|
263acaf4e4 | ||
|
|
db1be8f500 | ||
|
|
452eca5288 | ||
|
|
a728dfcf48 | ||
|
|
2027dc4a3d | ||
|
|
1bc4e1431c | ||
|
|
9afe28be20 | ||
|
|
8073120351 | ||
|
|
86c5b3e258 | ||
|
|
b971faecda | ||
|
|
2708b0de0e | ||
|
|
8dd9893202 | ||
|
|
48d740da25 | ||
|
|
7ba5526fda | ||
|
|
58dfa3335a | ||
|
|
deec1aa2a2 | ||
|
|
a62c1946a6 | ||
|
|
a194308486 | ||
|
|
2e442dbaa0 | ||
|
|
ad9ed96960 | ||
|
|
efc3b4df07 | ||
|
|
d6da043a32 | ||
|
|
10a7a5aa38 | ||
|
|
5684f1e196 | ||
|
|
2376a63d18 | ||
|
|
744b821d03 | ||
|
|
f3eb6b5cb3 | ||
|
|
d973f64576 | ||
|
|
a6c9bcf780 | ||
|
|
7cae338e6d | ||
|
|
ed344650c5 | ||
|
|
2d03e95ece | ||
|
|
69179f5d25 | ||
|
|
27cf82b270 | ||
|
|
d28891eaf4 | ||
|
|
b1c1e5f9f2 | ||
|
|
8a86c6a637 | ||
|
|
631811145f | ||
|
|
78ff062787 | ||
|
|
427f50b390 | ||
|
|
21f0d96146 | ||
|
|
7fcf922ee0 | ||
|
|
22dc44c9e5 | ||
|
|
6feb62d728 | ||
|
|
29060f7b5e | ||
|
|
6df6af0df0 | ||
|
|
d433a45200 | ||
|
|
d5e57ba0ba | ||
|
|
d69236cb25 | ||
|
|
422aa5b407 | ||
|
|
f97f51b895 | ||
|
|
4ea0093f12 | ||
|
|
2e35bb97d0 | ||
|
|
519b9f1a73 | ||
|
|
85c1f0d1aa | ||
|
|
f2738a79a0 | ||
|
|
4ae69372d4 | ||
|
|
cce2509b2e | ||
|
|
aa4d396960 | ||
|
|
5f11cc4cf8 | ||
|
|
956ecda77e | ||
|
|
bb554b1271 | ||
|
|
15000ff48a | ||
|
|
14d0ebdd71 | ||
|
|
c95b8cf939 | ||
|
|
74575440d7 | ||
|
|
fcb7798e9e | ||
|
|
b0a054ada7 | ||
|
|
b83d42efee | ||
|
|
2e18c96328 | ||
|
|
2daccbe29d | ||
|
|
51d9c30315 | ||
|
|
3d79e3fe8f | ||
|
|
57b34d2d91 | ||
|
|
a5a8863b52 | ||
|
|
d92b0aabee | ||
|
|
48472b6150 | ||
|
|
fc29424600 | ||
|
|
c4942dd747 | ||
|
|
30b9afe165 | ||
|
|
6d66969ff3 | ||
|
|
667f258ec2 | ||
|
|
badff05995 | ||
|
|
052128da81 | ||
|
|
57629051bd | ||
|
|
2cc89ad167 | ||
|
|
387ab05fd2 | ||
|
|
5de85c9d97 | ||
|
|
d7fa2ca5b9 | ||
|
|
3d2866a2a0 | ||
|
|
5d805a123e | ||
|
|
584e1fade0 | ||
|
|
98fa1fd071 | ||
|
|
2b0b34a3a6 | ||
|
|
a3213bba43 | ||
|
|
1fc6b945f6 | ||
|
|
58aaed567f | ||
|
|
51628604bf | ||
|
|
a35ff29363 | ||
|
|
b52be60776 | ||
|
|
d60d634208 | ||
|
|
b112597df8 | ||
|
|
a965bbd39f | ||
|
|
cc70570e65 | ||
|
|
c1fae7fd24 | ||
|
|
d620516055 | ||
|
|
0918c81d58 | ||
|
|
39b79e2a05 | ||
|
|
3340ca2b10 | ||
|
|
67dc148069 | ||
|
|
c6ba656f1d | ||
|
|
acf4c7a28a | ||
|
|
b38dea4bba | ||
|
|
3cf398618e | ||
|
|
462f163d8a | ||
|
|
96b3e9a891 | ||
|
|
b56242808d | ||
|
|
793f94d302 | ||
|
|
a23569e0c4 | ||
|
|
80a8b63498 | ||
|
|
eee8d71381 | ||
|
|
9013910cec | ||
|
|
ddff9180ac | ||
|
|
4988d6eb04 | ||
|
|
5ff86a40d9 | ||
|
|
05133aa319 | ||
|
|
626e2a1db1 | ||
|
|
59e1a64f2d | ||
|
|
a9ac7d9e10 | ||
|
|
7b2789479d | ||
|
|
4e8db37060 | ||
|
|
80b0a8b942 | ||
|
|
32924bc054 | ||
|
|
96530a5bdf | ||
|
|
18eee44ee6 | ||
|
|
0d0cce9195 | ||
|
|
92bf38a18e | ||
|
|
08e4b306f0 | ||
|
|
fcf04a4584 | ||
|
|
344932c063 | ||
|
|
b8fd4014ec | ||
|
|
b22eae043c | ||
|
|
0f2815b202 | ||
|
|
ddc73d13c2 | ||
|
|
cd82ff981f | ||
|
|
4fb7f612b1 | ||
|
|
58f12afdcd | ||
|
|
8ab0b682db | ||
|
|
646b46f2b6 | ||
|
|
b052ba5c59 | ||
|
|
2af1411a9e | ||
|
|
72118db350 | ||
|
|
fef19c5c99 | ||
|
|
cc8db51b33 | ||
|
|
7245c67469 | ||
|
|
d6a4ac64fd | ||
|
|
196826062a | ||
|
|
402b907c8e | ||
|
|
b038bc3905 | ||
|
|
b41e7ab3db | ||
|
|
fd59c97ce1 | ||
|
|
e2eb232e0f | ||
|
|
62b3f80a4c | ||
|
|
4d9c632060 | ||
|
|
99240e997c | ||
|
|
bf067d4ac4 | ||
|
|
b00b8dc05a | ||
|
|
a651105675 | ||
|
|
55354389dc | ||
|
|
ce4c187fcf | ||
|
|
25a03b4c5e | ||
|
|
f0ae02fd8e | ||
|
|
aa6cfc205e | ||
|
|
ae1be5a53b | ||
|
|
d5467ca383 | ||
|
|
cb4735ba6b | ||
|
|
bee851155a | ||
|
|
7c13a6286e | ||
|
|
156993a517 | ||
|
|
074551c506 | ||
|
|
dead18c8fd | ||
|
|
0f2334052b | ||
|
|
7dc7b23dc3 | ||
|
|
9857d9444c | ||
|
|
4804a2994e | ||
|
|
5b8dc80779 | ||
|
|
04a037a91d | ||
|
|
0b30cebd6a | ||
|
|
64bc64dd95 | ||
|
|
7f45a40e5d | ||
|
|
17d98c9236 | ||
|
|
b3e7068279 | ||
|
|
5ffc5ff50a | ||
|
|
cc8c034520 | ||
|
|
b7e22f9ffe | ||
|
|
3b1c46d13f | ||
|
|
b6288aeb6b | ||
|
|
0d95e11f04 | ||
|
|
b6c670a55b | ||
|
|
e4a6b37fb0 | ||
|
|
1cefd3b35f | ||
|
|
46cde34254 | ||
|
|
1f0acf7a0a | ||
|
|
020994c23a | ||
|
|
3e2e0be05e | ||
|
|
7561f8f8da | ||
|
|
64c9c1fead | ||
|
|
fb8e2ce1a4 | ||
|
|
ed6bbe6d07 | ||
|
|
0a0e7c01fd | ||
|
|
0221a65f82 | ||
|
|
0e3b2a9bf1 | ||
|
|
5808c0a8a7 | ||
|
|
3b0e1d9a3b | ||
|
|
24435401a5 | ||
|
|
07d88199b4 | ||
|
|
46005c19ce | ||
|
|
442c0d6956 | ||
|
|
1e155ffc13 | ||
|
|
90bc24d5c0 | ||
|
|
fe27ee4f22 | ||
|
|
84e498e744 | ||
|
|
2af564bc03 | ||
|
|
b0c48ce2a7 | ||
|
|
14520dabc0 | ||
|
|
8a3d922882 | ||
|
|
faa1c37613 | ||
|
|
a0d267ed2f | ||
|
|
09209533d0 | ||
|
|
3784a41d9e | ||
|
|
78cb9f793a | ||
|
|
941d056db4 | ||
|
|
d69163199b | ||
|
|
b563a64eb6 | ||
|
|
a975974fc0 | ||
|
|
b87abb7603 | ||
|
|
0313f54d3e | ||
|
|
63cb32b7a7 | ||
|
|
323147c7f4 | ||
|
|
a9d17903cf | ||
|
|
b3b19bcaf6 | ||
|
|
e0800944a8 | ||
|
|
352a95decb | ||
|
|
85974da27b | ||
|
|
e1d2723283 | ||
|
|
0818a67e75 | ||
|
|
9b982df4f0 | ||
|
|
51af2e9662 | ||
|
|
f8f8c327af | ||
|
|
baf1e80cca | ||
|
|
5be8937518 | ||
|
|
2c9884cfa5 | ||
|
|
933bb62c6c | ||
|
|
b10b894b24 | ||
|
|
35a3b145f8 | ||
|
|
c04ea6d220 | ||
|
|
ea48279a90 | ||
|
|
9bc263b106 | ||
|
|
27619a9118 | ||
|
|
ff2603a8fe | ||
|
|
4a0002058e | ||
|
|
656675206e | ||
|
|
07fee4ac00 | ||
|
|
084609bfad | ||
|
|
e1742fa24d | ||
|
|
241ac4c4d0 | ||
|
|
d75ad72057 | ||
|
|
b2031c5e44 | ||
|
|
8fa41e2fd0 | ||
|
|
9016ce7149 | ||
|
|
cf38a3eec4 | ||
|
|
7bf1f9ca49 | ||
|
|
1f9950507c | ||
|
|
a4ddc99446 | ||
|
|
52c1830d42 | ||
|
|
79e4a73408 | ||
|
|
0f8f214367 | ||
|
|
914be40f02 | ||
|
|
f196d03f6f | ||
|
|
f089d4282e | ||
|
|
bedbc387eb | ||
|
|
4d85ff60ab | ||
|
|
b38e818499 | ||
|
|
b1fb7189de | ||
|
|
7792b54d26 | ||
|
|
8f398bd130 | ||
|
|
79db8a22e7 | ||
|
|
058787a366 | ||
|
|
4b99c584db | ||
|
|
b9d689baf1 | ||
|
|
fe6c34030b | ||
|
|
b8f7fa975c | ||
|
|
abdca9399d | ||
|
|
3b93fcff3c | ||
|
|
6987db54b5 | ||
|
|
71d011b7f8 | ||
|
|
b22c23dfee | ||
|
|
59b7c0c025 | ||
|
|
46e42f0ea2 | ||
|
|
bac286646b | ||
|
|
bd057018a0 | ||
|
|
f2a63cbbce | ||
|
|
247b1b4d78 | ||
|
|
4ac9f0654c | ||
|
|
7cb5d60dd3 | ||
|
|
2d04bfe2b9 | ||
|
|
954ae7cca0 | ||
|
|
0e6f2e2fb9 | ||
|
|
73226092dc | ||
|
|
0abade970a | ||
|
|
78948c06fe | ||
|
|
1df7fb44dd | ||
|
|
0048978cbd | ||
|
|
cd745f1df1 | ||
|
|
3a52e4cd83 | ||
|
|
a0d222d593 | ||
|
|
b7ce575a5f | ||
|
|
0c35ef8085 | ||
|
|
b59d5062c7 | ||
|
|
5105773996 | ||
|
|
0c0a54db69 | ||
|
|
3a90af5fdc | ||
|
|
bbad591334 | ||
|
|
15edf33a56 | ||
|
|
e8341448b7 | ||
|
|
40cc93e5f5 | ||
|
|
5af0f5f320 | ||
|
|
050037acb2 | ||
|
|
ba4c382fe8 | ||
|
|
2e7b991252 | ||
|
|
1ada274c56 | ||
|
|
5e3889a8a5 | ||
|
|
39d8888890 | ||
|
|
d894ec0a9f | ||
|
|
9b8b95017d | ||
|
|
cbe7b7f3bb | ||
|
|
7103d524e0 | ||
|
|
bfa41966d9 | ||
|
|
85ee91a38c | ||
|
|
0bcbf5825a | ||
|
|
99b4719335 | ||
|
|
a1f5a330d4 | ||
|
|
0f4d2990f7 | ||
|
|
fa9237f04b | ||
|
|
3c9082d978 | ||
|
|
f45d2520cf | ||
|
|
89029d4410 | ||
|
|
c39f11193e | ||
|
|
f14ebdb019 | ||
|
|
e3698b07f6 | ||
|
|
a57e5e0af4 | ||
|
|
d73405d1f4 | ||
|
|
cae32294a1 | ||
|
|
ce63939855 | ||
|
|
13de0f5702 | ||
|
|
cc331c7f72 | ||
|
|
20103def45 | ||
|
|
664adcb990 | ||
|
|
db5d832207 | ||
|
|
1210732852 | ||
|
|
9f263ca603 | ||
|
|
7597630d18 | ||
|
|
66e3554308 | ||
|
|
467c4c6258 | ||
|
|
4ccf908738 | ||
|
|
c1072bb744 | ||
|
|
5eb4b557e0 | ||
|
|
4d4c9674fa | ||
|
|
217b7048c5 | ||
|
|
bbd581e9c5 | ||
|
|
d7e492b711 | ||
|
|
80ec842042 | ||
|
|
b72151d968 | ||
|
|
3c3c805298 | ||
|
|
721faa7e1e | ||
|
|
54a3c849ed | ||
|
|
5582607b76 | ||
|
|
3889bdf293 | ||
|
|
4981344c24 | ||
|
|
2ca4b02548 | ||
|
|
180f215ab0 | ||
|
|
9f47c09cdf | ||
|
|
7b790cc84f | ||
|
|
281a01fca5 | ||
|
|
b5af98b150 | ||
|
|
216bbd0ce1 | ||
|
|
a78b8a604c | ||
|
|
6e7ee18e7f | ||
|
|
aa49610e31 | ||
|
|
e3a285e423 | ||
|
|
37f75428d2 | ||
|
|
f9eacfa2d0 | ||
|
|
bc6d3df0c2 | ||
|
|
79900e89a2 | ||
|
|
4efa2cb3c0 | ||
|
|
135abdb9e0 | ||
|
|
a48d2f97e8 | ||
|
|
c9b1883905 | ||
|
|
86b615a9a5 | ||
|
|
08a320cf2b | ||
|
|
c05c1062ce | ||
|
|
f61af1fe5c | ||
|
|
80f2f0f66f | ||
|
|
d3b6d468b0 | ||
|
|
27fec353dd | ||
|
|
ab6cfb56b4 | ||
|
|
a57aed9ffc | ||
|
|
7a817ad1d8 | ||
|
|
b3caa1f002 | ||
|
|
77606c32c5 | ||
|
|
d89be97297 | ||
|
|
da4419b848 | ||
|
|
7809037963 | ||
|
|
c28c4bd675 | ||
|
|
fa8cbd08d4 | ||
|
|
53aefa895b | ||
|
|
c9d5743c4a | ||
|
|
7d5e2637c4 | ||
|
|
e938dfb5c9 | ||
|
|
ed15cde281 | ||
|
|
96c62463d5 | ||
|
|
ce77c65e6e | ||
|
|
0f9280399b | ||
|
|
4f9115ca02 | ||
|
|
84a8310912 | ||
|
|
c45dbb2e07 | ||
|
|
b36ebc6e0f | ||
|
|
09a1c17052 | ||
|
|
bddda8e256 | ||
|
|
020089b1d3 | ||
|
|
e454a12346 | ||
|
|
b304307ad7 | ||
|
|
6039ea47fc | ||
|
|
8d26d1dd34 | ||
|
|
1507259cfe | ||
|
|
a79336116f | ||
|
|
f805e8b6c5 | ||
|
|
7015a44268 | ||
|
|
e33523ddc8 | ||
|
|
ec3c42e87c | ||
|
|
8edfac5cff | ||
|
|
1bd9e77a87 | ||
|
|
3e5581dd24 | ||
|
|
ebc0114284 | ||
|
|
f788f589fa | ||
|
|
44aa1d34b7 | ||
|
|
06aef10f25 | ||
|
|
9c58240474 | ||
|
|
4f29019351 | ||
|
|
59df6b61d0 | ||
|
|
4cf90dee55 | ||
|
|
01d4746b0c | ||
|
|
011c08676e | ||
|
|
ecaa870880 | ||
|
|
847a538912 | ||
|
|
7dbbb1c299 |
@@ -1,26 +1,26 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
/**
|
||||
* UserRightsMatrix (User management Module)
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
/**
|
||||
* UserRightsNull
|
||||
* User management Module - say Yeah! to everything
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
class UserRightsNull extends UserRightsAddOnAPI
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
/**
|
||||
* UserRightsProfile
|
||||
* User management Module, basing the right on profiles and a matrix (similar to UserRightsMatrix, but profiles and other decorations have been added)
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
define('ADMIN_PROFILE_NAME', 'Administrator');
|
||||
@@ -210,6 +210,25 @@ class URP_Profiles extends UserRightsBaseClassGUI
|
||||
$oLnk->DBDelete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of flags (OPT_ATT_HIDDEN, OPT_ATT_READONLY, OPT_ATT_MANDATORY...)
|
||||
* for the given attribute in the current state of the object
|
||||
* @param $sAttCode string $sAttCode The code of the attribute
|
||||
* @param $aReasons array To store the reasons why the attribute is read-only (info about the synchro replicas)
|
||||
* @param $sTargetState string The target state in which to evalutate the flags, if empty the current state will be used
|
||||
* @return integer Flags: the binary combination of the flags applicable to this attribute
|
||||
*/
|
||||
public function GetAttributeFlags($sAttCode, &$aReasons = array(), $sTargetState = '')
|
||||
{
|
||||
$iFlags = parent::GetAttributeFlags($sAttCode, $aReasons, $sTargetState);
|
||||
if (MetaModel::GetConfig()->Get('demo_mode'))
|
||||
{
|
||||
$aReasons[] = 'Sorry, profiles are read-only in the demonstration mode!';
|
||||
$iFlags |= OPT_ATT_READONLY;
|
||||
}
|
||||
return $iFlags;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -311,11 +330,9 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
||||
// Installation: create the very first user
|
||||
public function CreateAdministrator($sAdminUser, $sAdminPwd, $sLanguage = 'EN US')
|
||||
{
|
||||
// Create a change to record the history of the User object
|
||||
$oChange = MetaModel::NewObject("CMDBChange");
|
||||
$oChange->Set("date", time());
|
||||
$oChange->Set("userinfo", "Initialization");
|
||||
$iChangeId = $oChange->DBInsert();
|
||||
CMDBObject::SetTrackInfo('Initialization');
|
||||
|
||||
$oChange = CMDBObject::GetCurrentChange();
|
||||
|
||||
$iContactId = 0;
|
||||
// Support drastic data model changes: no organization class (or not writable)!
|
||||
@@ -565,72 +582,10 @@ exit;
|
||||
return true;
|
||||
}
|
||||
|
||||
$oExpression = new FieldExpression($sAttCode, $sClass);
|
||||
$oFilter = new DBObjectSearch($sClass);
|
||||
$oListExpr = ListExpression::FromScalars($aUserOrgs);
|
||||
|
||||
$oCondition = new BinaryExpression($oExpression, 'IN', $oListExpr);
|
||||
$oFilter->AddConditionExpression($oCondition);
|
||||
|
||||
if (self::HasSharing())
|
||||
{
|
||||
if (($sAttCode == 'id') && isset($aSettings['bSearchMode']) && $aSettings['bSearchMode'])
|
||||
{
|
||||
// Querying organizations (or derived)
|
||||
// and the expected list of organizations will be used as a search criteria
|
||||
// Therefore the query can also return organization having objects shared with the allowed organizations
|
||||
//
|
||||
// 1) build the list of organizations sharing something with the allowed organizations
|
||||
// Organization <== sharing_org_id == SharedObject having org_id IN {user orgs}
|
||||
$oShareSearch = new DBObjectSearch('SharedObject');
|
||||
$oOrgField = new FieldExpression('org_id', 'SharedObject');
|
||||
$oShareSearch->AddConditionExpression(new BinaryExpression($oOrgField, 'IN', $oListExpr));
|
||||
|
||||
$oSearchSharers = new DBObjectSearch('Organization');
|
||||
$oSearchSharers->AllowAllData();
|
||||
$oSearchSharers->AddCondition_ReferencedBy($oShareSearch, 'sharing_org_id');
|
||||
$aSharers = array();
|
||||
foreach($oSearchSharers->ToDataArray(array('id')) as $aRow)
|
||||
{
|
||||
$aSharers[] = $aRow['id'];
|
||||
}
|
||||
// 2) Enlarge the overall results: ... OR id IN(id1, id2, id3)
|
||||
if (count($aSharers) > 0)
|
||||
{
|
||||
$oSharersList = ListExpression::FromScalars($aSharers);
|
||||
$oFilter->MergeConditionExpression(new BinaryExpression($oExpression, 'IN', $oSharersList));
|
||||
}
|
||||
}
|
||||
|
||||
$aShareProperties = SharedObject::GetSharedClassProperties($sClass);
|
||||
if ($aShareProperties)
|
||||
{
|
||||
$sShareClass = $aShareProperties['share_class'];
|
||||
$sShareAttCode = $aShareProperties['attcode'];
|
||||
|
||||
$oSearchShares = new DBObjectSearch($sShareClass);
|
||||
$oSearchShares->AllowAllData();
|
||||
|
||||
$sHierarchicalKeyCode = MetaModel::IsHierarchicalClass('Organization');
|
||||
$oOrgField = new FieldExpression('org_id', $sShareClass);
|
||||
$oSearchShares->AddConditionExpression(new BinaryExpression($oOrgField, 'IN', $oListExpr));
|
||||
$aShared = array();
|
||||
foreach($oSearchShares->ToDataArray(array($sShareAttCode)) as $aRow)
|
||||
{
|
||||
$aShared[] = $aRow[$sShareAttCode];
|
||||
}
|
||||
if (count($aShared) > 0)
|
||||
{
|
||||
$oObjId = new FieldExpression('id', $sClass);
|
||||
$oSharedIdList = ListExpression::FromScalars($aShared);
|
||||
$oFilter->MergeConditionExpression(new BinaryExpression($oObjId, 'IN', $oSharedIdList));
|
||||
}
|
||||
}
|
||||
} // if HasSharing
|
||||
|
||||
return $oFilter;
|
||||
return $this->MakeSelectFilter($sClass, $aUserOrgs, $aSettings, $sAttCode);
|
||||
}
|
||||
|
||||
|
||||
// This verb has been made public to allow the development of an accurate feedback for the current configuration
|
||||
public function GetProfileActionGrant($iProfile, $sClass, $sAction)
|
||||
{
|
||||
|
||||
1088
addons/userrights/userrightsprofile.db.class.inc.php
Normal file
1088
addons/userrights/userrightsprofile.db.class.inc.php
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,27 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
/**
|
||||
* UserRightsProjection
|
||||
* User management Module, basing the right on profiles and a matrix (similar to UserRightsProfile, but enhanced with dimensions and projection of classes and profile over the dimensions)
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
define('ADMIN_PROFILE_ID', 1);
|
||||
|
||||
@@ -1,41 +1,40 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
/**
|
||||
* Simple web page with no includes, header or fancy formatting, useful to
|
||||
* generate HTML fragments when called by an AJAX method
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
require_once(APPROOT."/application/webpage.class.inc.php");
|
||||
|
||||
class ajax_page extends WebPage
|
||||
class ajax_page extends WebPage implements iTabbedPage
|
||||
{
|
||||
/**
|
||||
* Jquery style ready script
|
||||
* @var Hash
|
||||
*/
|
||||
protected $m_sReadyScript;
|
||||
protected $m_sCurrentTab;
|
||||
protected $m_sCurrentTabContainer;
|
||||
protected $m_aTabs;
|
||||
protected $m_oTabs;
|
||||
private $m_sMenu; // If set, then the menu will be updated
|
||||
|
||||
/**
|
||||
* constructor for the web page
|
||||
@@ -47,52 +46,84 @@ class ajax_page extends WebPage
|
||||
$this->m_sReadyScript = "";
|
||||
//$this->add_header("Content-type: text/html; charset=utf-8");
|
||||
$this->add_header("Cache-control: no-cache");
|
||||
$this->m_sCurrentTabContainer = '';
|
||||
$this->m_sCurrentTab = '';
|
||||
$this->m_aTabs = array();
|
||||
$this->m_oTabs = new TabManager();
|
||||
$this->sContentType = 'text/html';
|
||||
$this->sContentDisposition = 'inline';
|
||||
$this->m_sMenu = "";
|
||||
}
|
||||
|
||||
public function AddTabContainer($sTabContainer, $sPrefix = '')
|
||||
{
|
||||
$this->m_aTabs[$sTabContainer] = array('content' =>'', 'prefix' => $sPrefix);
|
||||
$this->add("\$Tabs:$sTabContainer\$");
|
||||
$this->add($this->m_oTabs->AddTabContainer($sTabContainer, $sPrefix));
|
||||
}
|
||||
|
||||
|
||||
public function AddToTab($sTabContainer, $sTabLabel, $sHtml)
|
||||
{
|
||||
if (!isset($this->m_aTabs[$sTabContainer]['content'][$sTabLabel]))
|
||||
{
|
||||
// Set the content of the tab
|
||||
$this->m_aTabs[$sTabContainer]['content'][$sTabLabel] = $sHtml;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Append to the content of the tab
|
||||
$this->m_aTabs[$sTabContainer]['content'][$sTabLabel] .= $sHtml;
|
||||
}
|
||||
$this->add($this->m_oTabs->AddToTab($sTabContainer, $sTabLabel, $sHtml));
|
||||
}
|
||||
|
||||
public function SetCurrentTabContainer($sTabContainer = '')
|
||||
{
|
||||
$sPreviousTabContainer = $this->m_sCurrentTabContainer;
|
||||
$this->m_sCurrentTabContainer = $sTabContainer;
|
||||
return $sPreviousTabContainer;
|
||||
return $this->m_oTabs->SetCurrentTabContainer($sTabContainer);
|
||||
}
|
||||
|
||||
public function SetCurrentTab($sTabLabel = '')
|
||||
{
|
||||
$sPreviousTab = $this->m_sCurrentTab;
|
||||
$this->m_sCurrentTab = $sTabLabel;
|
||||
return $sPreviousTab;
|
||||
return $this->m_oTabs->SetCurrentTab($sTabLabel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a tab which content will be loaded asynchronously via the supplied URL
|
||||
*
|
||||
* Limitations:
|
||||
* Cross site scripting is not not allowed for security reasons. Use a normal tab with an IFRAME if you want to pull content from another server.
|
||||
* Static content cannot be added inside such tabs.
|
||||
*
|
||||
* @param string $sTabLabel The (localised) label of the tab
|
||||
* @param string $sUrl The URL to load (on the same server)
|
||||
* @param boolean $bCache Whether or not to cache the content of the tab once it has been loaded. flase will cause the tab to be reloaded upon each activation.
|
||||
* @since 2.0.3
|
||||
*/
|
||||
public function AddAjaxTab($sTabLabel, $sUrl, $bCache = true)
|
||||
{
|
||||
$this->add($this->m_oTabs->AddAjaxTab($sTabLabel, $sUrl, $bCache));
|
||||
}
|
||||
|
||||
public function GetCurrentTab()
|
||||
{
|
||||
return $this->m_sCurrentTab;
|
||||
return $this->m_oTabs->GetCurrentTab();
|
||||
}
|
||||
|
||||
public function RemoveTab($sTabLabel, $sTabContainer = null)
|
||||
{
|
||||
$this->m_oTabs->RemoveTab($sTabLabel, $sTabContainer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the tab whose title matches a given pattern
|
||||
* @return mixed The name of the tab as a string or false if not found
|
||||
*/
|
||||
public function FindTab($sPattern, $sTabContainer = null)
|
||||
{
|
||||
return $this->m_oTabs->FindTab($sPattern, $sTabContainer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the given tab the active one, as if it were clicked
|
||||
* DOES NOT WORK: apparently in the *old* version of jquery
|
||||
* that we are using this is not supported... TO DO upgrade
|
||||
* the whole jquery bundle...
|
||||
*/
|
||||
public function SelectTab($sTabContainer, $sTabLabel)
|
||||
{
|
||||
$this->add_ready_script($this->m_oTabs->SelectTab($sTabContainer, $sTabLabel));
|
||||
}
|
||||
|
||||
public function AddToMenu($sHtml)
|
||||
{
|
||||
$this->m_sMenu .= $sHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Echoes the content of the whole page
|
||||
* @return void
|
||||
@@ -111,13 +142,26 @@ class ajax_page extends WebPage
|
||||
{
|
||||
header($s_header);
|
||||
}
|
||||
if (count($this->m_aTabs) > 0)
|
||||
if ($this->m_oTabs->TabsContainerCount() > 0)
|
||||
{
|
||||
$this->add_ready_script(
|
||||
$this->add_ready_script(
|
||||
<<<EOF
|
||||
// The "tab widgets" to handle.
|
||||
var tabs = $('div[id^=tabbedContent]');
|
||||
|
||||
|
||||
// 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
|
||||
// is taken into account and causes "local" tabs to be considered as Ajax
|
||||
// unless their URL is equal to the URL of the page...
|
||||
if ($('base').length > 0)
|
||||
{
|
||||
$('div[id^=tabbedContent] > ul > li > a').each(function() {
|
||||
var sHash = location.hash;
|
||||
var sCleanLocation = location.href.toString().replace(sHash, '').replace(/#$/, '');
|
||||
$(this).attr("href", sCleanLocation+$(this).attr("href"));
|
||||
});
|
||||
}
|
||||
if ($.bbq)
|
||||
{
|
||||
// This selector will be reused when selecting actual tab widget A elements.
|
||||
@@ -153,37 +197,34 @@ EOF
|
||||
);
|
||||
}
|
||||
// Render the tabs in the page (if any)
|
||||
foreach($this->m_aTabs as $sTabContainerName => $aTabContainer)
|
||||
$this->s_content = $this->m_oTabs->RenderIntoContent($this->s_content);
|
||||
|
||||
// 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)) )
|
||||
{
|
||||
$sTabs = '';
|
||||
$m_aTabs = $aTabContainer['content'];
|
||||
$sPrefix = $aTabContainer['prefix'];
|
||||
$container_index = 0;
|
||||
if (count($m_aTabs) > 0)
|
||||
{
|
||||
$sTabs = "<!-- tabs -->\n<div id=\"tabbedContent_{$sPrefix}{$sTabContainerName}\" class=\"light\">\n";
|
||||
$sTabs .= "<ul>\n";
|
||||
// Display the unordered list that will be rendered as the tabs
|
||||
$i = 0;
|
||||
foreach($m_aTabs as $sTabName => $sTabContent)
|
||||
{
|
||||
$sTabs .= "<li><a href=\"#tab_{$sPrefix}$i\" class=\"tab\"><span>".htmlentities($sTabName, ENT_QUOTES, 'UTF-8')."</span></a></li>\n";
|
||||
$i++;
|
||||
}
|
||||
$sTabs .= "</ul>\n";
|
||||
// Now add the content of the tabs themselves
|
||||
$i = 0;
|
||||
foreach($m_aTabs as $sTabName => $sTabContent)
|
||||
{
|
||||
$sTabs .= "<div id=\"tab_{$sPrefix}$i\">".$sTabContent."</div>\n";
|
||||
$i++;
|
||||
}
|
||||
$sTabs .= "</div>\n<!-- end of tabs-->\n";
|
||||
}
|
||||
$this->s_content = str_replace("\$Tabs:$sTabContainerName\$", $sTabs, $this->s_content);
|
||||
$container_index++;
|
||||
}
|
||||
|
||||
$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
|
||||
});
|
||||
EOF
|
||||
);
|
||||
}
|
||||
$s_captured_output = ob_get_contents();
|
||||
ob_end_clean();
|
||||
if (($this->sContentType == 'text/html') && ($this->sContentDisposition == 'inline'))
|
||||
@@ -195,6 +236,24 @@ EOF
|
||||
{
|
||||
echo $this->s_content;
|
||||
}
|
||||
if (!empty($this->m_sMenu))
|
||||
{
|
||||
$uid = time();
|
||||
echo "<div id=\"accordion_temp_$uid\">\n";
|
||||
echo "<div id=\"accordion\">\n";
|
||||
echo "<!-- Beginning of the accordion menu -->\n";
|
||||
echo self::FilterXSS($this->m_sMenu);
|
||||
echo "<!-- End of the accordion menu-->\n";
|
||||
echo "</div>\n";
|
||||
echo "</div>\n";
|
||||
|
||||
echo "<script type=\"text/javascript\">\n";
|
||||
echo "$('#inner_menu').html($('#accordion_temp_$uid').html());\n";
|
||||
echo "$('#accordion_temp_$uid').remove();\n";
|
||||
echo "$('#accordion').accordion({ header: 'h3', navigation: true, autoHeight: false, collapsible: false, icons: false });\n";
|
||||
echo "\n</script>\n";
|
||||
}
|
||||
|
||||
//echo $this->s_deferred_content;
|
||||
if (count($this->a_scripts) > 0)
|
||||
{
|
||||
@@ -214,10 +273,16 @@ EOF
|
||||
echo $this->m_sReadyScript; // Ready Scripts are output as simple scripts
|
||||
echo "\n</script>\n";
|
||||
}
|
||||
|
||||
if (trim($s_captured_output) != "")
|
||||
{
|
||||
echo self::FilterXSS($s_captured_output);
|
||||
}
|
||||
|
||||
if (class_exists('MetaModel'))
|
||||
{
|
||||
MetaModel::RecordQueryTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -232,9 +297,9 @@ EOF
|
||||
|
||||
public function add($sHtml)
|
||||
{
|
||||
if (!empty($this->m_sCurrentTabContainer) && !empty($this->m_sCurrentTab))
|
||||
if (($this->m_oTabs->GetCurrentTabContainer() != '') && ($this->m_oTabs->GetCurrentTab() != ''))
|
||||
{
|
||||
$this->AddToTab($this->m_sCurrentTabContainer, $this->m_sCurrentTab, $sHtml);
|
||||
$this->m_oTabs->AddToTab($this->m_oTabs->GetCurrentTabContainer(), $this->m_oTabs->GetCurrentTab(), $sHtml);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -248,15 +313,18 @@ EOF
|
||||
*/
|
||||
public function start_capture()
|
||||
{
|
||||
if (!empty($this->m_sCurrentTabContainer) && !empty($this->m_sCurrentTab))
|
||||
{
|
||||
$iOffset = isset($this->m_aTabs[$this->m_sCurrentTabContainer]['content'][$this->m_sCurrentTab]) ? strlen($this->m_aTabs[$this->m_sCurrentTabContainer]['content'][$this->m_sCurrentTab]): 0;
|
||||
return array('tc' => $this->m_sCurrentTabContainer, 'tab' => $this->m_sCurrentTab, 'offset' => $iOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
return parent::start_capture();
|
||||
}
|
||||
$sCurrentTabContainer = $this->m_oTabs->GetCurrentTabContainer();
|
||||
$sCurrentTab = $this->m_oTabs->GetCurrentTab();
|
||||
|
||||
if (!empty($sCurrentTabContainer) && !empty($sCurrentTab))
|
||||
{
|
||||
$iOffset = $this->m_oTabs->GetCurrentTabLength();
|
||||
return array('tc' => $sCurrentTabContainer, 'tab' => $sCurrentTab, 'offset' => $iOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
return parent::start_capture();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -267,23 +335,22 @@ EOF
|
||||
*/
|
||||
public function end_capture($offset)
|
||||
{
|
||||
if (is_array($offset))
|
||||
{
|
||||
if (isset($this->m_aTabs[$offset['tc']]['content'][$offset['tab']]))
|
||||
{
|
||||
$sCaptured = substr($this->m_aTabs[$offset['tc']]['content'][$offset['tab']], $offset['offset']);
|
||||
$this->m_aTabs[$offset['tc']]['content'][$offset['tab']] = substr($this->m_aTabs[$offset['tc']]['content'][$offset['tab']], 0, $offset['offset']);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sCaptured = '';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$sCaptured = parent::end_capture($offset);
|
||||
}
|
||||
return $sCaptured;
|
||||
if (is_array($offset))
|
||||
{
|
||||
if ($this->m_oTabs->TabExists($offset['tc'], $offset['tab']))
|
||||
{
|
||||
$sCaptured = $this->m_oTabs->TruncateTab($offset['tc'], $offset['tab'], $offset['offset']);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sCaptured = '';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$sCaptured = parent::end_capture($offset);
|
||||
}
|
||||
return $sCaptured;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Includes all the classes to have the application up and running
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
require_once(APPROOT.'/application/applicationcontext.class.inc.php');
|
||||
@@ -30,6 +31,7 @@ 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');
|
||||
require_once(APPROOT.'/setup/moduleinstallation.class.inc.php');
|
||||
//require_once(APPROOT.'/application/menunode.class.inc.php');
|
||||
require_once(APPROOT.'/application/utils.inc.php');
|
||||
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// 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 ApplicationContext
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
require_once(APPROOT."/application/utils.inc.php");
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,28 +1,29 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* This class manages the audit "categories". Each category defines a set of objects
|
||||
* to check and is linked to a set of rules that determine the valid or invalid objects
|
||||
* inside the set
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
require_once(APPROOT.'/application/cmdbabstract.class.inc.php');
|
||||
@@ -46,7 +47,7 @@ class AuditCategory extends cmdbAbstractObject
|
||||
MetaModel::Init_AddAttribute(new AttributeString("name", array("description"=>"Short name for this category", "allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeOQL("definition_set", array("allowed_values"=>null, "sql"=>"definition_set", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeLinkedSet("rules_list", array("linked_class"=>"AuditRule", "ext_key_to_me"=>"category_id", "allowed_values"=>null, "count_min"=>0, "count_max"=>0, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeLinkedSet("rules_list", array("linked_class"=>"AuditRule", "ext_key_to_me"=>"category_id", "allowed_values"=>null, "count_min"=>0, "count_max"=>0, "depends_on"=>array(), "edit_mode" => LINKSET_EDITMODE_INPLACE)));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('name', 'description', 'definition_set', 'rules_list')); // Attributes to be displayed for the complete details
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* This class manages the audit "rule" linked to a given audit category.
|
||||
@@ -20,10 +23,8 @@
|
||||
* or the "bad" ones. The core audit engines computes the complement to the definition
|
||||
* set when needed to obtain either the valid objects, or the ones with an error
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
require_once(APPROOT.'/application/audit.category.class.inc.php');
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* CLI page
|
||||
* The page adds the content-type text/XML and the encoding into the headers
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
require_once(APPROOT."/application/webpage.class.inc.php");
|
||||
@@ -34,6 +35,14 @@ class CLIPage implements Page
|
||||
|
||||
public function output()
|
||||
{
|
||||
if (class_exists('MetaModel'))
|
||||
{
|
||||
MetaModel::RecordQueryTrace();
|
||||
}
|
||||
if (class_exists('ExecutionKPI'))
|
||||
{
|
||||
ExecutionKPI::ReportStats();
|
||||
}
|
||||
}
|
||||
|
||||
public function add($sText)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,27 +1,28 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Simple web page with no includes or fancy formatting, useful to generateXML documents
|
||||
* The page adds the content-type text/XML and the encoding into the headers
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
require_once(APPROOT."/application/webpage.class.inc.php");
|
||||
@@ -31,8 +32,9 @@ class CSVPage extends WebPage
|
||||
function __construct($s_title)
|
||||
{
|
||||
parent::__construct($s_title);
|
||||
$this->add_header("Content-type: text/html; charset=utf-8");
|
||||
$this->add_header("Content-type: text/plain; charset=utf-8");
|
||||
$this->add_header("Cache-control: no-cache");
|
||||
//$this->add_header("Content-Transfer-Encoding: binary");
|
||||
}
|
||||
|
||||
public function output()
|
||||
@@ -43,6 +45,16 @@ class CSVPage extends WebPage
|
||||
header($s_header);
|
||||
}
|
||||
echo trim($this->s_content);
|
||||
echo "\n";
|
||||
|
||||
if (class_exists('MetaModel'))
|
||||
{
|
||||
MetaModel::RecordQueryTrace();
|
||||
}
|
||||
if (class_exists('ExecutionKPI'))
|
||||
{
|
||||
ExecutionKPI::ReportStats();
|
||||
}
|
||||
}
|
||||
|
||||
public function small_p($sText)
|
||||
|
||||
@@ -1,43 +1,54 @@
|
||||
<?php
|
||||
// Copyright (C) 2012 Combodo SARL
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
require_once(APPROOT.'application/dashboardlayout.class.inc.php');
|
||||
require_once(APPROOT.'application/dashlet.class.inc.php');
|
||||
require_once(APPROOT.'core/modelreflection.class.inc.php');
|
||||
|
||||
/**
|
||||
* A user editable dashboard page
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
abstract class Dashboard
|
||||
{
|
||||
protected $sTitle;
|
||||
protected $bAutoReload;
|
||||
protected $iAutoReloadSec;
|
||||
protected $sLayoutClass;
|
||||
protected $aWidgetsData;
|
||||
protected $oDOMNode;
|
||||
protected $sId;
|
||||
protected $aCells;
|
||||
protected $oMetaModel;
|
||||
|
||||
public function __construct($sId)
|
||||
{
|
||||
$this->sLayoutClass = null;
|
||||
$this->sTitle = '';
|
||||
$this->sLayoutClass = 'DashboardLayoutOneCol';
|
||||
$this->bAutoReload = false;
|
||||
$this->iAutoReloadSec = MetaModel::GetConfig()->GetStandardReloadInterval();
|
||||
$this->aCells = array();
|
||||
$this->oDOMNode = null;
|
||||
$this->sId = $sId;
|
||||
}
|
||||
|
||||
|
||||
public function FromXml($sXml)
|
||||
{
|
||||
$this->aCells = array(); // reset the content of the dashboard
|
||||
@@ -45,32 +56,101 @@ abstract class Dashboard
|
||||
$oDoc = new DOMDocument();
|
||||
$oDoc->loadXML($sXml);
|
||||
restore_error_handler();
|
||||
$this->oDOMNode = $oDoc->getElementsByTagName('dashboard')->item(0);
|
||||
|
||||
$oLayoutNode = $this->oDOMNode->getElementsByTagName('layout')->item(0);
|
||||
$this->sLayoutClass = $oLayoutNode->textContent;
|
||||
|
||||
$oTitleNode = $this->oDOMNode->getElementsByTagName('title')->item(0);
|
||||
$this->sTitle = $oTitleNode->textContent;
|
||||
|
||||
$oCellsNode = $this->oDOMNode->getElementsByTagName('cells')->item(0);
|
||||
$oCellsList = $oCellsNode->getElementsByTagName('cell');
|
||||
foreach($oCellsList as $oCellNode)
|
||||
{
|
||||
$aDashletList = array();
|
||||
$oDashletList = $oCellNode->getElementsByTagName('dashlet');
|
||||
foreach($oDashletList as $oDomNode)
|
||||
{
|
||||
$sDashletClass = $oDomNode->getAttribute('xsi:type');
|
||||
$sId = $oDomNode->getAttribute('id');
|
||||
$oNewDashlet = new $sDashletClass($sId);
|
||||
$oNewDashlet->FromDOMNode($oDomNode);
|
||||
$aDashletList[] = $oNewDashlet;
|
||||
}
|
||||
$this->aCells[] = $aDashletList;
|
||||
}
|
||||
$this->FromDOMDocument($oDoc);
|
||||
}
|
||||
|
||||
public function FromDOMDocument(DOMDocument $oDoc)
|
||||
{
|
||||
$this->oDOMNode = $oDoc->getElementsByTagName('dashboard')->item(0);
|
||||
|
||||
if ($oLayoutNode = $this->oDOMNode->getElementsByTagName('layout')->item(0))
|
||||
{
|
||||
$this->sLayoutClass = $oLayoutNode->textContent;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->sLayoutClass = 'DashboardLayoutOneCol';
|
||||
}
|
||||
|
||||
if ($oTitleNode = $this->oDOMNode->getElementsByTagName('title')->item(0))
|
||||
{
|
||||
$this->sTitle = $oTitleNode->textContent;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->sTitle = '';
|
||||
}
|
||||
|
||||
$this->bAutoReload = false;
|
||||
$this->iAutoReloadSec = MetaModel::GetConfig()->GetStandardReloadInterval();
|
||||
if ($oAutoReloadNode = $this->oDOMNode->getElementsByTagName('auto_reload')->item(0))
|
||||
{
|
||||
if ($oAutoReloadEnabled = $oAutoReloadNode->getElementsByTagName('enabled')->item(0))
|
||||
{
|
||||
$this->bAutoReload = ($oAutoReloadEnabled->textContent == 'true');
|
||||
}
|
||||
if ($oAutoReloadInterval = $oAutoReloadNode->getElementsByTagName('interval')->item(0))
|
||||
{
|
||||
$this->iAutoReloadSec = max(5, (int)$oAutoReloadInterval->textContent);
|
||||
}
|
||||
}
|
||||
|
||||
if ($oCellsNode = $this->oDOMNode->getElementsByTagName('cells')->item(0))
|
||||
{
|
||||
$oCellsList = $oCellsNode->getElementsByTagName('cell');
|
||||
$aCellOrder = array();
|
||||
$iCellRank = 0;
|
||||
foreach($oCellsList as $oCellNode)
|
||||
{
|
||||
$aDashletList = array();
|
||||
$oCellRank = $oCellNode->getElementsByTagName('rank')->item(0);
|
||||
if ($oCellRank)
|
||||
{
|
||||
$iCellRank = (float)$oCellRank->textContent;
|
||||
}
|
||||
$oDashletsNode = $oCellNode->getElementsByTagName('dashlets')->item(0);
|
||||
{
|
||||
$oDashletList = $oDashletsNode->getElementsByTagName('dashlet');
|
||||
$iRank = 0;
|
||||
$aDashletOrder = array();
|
||||
foreach($oDashletList as $oDomNode)
|
||||
{
|
||||
$sDashletClass = $oDomNode->getAttribute('xsi:type');
|
||||
$oRank = $oDomNode->getElementsByTagName('rank')->item(0);
|
||||
if ($oRank)
|
||||
{
|
||||
$iRank = (float)$oRank->textContent;
|
||||
}
|
||||
$sId = $oDomNode->getAttribute('id');
|
||||
$oNewDashlet = new $sDashletClass($this->oMetaModel, $sId);
|
||||
$oNewDashlet->FromDOMNode($oDomNode);
|
||||
$aDashletOrder[] = array('rank' => $iRank, 'dashlet' => $oNewDashlet);
|
||||
}
|
||||
usort($aDashletOrder, array(get_class($this), 'SortOnRank'));
|
||||
$aDashletList = array();
|
||||
foreach($aDashletOrder as $aItem)
|
||||
{
|
||||
$aDashletList[] = $aItem['dashlet'];
|
||||
}
|
||||
$aCellOrder[] = array('rank' => $iCellRank, 'dashlets' => $aDashletList);
|
||||
}
|
||||
}
|
||||
usort($aCellOrder, array(get_class($this), 'SortOnRank'));
|
||||
foreach($aCellOrder as $aItem)
|
||||
{
|
||||
$this->aCells[] = $aItem['dashlets'];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->aCells = array();
|
||||
}
|
||||
}
|
||||
|
||||
static function SortOnRank($aItem1, $aItem2)
|
||||
{
|
||||
return ($aItem1['rank'] > $aItem2['rank']) ? +1 : -1;
|
||||
}
|
||||
/**
|
||||
* Error handler to turn XML loading warnings into exceptions
|
||||
*/
|
||||
@@ -96,37 +176,66 @@ abstract class Dashboard
|
||||
$oMainNode->setAttribute('xmlns:xsi', "http://www.w3.org/2001/XMLSchema-instance");
|
||||
$oDoc->appendChild($oMainNode);
|
||||
|
||||
$oNode = $oDoc->createElement('layout', $this->sLayoutClass);
|
||||
$oMainNode->appendChild($oNode);
|
||||
|
||||
$oNode = $oDoc->createElement('title', $this->sTitle);
|
||||
$oMainNode->appendChild($oNode);
|
||||
|
||||
$oCellsNode = $oDoc->createElement('cells');
|
||||
$oMainNode->appendChild($oCellsNode);
|
||||
|
||||
foreach ($this->aCells as $aCell)
|
||||
{
|
||||
$oCellNode = $oDoc->createElement('cell');
|
||||
$oCellsNode->appendChild($oCellNode);
|
||||
foreach ($aCell as $oDashlet)
|
||||
{
|
||||
$oNode = $oDoc->createElement('dashlet');
|
||||
$oCellNode->appendChild($oNode);
|
||||
$oNode->setAttribute('id', $oDashlet->GetID());
|
||||
$oNode->setAttribute('xsi:type', get_class($oDashlet));
|
||||
$oDashlet->ToDOMNode($oNode);
|
||||
}
|
||||
}
|
||||
$this->ToDOMNode($oMainNode);
|
||||
|
||||
$sXml = $oDoc->saveXML();
|
||||
return $sXml;
|
||||
}
|
||||
|
||||
public function ToDOMNode($oDefinition)
|
||||
{
|
||||
$oDoc = $oDefinition->ownerDocument;
|
||||
|
||||
$oNode = $oDoc->createElement('layout', $this->sLayoutClass);
|
||||
$oDefinition->appendChild($oNode);
|
||||
|
||||
$oNode = $oDoc->createElement('title', $this->sTitle);
|
||||
$oDefinition->appendChild($oNode);
|
||||
|
||||
$oAutoReloadNode = $oDoc->createElement('auto_reload');
|
||||
$oDefinition->appendChild($oAutoReloadNode);
|
||||
$oNode = $oDoc->createElement('enabled', $this->bAutoReload ? 'true' : 'false');
|
||||
$oAutoReloadNode->appendChild($oNode);
|
||||
$oNode = $oDoc->createElement('interval', $this->iAutoReloadSec);
|
||||
$oAutoReloadNode->appendChild($oNode);
|
||||
|
||||
$oCellsNode = $oDoc->createElement('cells');
|
||||
$oDefinition->appendChild($oCellsNode);
|
||||
|
||||
$iCellRank = 0;
|
||||
foreach ($this->aCells as $aCell)
|
||||
{
|
||||
$oCellNode = $oDoc->createElement('cell');
|
||||
$oCellNode->setAttribute('id', $iCellRank);
|
||||
$oCellsNode->appendChild($oCellNode);
|
||||
$oCellRank = $oDoc->createElement('rank', $iCellRank);
|
||||
$oCellNode->appendChild($oCellRank);
|
||||
$iCellRank++;
|
||||
|
||||
$iDashletRank = 0;
|
||||
$oDashletsNode = $oDoc->createElement('dashlets');
|
||||
$oCellNode->appendChild($oDashletsNode);
|
||||
foreach ($aCell as $oDashlet)
|
||||
{
|
||||
$oNode = $oDoc->createElement('dashlet');
|
||||
$oDashletsNode->appendChild($oNode);
|
||||
$oNode->setAttribute('id', $oDashlet->GetID());
|
||||
$oNode->setAttribute('xsi:type', get_class($oDashlet));
|
||||
$oDashletRank = $oDoc->createElement('rank', $iDashletRank);
|
||||
$oNode->appendChild($oDashletRank);
|
||||
$iDashletRank++;
|
||||
$oDashlet->ToDOMNode($oNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function FromParams($aParams)
|
||||
{
|
||||
$this->sLayoutClass = $aParams['layout_class'];
|
||||
$this->sTitle = $aParams['title'];
|
||||
$this->bAutoReload = $aParams['auto_reload'] == 'true';
|
||||
$this->iAutoReloadSec = max(5, (int) $aParams['auto_reload_sec']);
|
||||
|
||||
foreach($aParams['cells'] as $aCell)
|
||||
{
|
||||
@@ -135,7 +244,7 @@ abstract class Dashboard
|
||||
{
|
||||
$sDashletClass = $aDashletParams['dashlet_class'];
|
||||
$sId = $aDashletParams['dashlet_id'];
|
||||
$oNewDashlet = new $sDashletClass($sId);
|
||||
$oNewDashlet = new $sDashletClass($this->oMetaModel, $sId);
|
||||
|
||||
$oForm = $oNewDashlet->GetForm();
|
||||
$oForm->SetParamsContainer($sId);
|
||||
@@ -148,7 +257,7 @@ abstract class Dashboard
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function Save()
|
||||
{
|
||||
|
||||
@@ -168,12 +277,32 @@ abstract class Dashboard
|
||||
{
|
||||
return $this->sTitle;
|
||||
}
|
||||
|
||||
|
||||
public function SetTitle($sTitle)
|
||||
{
|
||||
$this->sTitle = $sTitle;
|
||||
}
|
||||
|
||||
|
||||
public function GetAutoReload()
|
||||
{
|
||||
return $this->bAutoReload;
|
||||
}
|
||||
|
||||
public function SetAutoReload($bAutoReload)
|
||||
{
|
||||
$this->bAutoReload = $bAutoReload;
|
||||
}
|
||||
|
||||
public function GetAutoReloadInterval()
|
||||
{
|
||||
return $this->iAutoReloadSec;
|
||||
}
|
||||
|
||||
public function SetAutoReloadInterval($iAutoReloadSec)
|
||||
{
|
||||
$this->iAutoReloadSec = max(5, (int)$iAutoReloadSec);
|
||||
}
|
||||
|
||||
public function AddDashlet($oDashlet)
|
||||
{
|
||||
$sId = $this->GetNewDashletId();
|
||||
@@ -218,24 +347,61 @@ abstract class Dashboard
|
||||
$oPage->add('</div>');
|
||||
|
||||
$oForm = new DesignerForm();
|
||||
|
||||
$oField = new DesignerHiddenField('dashboard_id', '', $this->sId);
|
||||
$oForm->AddField($oField);
|
||||
|
||||
$oField = new DesignerLongTextField('dashboard_title', Dict::S('UI:DashboardEdit:DashboardTitle'), $this->sTitle);
|
||||
$oForm->AddField($oField);
|
||||
|
||||
$oField = new DesignerBooleanField('auto_reload', Dict::S('UI:DashboardEdit:AutoReload'), $this->bAutoReload);
|
||||
$oForm->AddField($oField);
|
||||
|
||||
$oField = new DesignerTextField('auto_reload_sec', Dict::S('UI:DashboardEdit:AutoReloadSec'), $this->iAutoReloadSec);
|
||||
$oField->SetValidationPattern('^$|^0*([5-9]|[1-9][0-9]+)$'); // Can be empty, or a number > 4
|
||||
$oForm->AddField($oField);
|
||||
|
||||
$this->SetFormParams($oForm);
|
||||
$oForm->RenderAsPropertySheet($oPage, false, ':itop-dashboard');
|
||||
|
||||
$oForm->RenderAsPropertySheet($oPage, false, '.itop-dashboard');
|
||||
|
||||
$oPage->add('</div>');
|
||||
|
||||
$sRateTitle = addslashes(Dict::S('UI:DashboardEdit:AutoReloadSec+'));
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
$('#select_layout').buttonset();
|
||||
$('#select_layout input').click( function() {
|
||||
var sLayoutClass = $(this).val();
|
||||
$(':itop-dashboard').dashboard('option', {layout_class: sLayoutClass});
|
||||
// Note: the title gets deleted by the validation mechanism
|
||||
$("#attr_auto_reload_sec").tooltip({items: 'input', content: '$sRateTitle'});
|
||||
$("#attr_auto_reload_sec").prop('disabled', !$('#attr_auto_reload').is(':checked'));
|
||||
|
||||
$('#attr_auto_reload').change( function(ev) {
|
||||
$("#attr_auto_reload_sec").prop('disabled', !$(this).is(':checked'));
|
||||
} );
|
||||
$('#row_attr_dashboard_title').property_field('option', {parent_selector: ':itop-dashboard', auto_apply: false, 'do_apply': function() {
|
||||
var sTitle = $('#attr_dashboard_title').val();
|
||||
$(':itop-dashboard').dashboard('option', {title: sTitle});
|
||||
return true;
|
||||
}
|
||||
|
||||
$('#select_layout').buttonset();
|
||||
$('#select_dashlet').droppable({
|
||||
accept: '.dashlet',
|
||||
drop: function(event, ui) {
|
||||
$( this ).find( ".placeholder" ).remove();
|
||||
var oDashlet = ui.draggable.data('itopDashlet');
|
||||
oDashlet._remove_dashlet();
|
||||
},
|
||||
});
|
||||
|
||||
$('#event_bus').bind('dashlet-selected', function(event, data){
|
||||
var sDashletId = data.dashlet_id;
|
||||
var sPropId = 'dashlet_properties_'+sDashletId;
|
||||
$('.dashlet_properties').each(function() {
|
||||
var sId = $(this).attr('id');
|
||||
var bShow = (sId == sPropId);
|
||||
if (bShow)
|
||||
{
|
||||
$(this).show();
|
||||
}
|
||||
else
|
||||
{
|
||||
$(this).hide();
|
||||
}
|
||||
});
|
||||
});
|
||||
EOF
|
||||
);
|
||||
@@ -270,7 +436,6 @@ EOF
|
||||
|
||||
$oPage->add('</div>');
|
||||
$oPage->add_ready_script("$('.dashlet_icon').draggable({helper: 'clone', appendTo: 'body', zIndex: 10000, revert:'invalid'});");
|
||||
$oPage->add_ready_script("$('.layout_cell').droppable({accept:'.dashlet_icon', hoverClass:'dragHover'});");
|
||||
}
|
||||
|
||||
public function RenderDashletsProperties($oPage)
|
||||
@@ -290,7 +455,7 @@ EOF
|
||||
$oPage->add('<div class="dashlet_properties" id="dashlet_properties_'.$sId.'" style="display:none">');
|
||||
$oForm = $oDashlet->GetForm();
|
||||
$this->SetFormParams($oForm);
|
||||
$oForm->RenderAsPropertySheet($oPage, false, ':itop-dashboard');
|
||||
$oForm->RenderAsPropertySheet($oPage, false, '.itop-dashboard');
|
||||
$oPage->add('</div>');
|
||||
}
|
||||
}
|
||||
@@ -319,11 +484,12 @@ EOF
|
||||
class RuntimeDashboard extends Dashboard
|
||||
{
|
||||
protected $bCustomized;
|
||||
|
||||
|
||||
public function __construct($sId)
|
||||
{
|
||||
parent::__construct($sId);
|
||||
$this->bCustomized = false;
|
||||
$this->oMetaModel = new ModelReflectionRuntime();
|
||||
}
|
||||
|
||||
public function SetCustomFlag($bCustomized)
|
||||
@@ -377,37 +543,34 @@ class RuntimeDashboard extends Dashboard
|
||||
}
|
||||
}
|
||||
|
||||
public function Render($oPage, $bEditMode = false, $aExtraParams = array())
|
||||
public function RenderEditionTools($oPage)
|
||||
{
|
||||
parent::Render($oPage, $bEditMode, $aExtraParams);
|
||||
if (!$bEditMode)
|
||||
$sEditMenu = "<td><span id=\"DashboardMenu\"><ul><li><img src=\"../images/edit.png\"><ul>";
|
||||
|
||||
$aActions = array();
|
||||
$oEdit = new JSPopupMenuItem('UI:Dashboard:Edit', Dict::S('UI:Dashboard:Edit'), "return EditDashboard('{$this->sId}')");
|
||||
$aActions[$oEdit->GetUID()] = $oEdit->GetMenuItem();
|
||||
|
||||
if ($this->bCustomized)
|
||||
{
|
||||
$sEditMenu = "<td><span id=\"DashboardMenu\"><ul><li><img src=\"../images/edit.png\"><ul>";
|
||||
|
||||
$aActions = array();
|
||||
$oEdit = new JSPopupMenuItem('UI:Dashboard:Edit', Dict::S('UI:Dashboard:Edit'), "return EditDashboard('{$this->sId}')");
|
||||
$aActions[$oEdit->GetUID()] = $oEdit->GetMenuItem();
|
||||
$oRevert = new JSPopupMenuItem('UI:Dashboard:RevertConfirm', Dict::S('UI:Dashboard:Revert'),
|
||||
"if (confirm('".addslashes(Dict::S('UI:Dashboard:RevertConfirm'))."')) return RevertDashboard('{$this->sId}'); else return false");
|
||||
$aActions[$oRevert->GetUID()] = $oRevert->GetMenuItem();
|
||||
}
|
||||
utils::GetPopupMenuItems($oPage, iPopupMenuExtension::MENU_DASHBOARD_ACTIONS, $this, $aActions);
|
||||
$sEditMenu .= $oPage->RenderPopupMenuItems($aActions);
|
||||
|
||||
|
||||
if ($this->bCustomized)
|
||||
{
|
||||
$oRevert = new JSPopupMenuItem('UI:Dashboard:RevertConfirm', Dict::S('UI:Dashboard:Revert'),
|
||||
"if (confirm('".addslashes(Dict::S('UI:Dashboard:RevertConfirm'))."')) return RevertDashboard('{$this->sId}'); else return false");
|
||||
$aActions[$oRevert->GetUID()] = $oRevert->GetMenuItem();
|
||||
}
|
||||
utils::GetPopupMenuItems($oPage, iPopupMenuExtension::MENU_DASHBOARD_ACTIONS, $this, $aActions);
|
||||
$sEditMenu .= $oPage->RenderPopupMenuItems($aActions);
|
||||
|
||||
|
||||
$sEditMenu = addslashes($sEditMenu);
|
||||
//$sEditBtn = addslashes('<div style="display: inline-block; height: 55px; width:200px;vertical-align:center;line-height:60px;text-align:left;"><button onclick="EditDashboard(\''.$this->sId.'\');">Edit This Page</button></div>');
|
||||
$oPage->add_ready_script(
|
||||
$sEditMenu = addslashes($sEditMenu);
|
||||
//$sEditBtn = addslashes('<div style="display: inline-block; height: 55px; width:200px;vertical-align:center;line-height:60px;text-align:left;"><button onclick="EditDashboard(\''.$this->sId.'\');">Edit This Page</button></div>');
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
$('#logOffBtn').parent().before('$sEditMenu');
|
||||
$('#DashboardMenu>ul').popupmenu();
|
||||
|
||||
EOF
|
||||
);
|
||||
$oPage->add_script(
|
||||
);
|
||||
$oPage->add_script(
|
||||
<<<EOF
|
||||
function EditDashboard(sId)
|
||||
{
|
||||
@@ -430,10 +593,42 @@ function RevertDashboard(sId)
|
||||
return false;
|
||||
}
|
||||
EOF
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function RenderProperties($oPage)
|
||||
{
|
||||
parent::RenderProperties($oPage);
|
||||
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
$('#select_layout input').click( function() {
|
||||
var sLayoutClass = $(this).val();
|
||||
$('.itop-dashboard').runtimedashboard('option', {layout_class: sLayoutClass});
|
||||
} );
|
||||
$('#row_attr_dashboard_title').property_field('option', {parent_selector: '.itop-dashboard', auto_apply: false, 'do_apply': function() {
|
||||
var sTitle = $('#attr_dashboard_title').val();
|
||||
$('.itop-dashboard').runtimedashboard('option', {title: sTitle});
|
||||
return true;
|
||||
}
|
||||
});
|
||||
$('#row_attr_auto_reload').property_field('option', {parent_selector: '.itop-dashboard', auto_apply: true, 'do_apply': function() {
|
||||
var bAutoReload = $('#attr_auto_reload').is(':checked');
|
||||
$('.itop-dashboard').runtimedashboard('option', {auto_reload: bAutoReload});
|
||||
return true;
|
||||
}
|
||||
});
|
||||
$('#row_attr_auto_reload_sec').property_field('option', {parent_selector: '.itop-dashboard', auto_apply: true, 'do_apply': function() {
|
||||
var iAutoReloadSec = $('#attr_auto_reload_sec').val();
|
||||
$('.itop-dashboard').runtimedashboard('option', {auto_reload_sec: iAutoReloadSec});
|
||||
return true;
|
||||
}
|
||||
});
|
||||
EOF
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function RenderEditor($oPage)
|
||||
{
|
||||
$oPage->add('<div id="dashboard_editor">');
|
||||
@@ -454,6 +649,8 @@ EOF
|
||||
|
||||
$sId = addslashes($this->sId);
|
||||
$sLayoutClass = addslashes($this->sLayoutClass);
|
||||
$sAutoReload = $this->bAutoReload ? 'true' : 'false';
|
||||
$sAutoReloadSec = (string) $this->iAutoReloadSec;
|
||||
$sTitle = addslashes($this->sTitle);
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php';
|
||||
|
||||
@@ -472,7 +669,7 @@ $('#dashboard_editor').dialog({
|
||||
title: '$sDialogTitle',
|
||||
buttons: [
|
||||
{ text: "$sOkButtonLabel", click: function() {
|
||||
var oDashboard = $(':itop-dashboard').data('dashboard');
|
||||
var oDashboard = $('.itop-dashboard').data('itopRuntimedashboard');
|
||||
if (oDashboard.is_dirty())
|
||||
{
|
||||
if (!confirm('$sAutoApplyConfirmationMessage'))
|
||||
@@ -488,7 +685,7 @@ $('#dashboard_editor').dialog({
|
||||
oDashboard.save();
|
||||
} },
|
||||
{ text: "$sCancelButtonLabel", click: function() {
|
||||
var oDashboard = $(':itop-dashboard').data('dashboard');
|
||||
var oDashboard = $('.itop-dashboard').data('itopRuntimedashboard');
|
||||
if (oDashboard.is_modified())
|
||||
{
|
||||
if (!confirm('$sCancelConfirmationMessage'))
|
||||
@@ -504,40 +701,14 @@ $('#dashboard_editor').dialog({
|
||||
close: function() { $(this).remove(); }
|
||||
});
|
||||
|
||||
$('#dashboard_editor .ui-layout-center').dashboard({
|
||||
$('#dashboard_editor .ui-layout-center').runtimedashboard({
|
||||
dashboard_id: '$sId', layout_class: '$sLayoutClass', title: '$sTitle',
|
||||
auto_reload: $sAutoReload, auto_reload_sec: $sAutoReloadSec,
|
||||
submit_to: '$sUrl', submit_parameters: {operation: 'save_dashboard'},
|
||||
render_to: '$sUrl', render_parameters: {operation: 'render_dashboard'},
|
||||
new_dashlet_parameters: {operation: 'new_dashlet'}
|
||||
});
|
||||
|
||||
$('#select_dashlet').droppable({
|
||||
accept: '.dashlet',
|
||||
drop: function(event, ui) {
|
||||
$( this ).find( ".placeholder" ).remove();
|
||||
var oDashlet = ui.draggable;
|
||||
oDashlet.remove();
|
||||
},
|
||||
});
|
||||
|
||||
$('#event_bus').bind('dashlet-selected', function(event, data){
|
||||
var sDashletId = data.dashlet_id;
|
||||
var sPropId = 'dashlet_properties_'+sDashletId;
|
||||
$('.dashlet_properties').each(function() {
|
||||
var sId = $(this).attr('id');
|
||||
var bShow = (sId == sPropId);
|
||||
if (bShow)
|
||||
{
|
||||
$(this).show();
|
||||
}
|
||||
else
|
||||
{
|
||||
$(this).hide();
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
dashboard_prop_size = GetUserPreference('dashboard_prop_size', 350);
|
||||
$('#dashboard_editor').layout({
|
||||
east: {
|
||||
@@ -558,7 +729,7 @@ $('#dashboard_editor').layout({
|
||||
window.onbeforeunload = function() {
|
||||
if (!window.bLeavingOnUserAction)
|
||||
{
|
||||
var oDashboard = $(':itop-dashboard').data('dashboard');
|
||||
var oDashboard = $('.itop-dashboard').data('itopRuntimedashboard');
|
||||
if (oDashboard)
|
||||
{
|
||||
if (oDashboard.is_dirty())
|
||||
@@ -639,7 +810,8 @@ EOF
|
||||
foreach($aDashlets as $sDashletClass => $aDashletInfo)
|
||||
{
|
||||
$oSubForm = new DesignerForm();
|
||||
$oDashlet = new $sDashletClass(0);
|
||||
$oMetaModel = new ModelReflectionRuntime();
|
||||
$oDashlet = new $sDashletClass($oMetaModel, 0);
|
||||
$oDashlet->GetPropertiesFieldsFromOQL($oSubForm, $sOQL);
|
||||
|
||||
$oSelectorField->AddSubForm($oSubForm, $aDashletInfo['label'], $aDashletInfo['class']);
|
||||
|
||||
@@ -1,4 +1,29 @@
|
||||
<?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/>
|
||||
|
||||
/**
|
||||
* Dashboard presentation
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
abstract class DashboardLayout
|
||||
{
|
||||
public function __construct()
|
||||
@@ -82,14 +107,16 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
|
||||
$oPage->add('<table style="width:100%"><tbody>');
|
||||
$iCellIdx = 0;
|
||||
$fColSize = 100 / $this->iNbCols;
|
||||
$sStyle = $bEditMode ? 'style="border: 1px #ccc dashed; width:'.$fColSize.'%;" class="layout_cell edit_mode"' : 'style="width: '.$fColSize.'%;" class="dashboard"';
|
||||
$sStyle = $bEditMode ? 'border: 1px #ccc dashed; width:'.$fColSize.'%;' : 'width: '.$fColSize.'%;';
|
||||
$sClass = $bEditMode ? 'layout_cell edit_mode' : 'dashboard';
|
||||
$iNbRows = ceil(count($aCells) / $this->iNbCols);
|
||||
for($iRows = 0; $iRows < $iNbRows; $iRows++)
|
||||
{
|
||||
$oPage->add('<tr>');
|
||||
for($iCols = 0; $iCols < $this->iNbCols; $iCols++)
|
||||
{
|
||||
$oPage->add("<td $sStyle>");
|
||||
$sCellClass = ($iRows == $iNbRows-1) ? $sClass.' layout_last_used_rank' : $sClass;
|
||||
$oPage->add("<td style=\"$sStyle\" class=\"$sCellClass\" data-dashboard-cell-index=\"$iCellIdx\">");
|
||||
if (array_key_exists($iCellIdx, $aCells))
|
||||
{
|
||||
$aDashlets = $aCells[$iCellIdx];
|
||||
@@ -119,7 +146,7 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
|
||||
}
|
||||
if ($bEditMode) // Add one row for extensibility
|
||||
{
|
||||
$sStyle = 'style="border: 1px #ccc dashed; width:'.$fColSize.'%;" class="layout_cell edit_mode layout_extension"';
|
||||
$sStyle = 'style="border: 1px #ccc dashed; width:'.$fColSize.'%;" class="layout_cell edit_mode layout_extension" data-dashboard-cell-index="'.$iCellIdx.'"';
|
||||
$oPage->add('<tr>');
|
||||
for($iCols = 0; $iCols < $this->iNbCols; $iCols++)
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,25 +1,25 @@
|
||||
<?php
|
||||
// Copyright (C) 2012 Combodo SARL
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
/**
|
||||
* Data Table to display a set of objects in a tabular manner in HTML
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html GPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
class DataTable
|
||||
@@ -40,7 +40,7 @@ class DataTable
|
||||
*/
|
||||
public function __construct($iListId, $oSet, $aClassAliases, $sTableId = null)
|
||||
{
|
||||
$this->iListId = $iListId;
|
||||
$this->iListId = utils::GetSafeId($iListId); // Make a "safe" ID for jQuery
|
||||
$this->oSet = $oSet;
|
||||
$this->aClassAliases = $aClassAliases;
|
||||
$this->sTableId = $sTableId;
|
||||
@@ -60,6 +60,10 @@ class DataTable
|
||||
{
|
||||
// Custom settings overload the default ones
|
||||
$this->bUseCustomSettings = true;
|
||||
if ($this->oDefaultSettings->iDefaultPageSize == 0)
|
||||
{
|
||||
$oCustomSettings->iDefaultPageSize = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -71,8 +75,40 @@ class DataTable
|
||||
$this->oSet->SetLimit($oCustomSettings->iDefaultPageSize);
|
||||
}
|
||||
$this->oSet->SetOrderBy($oCustomSettings->GetSortOrder());
|
||||
|
||||
|
||||
// Load only the requested columns
|
||||
$aColumnsToLoad = array();
|
||||
foreach($oCustomSettings->aColumns as $sAlias => $aColumnsInfo)
|
||||
{
|
||||
foreach($aColumnsInfo as $sAttCode => $aData)
|
||||
{
|
||||
if ($sAttCode != '_key_')
|
||||
{
|
||||
if ($aData['checked'])
|
||||
{
|
||||
$aColumnsToLoad[$sAlias][] = $sAttCode;
|
||||
}
|
||||
else
|
||||
{
|
||||
// See if this column is a must to load
|
||||
$sClass = $this->aClassAliases[$sAlias];
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
if ($oAttDef->alwaysLoadInTables())
|
||||
{
|
||||
$aColumnsToLoad[$sAlias][] = $sAttCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->oSet->OptimizeColumnLoad($aColumnsToLoad);
|
||||
|
||||
|
||||
$bToolkitMenu = true;
|
||||
if (isset($aExtraParams['toolkit_menu']))
|
||||
{
|
||||
$bToolkitMenu = (bool) $aExtraParams['toolkit_menu'];
|
||||
}
|
||||
if (UserRights::IsPortalUser())
|
||||
{
|
||||
// Portal users have a limited access to data, for now they can only see what's configured for them
|
||||
@@ -172,6 +208,8 @@ class DataTable
|
||||
if ($iPageSize < 1) // Display all
|
||||
{
|
||||
$sPagerStyle = 'style="display:none"'; // no limit: display the full table, so hide the "pager" UI
|
||||
// WARNING: mPDF does not take the "display" style into account
|
||||
// when applied to a <td> or a <table> tag, so make sure you apply this to a div
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -222,7 +260,8 @@ class DataTable
|
||||
$sSelectionMode = ($iNbPages == 1) ? '' : 'positive';
|
||||
$sHtml =
|
||||
<<<EOF
|
||||
<td $sPagerStyle colspan="2">
|
||||
<td colspan="2">
|
||||
<div $sPagerStyle>
|
||||
<table id="pager{$this->iListId}" class="pager"><tr>
|
||||
<td>$sPages</td>
|
||||
<td><img src="../images/first.png" class="first"/></td>
|
||||
@@ -235,6 +274,7 @@ class DataTable
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
EOF;
|
||||
return $sHtml;
|
||||
@@ -252,13 +292,13 @@ EOF;
|
||||
{
|
||||
$sMenuTitle = Dict::S('UI:ConfigureThisList');
|
||||
$sHtml = '<div class="itop_popup toolkit_menu" id="tk_'.$this->iListId.'"><ul><li><img src="../images/toolkit_menu.png"><ul>';
|
||||
|
||||
|
||||
$oMenuItem1 = new JSPopupMenuItem('iTop::ConfigureList', $sMenuTitle, "$('#datatable_dlg_".$this->iListId."').dialog('open');");
|
||||
$aActions = array(
|
||||
$oMenuItem1->GetUID() => $oMenuItem1->GetMenuItem(),
|
||||
);
|
||||
$this->oSet->Rewind();
|
||||
utils::GetPopupMenuItems($oPage, iPopupMenuExtension::MENU_OBJLIST_TOOLKIT, $this->oSet, $aActions);
|
||||
utils::GetPopupMenuItems($oPage, iPopupMenuExtension::MENU_OBJLIST_TOOLKIT, $this->oSet, $aActions, $this->sTableId, $this->iListId);
|
||||
$this->oSet->Rewind();
|
||||
$sHtml .= $oPage->RenderPopupMenuItems($aActions);
|
||||
return $sHtml;
|
||||
@@ -341,6 +381,12 @@ EOF;
|
||||
|
||||
protected function GetHTMLTableValues($aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams)
|
||||
{
|
||||
$bLocalize = true;
|
||||
if (isset($aExtraParams['localize_values']))
|
||||
{
|
||||
$bLocalize = (bool) $aExtraParams['localize_values'];
|
||||
}
|
||||
|
||||
$aValues = array();
|
||||
$this->oSet->Seek(0);
|
||||
$iMaxObjects = $iPageSize;
|
||||
@@ -350,41 +396,61 @@ EOF;
|
||||
$aRow = array();
|
||||
foreach($this->aClassAliases as $sAlias => $sClassName)
|
||||
{
|
||||
$sHilightClass = $aObjects[$sAlias]->GetHilightClass();
|
||||
if ($sHilightClass != '')
|
||||
if (is_object($aObjects[$sAlias]))
|
||||
{
|
||||
$aRow['@class'] = $sHilightClass;
|
||||
}
|
||||
if ((($sSelectMode == 'single') || ($sSelectMode == 'multiple')) && $bFirstObject)
|
||||
{
|
||||
if (array_key_exists('selection_enabled', $aExtraParams) && isset($aExtraParams['selection_enabled'][$aObjects[$sAlias]->GetKey()]))
|
||||
$sHilightClass = $aObjects[$sAlias]->GetHilightClass();
|
||||
if ($sHilightClass != '')
|
||||
{
|
||||
$sDisabled = ($aExtraParams['selection_enabled'][$aObjects[$sAlias]->GetKey()]) ? '' : ' disabled="disabled"';
|
||||
$aRow['@class'] = $sHilightClass;
|
||||
}
|
||||
else
|
||||
if ((($sSelectMode == 'single') || ($sSelectMode == 'multiple')) && $bFirstObject)
|
||||
{
|
||||
$sDisabled = '';
|
||||
}
|
||||
if ($sSelectMode == 'single')
|
||||
{
|
||||
$aRow['form::select'] = "<input type=\"radio\" $sDisabled class=\"selectList{$this->iListId}\" name=\"selectObject\" value=\"".$aObjects[$sAlias]->GetKey()."\"></input>";
|
||||
}
|
||||
else
|
||||
{
|
||||
$aRow['form::select'] = "<input type=\"checkBox\" $sDisabled class=\"selectList{$this->iListId}\" name=\"selectObject[]\" value=\"".$aObjects[$sAlias]->GetKey()."\"></input>";
|
||||
}
|
||||
}
|
||||
foreach($aColumns[$sAlias] as $sAttCode => $aData)
|
||||
{
|
||||
if ($aData['checked'])
|
||||
{
|
||||
if ($sAttCode == '_key_')
|
||||
if (array_key_exists('selection_enabled', $aExtraParams) && isset($aExtraParams['selection_enabled'][$aObjects[$sAlias]->GetKey()]))
|
||||
{
|
||||
$aRow['key_'.$sAlias] = $aObjects[$sAlias]->GetHyperLink();
|
||||
$sDisabled = ($aExtraParams['selection_enabled'][$aObjects[$sAlias]->GetKey()]) ? '' : ' disabled="disabled"';
|
||||
}
|
||||
else
|
||||
{
|
||||
$aRow[$sAttCode.'_'.$sAlias] = $aObjects[$sAlias]->GetAsHTML($sAttCode);
|
||||
$sDisabled = '';
|
||||
}
|
||||
if ($sSelectMode == 'single')
|
||||
{
|
||||
$aRow['form::select'] = "<input type=\"radio\" $sDisabled class=\"selectList{$this->iListId}\" name=\"selectObject\" value=\"".$aObjects[$sAlias]->GetKey()."\"></input>";
|
||||
}
|
||||
else
|
||||
{
|
||||
$aRow['form::select'] = "<input type=\"checkBox\" $sDisabled class=\"selectList{$this->iListId}\" name=\"selectObject[]\" value=\"".$aObjects[$sAlias]->GetKey()."\"></input>";
|
||||
}
|
||||
}
|
||||
foreach($aColumns[$sAlias] as $sAttCode => $aData)
|
||||
{
|
||||
if ($aData['checked'])
|
||||
{
|
||||
if ($sAttCode == '_key_')
|
||||
{
|
||||
$aRow['key_'.$sAlias] = $aObjects[$sAlias]->GetHyperLink();
|
||||
}
|
||||
else
|
||||
{
|
||||
$aRow[$sAttCode.'_'.$sAlias] = $aObjects[$sAlias]->GetAsHTML($sAttCode, $bLocalize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach($aColumns[$sAlias] as $sAttCode => $aData)
|
||||
{
|
||||
if ($aData['checked'])
|
||||
{
|
||||
if ($sAttCode == '_key_')
|
||||
{
|
||||
$aRow['key_'.$sAlias] = '';
|
||||
}
|
||||
else
|
||||
{
|
||||
$aRow[$sAttCode.'_'.$sAlias] = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -476,18 +542,22 @@ EOF;
|
||||
$aDefaultSort[] = "[".($iColOffset).",".($bAscending ? '0' : '1')."]";
|
||||
}
|
||||
}
|
||||
$sSortList = '';
|
||||
$sFakeSortList = '';
|
||||
if (count($aDefaultSort) > 0)
|
||||
{
|
||||
$sSortList = ', sortList: ['.implode(',', $aDefaultSort).']';
|
||||
$sFakeSortList = '['.implode(',', $aDefaultSort).']';
|
||||
}
|
||||
$sOQL = addslashes($this->oSet->GetFilter()->serialize());
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
var oTable = $('#{$this->iListId} table.listResults');
|
||||
oTable.tablesorter( { $sHeaders widgets: ['myZebra', 'truncatedList'] $sSortList} ).tablesorterPager({container: $('#pager{$this->iListId}'), totalRows:$iCount, size: $iPageSize, filter: '$sOQL', extra_params: '$sExtraParams', select_mode: '$sSelectModeJS', displayKey: $sDisplayKey, columns: $sJSColumns, class_aliases: $sJSClassAliases $sCssCount});
|
||||
oTable.tablesorter( { $sHeaders widgets: ['myZebra', 'truncatedList']} ).tablesorterPager({container: $('#pager{$this->iListId}'), totalRows:$iCount, size: $iPageSize, filter: '$sOQL', extra_params: '$sExtraParams', select_mode: '$sSelectModeJS', displayKey: $sDisplayKey, columns: $sJSColumns, class_aliases: $sJSClassAliases $sCssCount});
|
||||
EOF
|
||||
);
|
||||
if ($sFakeSortList != '')
|
||||
{
|
||||
$oPage->add_ready_script("oTable.trigger(\"fakesorton\", [$sFakeSortList]);");
|
||||
}
|
||||
//if ($iNbPages == 1)
|
||||
if (false)
|
||||
{
|
||||
@@ -694,7 +764,7 @@ class DataTableSettings implements Serializable
|
||||
}
|
||||
}
|
||||
|
||||
static public function GetTableSettings($aClassAliases, $sTableId = null)
|
||||
static public function GetTableSettings($aClassAliases, $sTableId = null, $bOnlyOnTable = false)
|
||||
{
|
||||
$pref = null;
|
||||
$oSettings = new DataTableSettings($aClassAliases, $sTableId);
|
||||
@@ -707,8 +777,11 @@ class DataTableSettings implements Serializable
|
||||
|
||||
if ($pref == null)
|
||||
{
|
||||
// Try the global preferred values for this class / set of classes
|
||||
$pref = appUserPreferences::GetPref($oSettings->GetPrefsKey(null), null);
|
||||
if (!$bOnlyOnTable)
|
||||
{
|
||||
// Try the global preferred values for this class / set of classes
|
||||
$pref = appUserPreferences::GetPref($oSettings->GetPrefsKey(null), null);
|
||||
}
|
||||
if ($pref == null)
|
||||
{
|
||||
// no such settings, use the default values provided by the data model
|
||||
@@ -738,12 +811,13 @@ class DataTableSettings implements Serializable
|
||||
return $aSortOrder;
|
||||
}
|
||||
|
||||
public function Save()
|
||||
public function Save($sTargetTableId = null)
|
||||
{
|
||||
if ($this->sTableId == null) return false; // Cannot save, the table is not identified, use SaveAsDefault instead
|
||||
$sSaveId = is_null($sTargetTableId) ? $this->sTableId : $sTargetTableId;
|
||||
if ($sSaveId == null) return false; // Cannot save, the table is not identified, use SaveAsDefault instead
|
||||
|
||||
$sSettings = $this->serialize();
|
||||
appUserPreferences::SetPref($this->GetPrefsKey($this->sTableId), $sSettings);
|
||||
appUserPreferences::SetPref($this->GetPrefsKey($sSaveId), $sSettings);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
/**
|
||||
* DisplayBlock and derived class
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
require_once(APPROOT.'/application/webpage.class.inc.php');
|
||||
@@ -52,13 +52,18 @@ class DisplayBlock
|
||||
|
||||
public function __construct(DBObjectSearch $oFilter, $sStyle = 'list', $bAsynchronous = false, $aParams = array(), $oSet = null)
|
||||
{
|
||||
$this->m_oFilter = clone $oFilter;
|
||||
$this->m_oFilter = $oFilter->DeepClone();
|
||||
$this->m_aConditions = array();
|
||||
$this->m_sStyle = $sStyle;
|
||||
$this->m_bAsynchronous = $bAsynchronous;
|
||||
$this->m_aParams = $aParams;
|
||||
$this->m_oSet = $oSet;
|
||||
}
|
||||
|
||||
public function GetFilter()
|
||||
{
|
||||
return $this->m_oFilter;
|
||||
}
|
||||
/**
|
||||
* Constructs a DisplayBlock object from a DBObjectSet already in memory
|
||||
* @param $oSet DBObjectSet
|
||||
@@ -73,7 +78,14 @@ class DisplayBlock
|
||||
$aKeys[] = $oObject->GetKey();
|
||||
}
|
||||
$oSet->Rewind();
|
||||
$oDummyFilter->AddCondition('id', $aKeys, 'IN');
|
||||
if (count($aKeys) > 0)
|
||||
{
|
||||
$oDummyFilter->AddCondition('id', $aKeys, 'IN');
|
||||
}
|
||||
else
|
||||
{
|
||||
$oDummyFilter->AddCondition('id', 0, '=');
|
||||
}
|
||||
$oBlock = new DisplayBlock($oDummyFilter, $sStyle, false, $aParams); // DisplayBlocks built this way are synchronous
|
||||
return $oBlock;
|
||||
}
|
||||
@@ -192,6 +204,11 @@ class DisplayBlock
|
||||
$bAutoReload = false;
|
||||
if (isset($aExtraParams['auto_reload']))
|
||||
{
|
||||
if ($aExtraParams['auto_reload'] === true)
|
||||
{
|
||||
// Note: does not work in the switch (case true) because a positive number evaluates to true!!!
|
||||
$aExtraParams['auto_reload'] = 'standard';
|
||||
}
|
||||
switch($aExtraParams['auto_reload'])
|
||||
{
|
||||
case 'fast':
|
||||
@@ -201,16 +218,15 @@ class DisplayBlock
|
||||
|
||||
case 'standard':
|
||||
case 'true':
|
||||
case true:
|
||||
$bAutoReload = true;
|
||||
$iReloadInterval = MetaModel::GetConfig()->GetStandardReloadInterval()*1000;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (is_numeric($aExtraParams['auto_reload']))
|
||||
if (is_numeric($aExtraParams['auto_reload']) && ($aExtraParams['auto_reload'] > 0))
|
||||
{
|
||||
$bAutoReload = true;
|
||||
$iReloadInterval = $aExtraParams['auto_reload']*1000;
|
||||
$iReloadInterval = max(5, $aExtraParams['auto_reload'])*1000;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -245,7 +261,7 @@ class DisplayBlock
|
||||
);
|
||||
');
|
||||
}
|
||||
if ($bAutoReload)
|
||||
if (($bAutoReload) && ($this->m_sStyle != 'search')) // Search form do NOT auto-reload
|
||||
{
|
||||
$oPage->add_script('setInterval("ReloadBlock(\''.$sId.'\', \''.$this->m_sStyle.'\', \''.$sFilter.'\', \"'.$sExtraParams.'\")", '.$iReloadInterval.');');
|
||||
}
|
||||
@@ -300,23 +316,40 @@ class DisplayBlock
|
||||
}
|
||||
foreach($aFilterCodes as $sFilterCode)
|
||||
{
|
||||
$sExternalFilterValue = utils::ReadParam($sFilterCode, '', false, 'raw_data');
|
||||
$externalFilterValue = utils::ReadParam($sFilterCode, '', false, 'raw_data');
|
||||
$condition = null;
|
||||
if (isset($aExtraParams[$sFilterCode]))
|
||||
{
|
||||
$condition = $aExtraParams[$sFilterCode];
|
||||
}
|
||||
// else if ($bDoSearch && $sExternalFilterValue != "")
|
||||
if ($bDoSearch && $sExternalFilterValue != "")
|
||||
if ($bDoSearch && $externalFilterValue != "")
|
||||
{
|
||||
// Search takes precedence over context params...
|
||||
unset($aExtraParams[$sFilterCode]);
|
||||
$condition = trim($sExternalFilterValue);
|
||||
if (!is_array($externalFilterValue))
|
||||
{
|
||||
$condition = trim($externalFilterValue);
|
||||
}
|
||||
else if (count($externalFilterValue) == 1)
|
||||
{
|
||||
$condition = trim($externalFilterValue[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
$condition = $externalFilterValue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_null($condition))
|
||||
{
|
||||
$this->AddCondition($sFilterCode, $condition);
|
||||
$sOpCode = null; // default operator
|
||||
if (is_array($condition))
|
||||
{
|
||||
// Multiple values, add them as AND X IN (v1, v2, v3...)
|
||||
$sOpCode = 'IN';
|
||||
}
|
||||
|
||||
$this->AddCondition($sFilterCode, $condition, $sOpCode);
|
||||
}
|
||||
}
|
||||
if ($bDoSearch)
|
||||
@@ -373,7 +406,7 @@ class DisplayBlock
|
||||
|
||||
$aGroupBy = array();
|
||||
$aGroupBy['grouped_by_1'] = $oGroupByExp;
|
||||
$sSql = MetaModel::MakeGroupByQuery($this->m_oFilter, $aQueryParams, $aGroupBy);
|
||||
$sSql = MetaModel::MakeGroupByQuery($this->m_oFilter, $aQueryParams, $aGroupBy, true);
|
||||
$aRes = CMDBSource::QueryToArray($sSql);
|
||||
|
||||
$aGroupBy = array();
|
||||
@@ -397,7 +430,7 @@ class DisplayBlock
|
||||
foreach($aGroupBy as $iRow => $iCount)
|
||||
{
|
||||
// Build the search for this subset
|
||||
$oSubsetSearch = clone $this->m_oFilter;
|
||||
$oSubsetSearch = $this->m_oFilter->DeepClone();
|
||||
$oCondition = new BinaryExpression($oGroupByExp, '=', new ScalarExpression($aValues[$iRow]));
|
||||
$oSubsetSearch->AddConditionExpression($oCondition);
|
||||
$sFilter = urlencode($oSubsetSearch->serialize());
|
||||
@@ -475,7 +508,7 @@ class DisplayBlock
|
||||
|
||||
$sHtml .= "<table>\n";
|
||||
// Construct a new (parametric) query that will return the content of this block
|
||||
$oBlockFilter = clone $this->m_oFilter;
|
||||
$oBlockFilter = $this->m_oFilter->DeepClone();
|
||||
$aExpressions = array();
|
||||
$index = 0;
|
||||
foreach($aGroupByFields as $aField)
|
||||
@@ -568,11 +601,13 @@ class DisplayBlock
|
||||
{
|
||||
if ((UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY) == UR_ALLOWED_YES))
|
||||
{
|
||||
$sLinkTarget = '';
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sParams = $oAppContext->GetForLink();
|
||||
// 1:n links, populate the target object as a default value when creating a new linked object
|
||||
if (isset($aExtraParams['target_attr']))
|
||||
{
|
||||
$sLinkTarget = ' target="_blank" ';
|
||||
$aExtraParams['default'][$aExtraParams['target_attr']] = $aExtraParams['object_id'];
|
||||
}
|
||||
$sDefault = '';
|
||||
@@ -584,7 +619,7 @@ class DisplayBlock
|
||||
}
|
||||
}
|
||||
|
||||
$sHtml .= $oPage->GetP("<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=new&class=$sClass&$sParams{$sDefault}\">".Dict::Format('UI:ClickToCreateNew', Metamodel::GetName($sClass))."</a>\n");
|
||||
$sHtml .= $oPage->GetP("<a{$sLinkTarget} href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=new&class=$sClass&$sParams{$sDefault}\">".Dict::Format('UI:ClickToCreateNew', Metamodel::GetName($sClass))."</a>\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -707,11 +742,11 @@ class DisplayBlock
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sStateAttrCode);
|
||||
foreach($aStates as $sStateValue)
|
||||
{
|
||||
$oFilter = clone($this->m_oFilter);
|
||||
$oFilter = $this->m_oFilter->DeepClone();
|
||||
$oFilter->AddCondition($sStateAttrCode, $sStateValue, '=');
|
||||
$oSet = new DBObjectSet($oFilter);
|
||||
$aCounts[$sStateValue] = $oSet->Count();
|
||||
$aStateLabels[$sStateValue] = $oAttDef->GetValueLabel($sStateValue);
|
||||
$aStateLabels[$sStateValue] = htmlentities($oAttDef->GetValueLabel($sStateValue), ENT_QUOTES, 'UTF-8');
|
||||
if ($aCounts[$sStateValue] == 0)
|
||||
{
|
||||
$aCounts[$sStateValue] = '-';
|
||||
@@ -733,9 +768,72 @@ class DisplayBlock
|
||||
break;
|
||||
|
||||
case 'csv':
|
||||
$sHtml .= "<textarea style=\"width:95%;height:98%\">\n";
|
||||
$sHtml .= cmdbAbstractObject::GetSetAsCSV($this->m_oSet);
|
||||
$bAdvancedMode = utils::ReadParam('advanced', false);
|
||||
|
||||
$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';
|
||||
if ($bAdvancedMode)
|
||||
{
|
||||
$sDownloadLink .= '&fields_advanced=1';
|
||||
$sChecked = 'CHECKED';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sLinkToToggle = $sLinkToToggle.'&advanced=1';
|
||||
$sChecked = '';
|
||||
}
|
||||
|
||||
/*
|
||||
$sCSVData = cmdbAbstractObject::GetSetAsCSV($this->m_oSet, array('fields_advanced' => $bAdvancedMode));
|
||||
$sCharset = MetaModel::GetConfig()->Get('csv_file_default_charset');
|
||||
if ($sCharset == 'UTF-8')
|
||||
{
|
||||
$bLostChars = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sConverted = @iconv('UTF-8', $sCharset, $sCSVData);
|
||||
$sRestored = @iconv($sCharset, 'UTF-8', $sConverted);
|
||||
$bLostChars = ($sRestored != $sCSVData);
|
||||
}
|
||||
|
||||
if ($bLostChars)
|
||||
{
|
||||
$sCharsetNotice = " <span id=\"csv_charset_issue\">";
|
||||
$sCharsetNotice .= '<img src="../images/error.png" style="vertical-align:middle"/>';
|
||||
$sCharsetNotice .= "</span>";
|
||||
|
||||
$sTip = "<p>".htmlentities(Dict::S('UI:CSVExport:LostChars'), ENT_QUOTES, 'UTF-8')."</p>";
|
||||
$sTip .= "<p>".htmlentities(Dict::Format('UI:CSVExport:LostChars+', $sCharset), ENT_QUOTES, 'UTF-8')."</p>";
|
||||
$oPage->add_ready_script("$('#csv_charset_issue').qtip( { content: '$sTip', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );");
|
||||
}
|
||||
else
|
||||
{
|
||||
$sCharsetNotice = '';
|
||||
}
|
||||
|
||||
*/
|
||||
$sCharsetNotice = false;
|
||||
$sHtml .= "<div>";
|
||||
$sHtml .= '<table style="width:100%" class="transparent">';
|
||||
$sHtml .= '<tr>';
|
||||
$sHtml .= '<td><a href="'.$sDownloadLink.'">'.Dict::Format('UI:Download-CSV', $sCsvFile).'</a>'.$sCharsetNotice.'</td>';
|
||||
$sHtml .= '<td style="text-align:right"><input type="checkbox" '.$sChecked.' onClick="window.location.href=\''.$sLinkToToggle.'\'"> '.Dict::S('UI:CSVExport:AdvancedMode').'</td>';
|
||||
$sHtml .= '</tr>';
|
||||
$sHtml .= '</table>';
|
||||
if ($bAdvancedMode)
|
||||
{
|
||||
$sHtml .= "<p>";
|
||||
$sHtml .= htmlentities(Dict::S('UI:CSVExport:AdvancedMode+'), ENT_QUOTES, 'UTF-8');
|
||||
$sHtml .= "</p>";
|
||||
}
|
||||
$sHtml .= "</div>";
|
||||
|
||||
$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('$sDownloadLink', {}, function(data) { $('#csv_content').html(data); $('#csv_content_loading').hide(); $('#csv_content').show();} );");
|
||||
break;
|
||||
|
||||
case 'modify':
|
||||
@@ -810,7 +908,7 @@ EOF
|
||||
|
||||
$aGroupBy = array();
|
||||
$aGroupBy['grouped_by_1'] = $oGroupByExp;
|
||||
$sSql = MetaModel::MakeGroupByQuery($this->m_oFilter, $aQueryParams, $aGroupBy);
|
||||
$sSql = MetaModel::MakeGroupByQuery($this->m_oFilter, $aQueryParams, $aGroupBy, true);
|
||||
$aRes = CMDBSource::QueryToArray($sSql);
|
||||
|
||||
$aGroupBy = array();
|
||||
@@ -833,7 +931,7 @@ EOF
|
||||
foreach($aGroupBy as $iRow => $iCount)
|
||||
{
|
||||
// Build the search for this subset
|
||||
$oSubsetSearch = clone $this->m_oFilter;
|
||||
$oSubsetSearch = $this->m_oFilter->DeepClone();
|
||||
$oCondition = new BinaryExpression($oGroupByExp, '=', new ScalarExpression($aValues[$iRow]));
|
||||
$oSubsetSearch->AddConditionExpression($oCondition);
|
||||
$aURLs[$idx] = $oSubsetSearch->serialize();
|
||||
@@ -885,7 +983,7 @@ EOF
|
||||
|
||||
$aGroupBy = array();
|
||||
$aGroupBy['grouped_by_1'] = $oGroupByExp;
|
||||
$sSql = MetaModel::MakeGroupByQuery($this->m_oFilter, $aQueryParams, $aGroupBy);
|
||||
$sSql = MetaModel::MakeGroupByQuery($this->m_oFilter, $aQueryParams, $aGroupBy, true);
|
||||
$aRes = CMDBSource::QueryToArray($sSql);
|
||||
|
||||
$aGroupBy = array();
|
||||
@@ -966,7 +1064,8 @@ EOF
|
||||
|
||||
$aGroupBy = array();
|
||||
$aGroupBy['grouped_by_1'] = $oGroupByExp;
|
||||
$sSql = MetaModel::MakeGroupByQuery($this->m_oFilter, $aQueryParams, $aGroupBy);
|
||||
|
||||
$sSql = MetaModel::MakeGroupByQuery($this->m_oFilter, $aQueryParams, $aGroupBy, true);
|
||||
$aRes = CMDBSource::QueryToArray($sSql);
|
||||
|
||||
$aGroupBy = array();
|
||||
@@ -1010,6 +1109,7 @@ EOF
|
||||
|
||||
$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 );
|
||||
@@ -1028,7 +1128,7 @@ EOF
|
||||
* Add a condition (restriction) to the current DBObjectSearch on which the display block is based
|
||||
* taking into account the hierarchical keys for which the condition is based on the 'below' operator
|
||||
*/
|
||||
protected function AddCondition($sFilterCode, $condition)
|
||||
protected function AddCondition($sFilterCode, $condition, $sOpCode = null)
|
||||
{
|
||||
// Workaround to an issue revealed whenever a condition on org_id is applied twice (with a hierarchy of organizations)
|
||||
// Moreover, it keeps the query as simple as possible
|
||||
@@ -1054,12 +1154,29 @@ EOF
|
||||
if ($sHierarchicalKeyCode !== false)
|
||||
{
|
||||
$oFilter = new DBObjectSearch($oAttDef->GetTargetClass());
|
||||
$oFilter->AddCondition('id', $condition);
|
||||
if (($sOpCode == 'IN') && is_array($condition))
|
||||
{
|
||||
$oFilter->AddConditionExpression(self::GetConditionIN($oFilter, 'id', $condition));
|
||||
}
|
||||
else
|
||||
{
|
||||
$oFilter->AddCondition('id', $condition);
|
||||
}
|
||||
$oHKFilter = new DBObjectSearch($oAttDef->GetTargetClass());
|
||||
$oHKFilter->AddCondition_PointingTo($oFilter, $sHierarchicalKeyCode, TREE_OPERATOR_BELOW); // Use the 'below' operator by default
|
||||
$this->m_oFilter->AddCondition_PointingTo($oHKFilter, $sFilterCode);
|
||||
$bConditionAdded = true;
|
||||
}
|
||||
else if (($sOpCode == 'IN') && is_array($condition))
|
||||
{
|
||||
$this->m_oFilter->AddConditionExpression(self::GetConditionIN($this->m_oFilter, $sFilterCode, $condition));
|
||||
$bConditionAdded = true;
|
||||
}
|
||||
}
|
||||
else if (($sOpCode == 'IN') && is_array($condition))
|
||||
{
|
||||
$this->m_oFilter->AddConditionExpression(self::GetConditionIN($this->m_oFilter, $sFilterCode, $condition));
|
||||
$bConditionAdded = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1069,6 +1186,15 @@ EOF
|
||||
$this->m_oFilter->AddCondition($sFilterCode, $condition); // Use the default 'loose' operator
|
||||
}
|
||||
}
|
||||
|
||||
static protected function GetConditionIN($oFilter, $sFilterCode, $condition)
|
||||
{
|
||||
$oField = new FieldExpression($sFilterCode, $oFilter->GetClassAlias());
|
||||
$sListExpr = '('.implode(', ', CMDBSource::Quote($condition)).')';
|
||||
$sOQLCondition = $oField->Render()." IN $sListExpr";
|
||||
$oNewCondition = Expression::FromOQL($sOQLCondition);
|
||||
return $oNewCondition;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1084,10 +1210,35 @@ EOF
|
||||
*/
|
||||
class HistoryBlock extends DisplayBlock
|
||||
{
|
||||
protected $iLimitCount;
|
||||
protected $iLimitStart;
|
||||
|
||||
public function __construct(DBObjectSearch $oFilter, $sStyle = 'list', $bAsynchronous = false, $aParams = array(), $oSet = null)
|
||||
{
|
||||
parent::__construct($oFilter, $sStyle, $bAsynchronous, $aParams, $oSet);
|
||||
$this->iLimitStart = 0;
|
||||
$this->iLimitCount = 0;
|
||||
}
|
||||
|
||||
public function SetLimit($iCount, $iStart = 0)
|
||||
{
|
||||
$this->iLimitStart = $iStart;
|
||||
$this->iLimitCount = $iCount;
|
||||
}
|
||||
|
||||
public function GetRenderContent(WebPage $oPage, $aExtraParams = array(), $sId)
|
||||
{
|
||||
$sHtml = '';
|
||||
$bTruncated = false;
|
||||
$oSet = new CMDBObjectSet($this->m_oFilter, array('date'=>false));
|
||||
if (($this->iLimitStart > 0) || ($this->iLimitCount > 0))
|
||||
{
|
||||
$oSet->SetLimit($this->iLimitCount, $this->iLimitStart);
|
||||
if (($this->iLimitCount - $this->iLimitStart) < $oSet->Count())
|
||||
{
|
||||
$bTruncated = true;
|
||||
}
|
||||
}
|
||||
$sHtml .= "<!-- filter: ".($this->m_oFilter->ToOQL())."-->\n";
|
||||
switch($this->m_sStyle)
|
||||
{
|
||||
@@ -1113,7 +1264,21 @@ class HistoryBlock extends DisplayBlock
|
||||
|
||||
case 'table':
|
||||
default:
|
||||
$sHtml .= $this->GetHistoryTable($oPage, $oSet);
|
||||
if ($bTruncated)
|
||||
{
|
||||
$sFilter = $this->m_oFilter->serialize();
|
||||
$sHtml .= '<div id="history_container"><p>';
|
||||
$sHtml .= Dict::Format('UI:TruncatedResults', $this->iLimitCount, $oSet->Count());
|
||||
$sHtml .= ' ';
|
||||
$sHtml .= '<a href="#" onclick="DisplayHistory(\'#history_container\', \''.$sFilter.'\', 0, 0); return false;">'.Dict::S('UI:DisplayAll').'</a>';
|
||||
$sHtml .= $this->GetHistoryTable($oPage, $oSet);
|
||||
$sHtml .= '</p></div>';
|
||||
$oPage->add_ready_script("$('#{$sId} table.listResults tr:last td').addClass('truncated');");
|
||||
}
|
||||
else
|
||||
{
|
||||
$sHtml .= $this->GetHistoryTable($oPage, $oSet);
|
||||
}
|
||||
|
||||
}
|
||||
return $sHtml;
|
||||
@@ -1185,6 +1350,7 @@ class MenuBlock extends DisplayBlock
|
||||
$sContext = '&'.$sContext;
|
||||
}
|
||||
$sClass = $this->m_oFilter->GetClass();
|
||||
$oReflectionClass = new ReflectionClass($sClass);
|
||||
$oSet = new CMDBObjectSet($this->m_oFilter);
|
||||
$sFilter = $this->m_oFilter->serialize();
|
||||
$sFilterDesc = $this->m_oFilter->ToOql(true);
|
||||
@@ -1204,7 +1370,7 @@ class MenuBlock extends DisplayBlock
|
||||
$sDefault.= "&default[$sKey]=$sValue";
|
||||
}
|
||||
}
|
||||
$bIsCreationAllowed = (UserRights::IsActionAllowed($sClass, UR_ACTION_CREATE) == UR_ALLOWED_YES);
|
||||
$bIsCreationAllowed = (UserRights::IsActionAllowed($sClass, UR_ACTION_CREATE) == UR_ALLOWED_YES) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject'));
|
||||
switch($oSet->Count())
|
||||
{
|
||||
case 0:
|
||||
@@ -1215,10 +1381,8 @@ class MenuBlock extends DisplayBlock
|
||||
case 1:
|
||||
$oObj = $oSet->Fetch();
|
||||
$id = $oObj->GetKey();
|
||||
$bIsModifyAllowed = (UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY, $oSet) == UR_ALLOWED_YES);
|
||||
$bIsModifyAllowed = (UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY, $oSet) == UR_ALLOWED_YES) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject'));
|
||||
$bIsDeleteAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_DELETE, $oSet);
|
||||
$bIsBulkModifyAllowed = (!MetaModel::IsAbstract($sClass)) && UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_MODIFY, $oSet);
|
||||
$bIsBulkDeleteAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_DELETE, $oSet);
|
||||
// Just one object in the set, possible actions are "new / clone / modify and delete"
|
||||
if (!isset($aExtraParams['link_attr']))
|
||||
{
|
||||
@@ -1255,6 +1419,7 @@ class MenuBlock extends DisplayBlock
|
||||
$aActions[$sRelationCode] = array ('label' => MetaModel::GetRelationVerbUp($sRelationCode), 'url' => "{$sRootUrl}pages/$sUIPage?operation=swf_navigator&relation=$sRelationCode&class=$sClass&id=$id{$sContext}");
|
||||
}
|
||||
}
|
||||
/*
|
||||
$this->AddMenuSeparator($aActions);
|
||||
// Static menus: Email this page & CSV Export
|
||||
$sUrl = ApplicationContext::MakeObjectUrl($sClass, $id);
|
||||
@@ -1267,6 +1432,7 @@ class MenuBlock extends DisplayBlock
|
||||
$sOQL = addslashes($sFilterDesc);
|
||||
$aActions['UI:Menu:AddToDashboard'] = array ('label' => Dict::S('UI:Menu:AddToDashboard'), 'url' => "#", 'onclick' => "return DashletCreationDlg('$sOQL')");
|
||||
}
|
||||
*/
|
||||
}
|
||||
$this->AddMenuSeparator($aActions);
|
||||
foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance)
|
||||
@@ -1282,8 +1448,8 @@ class MenuBlock extends DisplayBlock
|
||||
default:
|
||||
// Check rights
|
||||
// New / Modify
|
||||
$bIsModifyAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY, $oSet);
|
||||
$bIsBulkModifyAllowed = (!MetaModel::IsAbstract($sClass)) && UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_MODIFY, $oSet);
|
||||
$bIsModifyAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY, $oSet) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject'));
|
||||
$bIsBulkModifyAllowed = (!MetaModel::IsAbstract($sClass)) && UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_MODIFY, $oSet) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject'));
|
||||
$bIsBulkDeleteAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_DELETE, $oSet);
|
||||
if (isset($aExtraParams['link_attr']))
|
||||
{
|
||||
@@ -1305,20 +1471,27 @@ class MenuBlock extends DisplayBlock
|
||||
|
||||
// Stimuli
|
||||
$aStates = MetaModel::EnumStates($sClass);
|
||||
if (count($aStates) > 0)
|
||||
// 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)))
|
||||
{
|
||||
// Life cycle actions may be available... if all objects are in the same state
|
||||
$oSet->Rewind();
|
||||
$aStates = array();
|
||||
while($oObj = $oSet->Fetch())
|
||||
//
|
||||
// Group by <state>
|
||||
$oGroupByExp = new FieldExpression(MetaModel::GetStateAttributeCode($sClass), $this->m_oFilter->GetClassAlias());
|
||||
$aGroupBy = array('__state__' => $oGroupByExp);
|
||||
$aQueryParams = array();
|
||||
if (isset($aExtraParams['query_params']))
|
||||
{
|
||||
$aStates[$oObj->GetState()] = $oObj->GetState();
|
||||
$aQueryParams = $aExtraParams['query_params'];
|
||||
}
|
||||
$oSet->Rewind();
|
||||
if (count($aStates) == 1)
|
||||
$sSql = MetaModel::MakeGroupByQuery($this->m_oFilter, $aQueryParams, $aGroupBy);
|
||||
$aRes = CMDBSource::QueryToArray($sSql);
|
||||
if (count($aRes) == 1)
|
||||
{
|
||||
// All objects are in the same state...
|
||||
$sState = array_pop($aStates);
|
||||
$sState = $aRes[0]['__state__'];
|
||||
$aTransitions = Metamodel::EnumTransitions($sClass, $sState);
|
||||
if (count($aTransitions))
|
||||
{
|
||||
@@ -1326,8 +1499,11 @@ class MenuBlock extends DisplayBlock
|
||||
$aStimuli = Metamodel::EnumStimuli($sClass);
|
||||
foreach($aTransitions as $sStimulusCode => $aTransitionDef)
|
||||
{
|
||||
$oChecker = new StimulusChecker($this->m_oFilter, $sState, $sStimulusCode);
|
||||
$iActionAllowed = (get_class($aStimuli[$sStimulusCode]) == 'StimulusUserAction') ? $oChecker->IsAllowed() : UR_ALLOWED_NO;
|
||||
$oSet->Rewind();
|
||||
// As soon as the user rights implementation will browse the object set,
|
||||
// then we might consider using OptimizeColumnLoad() here
|
||||
$iActionAllowed = UserRights::IsStimulusAllowed($sClass, $sStimulusCode, $oSet);
|
||||
$iActionAllowed = (get_class($aStimuli[$sStimulusCode]) == 'StimulusUserAction') ? $iActionAllowed : UR_ALLOWED_NO;
|
||||
switch($iActionAllowed)
|
||||
{
|
||||
case UR_ALLOWED_YES:
|
||||
@@ -1342,51 +1518,55 @@ class MenuBlock extends DisplayBlock
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
$this->AddMenuSeparator($aActions);
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot();
|
||||
$aActions['UI:Menu:EMail'] = array ('label' => Dict::S('UI:Menu:EMail'), 'url' => "mailto:?subject=$sFilterDesc&body=".urlencode("{$sUrl}pages/$sUIPage?operation=search&filter=".urlencode($sFilter)."{$sContext}"));
|
||||
$aActions['UI:Menu:CSVExport'] = array ('label' => Dict::S('UI:Menu:CSVExport'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=search&filter=".urlencode($sFilter)."&format=csv{$sContext}");
|
||||
$sOQL = addslashes($sFilterDesc);
|
||||
$aActions['UI:Menu:AddToDashboard'] = array ('label' => Dict::S('UI:Menu:AddToDashboard'), 'url' => "#", 'onclick' => "return DashletCreationDlg('$sOQL')");
|
||||
*/
|
||||
}
|
||||
$this->AddMenuSeparator($aActions);
|
||||
foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance)
|
||||
}
|
||||
|
||||
$this->AddMenuSeparator($aActions);
|
||||
foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance)
|
||||
{
|
||||
$oSet->Rewind();
|
||||
foreach($oExtensionInstance->EnumAllowedActions($oSet) as $sLabel => $data)
|
||||
{
|
||||
$oSet->Rewind();
|
||||
foreach($oExtensionInstance->EnumAllowedActions($oSet) as $sLabel => $data)
|
||||
if (is_array($data))
|
||||
{
|
||||
if (is_array($data))
|
||||
{
|
||||
// New plugins can provide javascript handlers via the 'onclick' property
|
||||
//TODO: enable extension of different menus by checking the 'target' property ??
|
||||
$aActions[$sLabel] = array ('label' => $sLabel, 'url' => isset($data['url']) ? $data['url'] : '#', 'onclick' => isset($data['onclick']) ? $data['onclick'] : '');
|
||||
}
|
||||
else
|
||||
{
|
||||
// Backward compatibility with old plugins
|
||||
$aActions[$sLabel] = array ('label' => $sLabel, 'url' => $data);
|
||||
}
|
||||
// New plugins can provide javascript handlers via the 'onclick' property
|
||||
//TODO: enable extension of different menus by checking the 'target' property ??
|
||||
$aActions[$sLabel] = array ('label' => $sLabel, 'url' => isset($data['url']) ? $data['url'] : '#', 'onclick' => isset($data['onclick']) ? $data['onclick'] : '');
|
||||
}
|
||||
else
|
||||
{
|
||||
// Backward compatibility with old plugins
|
||||
$aActions[$sLabel] = array ('label' => $sLabel, 'url' => $data);
|
||||
}
|
||||
}
|
||||
|
||||
// New extensions based on iPopupMenuItem interface
|
||||
switch($this->m_sStyle)
|
||||
{
|
||||
case 'list':
|
||||
$oSet->Rewind();
|
||||
$param = $oSet;
|
||||
$iMenuId = iPopupMenuExtension::MENU_OBJLIST_ACTIONS;
|
||||
break;
|
||||
|
||||
case 'details':
|
||||
$oSet->Rewind();
|
||||
$param = $oSet->Fetch();
|
||||
$iMenuId = iPopupMenuExtension::MENU_OBJDETAILS_ACTIONS;
|
||||
break;
|
||||
|
||||
}
|
||||
utils::GetPopupMenuItems($oPage, $iMenuId, $param, $aActions);
|
||||
}
|
||||
|
||||
// New extensions based on iPopupMenuItem interface
|
||||
switch($this->m_sStyle)
|
||||
{
|
||||
case 'list':
|
||||
$oSet->Rewind();
|
||||
$param = $oSet;
|
||||
$iMenuId = iPopupMenuExtension::MENU_OBJLIST_ACTIONS;
|
||||
break;
|
||||
|
||||
case 'details':
|
||||
$oSet->Rewind();
|
||||
$param = $oSet->Fetch();
|
||||
$iMenuId = iPopupMenuExtension::MENU_OBJDETAILS_ACTIONS;
|
||||
break;
|
||||
|
||||
}
|
||||
utils::GetPopupMenuItems($oPage, $iMenuId, $param, $aActions);
|
||||
|
||||
$aFavoriteActions = array();
|
||||
$aCallSpec = array($sClass, 'GetShortcutActions');
|
||||
if (is_callable($aCallSpec))
|
||||
@@ -1446,87 +1626,3 @@ class MenuBlock extends DisplayBlock
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Some dummy menus for testing
|
||||
*/
|
||||
class ExtraMenus implements iPopupMenuExtension
|
||||
{
|
||||
/*
|
||||
const MENU_OBJLIST_ACTIONS = 1; // $param is a DBObjectSet containing the list of objects
|
||||
const MENU_OBJLIST_TOOLKIT = 2; // $param is a DBObjectSet containing the list of objects
|
||||
const MENU_OBJDETAILS_ACTIONS = 3; // $param is a DBObject instance: the object currently displayed
|
||||
const MENU_DASHBOARD_ACTIONS = 4; // $param is a Dashboard instance: the dashboard currently displayed
|
||||
const MENU_USER_ACTIONS = 5; // $param is a null ??
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the list of items to be added to a menu. The items will be inserted in the menu in the order of the returned array
|
||||
* @param int $iMenuId The identifier of the type of menu, as listed by the constants MENU_xxx above
|
||||
* @param mixed $param Depends on $iMenuId see the constants define above
|
||||
* @return Array An array of ApplicationPopupMenuItem or an empty array if no action is to be added to the menu
|
||||
*/
|
||||
public static function EnumItems($iMenuId, $param)
|
||||
{
|
||||
switch($iMenuId)
|
||||
{
|
||||
/*
|
||||
case iPopupMenuExtension::MENU_OBJLIST_ACTIONS:
|
||||
// $param is a DBObjectSet
|
||||
$aResult = array(
|
||||
new JSPopupMenuItem('Test::Item1', 'List Test 1', "alert('Test 1')"),
|
||||
new JSPopupMenuItem('Test::Item2', 'List Test 2', "alert('Test 2')"),
|
||||
);
|
||||
break;
|
||||
|
||||
case iPopupMenuExtension::MENU_OBJLIST_TOOLKIT:
|
||||
// $param is a DBObjectSet
|
||||
$aResult = array(
|
||||
new JSPopupMenuItem('Test::Item1', 'Toolkit Test 1', "alert('Test 1')"),
|
||||
new JSPopupMenuItem('Test::Item2', 'Toolkit Test 2', "alert('Test 2')"),
|
||||
);
|
||||
break;
|
||||
|
||||
case iPopupMenuExtension::MENU_OBJDETAILS_ACTIONS:
|
||||
// $param is a DBObject
|
||||
$aResult = array(
|
||||
new JSPopupMenuItem('Test::Item1', 'Object Test 1', "alert('Test 1')"),
|
||||
new JSPopupMenuItem('Test::Item2', 'Object Test 2', "alert('Test 2')"),
|
||||
);
|
||||
break;
|
||||
*/
|
||||
|
||||
case iPopupMenuExtension::MENU_DASHBOARD_ACTIONS:
|
||||
// $param is a Dashboard
|
||||
$oAppContext = new ApplicationContext();
|
||||
$aParams = $oAppContext->GetAsHash();
|
||||
$sMenuId = ApplicationMenu::GetActiveNodeId();
|
||||
$sDlgTitle = addslashes(Dict::S('UI:ImportDashboardTitle'));
|
||||
$sDlgText = addslashes(Dict::S('UI:ImportDashboardText'));
|
||||
$sCloseBtn = addslashes(Dict::S('UI:Button:Cancel'));
|
||||
$aResult = array(
|
||||
new SeparatorPopupMenuItem(),
|
||||
new URLPopupMenuItem('UI:ExportDashboard', Dict::S('UI:ExportDashBoard'), utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=export_dashboard&id='.$sMenuId),
|
||||
new JSPopupMenuItem('UI:ImportDashboard', Dict::S('UI:ImportDashBoard'), "UploadDashboard({dashboard_id: '$sMenuId', title: '$sDlgTitle', text: '$sDlgText', close_btn: '$sCloseBtn' })",
|
||||
array('../js/ajaxfileupload.js')),
|
||||
);
|
||||
break;
|
||||
|
||||
/*
|
||||
case iPopupMenuExtension::MENU_USER_ACTIONS:
|
||||
// $param is null ??
|
||||
$aResult = array(
|
||||
new SeparatorPopupMenuItem(),
|
||||
new JSPopupMenuItem('Test::Item1', 'Reset preferences...', "alert('Test 1')"),
|
||||
new JSPopupMenuItem('Test::Item2', 'Do Something Stupid', "alert('Hey Dude !')"),
|
||||
);
|
||||
break;
|
||||
*/
|
||||
|
||||
default:
|
||||
// Unknown type of menu, do nothing
|
||||
$aResult = array();
|
||||
}
|
||||
return $aResult;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2012 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
/**
|
||||
* Helper class to build interactive forms to be used either in stand-alone
|
||||
* modal dialog or in "property-sheet" panes.
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
class DesignerForm
|
||||
{
|
||||
@@ -32,6 +36,9 @@ class DesignerForm
|
||||
protected $aSubmitParams;
|
||||
protected $sSubmitTo;
|
||||
protected $bReadOnly;
|
||||
protected $sHierarchyPath; // Needed to manage the visibility of nested subform
|
||||
protected $sHierarchyParent; // Needed to manage the visibility of nested subform
|
||||
protected $bDisplayed;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
@@ -44,7 +51,10 @@ class DesignerForm
|
||||
$this->sFormId = 'form_'.rand();
|
||||
$this->oParentForm = null;
|
||||
$this->bReadOnly = false;
|
||||
$this->sHierarchyPath = '';
|
||||
$this->sHierarchyParent = '';
|
||||
$this->StartFieldSet($this->sCurrentFieldSet);
|
||||
$this->bDisplayed = true;
|
||||
}
|
||||
|
||||
public function AddField(DesignerFormField $oField)
|
||||
@@ -68,7 +78,6 @@ class DesignerForm
|
||||
|
||||
public function Render($oP, $bReturnHTML = false)
|
||||
{
|
||||
$sReturn = '';
|
||||
if ($this->oParentForm == null)
|
||||
{
|
||||
$sFormId = $this->sFormId;
|
||||
@@ -76,6 +85,7 @@ class DesignerForm
|
||||
}
|
||||
else
|
||||
{
|
||||
$sReturn = '';
|
||||
$sFormId = $this->oParentForm->sFormId;
|
||||
}
|
||||
$sHiddenFields = '';
|
||||
@@ -143,6 +153,38 @@ class DesignerForm
|
||||
$this->aSubmitParams = $oParentForm->aSubmitParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to handle subforms hide/show
|
||||
*/
|
||||
public function SetHierarchyPath($sHierarchy)
|
||||
{
|
||||
$this->sHierarchyPath = $sHierarchy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to handle subforms hide/show
|
||||
*/
|
||||
public function GetHierarchyPath()
|
||||
{
|
||||
return $this->sHierarchyPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to handle subforms hide/show
|
||||
*/
|
||||
public function SetHierarchyParent($sHierarchy)
|
||||
{
|
||||
$this->sHierarchyParent = $sHierarchy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to handle subforms hide/show
|
||||
*/
|
||||
public function GetHierarchyParent()
|
||||
{
|
||||
return $this->sHierarchyParent;
|
||||
}
|
||||
|
||||
|
||||
public function RenderAsPropertySheet($oP, $bReturnHTML = false, $sNotifyParentSelector = null)
|
||||
{
|
||||
@@ -246,12 +288,17 @@ EOF
|
||||
$oP->add($sReturn);
|
||||
}
|
||||
}
|
||||
public function RenderAsDialog($oPage, $sDialogId, $sDialogTitle, $iDialogWidth, $sOkButtonLabel)
|
||||
public function RenderAsDialog($oPage, $sDialogId, $sDialogTitle, $iDialogWidth, $sOkButtonLabel, $sIntroduction = null)
|
||||
{
|
||||
$sDialogTitle = addslashes($sDialogTitle);
|
||||
$sOkButtonLabel = addslashes($sOkButtonLabel);
|
||||
$sCancelButtonLabel = 'Cancel'; //TODO: localize
|
||||
$sCancelButtonLabel = Dict::S('UI:Button:Cancel');
|
||||
|
||||
$oPage->add("<div id=\"$sDialogId\">");
|
||||
if ($sIntroduction != null)
|
||||
{
|
||||
$oPage->add('<div class="ui-dialog-header">'.$sIntroduction.'</div>');
|
||||
}
|
||||
$this->Render($oPage);
|
||||
$oPage->add('</div>');
|
||||
|
||||
@@ -264,7 +311,7 @@ $('#$sDialogId').dialog({
|
||||
title: '$sDialogTitle',
|
||||
buttons: [
|
||||
{ text: "$sOkButtonLabel", click: function() {
|
||||
var oForm = $(this).parents('.ui-dialog :first').find('form');
|
||||
var oForm = $(this).closest('.ui-dialog').find('form');
|
||||
oForm.submit();
|
||||
} },
|
||||
{ text: "$sCancelButtonLabel", click: function() { KillAllMenus(); $(this).dialog( "close" ); $(this).remove(); } },
|
||||
@@ -339,6 +386,28 @@ EOF
|
||||
$this->oParentForm = $oParentForm;
|
||||
}
|
||||
|
||||
public function GetParentForm()
|
||||
{
|
||||
return $this->oParentForm;
|
||||
}
|
||||
|
||||
public function SetDisplayed($bDisplayed)
|
||||
{
|
||||
$this->bDisplayed = $bDisplayed;
|
||||
}
|
||||
|
||||
public function IsDisplayed()
|
||||
{
|
||||
if ($this->oParentForm == null)
|
||||
{
|
||||
return $this->bDisplayed;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ($this->bDisplayed && $this->oParentForm->IsDisplayed());
|
||||
}
|
||||
}
|
||||
|
||||
public function AddScript($sScript)
|
||||
{
|
||||
$this->sScript .= $sScript;
|
||||
@@ -485,6 +554,8 @@ class DesignerFormField
|
||||
protected $bMandatory;
|
||||
protected $bReadOnly;
|
||||
protected $bAutoApply;
|
||||
protected $aCSSClasses;
|
||||
protected $bDisplayed;
|
||||
|
||||
public function __construct($sCode, $sLabel, $defaultValue)
|
||||
{
|
||||
@@ -494,6 +565,8 @@ class DesignerFormField
|
||||
$this->bMandatory = false;
|
||||
$this->bReadOnly = false;
|
||||
$this->bAutoApply = false;
|
||||
$this->aCSSClasses = array();
|
||||
$this->bDisplayed = true;
|
||||
}
|
||||
|
||||
public function GetCode()
|
||||
@@ -532,6 +605,16 @@ class DesignerFormField
|
||||
return $this->bAutoApply;
|
||||
}
|
||||
|
||||
public function SetDisplayed($bDisplayed)
|
||||
{
|
||||
$this->bDisplayed = $bDisplayed;
|
||||
}
|
||||
|
||||
public function IsDisplayed()
|
||||
{
|
||||
return $this->bDisplayed;
|
||||
}
|
||||
|
||||
public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
|
||||
{
|
||||
$sId = $this->oForm->GetFieldId($this->sCode);
|
||||
@@ -570,6 +653,11 @@ class DesignerFormField
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function AddCSSClass($sCSSClass)
|
||||
{
|
||||
$this->aCSSClasses[] = $sCSSClass;
|
||||
}
|
||||
}
|
||||
|
||||
class DesignerLabelField extends DesignerFormField
|
||||
@@ -602,34 +690,75 @@ class DesignerLabelField extends DesignerFormField
|
||||
class DesignerTextField extends DesignerFormField
|
||||
{
|
||||
protected $sValidationPattern;
|
||||
protected $aForbiddenValues;
|
||||
protected $sExplainForbiddenValues;
|
||||
public function __construct($sCode, $sLabel = '', $defaultValue = '')
|
||||
{
|
||||
parent::__construct($sCode, $sLabel, $defaultValue);
|
||||
$this->sValidationPattern = '';
|
||||
$this->aForbiddenValues = null;
|
||||
$this->sExplainForbiddenValues = null;
|
||||
}
|
||||
|
||||
public function SetValidationPattern($sValidationPattern)
|
||||
{
|
||||
$this->sValidationPattern = $sValidationPattern;
|
||||
}
|
||||
|
||||
public function SetForbiddenValues($aValues, $sExplain)
|
||||
{
|
||||
$this->aForbiddenValues = $aValues;
|
||||
|
||||
$iDefaultKey = array_search($this->defaultValue, $this->aForbiddenValues);
|
||||
if ($iDefaultKey !== false)
|
||||
{
|
||||
// The default (current) value is always allowed...
|
||||
unset($this->aForbiddenValues[$iDefaultKey]);
|
||||
|
||||
}
|
||||
|
||||
$this->sExplainForbiddenValues = $sExplain;
|
||||
}
|
||||
|
||||
public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
|
||||
{
|
||||
$sId = $this->oForm->GetFieldId($this->sCode);
|
||||
$sName = $this->oForm->GetFieldName($this->sCode);
|
||||
$sPattern = addslashes($this->sValidationPattern);
|
||||
$sMandatory = $this->bMandatory ? 'true' : 'false';
|
||||
$sReadOnly = $this->IsReadOnly() ? 'readonly' : '';
|
||||
$oP->add_ready_script(
|
||||
if ($this->IsReadOnly())
|
||||
{
|
||||
$sHtmlValue = "<span>".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\"/></span>";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sPattern = addslashes($this->sValidationPattern);
|
||||
if (is_array($this->aForbiddenValues))
|
||||
{
|
||||
$sForbiddenValues = json_encode($this->aForbiddenValues);
|
||||
$sExplainForbiddenValues = addslashes($this->sExplainForbiddenValues);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sForbiddenValues = 'null';
|
||||
$sExplainForbiddenValues = 'null';
|
||||
}
|
||||
$sMandatory = $this->bMandatory ? 'true' : 'false';
|
||||
$oP->add_ready_script(
|
||||
<<<EOF
|
||||
$('#$sId').bind('change keyup validate', function() { ValidateWithPattern('$sId', $sMandatory, '$sPattern', '$sFormId'); } );
|
||||
$('#$sId').bind('change keyup validate', function() { ValidateWithPattern('$sId', $sMandatory, '$sPattern', '$sFormId', $sForbiddenValues, '$sExplainForbiddenValues'); } );
|
||||
{
|
||||
var myTimer = null;
|
||||
$('#$sId').bind('keyup', function() { clearTimeout(myTimer); myTimer = setTimeout(function() { $('#$sId').trigger('change', {} ); }, 100); });
|
||||
}
|
||||
EOF
|
||||
);
|
||||
return array('label' => $this->sLabel, 'value' => "<input type=\"text\" id=\"$sId\" $sReadOnly name=\"$sName\" value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\">");
|
||||
);
|
||||
$sCSSClasses = '';
|
||||
if (count($this->aCSSClasses) > 0)
|
||||
{
|
||||
$sCSSClasses = 'class="'.implode(' ', $this->aCSSClasses).'"';
|
||||
}
|
||||
$sHtmlValue = "<input type=\"text\" $sCSSClasses id=\"$sId\" name=\"$sName\" value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\">";
|
||||
}
|
||||
return array('label' => $this->sLabel, 'value' => $sHtmlValue);
|
||||
}
|
||||
|
||||
public function ReadParam(&$aValues)
|
||||
@@ -640,6 +769,11 @@ EOF
|
||||
{
|
||||
$aValues[$this->sCode] = $this->defaultValue;
|
||||
}
|
||||
else if(($this->aForbiddenValues != null) && in_array($aValues[$this->sCode], $this->aForbiddenValues))
|
||||
{
|
||||
// Reject the value...
|
||||
$aValues[$this->sCode] = $this->defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -650,18 +784,33 @@ class DesignerLongTextField extends DesignerTextField
|
||||
$sId = $this->oForm->GetFieldId($this->sCode);
|
||||
$sName = $this->oForm->GetFieldName($this->sCode);
|
||||
$sPattern = addslashes($this->sValidationPattern);
|
||||
if (is_array($this->aForbiddenValues))
|
||||
{
|
||||
$sForbiddenValues = json_encode($this->aForbiddenValues);
|
||||
$sExplainForbiddenValues = addslashes($this->sExplainForbiddenValues);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sForbiddenValues = 'null';
|
||||
$sExplainForbiddenValues = 'null';
|
||||
}
|
||||
$sMandatory = $this->bMandatory ? 'true' : 'false';
|
||||
$sReadOnly = $this->IsReadOnly() ? 'readonly' : '';
|
||||
$oP->add_ready_script(
|
||||
<<<EOF
|
||||
$('#$sId').bind('change keyup validate', function() { ValidateWithPattern('$sId', $sMandatory, '$sPattern', '$sFormId'); } );
|
||||
$('#$sId').bind('change keyup validate', function() { ValidateWithPattern('$sId', $sMandatory, '$sPattern', '$sFormId', $sForbiddenValues, '$sExplainForbiddenValues'); } );
|
||||
{
|
||||
var myTimer = null;
|
||||
$('#$sId').bind('keyup', function() { clearTimeout(myTimer); myTimer = setTimeout(function() { $('#$sId').trigger('change', {} ); }, 100); });
|
||||
}
|
||||
EOF
|
||||
);
|
||||
return array('label' => $this->sLabel, 'value' => "<textarea id=\"$sId\" $sReadOnly name=\"$sName\">".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."</textarea>");
|
||||
$sCSSClasses = '';
|
||||
if (count($this->aCSSClasses) > 0)
|
||||
{
|
||||
$sCSSClasses = 'class="'.implode(' ', $this->aCSSClasses).'"';
|
||||
}
|
||||
return array('label' => $this->sLabel, 'value' => "<textarea $sCSSClasses id=\"$sId\" $sReadOnly name=\"$sName\">".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."</textarea>");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -705,39 +854,72 @@ class DesignerComboField extends DesignerFormField
|
||||
$sChecked = $this->defaultValue ? 'checked' : '';
|
||||
$sMandatory = $this->bMandatory ? 'true' : 'false';
|
||||
$sReadOnly = $this->IsReadOnly() ? 'disabled="disabled"' : '';
|
||||
if ($this->bMultipleSelection)
|
||||
$sCSSClasses = '';
|
||||
if (count($this->aCSSClasses) > 0)
|
||||
{
|
||||
$sHtml = "<select multiple size=\"8\"id=\"$sId\" name=\"$sName\" $sReadOnly>";
|
||||
$sCSSClasses = 'class="'.implode(' ', $this->aCSSClasses).'"';
|
||||
}
|
||||
if ($this->IsReadOnly())
|
||||
{
|
||||
$aSelected = array();
|
||||
$aHiddenValues = array();
|
||||
foreach($this->aAllowedValues as $sKey => $sDisplayValue)
|
||||
{
|
||||
if ($this->bMultipleSelection)
|
||||
{
|
||||
if(in_array($sKey, $this->defaultValue))
|
||||
{
|
||||
$aSelected[] = $sDisplayValue;
|
||||
$aHiddenValues[] = "<input type=\"hidden\" name=\"{$sName}[]\" value=\"".htmlentities($sKey, ENT_QUOTES, 'UTF-8')."\"/>";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($sKey == $this->defaultValue)
|
||||
{
|
||||
$aSelected[] = $sDisplayValue;
|
||||
$aHiddenValues[] = "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".htmlentities($sKey, ENT_QUOTES, 'UTF-8')."\"/>";
|
||||
}
|
||||
}
|
||||
}
|
||||
$sHtml = "<span $sCSSClasses>".htmlentities(implode(', ', $aSelected), ENT_QUOTES, 'UTF-8').implode($aHiddenValues)."</span>";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sHtml = "<select id=\"$sId\" name=\"$sName\" $sReadOnly>";
|
||||
$sHtml .= "<option value=\"\">".Dict::S('UI:SelectOne')."</option>";
|
||||
}
|
||||
foreach($this->aAllowedValues as $sKey => $sDisplayValue)
|
||||
{
|
||||
if ($this->bMultipleSelection)
|
||||
{
|
||||
$sSelected = in_array($sKey, $this->defaultValue) ? 'selected' : '';
|
||||
$sHtml = "<select $sCSSClasses multiple size=\"8\"id=\"$sId\" name=\"$sName\">";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sSelected = ($sKey == $this->defaultValue) ? 'selected' : '';
|
||||
$sHtml = "<select $sCSSClasses id=\"$sId\" name=\"$sName\">";
|
||||
$sHtml .= "<option value=\"\">".Dict::S('UI:SelectOne')."</option>";
|
||||
}
|
||||
// Quick and dirty: display the menu parents as a tree
|
||||
$sHtmlValue = str_replace(' ', ' ', htmlentities($sDisplayValue, ENT_QUOTES, 'UTF-8'));
|
||||
$sHtml .= "<option value=\"".htmlentities($sKey, ENT_QUOTES, 'UTF-8')."\" $sSelected>$sHtmlValue</option>";
|
||||
}
|
||||
$sHtml .= "</select>";
|
||||
if ($this->bOtherChoices)
|
||||
{
|
||||
$sHtml .= '<br/><input type="checkbox" id="other_chk_'.$sId.'"><label for="other_chk_'.$sId.'"> Other:</label> <input type="text" id="other_'.$sId.'" name="other_'.$sName.'" size="30"/>';
|
||||
}
|
||||
$oP->add_ready_script(
|
||||
foreach($this->aAllowedValues as $sKey => $sDisplayValue)
|
||||
{
|
||||
if ($this->bMultipleSelection)
|
||||
{
|
||||
$sSelected = in_array($sKey, $this->defaultValue) ? 'selected' : '';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sSelected = ($sKey == $this->defaultValue) ? 'selected' : '';
|
||||
}
|
||||
// Quick and dirty: display the menu parents as a tree
|
||||
$sHtmlValue = str_replace(' ', ' ', htmlentities($sDisplayValue, ENT_QUOTES, 'UTF-8'));
|
||||
$sHtml .= "<option value=\"".htmlentities($sKey, ENT_QUOTES, 'UTF-8')."\" $sSelected>$sHtmlValue</option>";
|
||||
}
|
||||
$sHtml .= "</select>";
|
||||
if ($this->bOtherChoices)
|
||||
{
|
||||
$sHtml .= '<br/><input type="checkbox" id="other_chk_'.$sId.'"><label for="other_chk_'.$sId.'"> Other:</label> <input type="text" id="other_'.$sId.'" name="other_'.$sName.'" size="30"/>';
|
||||
}
|
||||
$oP->add_ready_script(
|
||||
<<<EOF
|
||||
$('#$sId').bind('change validate', function() { ValidateWithPattern('$sId', $sMandatory, '', '$sFormId'); } );
|
||||
$('#$sId').bind('change validate', function() { ValidateWithPattern('$sId', $sMandatory, '', '$sFormId', null, null); } );
|
||||
EOF
|
||||
);
|
||||
);
|
||||
}
|
||||
return array('label' => $this->sLabel, 'value' => $sHtml);
|
||||
}
|
||||
|
||||
@@ -764,9 +946,21 @@ class DesignerBooleanField extends DesignerFormField
|
||||
$sId = $this->oForm->GetFieldId($this->sCode);
|
||||
$sName = $this->oForm->GetFieldName($this->sCode);
|
||||
$sChecked = $this->defaultValue ? 'checked' : '';
|
||||
$sReadOnly = $this->IsReadOnly() ? 'disabled' : ''; // readonly does not work as expected on checkboxes:
|
||||
// readonly prevents the user from changing the input's value not its state (checked/unchecked)
|
||||
return array('label' => $this->sLabel, 'value' => "<input type=\"checkbox\" $sChecked $sReadOnly id=\"$sId\" name=\"$sName\" value=\"true\">");
|
||||
if ($this->IsReadOnly())
|
||||
{
|
||||
$sLabel = $this->defaultValue ? Dict::S('UI:UserManagement:ActionAllowed:Yes') : Dict::S('UI:UserManagement:ActionAllowed:No'); //TODO use our own yes/no translations
|
||||
$sHtmlValue = "<span>".htmlentities($sLabel)."<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\"/></span>";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sCSSClasses = '';
|
||||
if (count($this->aCSSClasses) > 0)
|
||||
{
|
||||
$sCSSClasses = 'class="'.implode(' ', $this->aCSSClasses).'"';
|
||||
}
|
||||
$sHtmlValue = "<input $sCSSClasses type=\"checkbox\" $sChecked id=\"$sId\" name=\"$sName\" value=\"true\">";
|
||||
}
|
||||
return array('label' => $this->sLabel, 'value' => $sHtmlValue);
|
||||
}
|
||||
|
||||
public function ReadParam(&$aValues)
|
||||
@@ -794,8 +988,8 @@ class DesignerBooleanField extends DesignerFormField
|
||||
{
|
||||
$sValue = utils::ReadParam($this->oForm->GetParamName($this->sCode), 'false', false, 'raw_data');
|
||||
}
|
||||
$aValues[$this->sCode] = ($sValue == 'true');
|
||||
}
|
||||
$aValues[$this->sCode] = ($sValue == 'true');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -824,19 +1018,26 @@ class DesignerHiddenField extends DesignerFormField
|
||||
|
||||
class DesignerIconSelectionField extends DesignerFormField
|
||||
{
|
||||
protected $sUploadUrl;
|
||||
protected $aAllowedValues;
|
||||
|
||||
public function __construct($sCode, $sLabel = '', $defaultValue = '')
|
||||
{
|
||||
parent::__construct($sCode, $sLabel, $defaultValue);
|
||||
$this->bAutoApply = true;
|
||||
$this->sUploadUrl = null;
|
||||
}
|
||||
|
||||
public function SetAllowedValues($aAllowedValues)
|
||||
{
|
||||
$this->aAllowedValues = $aAllowedValues;
|
||||
}
|
||||
|
||||
|
||||
public function EnableUpload($sIconUploadUrl)
|
||||
{
|
||||
$this->sUploadUrl = $sIconUploadUrl;
|
||||
}
|
||||
|
||||
public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
|
||||
{
|
||||
$sId = $this->oForm->GetFieldId($this->sCode);
|
||||
@@ -851,11 +1052,12 @@ class DesignerIconSelectionField extends DesignerFormField
|
||||
}
|
||||
}
|
||||
$sJSItems = json_encode($this->aAllowedValues);
|
||||
$sPostUploadTo = ($this->sUploadUrl == null) ? 'null' : "'{$this->sUploadUrl}'";
|
||||
if (!$this->IsReadOnly())
|
||||
{
|
||||
$oP->add_ready_script(
|
||||
<<<EOF
|
||||
$('#$sId').icon_select({current_idx: $idx, items: $sJSItems});
|
||||
$('#$sId').icon_select({current_idx: $idx, items: $sJSItems, post_upload_to: $sPostUploadTo});
|
||||
EOF
|
||||
);
|
||||
}
|
||||
@@ -864,6 +1066,71 @@ EOF
|
||||
}
|
||||
}
|
||||
|
||||
class RunTimeIconSelectionField extends DesignerIconSelectionField
|
||||
{
|
||||
public function __construct($sCode, $sLabel = '', $defaultValue = '')
|
||||
{
|
||||
parent::__construct($sCode, $sLabel, $defaultValue);
|
||||
|
||||
$aAllIcons = self::FindIconsOnDisk(APPROOT.'env-'.utils::GetCurrentEnvironment());
|
||||
ksort($aAllIcons);
|
||||
$aValues = array();
|
||||
foreach($aAllIcons as $sFilePath)
|
||||
{
|
||||
$aValues[] = array('value' => $sFilePath, 'label' => basename($sFilePath), 'icon' => utils::GetAbsoluteUrlModulesRoot().$sFilePath);
|
||||
}
|
||||
$this->SetAllowedValues($aValues);
|
||||
}
|
||||
|
||||
static protected function FindIconsOnDisk($sBaseDir, $sDir = '')
|
||||
{
|
||||
$aResult = array();
|
||||
// Populate automatically the list of icon files
|
||||
if ($hDir = @opendir($sBaseDir.'/'.$sDir))
|
||||
{
|
||||
while (($sFile = readdir($hDir)) !== false)
|
||||
{
|
||||
$aMatches = array();
|
||||
if (($sFile != '.') && ($sFile != '..') && ($sFile != 'lifecycle') && is_dir($sBaseDir.'/'.$sDir.'/'.$sFile))
|
||||
{
|
||||
$sDirSubPath = ($sDir == '') ? $sFile : $sDir.'/'.$sFile;
|
||||
$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
|
||||
{
|
||||
$aResult[$sFile.'_'.$sDir] = $sDir.'/'.$sFile;
|
||||
}
|
||||
}
|
||||
closedir($hDir);
|
||||
}
|
||||
return $aResult;
|
||||
}
|
||||
|
||||
public function ValueFromDOMNode($oDOMNode)
|
||||
{
|
||||
return $oDOMNode->textContent;
|
||||
}
|
||||
|
||||
public function ValueToDOMNode($oDOMNode, $value)
|
||||
{
|
||||
$oTextNode = $oDOMNode->ownerDocument->createTextNode($value);
|
||||
$oDOMNode->appendChild($oTextNode);
|
||||
}
|
||||
|
||||
public function MakeFileUrl($value)
|
||||
{
|
||||
return utils::GetAbsoluteUrlModulesRoot().$value;
|
||||
}
|
||||
|
||||
public function GetDefaultValue($sClass = 'Contact')
|
||||
{
|
||||
$sIconPath = MetaModel::GetClassIcon($sClass, false);
|
||||
$sIcon = str_replace(utils::GetAbsoluteUrlModulesRoot(), '', $sIconPath);
|
||||
return $sIcon;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class DesignerSortableField extends DesignerFormField
|
||||
{
|
||||
protected $aAllowedValues;
|
||||
@@ -884,31 +1151,16 @@ class DesignerSortableField extends DesignerFormField
|
||||
$bOpen = false;
|
||||
$sId = $this->oForm->GetFieldId($this->sCode);
|
||||
$sName = $this->oForm->GetFieldName($this->sCode);
|
||||
$sHtml = "<span class=\"sort_$sId fieldslist\" id=\"sortable_$sId\">";
|
||||
foreach($this->defaultValue as $sValue)
|
||||
{
|
||||
$sHtml .= "<span class=\"movable_attr\">$sValue</span>";
|
||||
}
|
||||
$sHtml .="</span>";
|
||||
$sIconClass = $bOpen ? 'ui-icon-circle-triangle-s' : 'ui-icon-circle-triangle-e';
|
||||
$sStyle = $bOpen ? '' : 'style="display:none"';
|
||||
$sHtml .= "<div class=\"fieldspicker\"><table><tr><td><span id=\"collapse_$sId\" class=\"ui-icon $sIconClass\" style=\"opacity: 0.5\"></span>Fields</td></tr><tr><td><div $sStyle id=\"fieldsbasket_$sId\" class=\"sort_$sId fieldsbasket\">";
|
||||
foreach($this->aAllowedValues as $sKey => $sDisplayValue)
|
||||
{
|
||||
$sHtml .= "<span class=\"movable_attr\">$sDisplayValue</span>";
|
||||
}
|
||||
$sHtml .="</div></td></tr>";
|
||||
$sHtml .="<tr id=\"trash_icon_$sId\" $sStyle><td><span class=\"ui-icon ui-icon-trash\" style=\"opacity: 0.5\"></span>Trash</td></tr><tr id=\"trash_$sId\" $sStyle><td><div id=\"recycle_$sId\" class=\"sort_$sId fieldstrash\"></div></div></td></tr></table></div>";
|
||||
$sReadOnly = $this->IsReadOnly() ? 'readonly="readonly"' : '';
|
||||
$aResult = array('label' => $this->sLabel, 'value' => "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" $sReadOnly value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\">");
|
||||
|
||||
|
||||
$sJSFields = json_encode(array_keys($this->aAllowedValues));
|
||||
$oP->add_ready_script(
|
||||
<<<EOF
|
||||
$('#collapse_$sId').click(function() { $(this).toggleClass('ui-icon-circle-triangle-s').toggleClass('ui-icon-circle-triangle-e'); $('#fieldsbasket_$sId').toggle(); $('#trash_icon_$sId').toggle(); $('#trash_$sId').toggle(); } );
|
||||
$('#fieldsbasket_$sId .movable_attr').draggable({connectToSortable: '#sortable_$sId', helper: 'clone', revert: false });
|
||||
$('#recycle_$sId').sortable({ receive: function(event, ui) { ui.item.animate({opacity: 0.25}, { complete: function() { $(this).remove(); } });} });
|
||||
$('#sortable_$sId').sortable({connectWith: '#recycle_$sId', forcePlaceholderSize: true});
|
||||
$('#sortable_$sId').disableSelection();
|
||||
EOF
|
||||
"$('#$sId').sortable_field({aAvailableFields: $sJSFields});"
|
||||
);
|
||||
return array('label' => $this->sLabel, 'value' => $sHtml);
|
||||
|
||||
return $aResult;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -940,15 +1192,44 @@ class DesignerFormSelectorField extends DesignerFormField
|
||||
$sName = $this->oForm->GetFieldName($this->sCode);
|
||||
$sReadOnly = $this->IsReadOnly() ? 'disabled="disabled"' : '';
|
||||
|
||||
$sHtml = "<select id=\"$sId\" name=\"$sName\" $sReadOnly>";
|
||||
foreach($this->aSubForms as $sKey => $aFormData)
|
||||
$sCSSClasses = '';
|
||||
if (count($this->aCSSClasses) > 0)
|
||||
{
|
||||
$sDisplayValue = $aFormData['label'];
|
||||
$sSelected = ($sKey == $this->defaultValue) ? 'selected' : '';
|
||||
$sHtml .= "<option value=\"".htmlentities($sKey, ENT_QUOTES, 'UTF-8')."\" $sSelected>".htmlentities($sDisplayValue, ENT_QUOTES, 'UTF-8')."</option>";
|
||||
$sCSSClasses = 'class="'.implode(' ', $this->aCSSClasses).'"';
|
||||
}
|
||||
$sHtml .= "</select>";
|
||||
|
||||
|
||||
if ($this->IsReadOnly())
|
||||
{
|
||||
$aSelected = array();
|
||||
$aHiddenValues = array();
|
||||
$sDisplayValue = '';
|
||||
$sHiddenValue = '';
|
||||
foreach($this->aSubForms as $sKey => $aFormData)
|
||||
{
|
||||
if ($sKey == $this->defaultValue)
|
||||
{
|
||||
$sDisplayValue = htmlentities($aFormData['label'], ENT_QUOTES, 'UTF-8');
|
||||
$sHiddenValue = "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".htmlentities($sKey, ENT_QUOTES, 'UTF-8')."\"/>";
|
||||
break;
|
||||
}
|
||||
}
|
||||
$sHtml = "<span $sCSSClasses>".$sDisplayValue.$sHiddenValue."</span>";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
|
||||
$sHtml = "<select $sCSSClasses id=\"$sId\" name=\"$sName\" $sReadOnly>";
|
||||
foreach($this->aSubForms as $sKey => $aFormData)
|
||||
{
|
||||
$sDisplayValue = htmlentities($aFormData['label'], ENT_QUOTES, 'UTF-8');;
|
||||
$sSelected = ($sKey == $this->defaultValue) ? 'selected' : '';
|
||||
$sHtml .= "<option value=\"".htmlentities($sKey, ENT_QUOTES, 'UTF-8')."\" $sSelected>".$sDisplayValue."</option>";
|
||||
}
|
||||
$sHtml .= "</select>";
|
||||
}
|
||||
|
||||
if ($sRenderMode == 'property')
|
||||
{
|
||||
$sHtml .= '</td><td class="prop_icon prop_apply"><span title="Apply" class="ui-icon ui-icon-circle-check"/></td><td class="prop_icon prop_cancel"><span title="Revert" class="ui-icon ui-icon-circle-close"/></td></tr>';
|
||||
@@ -957,7 +1238,7 @@ class DesignerFormSelectorField extends DesignerFormField
|
||||
foreach($this->aSubForms as $sKey => $aFormData)
|
||||
{
|
||||
$sId = $this->oForm->GetFieldId($this->sCode);
|
||||
$sStyle = ($sKey == $this->defaultValue) ? '' : 'style="display:none"';
|
||||
$sStyle = (($sKey == $this->defaultValue) && $this->oForm->IsDisplayed()) ? '' : 'style="display:none"';
|
||||
$oSubForm = $aFormData['form'];
|
||||
$oSubForm->SetParentForm($this->oForm);
|
||||
$oSubForm->CopySubmitParams($this->oForm);
|
||||
@@ -965,22 +1246,77 @@ class DesignerFormSelectorField extends DesignerFormField
|
||||
|
||||
if ($sRenderMode == 'property')
|
||||
{
|
||||
$sHtml .= "</tbody><tbody class=\"subform\" id=\"{$sId}_{$sKey}\" $sStyle>";
|
||||
$sHtml .= $oSubForm->RenderAsPropertySheet($oP, true);
|
||||
// Note: Managing the visibility of nested subforms had several implications
|
||||
// 1) Attributes are displayed in a table and we have to group them in as many tbodys as necessary to hide/show the various options depending on the current selection
|
||||
// 2) It is not possible to nest tbody tags. Therefore, it is not possible to manage the visibility the same way as it is done for the dialog mode (using nested divs).
|
||||
// The div hierarchy has been emulated by adding attributes to the tbody tags:
|
||||
// - data-selector : uniquely identifies the DesignerFormSelectorField that has an impact on the visibility of the node
|
||||
// - data-path : uniquely identifies the combination of users choices that must be made to show the node
|
||||
// - data-state : records the state, depending on the user choice on the FormSelectorField just above the node, but indepentantly from the visibility in the page (can be visible in the form itself being in a hidden form)
|
||||
// Then a series of actions are performed to hide and show the relevant nodes, depending on the user choice
|
||||
$sSelector = $this->oForm->GetHierarchyPath().'/'.$this->sCode;
|
||||
$oSubForm->SetHierarchyParent($sSelector);
|
||||
$sPath = $this->oForm->GetHierarchyPath().'/'.$this->sCode.':'.$sKey;
|
||||
$oSubForm->SetHierarchyPath($sPath);
|
||||
|
||||
$oSubForm->SetDisplayed($sKey == $this->defaultValue);
|
||||
$sState = ($sKey == $this->defaultValue) ? 'visible' : 'hidden';
|
||||
$sHtml .= "</tbody><tbody data-selector=\"$sSelector\" data-path=\"$sPath\" data-state=\"$sState\" $sStyle>";
|
||||
$sHtml .= $oSubForm->RenderAsPropertySheet($oP, true);
|
||||
|
||||
$sState = $this->oForm->IsDisplayed() ? 'visible' : 'hidden';
|
||||
if ($oParent = $this->oForm->GetParentForm())
|
||||
{
|
||||
$sParentSelector = $oParent->GetHierarchyParent();
|
||||
$sParentPath = $oParent->GetHierarchyPath();
|
||||
}
|
||||
else
|
||||
{
|
||||
$sParentSelector = '';
|
||||
$sParentPath = '';
|
||||
}
|
||||
$sHtml .= "</tbody><tbody data-selector=\"$sParentSelector\" data-path=\"$sParentPath\" data-state=\"$sState\" $sStyle>";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sHtml .= "<div class=\"subform\" id=\"{$sId}_{$sKey}\" $sStyle>";
|
||||
$sHtml .= "<div class=\"subform_{$sId} {$sId}_{$sKey}\" $sStyle>";
|
||||
$sHtml .= $oSubForm->Render($oP, true);
|
||||
$sHtml .= "</div>";
|
||||
}
|
||||
}
|
||||
|
||||
$oP->add_ready_script(
|
||||
if ($sRenderMode == 'property')
|
||||
{
|
||||
$sSelector = $this->oForm->GetHierarchyPath().'/'.$this->sCode;
|
||||
$oP->add_ready_script(
|
||||
<<<EOF
|
||||
$('#$sId').bind('change reverted', function() { $('.subform').hide(); $('#{$sId}_'+this.value).show(); } );
|
||||
$('#$sId').bind('change reverted', function() {
|
||||
// Mark all the direct children as hidden
|
||||
$('tbody[data-selector="$sSelector"]').attr('data-state', 'hidden');
|
||||
// Mark the selected one as visible
|
||||
var sSelectedHierarchy = '$sSelector:'+this.value;
|
||||
$('tbody[data-path="'+sSelectedHierarchy+'"]').attr('data-state', 'visible');
|
||||
|
||||
// Show all items behind the current one
|
||||
$('tbody[data-path^="$sSelector"]').show();
|
||||
// Hide items behind the current one as soon as it is behind a hidden node (or itself is marked as hidden)
|
||||
$('tbody[data-path^="$sSelector"][data-state="hidden"]').each(function(){
|
||||
$(this).hide();
|
||||
var sPath = $(this).attr('data-path');
|
||||
$('tbody[data-path^="'+sPath+'/"]').hide();
|
||||
});
|
||||
});
|
||||
EOF
|
||||
);
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oP->add_ready_script(
|
||||
<<<EOF
|
||||
$('#$sId').bind('change reverted', function() { $('.subform_{$sId}').hide(); $('.{$sId}_'+this.value).show(); } );
|
||||
EOF
|
||||
);
|
||||
}
|
||||
return array('label' => $this->sLabel, 'value' => $sHtml);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Persistent class InputOutputTask
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
require_once(APPROOT.'/application/cmdbabstract.class.inc.php');
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2014 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program 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 General Public License for more details.
|
||||
// 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.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// 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 iTopWebPage
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2013 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
require_once(APPROOT."/application/nicewebpage.class.inc.php");
|
||||
@@ -29,25 +30,21 @@ require_once(APPROOT."/application/user.preferences.class.inc.php");
|
||||
/**
|
||||
* Web page with some associated CSS and scripts (jquery) for a fancier display
|
||||
*/
|
||||
class iTopWebPage extends NiceWebPage
|
||||
class iTopWebPage extends NiceWebPage implements iTabbedPage
|
||||
{
|
||||
private $m_sMenu;
|
||||
// private $m_currentOrganization;
|
||||
private $m_aTabs;
|
||||
private $m_sCurrentTabContainer;
|
||||
private $m_sCurrentTab;
|
||||
private $m_sMessage;
|
||||
private $m_sInitScript;
|
||||
protected $m_oTabs;
|
||||
|
||||
public function __construct($sTitle)
|
||||
{
|
||||
parent::__construct($sTitle);
|
||||
$this->m_oTabs = new TabManager();
|
||||
|
||||
ApplicationContext::SetUrlMakerClass('iTopStandardURLMaker');
|
||||
|
||||
$this->m_sCurrentTabContainer = '';
|
||||
$this->m_sCurrentTab = '';
|
||||
$this->m_aTabs = array();
|
||||
$this->m_sMenu = "";
|
||||
$this->m_sMessage = '';
|
||||
$this->SetRootUrl(utils::GetAbsoluteUrlAppRoot());
|
||||
@@ -56,12 +53,11 @@ class iTopWebPage extends NiceWebPage
|
||||
$this->add_linked_stylesheet("../css/jquery.treeview.css");
|
||||
$this->add_linked_stylesheet("../css/jquery.autocomplete.css");
|
||||
$this->add_linked_stylesheet("../css/fg.menu.css");
|
||||
$this->add_linked_stylesheet("../css/jquery.multiselect.css");
|
||||
$this->add_linked_script('../js/jquery.layout.min.js');
|
||||
$this->add_linked_script('../js/jquery.ba-bbq.min.js');
|
||||
$this->add_linked_script("../js/jquery.treeview.js");
|
||||
$this->add_linked_script("../js/jquery.autocomplete.js");
|
||||
$this->add_linked_script("../js/jquery.positionBy.js");
|
||||
$this->add_linked_script("../js/jquery.popupmenu.js");
|
||||
$this->add_linked_script("../js/date.js");
|
||||
$this->add_linked_script("../js/jquery.blockUI.js");
|
||||
$this->add_linked_script("../js/utils.js");
|
||||
@@ -77,8 +73,57 @@ class iTopWebPage extends NiceWebPage
|
||||
$this->add_linked_script('../js/g.pie.js');
|
||||
$this->add_linked_script('../js/g.dot.js');
|
||||
$this->add_linked_script('../js/charts.js');
|
||||
$this->add_linked_script('../js/field_sorter.js');
|
||||
$this->add_linked_script('../js/datatable.js');
|
||||
$this->add_linked_script('../js/jquery.multiselect.min.js');
|
||||
$this->add_linked_script('../js/ajaxfileupload.js');
|
||||
|
||||
$aMultiselectOptions = array(
|
||||
'header' => true,
|
||||
'checkAllText' => Dict::S('UI:SearchValue:CheckAll'),
|
||||
'uncheckAllText' => Dict::S('UI:SearchValue:UncheckAll'),
|
||||
'noneSelectedText' => Dict::S('UI:SearchValue:Any'),
|
||||
'selectedText' => Dict::S('UI:SearchValue:NbSelected'),
|
||||
'selectedList' => 1,
|
||||
);
|
||||
$sJSMultiselectOptions = json_encode($aMultiselectOptions);
|
||||
$sSearchAny = addslashes(Dict::S('UI:SearchValue:Any'));
|
||||
$sSearchNbSelected = addslashes(Dict::S('UI:SearchValue:NbSelected'));
|
||||
$this->add_dict_entry('UI:FillAllMandatoryFields');
|
||||
|
||||
$bForceMenuPane = utils::ReadParam('force_menu_pane', null);
|
||||
$sInitClosed = '';
|
||||
if (($bForceMenuPane !== null) && ($bForceMenuPane == 0))
|
||||
{
|
||||
$sInitClosed = 'initClosed: true,';
|
||||
}
|
||||
|
||||
$this->add_script(
|
||||
<<<EOF
|
||||
function ShowAboutBox()
|
||||
{
|
||||
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', {operation: 'about_box'}, function(data){
|
||||
$('body').append(data);
|
||||
});
|
||||
return false;
|
||||
}
|
||||
EOF
|
||||
);
|
||||
|
||||
if (MetaModel::GetConfig()->Get('demo_mode'))
|
||||
{
|
||||
// Leave the pane opened
|
||||
$sConfigureWestPane = '';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sConfigureWestPane =
|
||||
<<<EOF
|
||||
if (GetUserPreference('menu_pane', 'open') == 'closed')
|
||||
{
|
||||
myLayout.close('west');
|
||||
}
|
||||
myLayout.addPinBtn( "#tPinMenu", "west" );
|
||||
EOF;
|
||||
}
|
||||
|
||||
$this->m_sInitScript =
|
||||
<<< EOF
|
||||
@@ -90,7 +135,7 @@ class iTopWebPage extends NiceWebPage
|
||||
paneSize = GetUserPreference('menu_size', 300)
|
||||
myLayout = $('body').layout({
|
||||
west : {
|
||||
minSize: 200, size: paneSize, spacing_open: 16, spacing_close: 16, slideTrigger_open: "mouseover", hideTogglerOnSlide: true, enableCursorHotkey: false,
|
||||
$sInitClosed minSize: 200, size: paneSize, spacing_open: 16, spacing_close: 16, slideTrigger_open: "mouseover", hideTogglerOnSlide: true, enableCursorHotkey: false,
|
||||
onclose_end: function(name, elt, state, options, layout)
|
||||
{
|
||||
if (state.isSliding == false)
|
||||
@@ -130,18 +175,15 @@ class iTopWebPage extends NiceWebPage
|
||||
|
||||
}
|
||||
});
|
||||
myLayout.addPinBtn( "#tPinMenu", "west" );
|
||||
window.clearTimeout(iPaneVisWatchDog);
|
||||
//myLayout.open( "west" );
|
||||
$('.ui-layout-resizer-west .ui-layout-toggler').css({background: 'transparent'});
|
||||
if (GetUserPreference('menu_pane', 'open') == 'closed')
|
||||
{
|
||||
myLayout.close('west');
|
||||
}
|
||||
$sConfigureWestPane
|
||||
|
||||
$('#left-pane').layout({ resizable: false, spacing_open: 0, south: { size: 94 }, enableCursorHotkey: false });
|
||||
|
||||
// Accordion Menu
|
||||
$("#accordion").accordion({ header: "h3", navigation: true, autoHeight: false, collapsible: false, icons: false }); // collapsible will be enabled once the item will be selected
|
||||
$("#accordion").accordion({ header: "h3", navigation: true, heightStyle: "content", collapsible: false, icons: false }); // collapsible will be enabled once the item will be selected
|
||||
|
||||
// Tabs, using JQuery BBQ to store the history
|
||||
// The "tab widgets" to handle.
|
||||
@@ -150,15 +192,43 @@ class iTopWebPage extends NiceWebPage
|
||||
// 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
|
||||
// is taken into account and causes "local" tabs to be considered as Ajax
|
||||
// unless their URL is equal to the URL of the page...
|
||||
$('div[id^=tabbedContent] > ul > li > a').each(function() {
|
||||
var sHash = location.hash;
|
||||
var sHref = $(this).attr("href");
|
||||
if (sHref.match(/^#/))
|
||||
{
|
||||
var sCleanLocation = location.href.toString().replace(sHash, '').replace(/#$/, '');
|
||||
$(this).attr("href", sCleanLocation+$(this).attr("href"));
|
||||
}
|
||||
});
|
||||
|
||||
// Enable tabs on all tab widgets. The `event` property must be overridden so
|
||||
// that the tabs aren't changed on click, and any custom event name can be
|
||||
// specified. Note that if you define a callback for the 'select' event, it
|
||||
// will be executed for the selected tab whenever the hash changes.
|
||||
tabs.tabs({ event: 'change', 'show': function(event, ui) {
|
||||
tabs.tabs({
|
||||
event: 'change', 'show': function(event, ui) {
|
||||
$('.resizable', ui.panel).resizable(); // Make resizable everything that claims to be resizable !
|
||||
},
|
||||
beforeLoad: function( event, ui ) {
|
||||
if ( ui.tab.data('loaded') && (ui.tab.attr('data-cache') == 'true')) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
ui.panel.html('<div><img src="../images/indicator.gif"></div>');
|
||||
ui.jqXHR.success(function() {
|
||||
ui.tab.data( "loaded", true );
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$('.multiselect').multiselect($sJSMultiselectOptions);
|
||||
|
||||
$('.resizable').filter(':visible').resizable();
|
||||
}
|
||||
catch(err)
|
||||
@@ -168,6 +238,7 @@ class iTopWebPage extends NiceWebPage
|
||||
}
|
||||
EOF
|
||||
;
|
||||
|
||||
$this->add_ready_script(
|
||||
<<< EOF
|
||||
|
||||
@@ -285,7 +356,7 @@ EOF
|
||||
changeMonth: true,
|
||||
changeYear: true
|
||||
});
|
||||
$(".datetime-pick").datepicker({
|
||||
$(".datetime-pick").datepicker({
|
||||
showOn: 'button',
|
||||
buttonImage: '../images/calendar.png',
|
||||
buttonImageOnly: true,
|
||||
@@ -294,23 +365,6 @@ EOF
|
||||
changeMonth: true,
|
||||
changeYear: true
|
||||
});
|
||||
// Restore the persisted sortable order, for all sortable lists... if any
|
||||
$('.sortable').each(function()
|
||||
{
|
||||
var sTemp = GetUserPreference(this.id+'_order', undefined);
|
||||
if (sTemp != undefined)
|
||||
{
|
||||
var aSerialized = sTemp.split(',');
|
||||
var sortable = $(this);
|
||||
$.each(aSerialized, function(i,v) {
|
||||
var item = $('#menu_'+v);
|
||||
if (item.length > 0) // Check that the menu exists
|
||||
{
|
||||
sortable.append(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Make sortable, everything that claims to be sortable
|
||||
$('.sortable').sortable( {axis: 'y', cursor: 'move', handle: '.drag_handle', stop: function()
|
||||
@@ -375,7 +429,7 @@ EOF
|
||||
{
|
||||
if ($('#rawOutput > div').html() != '')
|
||||
{
|
||||
$('#rawOutput').dialog( {autoOpen: true, modal:false});
|
||||
$('#rawOutput').dialog( {autoOpen: true, modal:false, width: '80%'});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -395,6 +449,11 @@ EOF
|
||||
|
||||
});
|
||||
|
||||
|
||||
function FixPaneVis()
|
||||
{
|
||||
$('.ui-layout-center, .ui-layout-north, .ui-layout-south').css({display: 'block'});
|
||||
}
|
||||
EOF
|
||||
);
|
||||
}
|
||||
@@ -450,7 +509,9 @@ EOF
|
||||
|
||||
$sFavoriteOrgs = '';
|
||||
$oWidget = new UIExtKeyWidget('Organization', 'org_id', '', true /* search mode */);
|
||||
$sHtml .= $oWidget->Display($this, 50, false, '', $oSet, $iCurrentOrganization, 'org_id', false, 'c[org_id]', '', array('iFieldSize' => 20, 'iMinChars' => MetaModel::GetConfig()->Get('min_autocomplete_chars'), 'sDefaultValue' => Dict::S('UI:AllOrganizations')));
|
||||
$sHtml .= $oWidget->Display($this, 50, false, '', $oSet, $iCurrentOrganization, 'org_id', false, 'c[org_id]', '',
|
||||
array('iFieldSize' => 20, 'iMinChars' => MetaModel::GetConfig()->Get('min_autocomplete_chars'), 'sDefaultValue' => Dict::S('UI:AllOrganizations')),
|
||||
null, 'select', false /* bSearchMultiple */);
|
||||
$this->add_ready_script('$("#org_id").bind("extkeychange", function() { $("#SiloSelection form").submit(); } )');
|
||||
$this->add_ready_script("$('#label_org_id').click( function() { $(this).val(''); $('#org_id').val(''); return true; } );\n");
|
||||
// Add other dimensions/context information to this form
|
||||
@@ -483,6 +544,31 @@ EOF
|
||||
$sForm = $this->GetSiloSelectionForm();
|
||||
$this->DisplayMenu(); // Compute the menu
|
||||
|
||||
// Call the extensions to add content to the page, so that they can also add styles or scripts
|
||||
$sBannerExtraHtml = '';
|
||||
foreach (MetaModel::EnumPlugins('iPageUIExtension') as $oExtensionInstance)
|
||||
{
|
||||
$sBannerExtraHtml .= $oExtensionInstance->GetBannerHtml($this);
|
||||
}
|
||||
|
||||
$sNorthPane = '';
|
||||
foreach (MetaModel::EnumPlugins('iPageUIExtension') as $oExtensionInstance)
|
||||
{
|
||||
$sNorthPane .= $oExtensionInstance->GetNorthPaneHtml($this);
|
||||
}
|
||||
|
||||
if (UserRights::IsAdministrator() && ExecutionKPI::IsEnabled())
|
||||
{
|
||||
$sNorthPane .= '<div id="admin-banner"><span style="padding:5px;">'.ExecutionKPI::GetDescription().'<span></div>';
|
||||
}
|
||||
|
||||
//$sSouthPane = '<p>Peak memory Usage: '.sprintf('%.3f MB', memory_get_peak_usage(true) / (1024*1024)).'</p>';
|
||||
$sSouthPane = '';
|
||||
foreach (MetaModel::EnumPlugins('iPageUIExtension') as $oExtensionInstance)
|
||||
{
|
||||
$sSouthPane .= $oExtensionInstance->GetSouthPaneHtml($this);
|
||||
}
|
||||
|
||||
// Put here the 'ready scripts' that must be executed after all others
|
||||
$this->add_ready_script(
|
||||
<<<EOF
|
||||
@@ -530,6 +616,7 @@ EOF
|
||||
|
||||
if ($this->GetOutputFormat() == 'html')
|
||||
{
|
||||
$sHtml .= $this->output_dict_entries(true); // before any script so that they can benefit from the translations
|
||||
foreach($this->a_linked_scripts as $s_script)
|
||||
{
|
||||
// Make sure that the URL to the script contains the application's version number
|
||||
@@ -544,7 +631,7 @@ EOF
|
||||
}
|
||||
$sHtml .= "<script type=\"text/javascript\" src=\"$s_script\"></script>\n";
|
||||
}
|
||||
$this->add_script("\$(document).ready(function() {\n{$this->m_sInitScript};\nwindow.setTimeout('onDelayedReady()',10)\n});");
|
||||
$this->add_script("var iPaneVisWatchDog = window.setTimeout('FixPaneVis()',5000);\n\$(document).ready(function() {\n{$this->m_sInitScript};\nwindow.setTimeout('onDelayedReady()',10)\n});");
|
||||
if (count($this->m_aReadyScripts)>0)
|
||||
{
|
||||
$this->add_script("\nonDelayedReady = function() {\n".implode("\n", $this->m_aReadyScripts)."\n}\n");
|
||||
@@ -575,12 +662,6 @@ EOF
|
||||
$sHtml .= "</head>\n";
|
||||
$sHtml .= "<body>\n";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Render the revision number
|
||||
if (ITOP_REVISION == '$WCREV$')
|
||||
{
|
||||
@@ -605,34 +686,7 @@ EOF
|
||||
$sOnClick = " onclick=\"this.value='';this.onclick=null;\"";
|
||||
}
|
||||
// Render the tabs in the page (if any)
|
||||
foreach($this->m_aTabs as $sTabContainerName => $m_aTabs)
|
||||
{
|
||||
$sTabs = '';
|
||||
$container_index = 0;
|
||||
if (count($m_aTabs) > 0)
|
||||
{
|
||||
$sTabs = "<!-- tabs -->\n<div id=\"tabbedContent_{$container_index}\" class=\"light\">\n";
|
||||
$sTabs .= "<ul>\n";
|
||||
// Display the unordered list that will be rendered as the tabs
|
||||
$i = 0;
|
||||
foreach($m_aTabs as $sTabName => $sTabContent)
|
||||
{
|
||||
$sTabs .= "<li><a href=\"#tab_$i\" class=\"tab\"><span>".htmlentities($sTabName, ENT_QUOTES, 'UTF-8')."</span></a></li>\n";
|
||||
$i++;
|
||||
}
|
||||
$sTabs .= "</ul>\n";
|
||||
// Now add the content of the tabs themselves
|
||||
$i = 0;
|
||||
foreach($m_aTabs as $sTabName => $sTabContent)
|
||||
{
|
||||
$sTabs .= "<div id=\"tab_$i\">".$sTabContent."</div>\n";
|
||||
$i++;
|
||||
}
|
||||
$sTabs .= "</div>\n<!-- end of tabs-->\n";
|
||||
}
|
||||
$this->s_content = str_replace("\$Tabs:$sTabContainerName\$", $sTabs, $this->s_content);
|
||||
$container_index++;
|
||||
}
|
||||
$this->s_content = $this->m_oTabs->RenderIntoContent($this->s_content);
|
||||
|
||||
if ($this->GetOutputFormat() == 'html')
|
||||
{
|
||||
@@ -666,6 +720,10 @@ EOF
|
||||
$aActions[$oChangePwd->GetUID()] = $oChangePwd->GetMenuItem();
|
||||
}
|
||||
utils::GetPopupMenuItems($this, iPopupMenuExtension::MENU_USER_ACTIONS, null, $aActions);
|
||||
|
||||
$oAbout = new JSPopupMenuItem('UI:AboutBox', Dict::S('UI:AboutBox'), 'return ShowAboutBox();');
|
||||
$aActions[$oAbout->GetUID()] = $oAbout->GetMenuItem();
|
||||
|
||||
$sLogOffMenu .= $this->RenderPopupMenuItems($aActions);
|
||||
|
||||
|
||||
@@ -701,29 +759,40 @@ EOF
|
||||
$sApplicationBanner .= '<div id="admin-banner"><span style="padding:5px;">'.$this->m_sMessage.'<span></div>';
|
||||
}
|
||||
|
||||
$sEnvironment = utils::GetCurrentEnvironment();
|
||||
$sBackButton = utils::GetEnvironmentBackButton();
|
||||
if($sEnvironment != 'production')
|
||||
$sApplicationBanner .= $sBannerExtraHtml;
|
||||
|
||||
if (!empty($sNorthPane))
|
||||
{
|
||||
$sEnvLabel = trim(MetaModel::GetConfig()->Get('app_env_label'));
|
||||
if (strlen($sEnvLabel) == 0)
|
||||
{
|
||||
$sEnvLabel = $sEnvironment;
|
||||
}
|
||||
$sApplicationBanner .= '<div id="admin-banner"><span style="padding:5px;">'.Dict::Format('UI:ApplicationEnvironment', $sEnvLabel).$sBackButton.'<span></div>';
|
||||
$sNorthPane = '<div id="bottom-pane" class="ui-layout-north">'.$sNorthPane.'</div>';
|
||||
}
|
||||
|
||||
|
||||
if (!empty($sSouthPane))
|
||||
{
|
||||
$sSouthPane = '<div id="bottom-pane" class="ui-layout-south">'.$sSouthPane.'</div>';
|
||||
}
|
||||
|
||||
$sIconUrl = Utils::GetConfig()->Get('app_icon_url');
|
||||
$sOnlineHelpUrl = MetaModel::GetConfig()->Get('online_help');
|
||||
//$sLogOffMenu = "<span id=\"logOffBtn\" style=\"height:55px;padding:0;margin:0;\"><img src=\"../images/onOffBtn.png\"></span>";
|
||||
|
||||
$sDisplayIcon = utils::GetAbsoluteUrlAppRoot().'images/itop-logo.png';
|
||||
if (file_exists(MODULESROOT.'branding/main-logo.png'))
|
||||
{
|
||||
$sDisplayIcon = utils::GetAbsoluteUrlModulesRoot().'branding/main-logo.png';
|
||||
}
|
||||
|
||||
$sHtml .= $sNorthPane;
|
||||
$sHtml .= '<div id="left-pane" class="ui-layout-west">';
|
||||
$sHtml .= '<!-- Beginning of the left pane -->';
|
||||
$sHtml .= ' <div class="ui-layout-north">';
|
||||
$sHtml .= ' <div id="header-logo">';
|
||||
$sHtml .= ' <div id="top-left"></div><div id="logo"><a href="http://www.combodo.com/itop"><img src="../images/itop-logo.png" title="'.htmlentities($sVersionString, ENT_QUOTES, 'UTF-8').'" style="border:0; margin-top:16px; margin-right:40px;"/></a></div>';
|
||||
$sHtml .= ' <div id="top-left"></div><div id="logo"><a href="'.htmlentities($sIconUrl, ENT_QUOTES, 'UTF-8').'"><img src="'.$sDisplayIcon.'" title="'.htmlentities($sVersionString, ENT_QUOTES, 'UTF-8').'" style="border:0; margin-top:16px; margin-right:40px;"/></a></div>';
|
||||
$sHtml .= ' </div>';
|
||||
$sHtml .= ' <div class="header-menu">';
|
||||
$sHtml .= ' <div class="icon ui-state-default ui-corner-all"><span id="tPinMenu" class="ui-icon ui-icon-pin-w">pin</span></div>';
|
||||
if (!MetaModel::GetConfig()->Get('demo_mode'))
|
||||
{
|
||||
$sHtml .= ' <div class="icon ui-state-default ui-corner-all"><span id="tPinMenu" class="ui-icon ui-icon-pin-w">pin</span></div>';
|
||||
}
|
||||
$sHtml .= ' <div style="text-align:center;">'.self::FilterXSS($sForm).'</div>';
|
||||
$sHtml .= ' </div>';
|
||||
$sHtml .= ' </div>';
|
||||
@@ -736,7 +805,7 @@ EOF
|
||||
$sHtml .= ' </div>';
|
||||
$sHtml .= ' </div> <!-- /inner menu -->';
|
||||
$sHtml .= ' </div> <!-- /menu -->';
|
||||
$sHtml .= ' <div class="footer ui-layout-south"><a href="http://www.combodo.com" title="www.combodo.com" target="_blank"><img src="../images/logo-combodo.png"/></a></div>';
|
||||
$sHtml .= ' <div class="footer ui-layout-south"><div id="combodo_logo"><a href="http://www.combodo.com" title="www.combodo.com" target="_blank"><img src="../images/logo-combodo.png"/></a></div></div>';
|
||||
$sHtml .= '<!-- End of the left pane -->';
|
||||
$sHtml .= '</div>';
|
||||
|
||||
@@ -749,13 +818,14 @@ EOF
|
||||
$sHtml .= '<td style="padding-right:20px;padding-left:10px;">'.self::FilterXSS($sLogOffMenu).'</td><td><input type="hidden" name="operation" value="full_text"/></td></tr></table></form></div>';
|
||||
//echo '<td> <input type="hidden" name="operation" value="full_text"/></td></tr></table></form></div>';
|
||||
$sHtml .= ' </div>';
|
||||
$sHtml .= ' <div class="ui-layout-content">';
|
||||
$sHtml .= ' <div class="ui-layout-content" style="overflow:auto;">';
|
||||
$sHtml .= ' <!-- Beginning of page content -->';
|
||||
$sHtml .= self::FilterXSS($this->s_content);
|
||||
$sHtml .= ' <!-- End of page content -->';
|
||||
$sHtml .= ' </div>';
|
||||
$sHtml .= '</div>';
|
||||
|
||||
$sHtml .= $sSouthPane;
|
||||
|
||||
// Add the captured output
|
||||
if (trim($s_captured_output) != "")
|
||||
{
|
||||
@@ -776,7 +846,9 @@ EOF
|
||||
|
||||
if ($this->GetOutputFormat() == 'html')
|
||||
{
|
||||
$oKPI = new ExecutionKPI();
|
||||
echo $sHtml;
|
||||
$oKPI->ComputeAndReport('Echoing ('.round(strlen($sHtml) / 1024).' Kb)');
|
||||
}
|
||||
else if ($this->GetOutputFormat() == 'pdf' && $this->IsOutputFormatAvailable('pdf') )
|
||||
{
|
||||
@@ -801,64 +873,55 @@ EOF
|
||||
}
|
||||
$oMPDF->Output($sOutputName, 'I');
|
||||
}
|
||||
MetaModel::RecordQueryTrace();
|
||||
ExecutionKPI::ReportStats();
|
||||
}
|
||||
|
||||
public function AddTabContainer($sTabContainer)
|
||||
public function AddTabContainer($sTabContainer, $sPrefix = '')
|
||||
{
|
||||
$this->m_aTabs[$sTabContainer] = array();
|
||||
$this->add("\$Tabs:$sTabContainer\$");
|
||||
$this->add($this->m_oTabs->AddTabContainer($sTabContainer, $sPrefix));
|
||||
}
|
||||
|
||||
public function AddToTab($sTabContainer, $sTabLabel, $sHtml)
|
||||
{
|
||||
if (!isset($this->m_aTabs[$sTabContainer][$sTabLabel]))
|
||||
{
|
||||
// Set the content of the tab
|
||||
$this->m_aTabs[$sTabContainer][$sTabLabel] = $sHtml;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Append to the content of the tab
|
||||
$this->m_aTabs[$sTabContainer][$sTabLabel] .= $sHtml;
|
||||
}
|
||||
$this->add($this->m_oTabs->AddToTab($sTabContainer, $sTabLabel, $sHtml));
|
||||
}
|
||||
|
||||
public function SetCurrentTabContainer($sTabContainer = '')
|
||||
{
|
||||
$sPreviousTabContainer = $this->m_sCurrentTabContainer;
|
||||
$this->m_sCurrentTabContainer = $sTabContainer;
|
||||
return $sPreviousTabContainer;
|
||||
return $this->m_oTabs->SetCurrentTabContainer($sTabContainer);
|
||||
}
|
||||
|
||||
public function SetCurrentTab($sTabLabel = '')
|
||||
{
|
||||
$sPreviousTab = $this->m_sCurrentTab;
|
||||
$this->m_sCurrentTab = $sTabLabel;
|
||||
return $sPreviousTab;
|
||||
return $this->m_oTabs->SetCurrentTab($sTabLabel);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a tab which content will be loaded asynchronously via the supplied URL
|
||||
*
|
||||
* Limitations:
|
||||
* Cross site scripting is not not allowed for security reasons. Use a normal tab with an IFRAME if you want to pull content from another server.
|
||||
* Static content cannot be added inside such tabs.
|
||||
*
|
||||
* @param string $sTabLabel The (localised) label of the tab
|
||||
* @param string $sUrl The URL to load (on the same server)
|
||||
* @param boolean $bCache Whether or not to cache the content of the tab once it has been loaded. flase will cause the tab to be reloaded upon each activation.
|
||||
* @since 2.0.3
|
||||
*/
|
||||
public function AddAjaxTab($sTabLabel, $sUrl, $bCache = true)
|
||||
{
|
||||
$this->add($this->m_oTabs->AddAjaxTab($sTabLabel, $sUrl, $bCache));
|
||||
}
|
||||
|
||||
public function GetCurrentTab()
|
||||
{
|
||||
return $this->m_sCurrentTab;
|
||||
return $this->m_oTabs->GetCurrentTab();
|
||||
}
|
||||
|
||||
public function RemoveTab($sTabLabel, $sTabContainer = null)
|
||||
{
|
||||
if ($sTabContainer == null)
|
||||
{
|
||||
$sTabContainer = $this->m_sCurrentTabContainer;
|
||||
}
|
||||
if (isset($this->m_aTabs[$sTabContainer][$sTabLabel]))
|
||||
{
|
||||
// Delete the content of the tab
|
||||
unset($this->m_aTabs[$sTabContainer][$sTabLabel]);
|
||||
|
||||
// If we just removed the active tab, let's reset the active tab
|
||||
if (($this->m_sCurrentTabContainer == $sTabContainer) && ($this->m_sCurrentTab == $sTabLabel))
|
||||
{
|
||||
$this->m_sCurrentTab = '';
|
||||
}
|
||||
}
|
||||
$this->m_oTabs->RemoveTab($sTabLabel, $sTabContainer);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -867,20 +930,7 @@ EOF
|
||||
*/
|
||||
public function FindTab($sPattern, $sTabContainer = null)
|
||||
{
|
||||
$return = false;
|
||||
if ($sTabContainer == null)
|
||||
{
|
||||
$sTabContainer = $this->m_sCurrentTabContainer;
|
||||
}
|
||||
foreach($this->m_aTabs[$sTabContainer] as $sTabLabel => $void)
|
||||
{
|
||||
if (preg_match($sPattern, $sTabLabel))
|
||||
{
|
||||
$result = $sTabLabel;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
return $this->m_oTabs->FindTab($sPattern, $sTabContainer);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -891,26 +941,7 @@ EOF
|
||||
*/
|
||||
public function SelectTab($sTabContainer, $sTabLabel)
|
||||
{
|
||||
$container_index = 0;
|
||||
$tab_index = 0;
|
||||
foreach($this->m_aTabs as $sCurrentTabContainerName => $aTabs)
|
||||
{
|
||||
if ($sTabContainer == $sCurrentTabContainerName)
|
||||
{
|
||||
foreach($aTabs as $sCurrentTabLabel => $void)
|
||||
{
|
||||
if ($sCurrentTabLabel == $sTabLabel)
|
||||
{
|
||||
break;
|
||||
}
|
||||
$tab_index++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
$container_index++;
|
||||
}
|
||||
$sSelector = '#tabbedContent_'.$container_index.' > ul';
|
||||
$this->add_ready_script("$('$sSelector').tabs('select', $tab_index);");
|
||||
$this->add_ready_script($this->m_oTabs->SelectTab($sTabContainer, $sTabLabel));
|
||||
}
|
||||
|
||||
public function StartCollapsibleSection($sSectionLabel, $bOpen = false)
|
||||
@@ -944,9 +975,9 @@ EOF
|
||||
|
||||
public function add($sHtml)
|
||||
{
|
||||
if (!empty($this->m_sCurrentTabContainer) && !empty($this->m_sCurrentTab))
|
||||
if (($this->m_oTabs->GetCurrentTabContainer() != '') && ($this->m_oTabs->GetCurrentTab() != ''))
|
||||
{
|
||||
$this->AddToTab($this->m_sCurrentTabContainer, $this->m_sCurrentTab, $sHtml);
|
||||
$this->m_oTabs->AddToCurrentTab($sHtml);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -960,10 +991,13 @@ EOF
|
||||
*/
|
||||
public function start_capture()
|
||||
{
|
||||
if (!empty($this->m_sCurrentTabContainer) && !empty($this->m_sCurrentTab))
|
||||
$sCurrentTabContainer = $this->m_oTabs->GetCurrentTabContainer();
|
||||
$sCurrentTab = $this->m_oTabs->GetCurrentTab();
|
||||
|
||||
if (!empty($sCurrentTabContainer) && !empty($sCurrentTab))
|
||||
{
|
||||
$iOffset = isset($this->m_aTabs[$this->m_sCurrentTabContainer][$this->m_sCurrentTab]) ? strlen($this->m_aTabs[$this->m_sCurrentTabContainer][$this->m_sCurrentTab]): 0;
|
||||
return array('tc' => $this->m_sCurrentTabContainer, 'tab' => $this->m_sCurrentTab, 'offset' => $iOffset);
|
||||
$iOffset = $this->m_oTabs->GetCurrentTabLength();
|
||||
return array('tc' => $sCurrentTabContainer, 'tab' => $sCurrentTab, 'offset' => $iOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -981,10 +1015,9 @@ EOF
|
||||
{
|
||||
if (is_array($offset))
|
||||
{
|
||||
if (isset($this->m_aTabs[$offset['tc']][$offset['tab']]))
|
||||
if ($this->m_oTabs->TabExists($offset['tc'], $offset['tab']))
|
||||
{
|
||||
$sCaptured = substr($this->m_aTabs[$offset['tc']][$offset['tab']], $offset['offset']);
|
||||
$this->m_aTabs[$offset['tc']][$offset['tab']] = substr($this->m_aTabs[$offset['tc']][$offset['tab']], 0, $offset['offset']);
|
||||
$sCaptured = $this->m_oTabs->TruncateTab($offset['tc'], $offset['tab'], $offset['offset']);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// 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 iTopWizardWebPage
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
require_once('itopwebpage.class.inc.php');
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// 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 LoginWebPage
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2013 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
require_once(APPROOT."/application/nicewebpage.class.inc.php");
|
||||
@@ -30,72 +31,74 @@ require_once(APPROOT."/application/nicewebpage.class.inc.php");
|
||||
|
||||
class LoginWebPage extends NiceWebPage
|
||||
{
|
||||
const EXIT_PROMPT = 0;
|
||||
const EXIT_HTTP_401 = 1;
|
||||
const EXIT_RETURN = 2;
|
||||
|
||||
const EXIT_CODE_OK = 0;
|
||||
const EXIT_CODE_MISSINGLOGIN = 1;
|
||||
const EXIT_CODE_MISSINGPASSWORD = 2;
|
||||
const EXIT_CODE_WRONGCREDENTIALS = 3;
|
||||
const EXIT_CODE_MUSTBEADMIN = 4;
|
||||
const EXIT_CODE_PORTALUSERNOTAUTHORIZED = 5;
|
||||
|
||||
protected static $sHandlerClass = __class__;
|
||||
public static function RegisterHandler($sClass)
|
||||
{
|
||||
self::$sHandlerClass = $sClass;
|
||||
}
|
||||
|
||||
public static function NewLoginWebPage()
|
||||
{
|
||||
return new self::$sHandlerClass;
|
||||
}
|
||||
|
||||
protected static $m_sLoginFailedMessage = '';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct("iTop Login");
|
||||
$this->add_style(<<<EOF
|
||||
body {
|
||||
background: #eee;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
#login-logo {
|
||||
margin-top: 150px;
|
||||
width: 300px;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
background: #f6f6f1;
|
||||
height: 54px;
|
||||
border-top: 1px solid #000;
|
||||
border-left: 1px solid #000;
|
||||
border-right: 1px solid #000;
|
||||
border-bottom: 0;
|
||||
text-align: center;
|
||||
}
|
||||
#login-logo img {
|
||||
border: 0;
|
||||
}
|
||||
#login {
|
||||
width: 300px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding: 20px;
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid #000;
|
||||
border-left: 1px solid #000;
|
||||
border-right: 1px solid #000;
|
||||
border-top: 0;
|
||||
text-align: center;
|
||||
}
|
||||
#pwd, #user,#old_pwd, #new_pwd, #retype_new_pwd {
|
||||
width: 10em;
|
||||
}
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #1C94C4;
|
||||
font-size: 16pt;
|
||||
}
|
||||
.v-spacer {
|
||||
padding-top: 1em;
|
||||
}
|
||||
EOF
|
||||
);
|
||||
public function __construct($sTitle = 'iTop Login')
|
||||
{
|
||||
parent::__construct($sTitle);
|
||||
$this->SetStyleSheet();
|
||||
$this->add_header("Cache-control: no-cache");
|
||||
}
|
||||
|
||||
public function SetStyleSheet()
|
||||
{
|
||||
$this->add_linked_stylesheet("../css/login.css");
|
||||
}
|
||||
|
||||
public static function SetLoginFailedMessage($sMessage)
|
||||
{
|
||||
self::$m_sLoginFailedMessage = $sMessage;
|
||||
}
|
||||
|
||||
|
||||
public function EnableResetPassword()
|
||||
{
|
||||
return MetaModel::GetConfig()->Get('forgot_password');
|
||||
}
|
||||
|
||||
public function DisplayLoginHeader($bMainAppLogo = false)
|
||||
{
|
||||
if ($bMainAppLogo)
|
||||
{
|
||||
$sLogo = 'itop-logo.png';
|
||||
$sBrandingLogo = 'main-logo.png';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sLogo = 'itop-logo-external.png';
|
||||
$sBrandingLogo = 'login-logo.png';
|
||||
}
|
||||
$sVersionShort = Dict::Format('UI:iTopVersion:Short', ITOP_VERSION);
|
||||
$sIconUrl = Utils::GetConfig()->Get('app_icon_url');
|
||||
$sDisplayIcon = utils::GetAbsoluteUrlAppRoot().'images/'.$sLogo;
|
||||
if (file_exists(MODULESROOT.'branding/'.$sBrandingLogo))
|
||||
{
|
||||
$sDisplayIcon = utils::GetAbsoluteUrlModulesRoot().'branding/'.$sBrandingLogo;
|
||||
}
|
||||
$this->add("<div id=\"login-logo\"><a href=\"".htmlentities($sIconUrl, ENT_QUOTES, 'UTF-8')."\"><img title=\"$sVersionShort\" src=\"$sDisplayIcon\"></a></div>\n");
|
||||
}
|
||||
|
||||
public function DisplayLoginForm($sLoginType, $bFailedLogin = false)
|
||||
{
|
||||
switch($sLoginType)
|
||||
@@ -110,6 +113,7 @@ EOF
|
||||
case 'url':
|
||||
$this->add_header('WWW-Authenticate: Basic realm="'.Dict::Format('UI:iTopVersion:Short', 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
|
||||
$this->add('<p><strong>'.Dict::S('UI:Login:Error:AccessRestricted').'</strong></p>');
|
||||
break;
|
||||
@@ -120,8 +124,7 @@ EOF
|
||||
$sAuthUser = utils::ReadParam('auth_user', '', true, 'raw_data');
|
||||
$sAuthPwd = utils::ReadParam('suggest_pwd', '', true, 'raw_data');
|
||||
|
||||
$sVersionShort = Dict::Format('UI:iTopVersion:Short', ITOP_VERSION);
|
||||
$this->add("<div id=\"login-logo\"><a href=\"http://www.combodo.com/itop\"><img title=\"$sVersionShort\" src=\"../images/itop-logo-external.png\"></a></div>\n");
|
||||
$this->DisplayLoginHeader();
|
||||
$this->add("<div id=\"login\">\n");
|
||||
$this->add("<h1>".Dict::S('UI:Login:Welcome')."</h1>\n");
|
||||
if ($bFailedLogin)
|
||||
@@ -140,12 +143,39 @@ EOF
|
||||
$this->add("<p>".Dict::S('UI:Login:IdentifyYourself')."</p>\n");
|
||||
}
|
||||
$this->add("<form method=\"post\">\n");
|
||||
$this->add("<table width=\"100%\">\n");
|
||||
$this->add("<table>\n");
|
||||
$sForgotPwd = $this->EnableResetPassword() ? $this->ForgotPwdLink() : '';
|
||||
$this->add("<tr><td style=\"text-align:right\"><label for=\"user\">".Dict::S('UI:Login:UserNamePrompt').":</label></td><td style=\"text-align:left\"><input id=\"user\" type=\"text\" name=\"auth_user\" value=\"".htmlentities($sAuthUser, ENT_QUOTES, 'UTF-8')."\" /></td></tr>\n");
|
||||
$this->add("<tr><td style=\"text-align:right\"><label for=\"pwd\">".Dict::S('UI:Login:PasswordPrompt').":</label></td><td style=\"text-align:left\"><input id=\"pwd\" type=\"password\" name=\"auth_pwd\" value=\"".htmlentities($sAuthPwd, ENT_QUOTES, 'UTF-8')."\" /></td></tr>\n");
|
||||
$this->add("<tr><td colspan=\"2\" class=\"center v-spacer\"> <input type=\"submit\" value=\"".Dict::S('UI:Button:Login')."\" /></td></tr>\n");
|
||||
$this->add("<tr><td colspan=\"2\" class=\"center v-spacer\"><span class=\"btn_border\"><input type=\"submit\" value=\"".Dict::S('UI:Button:Login')."\" /></span></td></tr>\n");
|
||||
if (strlen($sForgotPwd) > 0)
|
||||
{
|
||||
$this->add("<tr><td colspan=\"2\" class=\"center v-spacer\">$sForgotPwd</td></tr>\n");
|
||||
}
|
||||
$this->add("</table>\n");
|
||||
$this->add("<input type=\"hidden\" name=\"loginop\" value=\"login\" />\n");
|
||||
|
||||
$this->add_ready_script('$("#user").focus();');
|
||||
|
||||
// Keep the OTHER parameters posted
|
||||
foreach($_POST as $sPostedKey => $postedValue)
|
||||
{
|
||||
if (!in_array($sPostedKey, array('auth_user', 'auth_pwd')))
|
||||
{
|
||||
if (is_array($postedValue))
|
||||
{
|
||||
foreach($postedValue as $sKey => $sValue)
|
||||
{
|
||||
$this->add("<input type=\"hidden\" name=\"".htmlentities($sPostedKey, ENT_QUOTES, 'UTF-8')."[".htmlentities($sKey, ENT_QUOTES, 'UTF-8')."]\" value=\"".htmlentities($sValue, ENT_QUOTES, 'UTF-8')."\" />\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->add("<input type=\"hidden\" name=\"".htmlentities($sPostedKey, ENT_QUOTES, 'UTF-8')."\" value=\"".htmlentities($postedValue, ENT_QUOTES, 'UTF-8')."\" />\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->add("</form>\n");
|
||||
$this->add(Dict::S('UI:Login:About'));
|
||||
$this->add("</div>\n");
|
||||
@@ -153,11 +183,201 @@ EOF
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return '' to disable this feature
|
||||
*/
|
||||
public function ForgotPwdLink()
|
||||
{
|
||||
$sUrl = '?loginop=forgot_pwd';
|
||||
$sHtml = "<a href=\"$sUrl\" target=\"_blank\">".Dict::S('UI:Login:ForgotPwd')."</a>";
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
public function DisplayForgotPwdForm($bFailedToReset = false, $sFailureReason = null)
|
||||
{
|
||||
$this->DisplayLoginHeader();
|
||||
$this->add("<div id=\"login\">\n");
|
||||
$this->add("<h1>".Dict::S('UI:Login:ForgotPwdForm')."</h1>\n");
|
||||
$this->add("<p>".Dict::S('UI:Login:ForgotPwdForm+')."</p>\n");
|
||||
if ($bFailedToReset)
|
||||
{
|
||||
$this->add("<p class=\"hilite\">".Dict::Format('UI:Login:ResetPwdFailed', htmlentities($sFailureReason, ENT_QUOTES, 'UTF-8'))."</p>\n");
|
||||
}
|
||||
$sAuthUser = utils::ReadParam('auth_user', '', true, 'raw_data');
|
||||
$this->add("<form method=\"post\">\n");
|
||||
$this->add("<table>\n");
|
||||
$this->add("<tr><td colspan=\"2\" class=\"center\"><label for=\"user\">".Dict::S('UI:Login:UserNamePrompt').":</label><input id=\"user\" type=\"text\" name=\"auth_user\" value=\"".htmlentities($sAuthUser, ENT_QUOTES, 'UTF-8')."\" /></td></tr>\n");
|
||||
$this->add("<tr><td colspan=\"2\" class=\"center v-spacer\"><span class=\"btn_border\"><input type=\"button\" onClick=\"window.close();\" value=\"".Dict::S('UI:Button:Cancel')."\" /></span> <span class=\"btn_border\"><input type=\"submit\" value=\"".Dict::S('UI:Login:ResetPassword')."\" /></span></td></tr>\n");
|
||||
$this->add("</table>\n");
|
||||
$this->add("<input type=\"hidden\" name=\"loginop\" value=\"forgot_pwd_go\" />\n");
|
||||
$this->add("</form>\n");
|
||||
$this->add("</div>\n");
|
||||
|
||||
$this->add_ready_script('$("#user").focus();');
|
||||
}
|
||||
|
||||
protected function ForgotPwdGo()
|
||||
{
|
||||
$sAuthUser = utils::ReadParam('auth_user', '', true, 'raw_data');
|
||||
|
||||
try
|
||||
{
|
||||
UserRights::Login($sAuthUser); // Set the user's language (if possible!)
|
||||
$oUser = UserRights::GetUserObject();
|
||||
if ($oUser == null)
|
||||
{
|
||||
throw new Exception(Dict::Format('UI:ResetPwd-Error-WrongLogin', $sAuthUser));
|
||||
}
|
||||
if (!MetaModel::IsValidAttCode(get_class($oUser), 'reset_pwd_token'))
|
||||
{
|
||||
throw new Exception(Dict::S('UI:ResetPwd-Error-NotPossible'));
|
||||
}
|
||||
if (!$oUser->CanChangePassword())
|
||||
{
|
||||
throw new Exception(Dict::S('UI:ResetPwd-Error-FixedPwd'));
|
||||
}
|
||||
|
||||
$sTo = $oUser->GetResetPasswordEmail(); // throws Exceptions if not allowed
|
||||
if ($sTo == '')
|
||||
{
|
||||
throw new Exception(Dict::S('UI:ResetPwd-Error-NoEmail'));
|
||||
}
|
||||
|
||||
// This token allows the user to change the password without knowing the previous one
|
||||
$sToken = substr(md5(APPROOT.uniqid()), 0, 16);
|
||||
$oUser->Set('reset_pwd_token', $sToken);
|
||||
CMDBObject::SetTrackInfo('Reset password');
|
||||
$oUser->DBUpdate();
|
||||
|
||||
$oEmail = new Email();
|
||||
$oEmail->SetRecipientTO($sTo);
|
||||
$sFrom = MetaModel::GetConfig()->Get('forgot_password_from');
|
||||
if ($sFrom == '')
|
||||
{
|
||||
$sFrom = $sTo;
|
||||
}
|
||||
$oEmail->SetRecipientFrom($sFrom);
|
||||
$oEmail->SetSubject(Dict::S('UI:ResetPwd-EmailSubject'));
|
||||
$sResetUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?loginop=reset_pwd&auth_user='.urlencode($oUser->Get('login')).'&token='.urlencode($sToken);
|
||||
$oEmail->SetBody(Dict::Format('UI:ResetPwd-EmailBody', $sResetUrl));
|
||||
$iRes = $oEmail->Send($aIssues, true /* force synchronous exec */);
|
||||
switch ($iRes)
|
||||
{
|
||||
//case EMAIL_SEND_PENDING:
|
||||
case EMAIL_SEND_OK:
|
||||
break;
|
||||
|
||||
case EMAIL_SEND_ERROR:
|
||||
default:
|
||||
IssueLog::Error('Failed to send the email with the NEW password for '.$oUser->Get('friendlyname').': '.implode(', ', $aIssues));
|
||||
throw new Exception(Dict::S('UI:ResetPwd-Error-Send'));
|
||||
}
|
||||
|
||||
$this->DisplayLoginHeader();
|
||||
$this->add("<div id=\"login\">\n");
|
||||
$this->add("<h1>".Dict::S('UI:Login:ForgotPwdForm')."</h1>\n");
|
||||
$this->add("<p>".Dict::S('UI:ResetPwd-EmailSent')."</p>");
|
||||
$this->add("<form method=\"post\">\n");
|
||||
$this->add("<table>\n");
|
||||
$this->add("<tr><td colspan=\"2\" class=\"center v-spacer\"><input type=\"button\" onClick=\"window.close();\" value=\"".Dict::S('UI:Button:Done')."\" /></td></tr>\n");
|
||||
$this->add("</table>\n");
|
||||
$this->add("</form>\n");
|
||||
$this->add("</div\n");
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
$this->DisplayForgotPwdForm(true, $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function DisplayResetPwdForm()
|
||||
{
|
||||
$sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data');
|
||||
$sToken = utils::ReadParam('token', '', false, 'raw_data');
|
||||
|
||||
UserRights::Login($sAuthUser); // Set the user's language
|
||||
$oUser = UserRights::GetUserObject();
|
||||
|
||||
$this->DisplayLoginHeader();
|
||||
$this->add("<div id=\"login\">\n");
|
||||
$this->add("<h1>".Dict::S('UI:ResetPwd-Title')."</h1>\n");
|
||||
if ($oUser == null)
|
||||
{
|
||||
$this->add("<p>".Dict::Format('UI:ResetPwd-Error-WrongLogin', $sAuthUser)."</p>\n");
|
||||
}
|
||||
elseif ($oUser->Get('reset_pwd_token') != $sToken)
|
||||
{
|
||||
$this->add("<p>".Dict::S('UI:ResetPwd-Error-InvalidToken')."</p>\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->add("<p>".Dict::Format('UI:ResetPwd-Error-EnterPassword', $oUser->GetFriendlyName())."</p>\n");
|
||||
|
||||
$sInconsistenPwdMsg = Dict::S('UI:Login:RetypePwdDoesNotMatch');
|
||||
$this->add_script(
|
||||
<<<EOF
|
||||
function DoCheckPwd()
|
||||
{
|
||||
if ($('#new_pwd').val() != $('#retype_new_pwd').val())
|
||||
{
|
||||
alert('$sInconsistenPwdMsg');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
EOF
|
||||
);
|
||||
$this->add("<form method=\"post\">\n");
|
||||
$this->add("<table>\n");
|
||||
$this->add("<tr><td style=\"text-align:right\"><label for=\"new_pwd\">".Dict::S('UI:Login:NewPasswordPrompt').":</label></td><td style=\"text-align:left\"><input type=\"password\" id=\"new_pwd\" name=\"new_pwd\" value=\"\" /></td></tr>\n");
|
||||
$this->add("<tr><td style=\"text-align:right\"><label for=\"retype_new_pwd\">".Dict::S('UI:Login:RetypeNewPasswordPrompt').":</label></td><td style=\"text-align:left\"><input type=\"password\" id=\"retype_new_pwd\" name=\"retype_new_pwd\" value=\"\" /></td></tr>\n");
|
||||
$this->add("<tr><td colspan=\"2\" class=\"center v-spacer\"><span class=\"btn_border\"><input type=\"submit\" onClick=\"return DoCheckPwd();\" value=\"".Dict::S('UI:Button:ChangePassword')."\" /></span></td></tr>\n");
|
||||
$this->add("</table>\n");
|
||||
$this->add("<input type=\"hidden\" name=\"loginop\" value=\"do_reset_pwd\" />\n");
|
||||
$this->add("<input type=\"hidden\" name=\"auth_user\" value=\"".htmlentities($sAuthUser, ENT_QUOTES, 'UTF-8')."\" />\n");
|
||||
$this->add("<input type=\"hidden\" name=\"token\" value=\"".htmlentities($sToken, ENT_QUOTES, 'UTF-8')."\" />\n");
|
||||
$this->add("</form>\n");
|
||||
$this->add("</div\n");
|
||||
}
|
||||
}
|
||||
|
||||
public function DoResetPassword()
|
||||
{
|
||||
$sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data');
|
||||
$sToken = utils::ReadParam('token', '', false, 'raw_data');
|
||||
$sNewPwd = utils::ReadPostedParam('new_pwd', '', false, 'raw_data');
|
||||
|
||||
UserRights::Login($sAuthUser); // Set the user's language
|
||||
$oUser = UserRights::GetUserObject();
|
||||
|
||||
$this->DisplayLoginHeader();
|
||||
$this->add("<div id=\"login\">\n");
|
||||
$this->add("<h1>".Dict::S('UI:ResetPwd-Title')."</h1>\n");
|
||||
if ($oUser == null)
|
||||
{
|
||||
$this->add("<p>".Dict::Format('UI:ResetPwd-Error-WrongLogin', $sAuthUser)."</p>\n");
|
||||
}
|
||||
elseif ($oUser->Get('reset_pwd_token') != $sToken)
|
||||
{
|
||||
$this->add("<p>".Dict::S('UI:ResetPwd-Error-InvalidToken')."</p>\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Trash the token and change the password
|
||||
$oUser->Set('reset_pwd_token', '');
|
||||
$oUser->SetPassword($sNewPwd); // Does record the change into the DB
|
||||
|
||||
$this->add("<p>".Dict::S('UI:ResetPwd-Ready')."</p>");
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot();
|
||||
$this->add("<p><a href=\"$sUrl\">".Dict::S('UI:ResetPwd-Login')."</a></p>");
|
||||
}
|
||||
$this->add("</div\n");
|
||||
}
|
||||
|
||||
public function DisplayChangePwdForm($bFailedLogin = false)
|
||||
{
|
||||
$sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data');
|
||||
|
||||
$sVersionShort = Dict::Format('UI:iTopVersion:Short', ITOP_VERSION);
|
||||
$sInconsistenPwdMsg = Dict::S('UI:Login:RetypePwdDoesNotMatch');
|
||||
$this->add_script(<<<EOF
|
||||
function GoBack()
|
||||
@@ -176,7 +396,7 @@ function DoCheckPwd()
|
||||
}
|
||||
EOF
|
||||
);
|
||||
$this->add("<div id=\"login-logo\"><a href=\"http://www.combodo.com/itop\"><img title=\"$sVersionShort\" src=\"../images/itop-logo.png\"></a></div>\n");
|
||||
$this->DisplayLoginHeader();
|
||||
$this->add("<div id=\"login\">\n");
|
||||
$this->add("<h1>".Dict::S('UI:Login:ChangeYourPassword')."</h1>\n");
|
||||
if ($bFailedLogin)
|
||||
@@ -184,11 +404,11 @@ EOF
|
||||
$this->add("<p class=\"hilite\">".Dict::S('UI:Login:IncorrectOldPassword')."</p>\n");
|
||||
}
|
||||
$this->add("<form method=\"post\">\n");
|
||||
$this->add("<table width=\"100%\">\n");
|
||||
$this->add("<table>\n");
|
||||
$this->add("<tr><td style=\"text-align:right\"><label for=\"old_pwd\">".Dict::S('UI:Login:OldPasswordPrompt').":</label></td><td style=\"text-align:left\"><input type=\"password\" id=\"old_pwd\" name=\"old_pwd\" value=\"\" /></td></tr>\n");
|
||||
$this->add("<tr><td style=\"text-align:right\"><label for=\"new_pwd\">".Dict::S('UI:Login:NewPasswordPrompt').":</label></td><td style=\"text-align:left\"><input type=\"password\" id=\"new_pwd\" name=\"new_pwd\" value=\"\" /></td></tr>\n");
|
||||
$this->add("<tr><td style=\"text-align:right\"><label for=\"retype_new_pwd\">".Dict::S('UI:Login:RetypeNewPasswordPrompt').":</label></td><td style=\"text-align:left\"><input type=\"password\" id=\"retype_new_pwd\" name=\"retype_new_pwd\" value=\"\" /></td></tr>\n");
|
||||
$this->add("<tr><td colspan=\"2\" class=\"center v-spacer\"> <input type=\"button\" onClick=\"GoBack();\" value=\"".Dict::S('UI:Button:Cancel')."\" /> <input type=\"submit\" onClick=\"return DoCheckPwd();\" value=\"".Dict::S('UI:Button:ChangePassword')."\" /></td></tr>\n");
|
||||
$this->add("<tr><td colspan=\"2\" class=\"center v-spacer\"><span class=\"btn_border\"><input type=\"button\" onClick=\"GoBack();\" value=\"".Dict::S('UI:Button:Cancel')."\" /></span> <span class=\"btn_border\"><input type=\"submit\" onClick=\"return DoCheckPwd();\" value=\"".Dict::S('UI:Button:ChangePassword')."\" /></span></td></tr>\n");
|
||||
$this->add("</table>\n");
|
||||
$this->add("<input type=\"hidden\" name=\"loginop\" value=\"do_change_pwd\" />\n");
|
||||
$this->add("</form>\n");
|
||||
@@ -217,20 +437,33 @@ EOF
|
||||
return MetaModel::GetConfig()->GetSecureConnectionRequired();
|
||||
}
|
||||
|
||||
static function IsConnectionSecure()
|
||||
/**
|
||||
* Guess if a string looks like an UTF-8 string based on some ranges of multi-bytes encoding
|
||||
* @param string $sString
|
||||
* @return bool True if the string contains some typical UTF-8 multi-byte sequences
|
||||
*/
|
||||
static function LooksLikeUTF8($sString)
|
||||
{
|
||||
$bSecured = false;
|
||||
|
||||
if (isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS']!='off'))
|
||||
{
|
||||
$bSecured = true;
|
||||
}
|
||||
return $bSecured;
|
||||
return preg_match('%(?:
|
||||
[\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
|
||||
|\xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
|
||||
|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
|
||||
|\xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
|
||||
|\xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
|
||||
|[\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
|
||||
|\xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
|
||||
)+%xs', $sString);
|
||||
}
|
||||
|
||||
protected static function Login()
|
||||
|
||||
/**
|
||||
* Attempt a login
|
||||
*
|
||||
* @param int iOnExit What action to take if the user is not logged on (one of the class constants EXIT_...)
|
||||
* @return int One of the class constants EXIT_CODE_...
|
||||
*/
|
||||
protected static function Login($iOnExit)
|
||||
{
|
||||
if (self::SecureConnectionRequired() && !self::IsConnectionSecure())
|
||||
if (self::SecureConnectionRequired() && !utils::IsConnectionSecure())
|
||||
{
|
||||
// Non secured URL... request for a secure connection
|
||||
throw new Exception('Secure connection required!');
|
||||
@@ -243,7 +476,7 @@ EOF
|
||||
//echo "User: ".$_SESSION['auth_user']."\n";
|
||||
// Already authentified
|
||||
UserRights::Login($_SESSION['auth_user']); // Login & set the user's language
|
||||
return true;
|
||||
return self::EXIT_CODE_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -270,8 +503,8 @@ EOF
|
||||
case 'form':
|
||||
// iTop standard mode: form based authentication
|
||||
$sAuthUser = utils::ReadPostedParam('auth_user', '', false, 'raw_data');
|
||||
$sAuthPwd = utils::ReadPostedParam('auth_pwd', '', false, 'raw_data');
|
||||
if ($sAuthUser != '')
|
||||
$sAuthPwd = utils::ReadPostedParam('auth_pwd', null, false, 'raw_data');
|
||||
if (($sAuthUser != '') && ($sAuthPwd !== null))
|
||||
{
|
||||
$sLoginMode = 'form';
|
||||
}
|
||||
@@ -288,7 +521,22 @@ EOF
|
||||
else if (isset($_SERVER['PHP_AUTH_USER']))
|
||||
{
|
||||
$sAuthUser = $_SERVER['PHP_AUTH_USER'];
|
||||
// Unfortunately, the RFC is not clear about the encoding...
|
||||
// IE and FF supply the user and password encoded in ISO-8859-1 whereas Chrome provides them encoded in UTF-8
|
||||
// So let's try to guess if it's an UTF-8 string or not... fortunately all encodings share the same ASCII base
|
||||
if (!self::LooksLikeUTF8($sAuthUser))
|
||||
{
|
||||
// Does not look like and UTF-8 string, try to convert it from iso-8859-1 to UTF-8
|
||||
// Supposed to be harmless in case of a plain ASCII string...
|
||||
$sAuthUser = iconv('iso-8859-1', 'utf-8', $sAuthUser);
|
||||
}
|
||||
$sAuthPwd = $_SERVER['PHP_AUTH_PW'];
|
||||
if (!self::LooksLikeUTF8($sAuthPwd))
|
||||
{
|
||||
// Does not look like and UTF-8 string, try to convert it from iso-8859-1 to UTF-8
|
||||
// Supposed to be harmless in case of a plain ASCII string...
|
||||
$sAuthPwd = iconv('iso-8859-1', 'utf-8', $sAuthPwd);
|
||||
}
|
||||
$sLoginMode = 'basic';
|
||||
}
|
||||
break;
|
||||
@@ -310,7 +558,7 @@ EOF
|
||||
// Credentials passed directly in the url
|
||||
$sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data');
|
||||
$sAuthPwd = utils::ReadParam('auth_pwd', null, false, 'raw_data');
|
||||
if (($sAuthUser != '') && ($sAuthPwd != null))
|
||||
if (($sAuthUser != '') && ($sAuthPwd !== null))
|
||||
{
|
||||
$sLoginMode = 'url';
|
||||
}
|
||||
@@ -331,10 +579,31 @@ EOF
|
||||
{
|
||||
$sLoginMode = $aAllowedLoginTypes[0]; // First in the list...
|
||||
}
|
||||
$oPage = new LoginWebPage();
|
||||
$oPage->DisplayLoginForm( $sLoginMode, false /* no previous failed attempt */);
|
||||
$oPage->output();
|
||||
exit;
|
||||
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)
|
||||
{
|
||||
if (($sAuthUser !== '') && ($sAuthPwd === null))
|
||||
{
|
||||
return self::EXIT_CODE_MISSINGPASSWORD;
|
||||
}
|
||||
else
|
||||
{
|
||||
return self::EXIT_CODE_MISSINGLOGIN;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$oPage = self::NewLoginWebPage();
|
||||
$oPage->DisplayLoginForm( $sLoginMode, false /* no previous failed attempt */);
|
||||
$oPage->output();
|
||||
exit;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -342,10 +611,24 @@ EOF
|
||||
{
|
||||
//echo "Check Credentials returned false for user $sAuthUser!";
|
||||
self::ResetSession();
|
||||
$oPage = new LoginWebPage();
|
||||
$oPage->DisplayLoginForm( $sLoginMode, true /* failed attempt */);
|
||||
$oPage->output();
|
||||
exit;
|
||||
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
|
||||
{
|
||||
@@ -366,16 +649,44 @@ EOF
|
||||
}
|
||||
}
|
||||
}
|
||||
return self::EXIT_CODE_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridable: depending on the user, head toward a dedicated portal
|
||||
* @param bool $bIsAllowedToPortalUsers Whether or not the current page is considered as part of the portal
|
||||
* @param int $iOnExit How to complete the call: redirect or return a code
|
||||
*/
|
||||
protected static function ChangeLocation($bIsAllowedToPortalUsers, $iOnExit = self::EXIT_PROMPT)
|
||||
{
|
||||
if ( (!$bIsAllowedToPortalUsers) && (UserRights::IsPortalUser()))
|
||||
{
|
||||
if ($iOnExit == self::EXIT_RETURN)
|
||||
{
|
||||
return self::EXIT_CODE_PORTALUSERNOTAUTHORIZED;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No rights to be here, redirect to the portal
|
||||
header('Location: '.utils::GetAbsoluteUrlAppRoot().'portal/index.php');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return self::EXIT_CODE_OK;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if the user is already authentified, if yes, then performs some additional validations:
|
||||
* - if $bMustBeAdmin is true, then the user must be an administrator, otherwise an error is displayed
|
||||
* - if $bIsAllowedToPortalUsers is false and the user has only access to the portal, then the user is redirected to the portal
|
||||
* @param bool $bMustBeAdmin Whether or not the user must be an admin to access the current page
|
||||
* @param bool $bIsAllowedToPortalUsers Whether or not the current page is considered as part of the portal
|
||||
* @param int iOnExit What action to take if the user is not logged on (one of the class constants EXIT_...)
|
||||
*/
|
||||
static function DoLogin($bMustBeAdmin = false, $bIsAllowedToPortalUsers = false)
|
||||
static function DoLogin($bMustBeAdmin = false, $bIsAllowedToPortalUsers = false, $iOnExit = self::EXIT_PROMPT)
|
||||
{
|
||||
$sMessage = ''; // In case we need to return a message to the calling web page
|
||||
$operation = utils::ReadParam('loginop', '');
|
||||
@@ -399,16 +710,44 @@ EOF
|
||||
}
|
||||
}
|
||||
self::ResetSession();
|
||||
$oPage = new LoginWebPage();
|
||||
$oPage = self::NewLoginWebPage();
|
||||
$oPage->DisplayLoginForm( $sLoginMode, false /* not a failed attempt */);
|
||||
$oPage->output();
|
||||
exit;
|
||||
}
|
||||
else if ($operation == 'forgot_pwd')
|
||||
{
|
||||
$oPage = self::NewLoginWebPage();
|
||||
$oPage->DisplayForgotPwdForm();
|
||||
$oPage->output();
|
||||
exit;
|
||||
}
|
||||
else if ($operation == 'forgot_pwd_go')
|
||||
{
|
||||
$oPage = self::NewLoginWebPage();
|
||||
$oPage->ForgotPwdGo();
|
||||
$oPage->output();
|
||||
exit;
|
||||
}
|
||||
else if ($operation == 'reset_pwd')
|
||||
{
|
||||
$oPage = self::NewLoginWebPage();
|
||||
$oPage->DisplayResetPwdForm();
|
||||
$oPage->output();
|
||||
exit;
|
||||
}
|
||||
else if ($operation == 'do_reset_pwd')
|
||||
{
|
||||
$oPage = self::NewLoginWebPage();
|
||||
$oPage->DoResetPassword();
|
||||
$oPage->output();
|
||||
exit;
|
||||
}
|
||||
else if ($operation == 'change_pwd')
|
||||
{
|
||||
$sAuthUser = $_SESSION['auth_user'];
|
||||
UserRights::Login($sAuthUser); // Set the user's language
|
||||
$oPage = new LoginWebPage();
|
||||
$oPage = self::NewLoginWebPage();
|
||||
$oPage->DisplayChangePwdForm();
|
||||
$oPage->output();
|
||||
exit;
|
||||
@@ -421,30 +760,43 @@ EOF
|
||||
$sNewPwd = utils::ReadPostedParam('new_pwd', '', false, 'raw_data');
|
||||
if (UserRights::CanChangePassword() && ((!UserRights::CheckCredentials($sAuthUser, $sOldPwd)) || (!UserRights::ChangePassword($sOldPwd, $sNewPwd))))
|
||||
{
|
||||
$oPage = new LoginWebPage();
|
||||
$oPage = self::NewLoginWebPage();
|
||||
$oPage->DisplayChangePwdForm(true); // old pwd was wrong
|
||||
$oPage->output();
|
||||
exit;
|
||||
}
|
||||
$sMessage = Dict::S('UI:Login:PasswordChanged');
|
||||
}
|
||||
|
||||
self::Login();
|
||||
$iRet = self::Login($iOnExit);
|
||||
|
||||
if ($bMustBeAdmin && !UserRights::IsAdministrator())
|
||||
{
|
||||
require_once(APPROOT.'/setup/setuppage.class.inc.php');
|
||||
$oP = new SetupPage(Dict::S('UI:PageTitle:FatalError'));
|
||||
$oP->add("<h1>".Dict::S('UI:Login:Error:AccessAdmin')."</h1>\n");
|
||||
$oP->p("<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/logoff.php\">".Dict::S('UI:LogOffMenu')."</a>");
|
||||
$oP->output();
|
||||
exit;
|
||||
}
|
||||
elseif ( (!$bIsAllowedToPortalUsers) && (UserRights::IsPortalUser()))
|
||||
if ($iRet == self::EXIT_CODE_OK)
|
||||
{
|
||||
// No rights to be here, redirect to the portal
|
||||
header('Location: '.utils::GetAbsoluteUrlAppRoot().'portal/index.php');
|
||||
if ($bMustBeAdmin && !UserRights::IsAdministrator())
|
||||
{
|
||||
if ($iOnExit == self::EXIT_RETURN)
|
||||
{
|
||||
return self::EXIT_CODE_MUSTBEADMIN;
|
||||
}
|
||||
else
|
||||
{
|
||||
require_once(APPROOT.'/setup/setuppage.class.inc.php');
|
||||
$oP = new SetupPage(Dict::S('UI:PageTitle:FatalError'));
|
||||
$oP->add("<h1>".Dict::S('UI:Login:Error:AccessAdmin')."</h1>\n");
|
||||
$oP->p("<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/logoff.php\">".Dict::S('UI:LogOffMenu')."</a>");
|
||||
$oP->output();
|
||||
exit;
|
||||
}
|
||||
}
|
||||
$iRet = call_user_func(array(self::$sHandlerClass, 'ChangeLocation'), $bIsAllowedToPortalUsers, $iOnExit);
|
||||
}
|
||||
if ($iOnExit == self::EXIT_RETURN)
|
||||
{
|
||||
return $iRet;
|
||||
}
|
||||
else
|
||||
{
|
||||
return $sMessage;
|
||||
}
|
||||
return $sMessage;
|
||||
}
|
||||
} // End of class
|
||||
?>
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Construction and display of the application's main menu
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
require_once(APPROOT.'/application/utils.inc.php');
|
||||
@@ -79,6 +80,15 @@ class ApplicationMenu
|
||||
call_user_func($aCallSpec);
|
||||
}
|
||||
}
|
||||
|
||||
// Build menus from the menus themselves (e.g. the ShortcutContainerMenuNode will do that)
|
||||
//
|
||||
foreach(self::$aRootMenus as $aMenu)
|
||||
{
|
||||
$oMenuNode = self::GetMenuNode($aMenu['index']);
|
||||
$oMenuNode->PopulateChildMenus();
|
||||
}
|
||||
|
||||
self::$bAdditionalMenusLoaded = true;
|
||||
}
|
||||
}
|
||||
@@ -130,7 +140,7 @@ class ApplicationMenu
|
||||
// they were not used to display the menus (redundant or unused)
|
||||
//
|
||||
$aBacktrace = debug_backtrace();
|
||||
$sFile = $aBacktrace[2]["file"];
|
||||
$sFile = isset($aBacktrace[2]["file"]) ? $aBacktrace[2]["file"] : $aBacktrace[1]["file"];
|
||||
self::$aMenusIndex[$index] = array('node' => $oMenuNode, 'children' => array(), 'parent' => $sParentId, 'rank' => $fRank, 'source_file' => $sFile);
|
||||
}
|
||||
else
|
||||
@@ -153,7 +163,7 @@ class ApplicationMenu
|
||||
/**
|
||||
* Entry point to display the whole menu into the web page, used by iTopWebPage
|
||||
*/
|
||||
static public function DisplayMenu(iTopWebPage $oPage, $aExtraParams)
|
||||
static public function DisplayMenu($oPage, $aExtraParams)
|
||||
{
|
||||
self::LoadAdditionalMenus();
|
||||
// Sort the root menu based on the rank
|
||||
@@ -174,8 +184,9 @@ class ApplicationMenu
|
||||
$oPage->AddToMenu('</ul>');
|
||||
if ($bActive)
|
||||
{
|
||||
$oPage->add_ready_script("$('#accordion').accordion('activate', $iAccordion);");
|
||||
$oPage->add_ready_script("$('#accordion').accordion('option', {collapsible: true});"); // Make it auto-collapsible once it has been opened properly
|
||||
//$oPage->add_ready_script("$('#accordion').accordion('activate', $iAccordion);");
|
||||
// $oPage->add_ready_script("$('#accordion').accordion('option', {collapsible: true});"); // Make it auto-collapsible once it has been opened properly
|
||||
$oPage->add_ready_script("$('#accordion').accordion('option', {collapsible: true, active: $iAccordion});"); // Make it auto-collapsible once it has been opened properly
|
||||
}
|
||||
}
|
||||
$oPage->AddToMenu('</div>');
|
||||
@@ -252,11 +263,11 @@ class ApplicationMenu
|
||||
/**
|
||||
* Helper function to get the list of child(ren) of a menu
|
||||
*/
|
||||
static protected function GetChildren($index)
|
||||
static public function GetChildren($index)
|
||||
{
|
||||
return self::$aMenusIndex[$index]['children'];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper function to get the ID of a menu based on its name
|
||||
* @param string $sTitle Title of the menu (as passed when creating the menu)
|
||||
@@ -289,7 +300,9 @@ class ApplicationMenu
|
||||
// Make sure the root menu is sorted on 'rank'
|
||||
usort(self::$aRootMenus, array('ApplicationMenu', 'CompareOnRank'));
|
||||
$oFirstGroup = self::GetMenuNode(self::$aRootMenus[0]['index']);
|
||||
$oMenuNode = self::GetMenuNode(self::$aMenusIndex[$oFirstGroup->GetIndex()]['children'][0]['index']);
|
||||
$aChildren = self::$aMenusIndex[$oFirstGroup->GetIndex()]['children'];
|
||||
usort($aChildren, array('ApplicationMenu', 'CompareOnRank'));
|
||||
$oMenuNode = self::GetMenuNode($aChildren[0]['index']);
|
||||
$sMenuId = $oMenuNode->GetMenuId();
|
||||
}
|
||||
return $sMenuId;
|
||||
@@ -406,6 +419,16 @@ abstract class MenuNode
|
||||
return $this->index;
|
||||
}
|
||||
|
||||
public function PopulateChildMenus()
|
||||
{
|
||||
foreach (ApplicationMenu::GetChildren($this->GetIndex()) as $aMenu)
|
||||
{
|
||||
$index = $aMenu['index'];
|
||||
$oMenu = ApplicationMenu::GetMenuNode($index);
|
||||
$oMenu->PopulateChildMenus();
|
||||
}
|
||||
}
|
||||
|
||||
public function GetHyperlink($aExtraParams)
|
||||
{
|
||||
$aExtraParams['c[menu]'] = $this->GetMenuId();
|
||||
@@ -616,31 +639,39 @@ class OQLMenuNode extends MenuNode
|
||||
|
||||
public function RenderContent(WebPage $oPage, $aExtraParams = array())
|
||||
{
|
||||
$aExtraParams = array_merge($aExtraParams, $this->m_aParams);
|
||||
try
|
||||
{
|
||||
$oSearch = DBObjectSearch::FromOQL($this->sOQL);
|
||||
$sIcon = MetaModel::GetClassIcon($oSearch->GetClass());
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
$sIcon = '';
|
||||
}
|
||||
OQLMenuNode::RenderOQLSearch
|
||||
(
|
||||
$this->sOQL,
|
||||
Dict::S($this->sPageTitle),
|
||||
'Menu_'.$this->GetMenuId(),
|
||||
$this->bSearch, // Search pane
|
||||
true, // Search open
|
||||
$oPage,
|
||||
array_merge($this->m_aParams, $aExtraParams)
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->bSearch)
|
||||
public static function RenderOQLSearch($sOql, $sTitle, $sUsageId, $bSearchPane, $bSearchOpen, WebPage $oPage, $aExtraParams = array())
|
||||
{
|
||||
$sUsageId = utils::GetSafeId($sUsageId);
|
||||
$oSearch = DBObjectSearch::FromOQL($sOql);
|
||||
$sIcon = MetaModel::GetClassIcon($oSearch->GetClass());
|
||||
|
||||
if ($bSearchPane)
|
||||
{
|
||||
$aParams = array_merge(array('open' => true, 'table_id' => 'Menu_'.$this->GetMenuId()), $aExtraParams);
|
||||
$aParams = array_merge(array('open' => $bSearchOpen, 'table_id' => $sUsageId), $aExtraParams);
|
||||
$oBlock = new DisplayBlock($oSearch, 'search', false /* Asynchronous */, $aParams);
|
||||
$oBlock->Display($oPage, 0);
|
||||
}
|
||||
|
||||
$oPage->add("<p class=\"page-header\">$sIcon ".Dict::S($this->sPageTitle)."</p>");
|
||||
$oPage->add("<p class=\"page-header\">$sIcon ".Dict::S($sTitle)."</p>");
|
||||
|
||||
$aParams = array_merge(array('table_id' => 'Menu_'.$this->GetMenuId()), $aExtraParams);
|
||||
$aParams = array_merge(array('table_id' => $sUsageId), $aExtraParams);
|
||||
$oBlock = new DisplayBlock($oSearch, 'list', false /* Asynchronous */, $aParams);
|
||||
$oBlock->Display($oPage, 1);
|
||||
$oBlock->Display($oPage, $sUsageId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class defines a menu item that displays a search form for the given class of objects
|
||||
*/
|
||||
@@ -672,7 +703,7 @@ class SearchMenuNode extends MenuNode
|
||||
public function RenderContent(WebPage $oPage, $aExtraParams = array())
|
||||
{
|
||||
$oSearch = new DBObjectSearch($this->sClass);
|
||||
$aParams = array_merge(array('open' => true, 'table_id' => 'Menu_'.$this->GetMenuId()), $aExtraParams);
|
||||
$aParams = array_merge(array('open' => true, 'table_id' => 'Menu_'.utils::GetSafeId($this->GetMenuId())), $aExtraParams);
|
||||
$oBlock = new DisplayBlock($oSearch, 'search', false /* Asynchronous */, $aParams);
|
||||
$oBlock->Display($oPage, 0);
|
||||
}
|
||||
@@ -848,8 +879,41 @@ class DashboardMenuNode extends MenuNode
|
||||
$oDashboard = $this->GetDashboard();
|
||||
if ($oDashboard != null)
|
||||
{
|
||||
$sDivId = preg_replace('/[^a-zA-Z0-9_]/', '', $this->sMenuId);
|
||||
$oPage->add('<div class="dashboard_contents" id="'.$sDivId.'">');
|
||||
$oDashboard->Render($oPage, false, $aExtraParams);
|
||||
|
||||
$oPage->add('</div>');
|
||||
$oDashboard->RenderEditionTools($oPage);
|
||||
|
||||
if ($oDashboard->GetAutoReload())
|
||||
{
|
||||
$sId = $this->sMenuId;
|
||||
$sExtraParams = json_encode($aExtraParams);
|
||||
$iReloadInterval = 1000 * $oDashboard->GetAutoReloadInterval();
|
||||
$oPage->add_script(
|
||||
<<<EOF
|
||||
setInterval("ReloadDashboard('$sDivId');", $iReloadInterval);
|
||||
|
||||
function ReloadDashboard(sDivId)
|
||||
{
|
||||
var oExtraParams = $sExtraParams;
|
||||
// Do not reload when a dialog box is active
|
||||
if (!($('.ui-dialog:visible').length > 0))
|
||||
{
|
||||
$('.dashboard_contents#'+sDivId).block();
|
||||
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php',
|
||||
{ operation: 'reload_dashboard', dashboard_id: '$sId', extra_params: oExtraParams},
|
||||
function(data){
|
||||
$('.dashboard_contents#'+sDivId).html(data);
|
||||
$('.dashboard_contents#'+sDivId).unblock();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
EOF
|
||||
);
|
||||
}
|
||||
|
||||
$bEdit = utils::ReadParam('edit', false);
|
||||
if ($bEdit)
|
||||
{
|
||||
@@ -892,3 +956,95 @@ class DashboardMenuNode extends MenuNode
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A shortcut container is the preferred destination of newly created shortcuts
|
||||
*/
|
||||
class ShortcutContainerMenuNode extends MenuNode
|
||||
{
|
||||
public function GetHyperlink($aExtraParams)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function RenderContent(WebPage $oPage, $aExtraParams = array())
|
||||
{
|
||||
}
|
||||
|
||||
public function PopulateChildMenus()
|
||||
{
|
||||
// Load user shortcuts in DB
|
||||
//
|
||||
$oBMSearch = new DBObjectSearch('Shortcut');
|
||||
$oBMSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
|
||||
$oBMSet = new DBObjectSet($oBMSearch, array('friendlyname' => true)); // ascending on friendlyname
|
||||
$fRank = 1;
|
||||
while ($oShortcut = $oBMSet->Fetch())
|
||||
{
|
||||
$sName = $this->GetMenuId().'_'.$oShortcut->GetKey();
|
||||
$oShortcutMenu = new ShortcutMenuNode($sName, $oShortcut, $this->GetIndex(), $fRank++);
|
||||
}
|
||||
|
||||
// Complete the tree
|
||||
//
|
||||
parent::PopulateChildMenus();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
require_once(APPROOT.'application/shortcut.class.inc.php');
|
||||
/**
|
||||
* This class defines a menu item which content is a shortcut.
|
||||
*/
|
||||
class ShortcutMenuNode extends MenuNode
|
||||
{
|
||||
protected $oShortcut;
|
||||
|
||||
/**
|
||||
* Create a menu item based on a custom template and inserts it into the application's main menu
|
||||
* @param string $sMenuId Unique identifier of the menu (used to identify the menu for bookmarking, and for getting the labels from the dictionary)
|
||||
* @param object $oShortcut Shortcut object
|
||||
* @param integer $iParentIndex ID of the parent menu
|
||||
* @param float $fRank Number used to order the list, any number will do, but for a given level (i.e same parent) all menus are sorted based on this value
|
||||
* @param string $sEnableClass Name of class of object
|
||||
* @param integer $iActionCode Either UR_ACTION_READ, UR_ACTION_MODIFY, UR_ACTION_DELETE, UR_ACTION_BULKREAD, UR_ACTION_BULKMODIFY or UR_ACTION_BULKDELETE
|
||||
* @param integer $iAllowedResults Expected "rights" for the action: either UR_ALLOWED_YES, UR_ALLOWED_NO, UR_ALLOWED_DEPENDS or a mix of them...
|
||||
* @return MenuNode
|
||||
*/
|
||||
public function __construct($sMenuId, $oShortcut, $iParentIndex, $fRank = 0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
|
||||
{
|
||||
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
|
||||
$this->oShortcut = $oShortcut;
|
||||
$this->aReflectionProperties['shortcut'] = $oShortcut->GetKey();
|
||||
}
|
||||
|
||||
public function GetHyperlink($aExtraParams)
|
||||
{
|
||||
$sContext = $this->oShortcut->Get('context');
|
||||
$aContext = unserialize($sContext);
|
||||
if (isset($aContext['menu']))
|
||||
{
|
||||
unset($aContext['menu']);
|
||||
}
|
||||
foreach ($aContext as $sArgName => $sArgValue)
|
||||
{
|
||||
$aExtraParams[$sArgName] = $sArgValue;
|
||||
}
|
||||
return parent::GetHyperlink($aExtraParams);
|
||||
}
|
||||
|
||||
public function RenderContent(WebPage $oPage, $aExtraParams = array())
|
||||
{
|
||||
$this->oShortcut->RenderContent($oPage, $aExtraParams);
|
||||
}
|
||||
|
||||
public function GetTitle()
|
||||
{
|
||||
return $this->oShortcut->Get('name');
|
||||
}
|
||||
|
||||
public function GetLabel()
|
||||
{
|
||||
return $this->oShortcut->Get('name');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// 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 NiceWebPage
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
require_once(APPROOT."/application/webpage.class.inc.php");
|
||||
@@ -36,14 +37,19 @@ class NiceWebPage extends WebPage
|
||||
{
|
||||
parent::__construct($s_title);
|
||||
$this->m_aReadyScripts = array();
|
||||
$this->add_linked_script("../js/jquery-1.7.1.min.js");
|
||||
$this->add_linked_stylesheet('../css/ui-lightness/jquery-ui-1.8.17.custom.css');
|
||||
$this->add_linked_script('../js/jquery-ui-1.8.17.custom.min.js');
|
||||
$this->add_linked_script("../js/jquery-1.10.0.min.js");
|
||||
$this->add_linked_script("../js/jquery-migrate-1.2.1.min.js"); // Needed since many other plugins still rely on oldies like $.browser
|
||||
$this->add_linked_stylesheet('../css/ui-lightness/jquery-ui-1.10.3.custom.min.css');
|
||||
$this->add_linked_script('../js/jquery-ui-1.10.3.custom.min.js');
|
||||
$this->add_linked_script("../js/hovertip.js");
|
||||
// table sorting
|
||||
$this->add_linked_script("../js/jquery.tablesorter.min.js");
|
||||
$this->add_linked_script("../js/jquery.tablesorter.js");
|
||||
$this->add_linked_script("../js/jquery.tablesorter.pager.js");
|
||||
$this->add_linked_script("../js/jquery.tablehover.js");
|
||||
$this->add_linked_script('../js/field_sorter.js');
|
||||
$this->add_linked_script('../js/datatable.js');
|
||||
$this->add_linked_script("../js/jquery.positionBy.js");
|
||||
$this->add_linked_script("../js/jquery.popupmenu.js");
|
||||
$this->add_ready_script(
|
||||
<<< EOF
|
||||
//add new widget called TruncatedList to properly display truncated lists when they are sorted
|
||||
@@ -93,6 +99,7 @@ EOF
|
||||
$this->m_sRootUrl = $this->GetAbsoluteUrlAppRoot();
|
||||
$sAbsURLAppRoot = addslashes($this->m_sRootUrl);
|
||||
$sAbsURLModulesRoot = addslashes($this->GetAbsoluteUrlModulesRoot());
|
||||
$sEnvironment = addslashes(utils::GetCurrentEnvironment());
|
||||
|
||||
$sAppContext = addslashes($this->GetApplicationContext());
|
||||
|
||||
@@ -107,6 +114,23 @@ function GetAbsoluteUrlModulesRoot()
|
||||
{
|
||||
return '$sAbsURLModulesRoot';
|
||||
}
|
||||
|
||||
function GetAbsoluteUrlModulePage(sModule, sPage, aArguments)
|
||||
{
|
||||
// aArguments is optional, it default to an empty hash
|
||||
aArguments = typeof aArguments !== 'undefined' ? aArguments : {};
|
||||
|
||||
var sUrl = '$sAbsURLAppRoot'+'pages/exec.php?exec_module='+sModule+'&exec_page='+sPage+'&exec_env='+'$sEnvironment';
|
||||
for (var sArgName in aArguments)
|
||||
{
|
||||
if (aArguments.hasOwnProperty(sArgName))
|
||||
{
|
||||
sUrl = sUrl + '&'+sArgName+'='+aArguments[sArgname];
|
||||
}
|
||||
}
|
||||
return sUrl;
|
||||
}
|
||||
|
||||
function AddAppContext(sURL)
|
||||
{
|
||||
var sContext = '$sAppContext';
|
||||
@@ -195,7 +219,7 @@ EOF
|
||||
*/
|
||||
public function output()
|
||||
{
|
||||
$this->set_base($this->m_sRootUrl.'pages/');
|
||||
//$this->set_base($this->m_sRootUrl.'pages/');
|
||||
if (count($this->m_aReadyScripts)>0)
|
||||
{
|
||||
$this->add_script("\$(document).ready(function() {\n".implode("\n", $this->m_aReadyScripts)."\n});");
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// 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 PortalWebPage
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2013 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
require_once(APPROOT."/application/nicewebpage.class.inc.php");
|
||||
@@ -59,10 +59,12 @@ class PortalWebPage extends NiceWebPage
|
||||
$this->add_header("Cache-control: no-cache");
|
||||
$this->add_linked_stylesheet("../css/jquery.treeview.css");
|
||||
$this->add_linked_stylesheet("../css/jquery.autocomplete.css");
|
||||
$this->add_linked_stylesheet("../css/jquery.multiselect.css");
|
||||
$sAbsURLAppRoot = addslashes(utils::GetAbsoluteUrlAppRoot()); // Pass it to Javascript scripts
|
||||
$sAbsURLModulesRoot = addslashes(utils::GetAbsoluteUrlModulesRoot()); // Pass it to Javascript scripts
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sAppContext = addslashes($oAppContext->GetForLink());
|
||||
$this->add_dict_entry('UI:FillAllMandatoryFields');
|
||||
if ($sAlternateStyleSheet != '')
|
||||
{
|
||||
$this->add_linked_stylesheet("../portal/$sAlternateStyleSheet/portal.css");
|
||||
@@ -86,6 +88,8 @@ class PortalWebPage extends NiceWebPage
|
||||
$this->add_linked_script("../js/forms-json-utils.js");
|
||||
$this->add_linked_script("../js/swfobject.js");
|
||||
$this->add_linked_script("../js/jquery.qtip-1.0.min.js");
|
||||
$this->add_linked_script('../js/jquery.multiselect.min.js');
|
||||
$this->add_linked_script("../js/ajaxfileupload.js");
|
||||
$this->add_ready_script(
|
||||
<<<EOF
|
||||
try
|
||||
@@ -135,6 +139,17 @@ try
|
||||
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
|
||||
});
|
||||
|
||||
//$('.resizable').resizable(); // Make resizable everything that claims to be resizable !
|
||||
$('.caselog_header').click( function () { $(this).toggleClass('open').next('.caselog_entry').toggle(); });
|
||||
}
|
||||
@@ -218,8 +233,21 @@ EOF
|
||||
}
|
||||
EOF
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
// For Wizard helper to process the ajax replies
|
||||
$this->add('<div id="ajax_content"></div>');
|
||||
|
||||
// Customize the logo (unless a customer CSS has been defined)
|
||||
if ($sAlternateStyleSheet == '')
|
||||
{
|
||||
if (file_exists(MODULESROOT.'branding/portal-logo.png'))
|
||||
{
|
||||
$sDisplayIcon = utils::GetAbsoluteUrlModulesRoot().'branding/portal-logo.png';
|
||||
$this->add_style("div#portal #logo {background: url(\"$sDisplayIcon\") no-repeat scroll 0 0 transparent;}");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function SetCurrentTab($sTabLabel = '')
|
||||
{
|
||||
@@ -250,6 +278,21 @@ EOF
|
||||
|
||||
public function output()
|
||||
{
|
||||
$sApplicationBanner = '';
|
||||
if (!MetaModel::DBHasAccess(ACCESS_USER_WRITE))
|
||||
{
|
||||
$sReadOnly = Dict::S('UI:AccessRO-Users');
|
||||
$sAdminMessage = trim(MetaModel::GetConfig()->Get('access_message'));
|
||||
$sApplicationBanner .= '<div id="admin-banner">';
|
||||
$sApplicationBanner .= '<img src="../images/locked.png" style="vertical-align:middle;">';
|
||||
$sApplicationBanner .= ' <b>'.$sReadOnly.'</b>';
|
||||
if (strlen($sAdminMessage) > 0)
|
||||
{
|
||||
$sApplicationBanner .= ' : '.$sAdminMessage.'';
|
||||
}
|
||||
$sApplicationBanner .= '</div>';
|
||||
}
|
||||
|
||||
$sMenu = '';
|
||||
if ($this->m_bEnableDisconnectButton)
|
||||
{
|
||||
@@ -259,7 +302,7 @@ EOF
|
||||
{
|
||||
$sMenu .= "<a class=\"button\" id=\"{$aMenuItem['id']}\" href=\"{$aMenuItem['hyperlink']}\"><span>".Dict::S($aMenuItem['label'])."</span></a>";
|
||||
}
|
||||
$this->s_content = '<div id="portal"><div id="welcome">'.$this->m_sWelcomeMsg.'</div><div id="banner"><div id="logo"></div><div id="menu">'.$sMenu.'</div></div><div id="content">'.$this->s_content.'</div></div>';
|
||||
$this->s_content = '<div id="portal"><div id="welcome">'.$this->m_sWelcomeMsg.'</div><div id="banner"><div id="logo"></div><div id="menu">'.$sMenu.'</div></div>'.$sApplicationBanner.'<div id="content">'.$this->s_content.'</div></div>';
|
||||
parent::output();
|
||||
}
|
||||
|
||||
@@ -279,13 +322,9 @@ EOF
|
||||
{
|
||||
// Home-made and very limited display of an object set
|
||||
|
||||
//
|
||||
//$oSet->Seek(0);// juste pour que le warning soit moins crado
|
||||
//$oSet->Fetch();// juste pour que le warning soit moins crado
|
||||
//
|
||||
|
||||
$this->add("<div id=\"listOf$sClass\">\n");
|
||||
cmdbAbstractObject::DisplaySet($this, $oSet, array('currentId' => $this->GetUniqueId()."$sClass", 'menu' => false, 'zlist' => false, 'extra_fields' => implode(',', $aZList)));
|
||||
$sUniqueId = $sClass.$this->GetUniqueId();
|
||||
$this->add("<div id=\"$sUniqueId\">\n"); // The id here MUST be the same as currentId, otherwise the pagination will be broken
|
||||
cmdbAbstractObject::DisplaySet($this, $oSet, array('currentId' => $sUniqueId, 'menu' => false, 'toolkit_menu' => false, 'zlist' => false, 'extra_fields' => implode(',', $aZList)));
|
||||
$this->add("</div>\n");
|
||||
}
|
||||
else
|
||||
@@ -381,7 +420,7 @@ EOF
|
||||
}
|
||||
$oObjSearch->AddCondition_ReferencedBy($oLinkSet->GetFilter(), $sRemoteAttCode);
|
||||
|
||||
$aExtraParams = array('menu' => false, 'zlist' => false, 'extra_fields' => implode(',', $aZList));
|
||||
$aExtraParams = array('menu' => false, 'toolkit_menu' => false, 'zlist' => false, 'extra_fields' => implode(',', $aZList));
|
||||
$oBlock = new DisplayBlock($oObjSearch, 'list', false);
|
||||
$oBlock->Display($this, 1, $aExtraParams);
|
||||
}
|
||||
@@ -391,8 +430,7 @@ EOF
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function DisplaySearchField($sClass, $sAttSpec, $aExtraParams, $sPrefix, $sFieldName = null)
|
||||
protected function DisplaySearchField($sClass, $sAttSpec, $aExtraParams, $sPrefix, $sFieldName = null, $aFilterParams = array())
|
||||
{
|
||||
if (is_null($sFieldName))
|
||||
{
|
||||
@@ -423,7 +461,7 @@ EOF
|
||||
{
|
||||
throw new Exception("Attribute specification '$sAttSpec', '$sAttCode' should be either a link set or an external key");
|
||||
}
|
||||
$this->DisplaySearchField($sTargetClass, $sSubSpec, $aExtraParams, $sPrefix, $sFieldName);
|
||||
$this->DisplaySearchField($sTargetClass, $sSubSpec, $aExtraParams, $sPrefix, $sFieldName, $aFilterParams);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -437,7 +475,24 @@ EOF
|
||||
if ($oAttDef->IsExternalKey())
|
||||
{
|
||||
$sTargetClass = $oAttDef->GetTargetClass();
|
||||
$oAllowedValues = new DBObjectSet(new DBObjectSearch($sTargetClass));
|
||||
$sFilterDefName = 'PORTAL_TICKETS_SEARCH_FILTER_'.$sAttSpec;
|
||||
if (defined($sFilterDefName))
|
||||
{
|
||||
try
|
||||
{
|
||||
$oFitlerWithParams = DBObjectSearch::FromOQL(constant($sFilterDefName));
|
||||
$sFilterOQL = $oFitlerWithParams->ToOQL(true, $aFilterParams);
|
||||
$oAllowedValues = new DBObjectSet(DBObjectSearch::FromOQL($sFilterOQL), array(), $aFilterParams);
|
||||
}
|
||||
catch(OQLException $e)
|
||||
{
|
||||
throw new Exception("Incorrect filter '$sFilterDefName' for attribute '$sAttcode': ".$e->getMessage());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$oAllowedValues = new DBObjectSet(new DBObjectSearch($sTargetClass));
|
||||
}
|
||||
|
||||
$iFieldSize = $oAttDef->GetMaxSize();
|
||||
$iMaxComboLength = $oAttDef->GetMaximumComboLength();
|
||||
@@ -457,7 +512,8 @@ EOF
|
||||
if (is_null($aAllowedValues))
|
||||
{
|
||||
// Any value is possible, display an input box
|
||||
$this->add("<label>".MetaModel::GetFilterLabel($sClass, $sAttSpec).":</label> <input class=\"textSearch\" name=\"$sPrefix$sFieldName\" value=\"$sFilterValue\"/>\n");
|
||||
$sSanitizedValue = htmlentities($sFilterValue, ENT_QUOTES, 'UTF-8');
|
||||
$this->add("<label>".MetaModel::GetFilterLabel($sClass, $sAttSpec).":</label> <input class=\"textSearch\" name=\"$sPrefix$sFieldName\" value=\"$sSanitizedValue\"/>\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -494,9 +550,30 @@ EOF
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get The organization of the current user (i.e. the organization of its contact)
|
||||
* @throws Exception
|
||||
*/
|
||||
function GetUserOrg()
|
||||
{
|
||||
$oOrg = null;
|
||||
$iContactId = UserRights::GetContactId();
|
||||
$oContact = MetaModel::GetObject('Contact', $iContactId, false); // false => Can fail
|
||||
if (is_object($oContact))
|
||||
{
|
||||
$oOrg = MetaModel::GetObject('Organization', $oContact->Get('org_id'), false); // false => can fail
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception(Dict::S('Portal:ErrorNoContactForThisUser'));
|
||||
}
|
||||
return $oOrg;
|
||||
}
|
||||
|
||||
public function DisplaySearchForm($sClass, $aAttList, $aExtraParams, $sPrefix, $bClosed = true)
|
||||
{
|
||||
$oUserOrg = $this->GetUserOrg();
|
||||
$aFilterParams = array('org_id' => $oUserOrg->GetKey(), 'contact_id' => UserRights::GetContactId());
|
||||
$sCSSClass = ($bClosed) ? 'DrawerClosed' : '';
|
||||
$this->add("<div id=\"ds_$sPrefix\" class=\"SearchDrawer $sCSSClass\">\n");
|
||||
$this->add_ready_script(
|
||||
@@ -513,13 +590,17 @@ EOF
|
||||
foreach($aAttList as $sAttSpec)
|
||||
{
|
||||
//$oAppContext->Reset($sAttSpec); // Make sure the same parameter will not be passed twice
|
||||
$this->DisplaySearchField($sClass, $sAttSpec, $aExtraParams, $sPrefix);
|
||||
$this->DisplaySearchField($sClass, $sAttSpec, $aExtraParams, $sPrefix, null, $aFilterParams);
|
||||
}
|
||||
$this->add("</p>\n");
|
||||
$this->add("<p align=\"right\"><input type=\"submit\" value=\"".Dict::S('UI:Button:Search')."\"></p>\n");
|
||||
foreach($aExtraParams as $sName => $sValue)
|
||||
{
|
||||
$this->add("<input type=\"hidden\" name=\"$sName\" value=\"$sValue\" />\n");
|
||||
// Note: use DumpHiddenParams() to transmit arrays as hidden params
|
||||
if (is_scalar($sValue))
|
||||
{
|
||||
$this->add("<input type=\"hidden\" name=\"$sName\" value=\"$sValue\" />\n");
|
||||
}
|
||||
}
|
||||
// $this->add($oAppContext->GetForForm());
|
||||
$this->add("</form>\n");
|
||||
@@ -598,7 +679,7 @@ EOF
|
||||
{
|
||||
$sFieldName = str_replace('->', PARAM_ARROW_SEP, $sAttSpec);
|
||||
$value = utils::ReadPostedParam($sPrefix.$sFieldName, null, 'raw_data');
|
||||
if (!is_null($value) && strlen($value) > 0)
|
||||
if (!is_null($value) && (is_array($value) ? count($value)>0 : strlen($value)>0))
|
||||
{
|
||||
$oFilter->AddConditionAdvanced($sAttSpec, $value);
|
||||
$iCountParams++;
|
||||
@@ -656,26 +737,24 @@ EOF
|
||||
}
|
||||
}
|
||||
|
||||
// Record the change
|
||||
//
|
||||
$oMyChange = MetaModel::NewObject("CMDBChange");
|
||||
$oMyChange->Set("date", time());
|
||||
$sUserString = CMDBChange::GetCurrentUserName();
|
||||
$oMyChange->Set("userinfo", $sUserString);
|
||||
$iChangeId = $oMyChange->DBInsert();
|
||||
$oObj->DBUpdateTracked($oMyChange);
|
||||
|
||||
// Trigger ?
|
||||
//
|
||||
$aClasses = MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL);
|
||||
$sClassList = implode(", ", CMDBSource::Quote($aClasses));
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnPortalUpdate AS t WHERE t.target_class IN ($sClassList)"));
|
||||
while ($oTrigger = $oSet->Fetch())
|
||||
if ($oObj->IsModified())
|
||||
{
|
||||
$oTrigger->DoActivate($oObj->ToArgs('this'));
|
||||
// Record the change
|
||||
//
|
||||
$oObj->DBUpdate();
|
||||
|
||||
// Trigger ?
|
||||
//
|
||||
$aClasses = MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL);
|
||||
$sClassList = implode(", ", CMDBSource::Quote($aClasses));
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnPortalUpdate AS t WHERE t.target_class IN ($sClassList)"));
|
||||
while ($oTrigger = $oSet->Fetch())
|
||||
{
|
||||
$oTrigger->DoActivate($oObj->ToArgs('this'));
|
||||
}
|
||||
|
||||
$this->p("<h1>".Dict::Format('UI:Class_Object_Updated', MetaModel::GetName(get_class($oObj)), $oObj->GetName())."</h1>\n");
|
||||
}
|
||||
|
||||
$this->p("<h1>".Dict::Format('UI:Class_Object_Updated', MetaModel::GetName(get_class($oObj)), $oObj->GetName())."</h1>\n");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -717,7 +796,7 @@ EOF
|
||||
}
|
||||
}
|
||||
|
||||
$oObj = MetaModel::GetObject($sClass, $iId, false);
|
||||
$oObj = MetaModel::GetObject($sClass, $iId, false);
|
||||
if (!is_object($oObj))
|
||||
{
|
||||
throw new Exception("Could not find the object $sClass/$iId");
|
||||
@@ -732,7 +811,7 @@ EOF
|
||||
$this->m_sWizardId = $sId;
|
||||
|
||||
// multipart... needed for file upload
|
||||
$this->add("<form id=\"{$this->m_sWizardId}\" method=\"$sMethod\" enctype=\"multipart/form-data\">\n");
|
||||
$this->add("<form id=\"{$this->m_sWizardId}\" method=\"$sMethod\" enctype=\"multipart/form-data\" onsubmit=\"window.bInSubmit = true;\">\n");
|
||||
|
||||
$aPreviousSteps = $this->GetWizardStepHistory();
|
||||
if (utils::ReadParam('step_back', 0) == 1)
|
||||
@@ -770,7 +849,10 @@ EOF
|
||||
}
|
||||
if ($iButtonFlags & BUTTON_BACK)
|
||||
{
|
||||
$aButtons[] = "<input id=\"btn_back\" type=\"submit\" value=\"".Dict::S('UI:Button:Back')."\" onClick=\"GoBack('{$this->m_sWizardId}');\">";
|
||||
if (utils::ReadParam('step_back', 1) != 1)
|
||||
{
|
||||
$aButtons[] = "<input id=\"btn_back\" type=\"submit\" value=\"".Dict::S('UI:Button:Back')."\" onClick=\"GoBack('{$this->m_sWizardId}');\">";
|
||||
}
|
||||
}
|
||||
if ($iButtonFlags & BUTTON_NEXT)
|
||||
{
|
||||
|
||||
@@ -1,28 +1,29 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Persistent class Event and derived
|
||||
* Application internal events
|
||||
* There is also a file log
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
abstract class Query extends cmdbAbstractObject
|
||||
@@ -46,13 +47,13 @@ abstract class Query extends cmdbAbstractObject
|
||||
MetaModel::Init_AddAttribute(new AttributeString("name", array("allowed_values"=>null, "sql"=>"name", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeText("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeString("fields", array("allowed_values"=>null, "sql"=>"fields", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeText("fields", array("allowed_values"=>null, "sql"=>"fields", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('name', 'description', 'fields')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('description')); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('standard_search', array('name', 'description', 'fields')); // Criteria of the std search form
|
||||
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
|
||||
}
|
||||
}
|
||||
@@ -81,26 +82,48 @@ class QueryOQL extends Query
|
||||
MetaModel::Init_SetZListItems('details', array('name', 'description', 'oql', 'fields')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('description')); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('standard_search', array('name', 'description', 'fields', 'oql')); // Criteria of the std search form
|
||||
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
|
||||
}
|
||||
|
||||
function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array())
|
||||
{
|
||||
parent::DisplayBareProperties($oPage, $bEditMode, $sPrefix, $aExtraParams);
|
||||
$aFieldsMap = parent::DisplayBareProperties($oPage, $bEditMode, $sPrefix, $aExtraParams);
|
||||
|
||||
if (!$bEditMode)
|
||||
{
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot().'webservices/export.php?format=spreadsheet&login_mode=basic&query='.$this->GetKey();
|
||||
$sOql = $this->Get('oql');
|
||||
$oSearch = DBObjectSearch::FromOQL($sOql);
|
||||
$aParameters = $oSearch->GetQueryParams();
|
||||
foreach($aParameters as $sParam => $val)
|
||||
$sMessage = null;
|
||||
try
|
||||
{
|
||||
$sUrl .= '&arg_'.$sParam.'=["'.$sParam.'"]';
|
||||
$oSearch = DBObjectSearch::FromOQL($sOql);
|
||||
$aParameters = $oSearch->GetQueryParams();
|
||||
foreach($aParameters as $sParam => $val)
|
||||
{
|
||||
$sUrl .= '&arg_'.$sParam.'=["'.$sParam.'"]';
|
||||
}
|
||||
|
||||
$oPage->p(Dict::S('UI:Query:UrlForExcel').':<br/><textarea cols="80" rows="3" READONLY>'.$sUrl.'</textarea>');
|
||||
|
||||
if (count($aParameters) == 0)
|
||||
{
|
||||
$oBlock = new DisplayBlock($oSearch, 'list');
|
||||
$aExtraParams = array(
|
||||
//'menu' => $sShowMenu,
|
||||
'table_id' => 'query_preview_'.$this->getKey(),
|
||||
);
|
||||
$sBlockId = 'block_query_preview_'.$this->GetKey(); // make a unique id (edition occuring in the same DOM)
|
||||
$oBlock->Display($oPage, $sBlockId, $aExtraParams);
|
||||
}
|
||||
}
|
||||
catch (OQLException $e)
|
||||
{
|
||||
$sMessage = '<div class="message message_error" style="padding-left: 30px;"><div style="padding: 10px;">'.Dict::Format('UI:RunQuery:Error', $e->getHtmlDesc()).'</div></div>';
|
||||
$oPage->p($sMessage);
|
||||
}
|
||||
$oPage->p(Dict::S('UI:Query:UrlForExcel').':<br/><textarea cols="80" rows="3" READONLY>'.$sUrl.'</textarea>');
|
||||
}
|
||||
return $aFieldsMap;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
338
application/shortcut.class.inc.php
Normal file
338
application/shortcut.class.inc.php
Normal file
@@ -0,0 +1,338 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Persistent class Shortcut and derived
|
||||
* Shortcuts of any kind
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
abstract class Shortcut extends DBObject implements iDisplay
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "gui,view_in_gui",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_shortcut",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "realclass",
|
||||
"display_template" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", array("targetclass"=>"User", "allowed_values"=>null, "sql"=>"user_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("name", array("allowed_values"=>null, "sql"=>"name", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeText("context", array("allowed_values"=>null, "sql"=>"context", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('name', 'context')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('name')); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
|
||||
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
|
||||
}
|
||||
|
||||
abstract public function RenderContent(WebPage $oPage, $aExtraParams = array());
|
||||
|
||||
protected function OnInsert()
|
||||
{
|
||||
$this->Set('user_id', UserRights::GetUserId());
|
||||
}
|
||||
|
||||
public function StartRenameDialog($oPage)
|
||||
{
|
||||
$oPage->add('<div id="shortcut_rename_dlg">');
|
||||
|
||||
$oForm = new DesignerForm();
|
||||
$sDefault = $this->Get('name');
|
||||
$oField = new DesignerTextField('name', Dict::S('Class:Shortcut/Attribute:name'), $sDefault);
|
||||
$oField->SetMandatory(true);
|
||||
$oForm->AddField($oField);
|
||||
$oForm->Render($oPage);
|
||||
$oPage->add('</div>');
|
||||
|
||||
$sDialogTitle = Dict::S('UI:ShortcutRenameDlg:Title');
|
||||
$sOkButtonLabel = Dict::S('UI:Button:Ok');
|
||||
$sCancelButtonLabel = Dict::S('UI:Button:Cancel');
|
||||
$iShortcut = $this->GetKey();
|
||||
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
function ShortcutRenameOK()
|
||||
{
|
||||
var oForm = $(this).find('form');
|
||||
var sFormId = oForm.attr('id');
|
||||
var oParams = null;
|
||||
var aErrors = ValidateForm(sFormId, false);
|
||||
if (aErrors.length == 0)
|
||||
{
|
||||
oParams = ReadFormParams(sFormId);
|
||||
}
|
||||
oParams.operation = 'shortcut_rename_go';
|
||||
oParams.id = $iShortcut;
|
||||
var me = $(this);
|
||||
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', oParams, function(data) {
|
||||
me.dialog( "close" );
|
||||
me.remove();
|
||||
$('body').append(data);
|
||||
});
|
||||
}
|
||||
|
||||
$('#shortcut_rename_dlg form').bind('submit', function() { return false; });
|
||||
|
||||
$('#shortcut_rename_dlg').dialog({
|
||||
width: 400,
|
||||
modal: true,
|
||||
title: '$sDialogTitle',
|
||||
buttons: [
|
||||
{ text: "$sOkButtonLabel", click: ShortcutRenameOK},
|
||||
{ text: "$sCancelButtonLabel", click: function() {
|
||||
$(this).dialog( "close" ); $(this).remove();
|
||||
} },
|
||||
],
|
||||
close: function() { $(this).remove(); }
|
||||
});
|
||||
EOF
|
||||
);
|
||||
}
|
||||
|
||||
// Minimual implementation of iDisplay: to make the shortcut be listable
|
||||
//
|
||||
public static function MapContextParam($sContextParam)
|
||||
{
|
||||
return (($sContextParam == 'menu') ? null : $sContextParam);
|
||||
}
|
||||
|
||||
public function GetHilightClass()
|
||||
{
|
||||
return HILIGHT_CLASS_NONE;
|
||||
}
|
||||
|
||||
public static function GetUIPage()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
function DisplayDetails(WebPage $oPage, $bEditMode = false)
|
||||
{
|
||||
}
|
||||
|
||||
function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array())
|
||||
{
|
||||
return array();
|
||||
}
|
||||
// End of the minimal implementation of iDisplay
|
||||
}
|
||||
|
||||
class ShortcutOQL extends Shortcut
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "gui,view_in_gui",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_shortcut_oql",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"display_template" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeOQL("oql", array("allowed_values"=>null, "sql"=>"oql", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeEnum("auto_reload", array("allowed_values"=>new ValueSetEnum('none,custom'), "sql"=>"auto_reload", "default_value"=>"none", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeInteger("auto_reload_sec", array("allowed_values"=>null, "sql"=>"auto_reload_sec", "default_value"=>60, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('name', 'context', 'oql')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('name')); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
|
||||
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
|
||||
}
|
||||
|
||||
public function RenderContent(WebPage $oPage, $aExtraParams = array())
|
||||
{
|
||||
$oPage->set_title($this->Get('name'));
|
||||
|
||||
switch($this->Get('auto_reload'))
|
||||
{
|
||||
case 'custom':
|
||||
$iRate = (int)$this->Get('auto_reload_sec');
|
||||
if ($iRate > 0)
|
||||
{
|
||||
// Must a string otherwise it can be evaluated to 'true' and defaults to "standard" refresh rate!
|
||||
$aExtraParams['auto_reload'] = (string)$iRate;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
case 'none':
|
||||
}
|
||||
|
||||
$bSearchPane = true;
|
||||
$bSearchOpen = false;
|
||||
try
|
||||
{
|
||||
OQLMenuNode::RenderOQLSearch($this->Get('oql'), $this->Get('name'), 'shortcut_'.$this->GetKey(), $bSearchPane, $bSearchOpen, $oPage, $aExtraParams);
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
throw new Exception("The OQL shortcut '".$this->Get('name')."' (id: ".$this->GetKey().") could not be displayed: ".$e->getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function CloneTableSettings($sTableSettings)
|
||||
{
|
||||
$aTableSettings = json_decode($sTableSettings, true);
|
||||
|
||||
$oFilter = DBObjectSearch::FromOQL($this->Get('oql'));
|
||||
$oCustomSettings = new DataTableSettings($oFilter->GetSelectedClasses());
|
||||
$oCustomSettings->iDefaultPageSize = $aTableSettings['iPageSize'];
|
||||
$oCustomSettings->aColumns = $aTableSettings['oColumns'];
|
||||
$oCustomSettings->Save('shortcut_'.$this->GetKey());
|
||||
}
|
||||
|
||||
public static function GetCreationForm($sOQL = null, $sTableSettings = null)
|
||||
{
|
||||
$oForm = new DesignerForm();
|
||||
|
||||
// Find a unique default name
|
||||
// -> The class of the query + an index if necessary
|
||||
if ($sOQL == null)
|
||||
{
|
||||
$sDefault = '';
|
||||
}
|
||||
else
|
||||
{
|
||||
$oBMSearch = new DBObjectSearch('Shortcut');
|
||||
$oBMSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
|
||||
$oBMSet = new DBObjectSet($oBMSearch);
|
||||
$aNames = $oBMSet->GetColumnAsArray('name');
|
||||
$oSearch = DBObjectSearch::FromOQL($sOQL);
|
||||
$sDefault = utils::MakeUniqueName($oSearch->GetClass(), $aNames);
|
||||
}
|
||||
|
||||
$oField = new DesignerTextField('name', Dict::S('Class:Shortcut/Attribute:name'), $sDefault);
|
||||
$oField->SetMandatory(true);
|
||||
$oForm->AddField($oField);
|
||||
|
||||
/*
|
||||
$oField = new DesignerComboField('auto_reload', Dict::S('Class:ShortcutOQL/Attribute:auto_reload'), 'none');
|
||||
$oAttDef = MetaModel::GetAttributeDef(__class__, 'auto_reload');
|
||||
$oField->SetAllowedValues($oAttDef->GetAllowedValues());
|
||||
$oField->SetMandatory(true);
|
||||
$oForm->AddField($oField);
|
||||
*/
|
||||
$oField = new DesignerBooleanField('auto_reload', Dict::S('Class:ShortcutOQL/Attribute:auto_reload'), false);
|
||||
$oForm->AddField($oField);
|
||||
|
||||
$oField = new DesignerTextField('auto_reload_sec', Dict::S('Class:ShortcutOQL/Attribute:auto_reload_sec'), MetaModel::GetConfig()->GetStandardReloadInterval());
|
||||
$oField->SetValidationPattern('^$|^0*([5-9]|[1-9][0-9]+)$'); // Can be empty, or a number > 4
|
||||
$oField->SetMandatory(false);
|
||||
$oForm->AddField($oField);
|
||||
|
||||
$oField = new DesignerHiddenField('oql', '', $sOQL);
|
||||
$oForm->AddField($oField);
|
||||
|
||||
$oField = new DesignerHiddenField('table_settings', '', $sTableSettings);
|
||||
$oForm->AddField($oField);
|
||||
|
||||
return $oForm;
|
||||
}
|
||||
|
||||
public static function GetCreationDlgFromOQL($oPage, $sOQL, $sTableSettings)
|
||||
{
|
||||
$oPage->add('<div id="shortcut_creation_dlg">');
|
||||
|
||||
$oForm = self::GetCreationForm($sOQL, $sTableSettings);
|
||||
|
||||
$oForm->Render($oPage);
|
||||
$oPage->add('</div>');
|
||||
|
||||
$sDialogTitle = Dict::S('UI:ShortcutListDlg:Title');
|
||||
$sOkButtonLabel = Dict::S('UI:Button:Ok');
|
||||
$sCancelButtonLabel = Dict::S('UI:Button:Cancel');
|
||||
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sContext = $oAppContext->GetForLink();
|
||||
|
||||
$sRateTitle = addslashes(Dict::S('Class:ShortcutOQL/Attribute:auto_reload_sec+'));
|
||||
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
|
||||
// Note: the title gets deleted by the validation mechanism
|
||||
$("#attr_auto_reload_sec").tooltip({items: 'input', content: '$sRateTitle'});
|
||||
$("#attr_auto_reload_sec").prop('disabled', !$('#attr_auto_reload').is(':checked'));
|
||||
|
||||
$('#attr_auto_reload').change( function(ev) {
|
||||
$("#attr_auto_reload_sec").prop('disabled', !$(this).is(':checked'));
|
||||
} );
|
||||
|
||||
function ShortcutCreationOK()
|
||||
{
|
||||
var oForm = $('#shortcut_creation_dlg form');
|
||||
var sFormId = oForm.attr('id');
|
||||
var oParams = null;
|
||||
var aErrors = ValidateForm(sFormId, false);
|
||||
if (aErrors.length == 0)
|
||||
{
|
||||
oParams = ReadFormParams(sFormId);
|
||||
}
|
||||
oParams.operation = 'shortcut_list_create';
|
||||
var me = $('#shortcut_creation_dlg');
|
||||
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php?$sContext', oParams, function(data) {
|
||||
me.dialog( "close" );
|
||||
me.remove();
|
||||
$('body').append(data);
|
||||
});
|
||||
}
|
||||
|
||||
$('#shortcut_creation_dlg form').bind('submit', function() { ShortcutCreationOK(); return false; });
|
||||
|
||||
$('#shortcut_creation_dlg').dialog({
|
||||
width: 400,
|
||||
modal: true,
|
||||
title: '$sDialogTitle',
|
||||
buttons: [
|
||||
{ text: "$sOkButtonLabel", click: ShortcutCreationOK },
|
||||
{ text: "$sCancelButtonLabel", click: function() {
|
||||
$(this).dialog( "close" ); $(this).remove();
|
||||
} },
|
||||
],
|
||||
close: function() { $(this).remove(); }
|
||||
});
|
||||
EOF
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -1,27 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// 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!
|
||||
*
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// 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
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
require_once(APPROOT.'/core/cmdbobject.class.inc.php');
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// 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 DisplayTemplate
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
require_once(APPROOT.'/application/displayblock.class.inc.php');
|
||||
@@ -282,9 +283,29 @@ class ObjectDetailsTemplate extends DisplayTemplate
|
||||
$sStateAttCode = MetaModel :: GetStateAttributeCode(get_class($this->m_oObj));
|
||||
$aTemplateFields = array();
|
||||
preg_match_all('/\\$this->([a-z0-9_]+)\\$/', $this->m_sTemplate, $aMatches);
|
||||
$aTemplateFields = $aMatches[1];
|
||||
foreach ($aMatches[1] as $sAttCode)
|
||||
{
|
||||
if (MetaModel::IsValidAttCode(get_class($this->m_oObj), $sAttCode))
|
||||
{
|
||||
$aTemplateFields[] = $sAttCode;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aParams['this->'.$sAttCode] = "<!--Unknown attribute: $sAttCode-->";
|
||||
}
|
||||
}
|
||||
preg_match_all('/\\$this->field\\(([a-z0-9_]+)\\)\\$/', $this->m_sTemplate, $aMatches);
|
||||
$aTemplateFields = array_merge($aTemplateFields, $aMatches[1]);
|
||||
foreach ($aMatches[1] as $sAttCode)
|
||||
{
|
||||
if (MetaModel::IsValidAttCode(get_class($this->m_oObj), $sAttCode))
|
||||
{
|
||||
$aTemplateFields[] = $sAttCode;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aParams['this->field('.$sAttCode.')'] = "<!--Unknown attribute: $sAttCode-->";
|
||||
}
|
||||
}
|
||||
$aFieldsComments = (isset($aParams['fieldsComments'])) ? $aParams['fieldsComments'] : array();
|
||||
$aFieldsMap = array();
|
||||
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
<div class="page_header" style="padding:0.5em;">
|
||||
<h1><itopstring>UI:NotificationsMenu:Title</itopstring></h1>
|
||||
</div>
|
||||
<itoptoggle name="UI:NotificationsMenu:Help" open="true">
|
||||
<div style="padding: 1em; font-size:10pt;background:#E8F3CF;margin-top: 0.25em;">
|
||||
<img src="../images/bell.png" style="margin-top: -60px; margin-right: 10px; float: right;">
|
||||
<itopstring>UI:NotificationsMenu:HelpContent</itopstring>
|
||||
</div>
|
||||
</itoptoggle>
|
||||
<p> </p>
|
||||
<itoptabs>
|
||||
<itoptab name="UI:NotificationsMenu:Triggers">
|
||||
<h2><itopstring>UI:NotificationsMenu:AvailableTriggers</itopstring></h2>
|
||||
<itopblock BlockClass="DisplayBlock" type="list" asynchronous="false" encoding="text/oql">SELECT Trigger</itopblock>
|
||||
</itoptab>
|
||||
<itoptab name="UI:NotificationsMenu:Actions">
|
||||
<h2><itopstring>UI:NotificationsMenu:AvailableActions</itopstring></h2>
|
||||
<itopblock BlockClass="DisplayBlock" type="list" asynchronous="false" encoding="text/oql">SELECT ActionEmail</itopblock>
|
||||
</itoptab>
|
||||
</itoptabs>
|
||||
@@ -1,24 +1,29 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
/**
|
||||
* This class records the pending "transactions" corresponding to forms that have not been
|
||||
* submitted yet, in order to prevent double submissions. When created a transaction remains valid
|
||||
* until the user's session expires
|
||||
*
|
||||
* @package iTop
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// 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 UIExtKeyWidget
|
||||
* UI wdiget for displaying and editing external keys when
|
||||
@@ -52,10 +54,8 @@
|
||||
* | | +--------+ +-----+ | |
|
||||
* | +--------------------------------------------+ |
|
||||
* +------------------------------------------------+
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
require_once(APPROOT.'/application/webpage.class.inc.php');
|
||||
@@ -96,12 +96,12 @@ class UIExtKeyWidget
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the HTML fragment corresponding to the linkset editing widget
|
||||
* Get the HTML fragment corresponding to the ext key editing widget
|
||||
* @param WebPage $oP The web page used for all the output
|
||||
* @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')
|
||||
public function Display(WebPage $oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName, $sFormPrefix = '', $aArgs = array(), $bSearchMode = null, $sDisplayStyle = 'select', $bSearchMultiple = true)
|
||||
{
|
||||
if (!is_null($bSearchMode))
|
||||
{
|
||||
@@ -169,14 +169,22 @@ class UIExtKeyWidget
|
||||
|
||||
$sHelpText = ''; //$this->oAttDef->GetHelpOnEdition();
|
||||
|
||||
$sHTMLValue = "<select title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" id=\"$this->iId\">\n";
|
||||
if ($this->bSearchMode)
|
||||
{
|
||||
$sDisplayValue = isset($aArgs['sDefaultValue']) ? $aArgs['sDefaultValue'] : Dict::S('UI:SearchValue:Any');
|
||||
$sHTMLValue .= "<option value=\"\">$sDisplayValue</option>\n";
|
||||
if ($bSearchMultiple)
|
||||
{
|
||||
$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";
|
||||
$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 .= "<option value=\"\">".Dict::S('UI:SelectOne')."</option>\n";
|
||||
}
|
||||
$oAllowedValues->Rewind();
|
||||
@@ -192,11 +200,24 @@ class UIExtKeyWidget
|
||||
}
|
||||
else
|
||||
{
|
||||
$sSelected = ($value == $key) ? ' selected' : '';
|
||||
$sSelected = (is_array($value) && in_array($key, $value)) || ($value == $key) ? ' selected' : '';
|
||||
}
|
||||
$sHTMLValue .= "<option value=\"$key\"$sSelected>$display_value</option>\n";
|
||||
}
|
||||
$sHTMLValue .= "</select>\n";
|
||||
if (($this->bSearchMode) && $bSearchMultiple)
|
||||
{
|
||||
$aOptions = array(
|
||||
'header' => true,
|
||||
'checkAllText' => Dict::S('UI:SearchValue:CheckAll'),
|
||||
'uncheckAllText' => Dict::S('UI:SearchValue:UncheckAll'),
|
||||
'noneSelectedText' => Dict::S('UI:SearchValue:Any'),
|
||||
'selectedText' => Dict::S('UI:SearchValue:NbSelected'),
|
||||
'selectedList' => 1,
|
||||
);
|
||||
$sJSOptions = json_encode($aOptions);
|
||||
$oPage->add_ready_script("$('.multiselect').multiselect($sJSOptions);");
|
||||
}
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', true, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode);
|
||||
@@ -213,6 +234,15 @@ EOF
|
||||
// Too many choices, use an autocomplete
|
||||
$sSelectMode = 'false';
|
||||
|
||||
// Check that the given value is allowed
|
||||
$oSearch = $oAllowedValues->GetFilter();
|
||||
$oSearch->AddCondition('id', $value);
|
||||
$oSet = new DBObjectSet($oSearch);
|
||||
if ($oSet->Count() == 0)
|
||||
{
|
||||
$value = null;
|
||||
}
|
||||
|
||||
if (is_null($value) || ($value == 0)) // Null values are displayed as ''
|
||||
{
|
||||
$sDisplayValue = isset($aArgs['sDefaultValue']) ? $aArgs['sDefaultValue'] : '';
|
||||
@@ -287,8 +317,9 @@ EOF
|
||||
if ( ($oCurrObject != null) && ($this->sAttCode != ''))
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($oCurrObject), $this->sAttCode);
|
||||
$aParams = array('query_params' => array('this' => $oCurrObject));
|
||||
$oSet = $oAttDef->GetAllowedValuesAsObjectSet($aParams);
|
||||
$aArgs = array('this' => $oCurrObject);
|
||||
$aParams = array('query_params' => $aArgs);
|
||||
$oSet = $oAttDef->GetAllowedValuesAsObjectSet($aArgs);
|
||||
$oFilter = $oSet->GetFilter();
|
||||
}
|
||||
else
|
||||
@@ -332,23 +363,15 @@ EOF
|
||||
{
|
||||
throw new Exception('Implementation: null value for allowed values definition');
|
||||
}
|
||||
try
|
||||
|
||||
$oFilter = DBObjectSearch::FromOQL($sFilter);
|
||||
if (strlen($sRemoteClass) > 0)
|
||||
{
|
||||
$oFilter = DBObjectSearch::FromOQL($sFilter);
|
||||
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
|
||||
$oBlock = new DisplayBlock($oFilter, 'list', false, array('query_params' => array('this' => $oObj)));
|
||||
$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
|
||||
}
|
||||
catch(MissingQueryArgument $e)
|
||||
{
|
||||
// When used in a search form the $this parameter may be missing, in this case return all possible values...
|
||||
// TODO check if we can improve this behavior...
|
||||
$sOQL = 'SELECT '.$sRemoteClass;
|
||||
$oFilter = DBObjectSearch::FromOQL($sOQL);
|
||||
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
|
||||
//$oBlock = new DisplayBlock($oFilter, 'list', false);
|
||||
//$oBlock->Display($oP, $this->iId.'_results', array('cssCount'=> '#count_'.$this->iId, 'menu' => false, 'selection_mode' => true, 'selection_type' => 'single')); // Don't display the 'Actions' menu on the results
|
||||
$oFilter->ChangeClass($sRemoteClass);
|
||||
}
|
||||
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
|
||||
$oBlock = new DisplayBlock($oFilter, 'list', false, array('query_params' => array('this' => $oObj)));
|
||||
$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
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -424,13 +447,13 @@ EOF
|
||||
// 3rd - set values from the page argument 'default'
|
||||
$oNewObj->UpdateObjectFromArg('default');
|
||||
|
||||
$sDialogTitle = addslashes($this->sTitle);
|
||||
$sDialogTitle = '';
|
||||
$oPage->add('<div id="ac_create_'.$this->iId.'"><div class="wizContainer" style="vertical-align:top;"><div id="dcr_'.$this->iId.'">');
|
||||
$oPage->add("<h1>".MetaModel::GetClassIcon($this->sTargetClass)." ".Dict::Format('UI:CreationTitle_Class', MetaModel::GetName($this->sTargetClass))."</h1>\n");
|
||||
cmdbAbstractObject::DisplayCreationForm($oPage, $this->sTargetClass, $oNewObj, array(), array('formPrefix' => $this->iId, 'noRelations' => true));
|
||||
$oPage->add('</div></div></div>');
|
||||
// $oPage->add_ready_script("\$('#ac_create_$this->iId').dialog({ width: $(window).width()*0.8, height: 'auto', autoOpen: false, modal: true, title: '$sDialogTitle'});\n");
|
||||
$oPage->add_ready_script("\$('#ac_create_$this->iId').dialog({ width: 'auto', height: 'auto', autoOpen: false, modal: true, title: '$sDialogTitle'});\n");
|
||||
$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}.DoCreateObject);");
|
||||
}
|
||||
@@ -485,12 +508,7 @@ EOF
|
||||
$aErrors = $oObj->UpdateObjectFromPostedForm($this->iId);
|
||||
if (count($aErrors) == 0)
|
||||
{
|
||||
$oMyChange = MetaModel::NewObject("CMDBChange");
|
||||
$oMyChange->Set("date", time());
|
||||
$sUserString = CMDBChange::GetCurrentUserName();
|
||||
$oMyChange->Set("userinfo", $sUserString);
|
||||
$iChangeId = $oMyChange->DBInsert();
|
||||
$oObj->DBInsertTracked($oMyChange);
|
||||
$oObj->DBInsert();
|
||||
return array('name' => $oObj->GetName(), 'id' => $oObj->GetKey());
|
||||
}
|
||||
else
|
||||
@@ -539,6 +557,7 @@ EOF
|
||||
$aSortedRoots = $aTree[$iRootId];
|
||||
asort($aSortedRoots);
|
||||
$oP->add("<ul>\n");
|
||||
$fUniqueId = microtime(true);
|
||||
foreach($aSortedRoots as $id => $sName)
|
||||
{
|
||||
if ($bSelect)
|
||||
@@ -546,14 +565,14 @@ EOF
|
||||
$sChecked = ($aNodes[$id]->GetKey() == $currValue) ? 'checked' : '';
|
||||
if ($bMultiple)
|
||||
{
|
||||
$sSelect = '<input type="checkbox" value="'.$aNodes[$id]->GetKey().'" name="selectObject[]" '.$sChecked.'> ';
|
||||
$sSelect = '<input id="input_'.$fUniqueId.'_'.$aNodes[$id]->GetKey().'" type="checkbox" value="'.$aNodes[$id]->GetKey().'" name="selectObject[]" '.$sChecked.'> ';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sSelect = '<input type="radio" value="'.$aNodes[$id]->GetKey().'" name="selectObject" '.$sChecked.'> ';
|
||||
$sSelect = '<input id="input_'.$fUniqueId.'_'.$aNodes[$id]->GetKey().'" type="radio" value="'.$aNodes[$id]->GetKey().'" name="selectObject" '.$sChecked.'> ';
|
||||
}
|
||||
}
|
||||
$oP->add('<li>'.$sSelect.$aNodes[$id]->GetHyperlink());
|
||||
$oP->add('<li>'.$sSelect.'<label for="input_'.$fUniqueId.'_'.$aNodes[$id]->GetKey().'">'.$aNodes[$id]->GetName().'</label>');
|
||||
$this->DumpNodes($oP, $id, $aTree, $aNodes, $currValue);
|
||||
$oP->add("</li>\n");
|
||||
}
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// 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 UIHTMLEditorWidget
|
||||
* UI wdiget for displaying and editing one-way encrypted passwords
|
||||
*
|
||||
* @author Phil Eddies
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
class UIHTMLEditorWidget
|
||||
|
||||
423
application/ui.linksdirectwidget.class.inc.php
Normal file
423
application/ui.linksdirectwidget.class.inc.php
Normal file
@@ -0,0 +1,423 @@
|
||||
<?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 UILinksWidgetDirect
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
class UILinksWidgetDirect
|
||||
{
|
||||
protected $sClass;
|
||||
protected $sAttCode;
|
||||
protected $sInputid;
|
||||
protected $sNameSuffix;
|
||||
protected $sLinkedClass;
|
||||
|
||||
public function __construct($sClass, $sAttCode, $sInputId, $sNameSuffix = '')
|
||||
{
|
||||
$this->sClass = $sClass;
|
||||
$this->sAttCode = $sAttCode;
|
||||
$this->sInputid = $sInputId;
|
||||
$this->sNameSuffix = $sNameSuffix;
|
||||
$this->aZlist = array();
|
||||
$this->sLinkedClass = '';
|
||||
|
||||
// Compute the list of attributes visible from the given objet:
|
||||
// All the attributes from the "list" Zlist of the Link class except
|
||||
// the ExternalKey that points to the current object and its related external fields
|
||||
$oLinksetDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
$this->sLinkedClass = $oLinksetDef->GetLinkedClass();
|
||||
$sExtKeyToMe = $oLinksetDef->GetExtKeyToMe();
|
||||
switch($oLinksetDef->GetEditMode())
|
||||
{
|
||||
case LINKSET_EDITMODE_INPLACE: // The whole linkset can be edited 'in-place'
|
||||
$aZList = MetaModel::FlattenZList(MetaModel::GetZListItems($this->sLinkedClass, 'details'));
|
||||
break;
|
||||
|
||||
default:
|
||||
$aZList = MetaModel::FlattenZList(MetaModel::GetZListItems($this->sLinkedClass, 'list'));
|
||||
array_unshift($aZList, 'friendlyname');
|
||||
}
|
||||
foreach($aZList as $sLinkedAttCode)
|
||||
{
|
||||
if ($sLinkedAttCode != $sExtKeyToMe)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->sLinkedClass, $sLinkedAttCode);
|
||||
|
||||
if ((!$oAttDef->IsExternalField() || ($oAttDef->GetKeyAttCode() != $sExtKeyToMe)) &&
|
||||
(!$oAttDef->IsLinkSet()) )
|
||||
{
|
||||
$this->aZlist[] = $sLinkedAttCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function Display(WebPage $oPage, DBObjectSet $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj)
|
||||
{
|
||||
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
|
||||
switch($oLinksetDef->GetEditMode())
|
||||
{
|
||||
case LINKSET_EDITMODE_NONE: // The linkset is read-only
|
||||
$this->DisplayAsBlock($oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, false /* bDisplayMenu*/);
|
||||
break;
|
||||
|
||||
case LINKSET_EDITMODE_ADDONLY: // The only possible action is to open (in a new window) the form to create a new object
|
||||
if ($oCurrentObj && !$oCurrentObj->IsNew())
|
||||
{
|
||||
$sTargetClass = $oLinksetDef->GetLinkedClass();
|
||||
$sExtKeyToMe = $oLinksetDef->GetExtKeyToMe();
|
||||
$sDefault = "default[$sExtKeyToMe]=".$oCurrentObj->GetKey();
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sParams = $oAppContext->GetForLink();
|
||||
$oPage->p("<a target=\"_blank\" href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=new&class=$sTargetClass&$sParams{$sDefault}\">".Dict::Format('UI:ClickToCreateNew', Metamodel::GetName($sTargetClass))."</a>\n");
|
||||
}
|
||||
$this->DisplayAsBlock($oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, false /* bDisplayMenu*/);
|
||||
break;
|
||||
|
||||
case LINKSET_EDITMODE_INPLACE: // The whole linkset can be edited 'in-place'
|
||||
$this->DisplayEditInPlace($oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj);
|
||||
break;
|
||||
|
||||
case LINKSET_EDITMODE_ADDREMOVE: // The whole linkset can be edited 'in-place'
|
||||
$sTargetClass = $oLinksetDef->GetLinkedClass();
|
||||
$sExtKeyToMe = $oLinksetDef->GetExtKeyToMe();
|
||||
$oExtKeyDef = MetaModel::GetAttributeDef($sTargetClass, $sExtKeyToMe);
|
||||
$aButtons = array('add');
|
||||
if ($oExtKeyDef->IsNullAllowed())
|
||||
{
|
||||
$aButtons = array('add', 'remove');
|
||||
}
|
||||
$this->DisplayEditInPlace($oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj, $aButtons);
|
||||
break;
|
||||
|
||||
case LINKSET_EDITMODE_ACTIONS:
|
||||
default:
|
||||
$this->DisplayAsBlock($oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, true /* bDisplayMenu*/);
|
||||
}
|
||||
}
|
||||
|
||||
protected function DisplayAsBlock(WebPage $oPage, DBObjectSet $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, $bDisplayMenu)
|
||||
{
|
||||
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
|
||||
$sTargetClass = $oLinksetDef->GetLinkedClass();
|
||||
if ($oCurrentObj && $oCurrentObj->IsNew() && $bDisplayMenu)
|
||||
{
|
||||
$oPage->p(Dict::Format('UI:BeforeAdding_Class_ObjectsSaveThisObject', MetaModel::GetName($sTargetClass)));
|
||||
}
|
||||
else
|
||||
{
|
||||
$oFilter = new DBObjectSearch($sTargetClass);
|
||||
$oFilter->AddCondition($oLinksetDef->GetExtKeyToMe(), $oCurrentObj->GetKey(),'=');
|
||||
|
||||
$aDefaults = array($oLinksetDef->GetExtKeyToMe() => $oCurrentObj->GetKey());
|
||||
$oAppContext = new ApplicationContext();
|
||||
foreach($oAppContext->GetNames() as $sKey)
|
||||
{
|
||||
// The linked object inherits the parent's value for the context
|
||||
if (MetaModel::IsValidAttCode($this->sClass, $sKey) && $oCurrentObj)
|
||||
{
|
||||
$aDefaults[$sKey] = $oCurrentObj->Get($sKey);
|
||||
}
|
||||
}
|
||||
$aParams = array(
|
||||
'target_attr' => $oLinksetDef->GetExtKeyToMe(),
|
||||
'object_id' => $oCurrentObj ? $oCurrentObj->GetKey() : null,
|
||||
'menu' => $bDisplayMenu,
|
||||
'default' => $aDefaults,
|
||||
'table_id' => $this->sClass.'_'.$this->sAttCode,
|
||||
);
|
||||
|
||||
$oBlock = new DisplayBlock($oFilter, 'list', false);
|
||||
$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 });");
|
||||
}
|
||||
|
||||
public function GetObjectCreationDlg(WebPage $oPage, $sProposedRealClass = '')
|
||||
{
|
||||
// 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
|
||||
$sRealClass = '';
|
||||
$oPage->add('<div class="wizContainer" style="vertical-align:top;"><div>');
|
||||
$aSubClasses = MetaModel::EnumChildClasses($this->sLinkedClass, ENUM_CHILD_CLASSES_ALL); // Including the specified class itself
|
||||
$aPossibleClasses = array();
|
||||
foreach($aSubClasses as $sCandidateClass)
|
||||
{
|
||||
if (!MetaModel::IsAbstract($sCandidateClass) && (UserRights::IsActionAllowed($sCandidateClass, UR_ACTION_MODIFY) == UR_ALLOWED_YES))
|
||||
{
|
||||
if ($sCandidateClass == $sProposedRealClass)
|
||||
{
|
||||
$sRealClass = $sProposedRealClass;
|
||||
}
|
||||
$aPossibleClasses[$sCandidateClass] = MetaModel::GetName($sCandidateClass);
|
||||
}
|
||||
}
|
||||
// Only one of the subclasses can be instantiated...
|
||||
if (count($aPossibleClasses) == 1)
|
||||
{
|
||||
$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));
|
||||
}
|
||||
else
|
||||
{
|
||||
$sClassLabel = MetaModel::GetName($this->sLinkedClass);
|
||||
$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="button" onclick="$(\'#'.$this->sInputid.'\').directlinks(\'subclassSelected\');">'.Dict::S('UI:Button:Apply').'</button><span class="indicator" style="display:inline-block;width:16px"></span></nobr></p>');
|
||||
}
|
||||
$oPage->add('</div></div>');
|
||||
}
|
||||
|
||||
public function GetObjectsSelectionDlg($oPage, $oCurrentObj)
|
||||
{
|
||||
$sHtml = "<div class=\"wizContainer\" style=\"vertical-align:top;\">\n";
|
||||
|
||||
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
|
||||
$valuesDef = $oLinksetDef->GetValuesDef();
|
||||
if ($valuesDef === null)
|
||||
{
|
||||
$oFilter = new DBObjectSearch($this->sLinkedClass);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!$valuesDef instanceof ValueSetObjects)
|
||||
{
|
||||
throw new Exception('Error: only ValueSetObjects are supported for "allowed_values" in AttributeLinkedSet ('.$this->sClass.'/'.$this->sAttCode.').');
|
||||
}
|
||||
$oFilter = DBObjectSearch::FromOQL($valuesDef->GetFilterExpression());
|
||||
}
|
||||
if ($oCurrentObj != null)
|
||||
{
|
||||
$this->SetSearchDefaultFromContext($oCurrentObj, $oFilter);
|
||||
}
|
||||
$oBlock = new DisplayBlock($oFilter, 'search', false);
|
||||
$sHtml .= $oBlock->GetDisplay($oPage, "SearchFormToAdd_{$this->sInputid}", array('open' => true));
|
||||
$sHtml .= "<form id=\"ObjectsAddForm_{$this->sInputid}\">\n";
|
||||
$sHtml .= "<div id=\"SearchResultsToAdd_{$this->sInputid}\" style=\"vertical-align:top;background: #fff;height:100%;overflow:auto;padding:0;border:0;\">\n";
|
||||
$sHtml .= "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>".Dict::S('UI:Message:EmptyList:UseSearchForm')."</p></div>\n";
|
||||
$sHtml .= "</div>\n";
|
||||
$sHtml .= "<input type=\"hidden\" id=\"count_{$this->sInputid}\" value=\"0\"/>";
|
||||
$sHtml .= "<button type=\"button\" class=\"cancel\">".Dict::S('UI:Button:Cancel')."</button> <button type=\"button\" class=\"ok\" disabled=\"disabled\">".Dict::S('UI:Button:Add')."</button>";
|
||||
$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...
|
||||
*/
|
||||
public function SearchObjectsToAdd(WebPage $oP, $sRemoteClass = '', $aAlreadyLinked = array(), $oCurrentObj = null)
|
||||
{
|
||||
if ($sRemoteClass == '')
|
||||
{
|
||||
$sRemoteClass = $this->sLinkedClass;
|
||||
}
|
||||
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
|
||||
$valuesDef = $oLinksetDef->GetValuesDef();
|
||||
if ($valuesDef === null)
|
||||
{
|
||||
$oFilter = new DBObjectSearch($this->sLinkedClass);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!$valuesDef instanceof ValueSetObjects)
|
||||
{
|
||||
throw new Exception('Error: only ValueSetObjects are supported for "allowed_values" in AttributeLinkedSet ('.$this->sClass.'/'.$this->sAttCode.').');
|
||||
}
|
||||
$oFilter = DBObjectSearch::FromOQL($valuesDef->GetFilterExpression());
|
||||
}
|
||||
|
||||
if (($oCurrentObj != null) && MetaModel::IsSameFamilyBranch($sRemoteClass, $this->sClass))
|
||||
{
|
||||
// Prevent linking to self if the linked object is of the same family
|
||||
// and laready present in the database
|
||||
if (!$oCurrentObj->IsNew())
|
||||
{
|
||||
$oFilter->AddCondition('id', $oCurrentObj->GetKey(), '!=');
|
||||
}
|
||||
}
|
||||
if (count($aAlreadyLinked) > 0)
|
||||
{
|
||||
$oFilter->AddCondition('id', $aAlreadyLinked, 'NOTIN');
|
||||
}
|
||||
if ($oCurrentObj != null)
|
||||
{
|
||||
$aArgs = array_merge($oCurrentObj->ToArgs('this'), $oFilter->GetInternalParams());
|
||||
$oFilter->SetInternalParams($aArgs);
|
||||
}
|
||||
$oBlock = new DisplayBlock($oFilter, 'list', false);
|
||||
$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
|
||||
}
|
||||
|
||||
public function DoAddObjects(WebPage $oP, $oFullSetFilter)
|
||||
{
|
||||
$aLinkedObjectIds = utils::ReadMultipleSelection($oFullSetFilter);
|
||||
foreach($aLinkedObjectIds as $iObjectId)
|
||||
{
|
||||
$oLinkObj = MetaModel::GetObject($this->sLinkedClass, $iObjectId);
|
||||
$oP->add($this->GetObjectRow($oP, $oLinkObj, $oLinkObj->GetKey()));
|
||||
}
|
||||
}
|
||||
|
||||
public function GetObjectModificationDlg()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected function GetTableConfig()
|
||||
{
|
||||
$aAttribs = array();
|
||||
$aAttribs['form::select'] = array('label' => "<input type=\"checkbox\" onClick=\"CheckAll('.selectList{$this->sInputid}:not(:disabled)', this.checked);\" class=\"checkAll\"></input>", 'description' => Dict::S('UI:SelectAllToggle+'));
|
||||
|
||||
foreach($this->aZlist as $sLinkedAttCode)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->sLinkedClass, $sLinkedAttCode);
|
||||
$aAttribs[$sLinkedAttCode] = array('label' => MetaModel::GetLabel($this->sLinkedClass, $sLinkedAttCode), 'description' => $oAttDef->GetOrderByHint());
|
||||
}
|
||||
return $aAttribs;
|
||||
}
|
||||
|
||||
public function GetRow($oPage, $sRealClass, $aValues, $iTempId)
|
||||
{
|
||||
if ($sRealClass == '')
|
||||
{
|
||||
$sRealClass = $this->sLinkedClass;
|
||||
}
|
||||
$oLinkObj = new $sRealClass();
|
||||
$oLinkObj->UpdateObjectFromPostedForm($this->sInputid);
|
||||
|
||||
return $this->GetObjectRow($oPage, $oLinkObj, $iTempId);
|
||||
}
|
||||
|
||||
protected function GetObjectRow($oPage, $oLinkObj, $iTempId)
|
||||
{
|
||||
$aAttribs = $this->GetTableConfig();
|
||||
$aRow = array();
|
||||
$aRow['form::select'] = '<input type="checkbox" class="selectList'.$this->sInputid.'" value="'.($iTempId).'"/>';
|
||||
foreach($this->aZlist as $sLinkedAttCode)
|
||||
{
|
||||
$aRow[$sLinkedAttCode] = $oLinkObj->GetAsHTML($sLinkedAttCode);
|
||||
}
|
||||
return $oPage->GetTableRow($aRow, $aAttribs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the default search parameters based on 1) a 'current' object and 2) the silos defined by the context
|
||||
* @param DBObject $oSourceObj
|
||||
* @param DBObjectSearch $oSearch
|
||||
*/
|
||||
protected function SetSearchDefaultFromContext($oSourceObj, &$oSearch)
|
||||
{
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sSrcClass = get_class($oSourceObj);
|
||||
$sDestClass = $oSearch->GetClass();
|
||||
foreach($oAppContext->GetNames() as $key)
|
||||
{
|
||||
// Find the value of the object corresponding to each 'context' parameter
|
||||
$aCallSpec = array($sSrcClass, 'MapContextParam');
|
||||
$sAttCode = '';
|
||||
if (is_callable($aCallSpec))
|
||||
{
|
||||
$sAttCode = call_user_func($aCallSpec, $key); // Returns null when there is no mapping for this parameter
|
||||
}
|
||||
|
||||
if (MetaModel::IsValidAttCode($sSrcClass, $sAttCode))
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($sSrcClass, $sAttCode);
|
||||
$defaultValue = $oSourceObj->Get($sAttCode);
|
||||
|
||||
// Find the attcode for the same 'context' parameter in the destination class
|
||||
// and sets its value as the default value for the search condition
|
||||
$aCallSpec = array($sDestClass, 'MapContextParam');
|
||||
$sAttCode = '';
|
||||
if (is_callable($aCallSpec))
|
||||
{
|
||||
$sAttCode = call_user_func($aCallSpec, $key); // Returns null when there is no mapping for this parameter
|
||||
}
|
||||
|
||||
if (MetaModel::IsValidAttCode($sDestClass, $sAttCode) && !empty($defaultValue))
|
||||
{
|
||||
$oSearch->AddCondition($sAttCode, $defaultValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// 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 UILinksWidget
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
require_once(APPROOT.'/application/webpage.class.inc.php');
|
||||
@@ -105,7 +106,7 @@ class UILinksWidget
|
||||
$sPrefix = "$this->m_sAttCode{$this->m_sNameSuffix}";
|
||||
$aRow = array();
|
||||
$aFieldsMap = array();
|
||||
if(is_object($linkObjOrId))
|
||||
if(is_object($linkObjOrId) && (!$linkObjOrId->IsNew()))
|
||||
{
|
||||
$key = $linkObjOrId->GetKey();
|
||||
$iRemoteObjKey = $linkObjOrId->Get($this->m_sExtKeyToRemote);
|
||||
@@ -119,7 +120,7 @@ class UILinksWidget
|
||||
foreach($this->m_aEditableFields as $sFieldCode)
|
||||
{
|
||||
$sFieldId = $this->m_iInputId.'_'.$sFieldCode.'['.$linkObjOrId->GetKey().']';
|
||||
$sSafeId = self::MakeID($sFieldId);
|
||||
$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;
|
||||
@@ -129,12 +130,24 @@ class UILinksWidget
|
||||
else
|
||||
{
|
||||
// form for creating a new record
|
||||
if (is_object($linkObjOrId))
|
||||
{
|
||||
// New link existing only in memory
|
||||
$oNewLinkObj = $linkObjOrId;
|
||||
$iRemoteObjKey = $oNewLinkObj->Get($this->m_sExtKeyToRemote);
|
||||
$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;
|
||||
$oNewLinkObj = MetaModel::NewObject($this->m_sLinkedClass);
|
||||
$oRemoteObj = MetaModel::GetObject($this->m_sRemoteClass, -$linkObjOrId);
|
||||
$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][";
|
||||
$iRemoteObjKey = -$linkObjOrId;
|
||||
$oNewLinkObj = MetaModel::NewObject($this->m_sLinkedClass);
|
||||
$oRemoteObj = MetaModel::GetObject($this->m_sRemoteClass, -$linkObjOrId);
|
||||
$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
|
||||
$sNameSuffix = "]"; // To make a tabular form
|
||||
$aArgs['prefix'] = $sPrefix;
|
||||
$aArgs['wizHelper'] = "oWizardHelper{$this->m_iInputId}_".(-$linkObjOrId);
|
||||
@@ -144,7 +157,7 @@ class UILinksWidget
|
||||
foreach($this->m_aEditableFields as $sFieldCode)
|
||||
{
|
||||
$sFieldId = $this->m_iInputId.'_'.$sFieldCode.'['.$linkObjOrId.']';
|
||||
$sSafeId = self::MakeID($sFieldId);
|
||||
$sSafeId = utils::GetSafeId($sFieldId);
|
||||
$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);
|
||||
$aFieldsMap[$sFieldCode] = $sSafeId;
|
||||
@@ -174,11 +187,11 @@ EOF
|
||||
);
|
||||
}
|
||||
|
||||
$sExtKeyToMeId = self::MakeID($sPrefix.$this->m_sExtKeyToMe);
|
||||
$sExtKeyToMeId = utils::GetSafeId($sPrefix.$this->m_sExtKeyToMe);
|
||||
$aFieldsMap[$this->m_sExtKeyToMe] = $sExtKeyToMeId;
|
||||
$aRow['form::checkbox'] .= "<input type=\"hidden\" id=\"$sExtKeyToMeId\" value=\"".$oCurrentObj->GetKey()."\">";
|
||||
|
||||
$sExtKeyToRemoteId = self::MakeID($sPrefix.$this->m_sExtKeyToRemote);
|
||||
$sExtKeyToRemoteId = utils::GetSafeId($sPrefix.$this->m_sExtKeyToRemote);
|
||||
$aFieldsMap[$this->m_sExtKeyToRemote] = $sExtKeyToRemoteId;
|
||||
$aRow['form::checkbox'] .= "<input type=\"hidden\" id=\"$sExtKeyToRemoteId\" value=\"$iRemoteObjKey\">";
|
||||
|
||||
@@ -199,11 +212,6 @@ EOF
|
||||
}
|
||||
return $aRow;
|
||||
}
|
||||
|
||||
protected function MakeID($sName)
|
||||
{
|
||||
return str_replace(array('[', ']', '-'), '_', $sName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display one row of the whole form
|
||||
@@ -279,6 +287,7 @@ EOF
|
||||
$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();
|
||||
while($oCurrentLink = $oValue->Fetch())
|
||||
@@ -288,7 +297,7 @@ EOF
|
||||
if ($oCurrentLink->IsNew())
|
||||
{
|
||||
$key = -$oLinkedObj->GetKey();
|
||||
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $key, $aArgs, $oCurrentObj);
|
||||
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs, $oCurrentObj);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -301,8 +310,9 @@ EOF
|
||||
$sDuplicates = ($this->m_bDuplicatesAllowed) ? '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);
|
||||
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}.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();\" >";
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// 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 UIPasswordWidget
|
||||
* UI wdiget for displaying and editing one-way encrypted passwords
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
require_once(APPROOT.'/application/webpage.class.inc.php');
|
||||
@@ -52,8 +52,10 @@ class UIPasswordWidget
|
||||
{
|
||||
$sCode = $this->sAttCode.$this->sNameSuffix;
|
||||
$iWidgetIndex = self::$iWidgetIndex;
|
||||
$sPasswordValue = utils::ReadPostedParam("attr_{$sCode}[value]", '*****', 'raw_data');
|
||||
$sConfirmPasswordValue = utils::ReadPostedParam("attr_{$sCode}[confirm]", '*****', 'raw_data');
|
||||
|
||||
$aPasswordValues = utils::ReadPostedParam("attr_{$sCode}", null, 'raw_data');
|
||||
$sPasswordValue = $aPasswordValues ? $aPasswordValues['value'] : '*****';
|
||||
$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><br/>';
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// 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
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
class UILinksWizard
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// 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 UIWizard
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
class UIWizard
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
/**
|
||||
* Store and retrieve user custom dashboards
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
require_once(APPROOT.'/core/dbobject.class.php');
|
||||
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
/**
|
||||
* Store and retrieve user's preferences (i.e persistent per user settings)
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
require_once(APPROOT.'/core/dbobject.class.php');
|
||||
require_once(APPROOT.'/core/userrights.class.inc.php');
|
||||
@@ -103,7 +103,7 @@ class appUserPreferences extends DBObject
|
||||
}
|
||||
else
|
||||
{
|
||||
unset($aPrefs[$sCode]);
|
||||
unset($aPrefs[$sCodeOrPattern]);
|
||||
self::$oUserPrefs->Set('preferences', $aPrefs);
|
||||
}
|
||||
// Save only if needed
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Static class utils
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
require_once(APPROOT.'/core/config.class.inc.php');
|
||||
@@ -507,7 +508,7 @@ class utils
|
||||
{
|
||||
// Build an absolute URL to this page on this server/port
|
||||
$sServerName = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '';
|
||||
$sProtocol = (isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS']!="off")) ? 'https' : 'http';
|
||||
$sProtocol = self::IsConnectionSecure() ? 'https' : 'http';
|
||||
$iPort = isset($_SERVER['SERVER_PORT']) ? $_SERVER['SERVER_PORT'] : 80;
|
||||
if ($sProtocol == 'http')
|
||||
{
|
||||
@@ -570,6 +571,25 @@ class utils
|
||||
return $sAppRootUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to handle the variety of HTTP servers
|
||||
* See #286 (fixed in [896]), and #634 (this fix)
|
||||
*
|
||||
* Though the official specs says 'a non empty string', some servers like IIS do set it to 'off' !
|
||||
* nginx set it to an empty string
|
||||
* Others might leave it unset (no array entry)
|
||||
*/
|
||||
static public function IsConnectionSecure()
|
||||
{
|
||||
$bSecured = false;
|
||||
|
||||
if (!empty($_SERVER['HTTPS']) && (strtolower($_SERVER['HTTPS']) != 'off'))
|
||||
{
|
||||
$bSecured = true;
|
||||
}
|
||||
return $bSecured;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether or not log off operation is supported.
|
||||
* Actually in only one case:
|
||||
@@ -612,7 +632,7 @@ class utils
|
||||
$bCASDebug = self::GetConfig()->Get('cas_debug');
|
||||
if ($bCASDebug)
|
||||
{
|
||||
phpCAS::setDebug(APPROOT.'/error.log');
|
||||
phpCAS::setDebug(APPROOT.'log/error.log');
|
||||
}
|
||||
|
||||
if (!self::$m_bCASClient)
|
||||
@@ -746,36 +766,87 @@ class utils
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the "Back" button to go out of the current environment
|
||||
* Merge standard menu items with plugin provided menus items
|
||||
*/
|
||||
public static function GetEnvironmentBackButton()
|
||||
public static function GetPopupMenuItems($oPage, $iMenuId, $param, &$aActions, $sTableId = null, $sDataTableId = null)
|
||||
{
|
||||
if (isset($_SESSION['itop_return_env']))
|
||||
// 1st - add standard built-in menu items
|
||||
//
|
||||
switch($iMenuId)
|
||||
{
|
||||
if (isset($_SESSION['itop_return_url']))
|
||||
case iPopupMenuExtension::MENU_OBJLIST_TOOLKIT:
|
||||
// $param is a DBObjectSet
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sContext = $oAppContext->GetForLink();
|
||||
$sDataTableId = is_null($sDataTableId) ? '' : $sDataTableId;
|
||||
$sUIPage = cmdbAbstractObject::ComputeStandardUIPage($param->GetFilter()->GetClass());
|
||||
$sOQL = addslashes($param->GetFilter()->ToOQL(true));
|
||||
$sFilter = urlencode($param->GetFilter()->serialize());
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/$sUIPage?operation=search&filter=".$sFilter."&{$sContext}";
|
||||
$aResult = array(
|
||||
new SeparatorPopupMenuItem(),
|
||||
// Static menus: Email this page, CSV Export & Add to Dashboard
|
||||
new URLPopupMenuItem('UI:Menu:EMail', Dict::S('UI:Menu:EMail'), "mailto:?body=".urlencode($sUrl).' '), // Add an extra space to make it work in Outlook
|
||||
new URLPopupMenuItem('UI:Menu:CSVExport', Dict::S('UI:Menu:CSVExport'), $sUrl."&format=csv"),
|
||||
new JSPopupMenuItem('UI:Menu:AddToDashboard', Dict::S('UI:Menu:AddToDashboard'), "DashletCreationDlg('$sOQL')"),
|
||||
new JSPopupMenuItem('UI:Menu:ShortcutList', Dict::S('UI:Menu:ShortcutList'), "ShortcutListDlg('$sOQL', '$sDataTableId', '$sContext')"),
|
||||
);
|
||||
break;
|
||||
|
||||
case iPopupMenuExtension::MENU_OBJDETAILS_ACTIONS:
|
||||
// $param is a DBObject
|
||||
$oObj = $param;
|
||||
$oFilter = DBobjectSearch::FromOQL("SELECT ".get_class($oObj)." WHERE id=".$oObj->GetKey());
|
||||
$sFilter = $oFilter->serialize();
|
||||
$sUrl = ApplicationContext::MakeObjectUrl(get_class($oObj), $oObj->GetKey());
|
||||
$sUIPage = cmdbAbstractObject::ComputeStandardUIPage(get_class($oObj));
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sContext = $oAppContext->GetForLink();
|
||||
$aResult = array(
|
||||
new SeparatorPopupMenuItem(),
|
||||
// Static menus: Email this page & CSV Export
|
||||
new URLPopupMenuItem('UI:Menu:EMail', Dict::S('UI:Menu:EMail'), "mailto:?subject=".urlencode($oObj->GetRawName())."&body=".urlencode($sUrl).' '), // Add an extra space to make it work in Outlook
|
||||
new URLPopupMenuItem('UI:Menu:CSVExport', Dict::S('UI:Menu:CSVExport'), utils::GetAbsoluteUrlAppRoot()."pages/$sUIPage?operation=search&filter=".urlencode($sFilter)."&format=csv&{$sContext}"),
|
||||
);
|
||||
break;
|
||||
|
||||
case iPopupMenuExtension::MENU_DASHBOARD_ACTIONS:
|
||||
// $param is a Dashboard
|
||||
$oAppContext = new ApplicationContext();
|
||||
$aParams = $oAppContext->GetAsHash();
|
||||
$sMenuId = ApplicationMenu::GetActiveNodeId();
|
||||
$sDlgTitle = addslashes(Dict::S('UI:ImportDashboardTitle'));
|
||||
$sDlgText = addslashes(Dict::S('UI:ImportDashboardText'));
|
||||
$sCloseBtn = addslashes(Dict::S('UI:Button:Cancel'));
|
||||
$aResult = array(
|
||||
new SeparatorPopupMenuItem(),
|
||||
new URLPopupMenuItem('UI:ExportDashboard', Dict::S('UI:ExportDashBoard'), utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=export_dashboard&id='.$sMenuId),
|
||||
new JSPopupMenuItem('UI:ImportDashboard', Dict::S('UI:ImportDashBoard'), "UploadDashboard({dashboard_id: '$sMenuId', title: '$sDlgTitle', text: '$sDlgText', close_btn: '$sCloseBtn' })"),
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
// Unknown type of menu, do nothing
|
||||
$aResult = array();
|
||||
}
|
||||
foreach($aResult as $oMenuItem)
|
||||
{
|
||||
$aActions[$oMenuItem->GetUID()] = $oMenuItem->GetMenuItem();
|
||||
}
|
||||
|
||||
// Invoke the plugins
|
||||
//
|
||||
foreach (MetaModel::EnumPlugins('iPopupMenuExtension') as $oExtensionInstance)
|
||||
{
|
||||
if (is_object($param) && !($param instanceof DBObject))
|
||||
{
|
||||
$sReturnUrl = $_SESSION['itop_return_url'];
|
||||
$tmpParam = clone $param; // In case the parameter is an DBObjectSet, clone it to prevent alterations
|
||||
}
|
||||
else
|
||||
{
|
||||
$sReturnUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?switch_env='.$_SESSION['itop_return_env'];
|
||||
$tmpParam = $param;
|
||||
}
|
||||
return ' <button onclick="window;location.href=\''.addslashes($sReturnUrl).'\'">'.Dict::S('UI:Button:Back').'</button>';
|
||||
}
|
||||
else
|
||||
{
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the "Back" button to go out of the current environment
|
||||
*/
|
||||
public static function GetPopupMenuItems($oPage, $iMenuId, $param, &$aActions)
|
||||
{
|
||||
foreach (MetaModel::EnumPlugins('iPopupMenuExtension') as $oExtensionInstance)
|
||||
{
|
||||
foreach($oExtensionInstance->EnumItems($iMenuId, $param) as $oMenuItem)
|
||||
foreach($oExtensionInstance->EnumItems($iMenuId, $tmpParam) as $oMenuItem)
|
||||
{
|
||||
if (is_object($oMenuItem))
|
||||
{
|
||||
@@ -810,5 +881,189 @@ class utils
|
||||
$sUrl = self::GetAbsoluteUrlAppRoot().'env-'.self::GetCurrentEnvironment().'/';
|
||||
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 ...
|
||||
*/
|
||||
static public function GetAbsoluteUrlModulePage($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;
|
||||
foreach($aArguments as $sName => $sValue)
|
||||
{
|
||||
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);
|
||||
}
|
||||
$sArgs = implode('&', $aArgs);
|
||||
return self::GetAbsoluteUrlAppRoot().'pages/exec.php?'.$sArgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a name unique amongst the given list
|
||||
* @param string $sProposed The default value
|
||||
* @param array $aExisting An array of existing values (strings)
|
||||
*/
|
||||
static public function MakeUniqueName($sProposed, $aExisting)
|
||||
{
|
||||
if (in_array($sProposed, $aExisting))
|
||||
{
|
||||
$i = 1;
|
||||
while (in_array($sProposed.$i, $aExisting) && ($i < 50))
|
||||
{
|
||||
$i++;
|
||||
}
|
||||
return $sProposed.$i;
|
||||
}
|
||||
else
|
||||
{
|
||||
return $sProposed;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Some characters cause troubles with jQuery when used inside DOM IDs, so let's replace them by the safe _ (underscore)
|
||||
* @param string $sId The ID to sanitize
|
||||
* @return string The sanitized ID
|
||||
*/
|
||||
static public function GetSafeId($sId)
|
||||
{
|
||||
return str_replace(array(':', '[', ']', '+', '-'), '_', $sId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to execute an HTTP POST request
|
||||
* Source: http://netevil.org/blog/2006/nov/http-post-from-php-without-curl
|
||||
* originaly named after do_post_request
|
||||
* Does not require cUrl but requires openssl for performing https POSTs.
|
||||
*
|
||||
* @param string $sUrl The URL to POST the data to
|
||||
* @param hash $aData The data to POST as an array('param_name' => value)
|
||||
* @param string $sOptionnalHeaders Additional HTTP headers as a string with newlines between headers
|
||||
* @param hash $aResponseHeaders An array to be filled with reponse headers: WARNING: the actual content of the array depends on the library used: cURL or fopen, test with both !! See: http://fr.php.net/manual/en/function.curl-getinfo.php
|
||||
* @return string The result of the POST request
|
||||
* @throws Exception
|
||||
*/
|
||||
static public function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null, &$aResponseHeaders = null)
|
||||
{
|
||||
// $sOptionnalHeaders is a string containing additional HTTP headers that you would like to send in your request.
|
||||
|
||||
if (function_exists('curl_init'))
|
||||
{
|
||||
// If cURL is available, let's use it, since it provides a greater control over the various HTTP/SSL options
|
||||
// For instance fopen does not allow to work around the bug: http://stackoverflow.com/questions/18191672/php-curl-ssl-routinesssl23-get-server-helloreason1112
|
||||
// by setting the SSLVERSION to 3 as done below.
|
||||
$aHeaders = explode("\n", $sOptionnalHeaders);
|
||||
$aHTTPHeaders = array();
|
||||
foreach($aHeaders as $sHeaderString)
|
||||
{
|
||||
if(preg_match('/^([^:]): (.+)$/', $sHeaderString, $aMatches))
|
||||
{
|
||||
$aHTTPHeaders[$aMatches[1]] = $aMatches[2];
|
||||
}
|
||||
}
|
||||
$aOptions = array(
|
||||
CURLOPT_RETURNTRANSFER => true, // return the content of the request
|
||||
CURLOPT_HEADER => false, // don't return the headers in the output
|
||||
CURLOPT_FOLLOWLOCATION => true, // follow redirects
|
||||
CURLOPT_ENCODING => "", // handle all encodings
|
||||
CURLOPT_USERAGENT => "spider", // who am i
|
||||
CURLOPT_AUTOREFERER => true, // set referer on redirect
|
||||
CURLOPT_CONNECTTIMEOUT => 120, // timeout on connect
|
||||
CURLOPT_TIMEOUT => 120, // timeout on response
|
||||
CURLOPT_MAXREDIRS => 10, // stop after 10 redirects
|
||||
CURLOPT_SSL_VERIFYPEER => false, // Disabled SSL Cert checks
|
||||
CURLOPT_SSLVERSION => 3, // MUST to prevent a strange SSL error: http://stackoverflow.com/questions/18191672/php-curl-ssl-routinesssl23-get-server-helloreason1112
|
||||
CURLOPT_POST => count($aData),
|
||||
CURLOPT_POSTFIELDS => http_build_query($aData),
|
||||
CURLOPT_HTTPHEADER => $aHTTPHeaders,
|
||||
);
|
||||
|
||||
$ch = curl_init($sUrl);
|
||||
curl_setopt_array($ch, $aOptions);
|
||||
$response = curl_exec($ch);
|
||||
$iErr = curl_errno($ch);
|
||||
$sErrMsg = curl_error( $ch );
|
||||
$aHeaders = curl_getinfo( $ch );
|
||||
if ($iErr !== 0)
|
||||
{
|
||||
throw new Exception("Problem opening URL: $sUrl, $sErrMsg");
|
||||
}
|
||||
if (is_array($aResponseHeaders))
|
||||
{
|
||||
$aHeaders = curl_getinfo($ch);
|
||||
foreach($aHeaders as $sCode => $sValue)
|
||||
{
|
||||
$sName = str_replace(' ' , '-', ucwords(str_replace('_', ' ', $sCode))); // Transform "content_type" into "Content-Type"
|
||||
$aResponseHeaders[$sName] = $sValue;
|
||||
}
|
||||
}
|
||||
curl_close( $ch );
|
||||
}
|
||||
else
|
||||
{
|
||||
// cURL is not available let's try with streams and fopen...
|
||||
|
||||
$sData = http_build_query($aData);
|
||||
$aParams = array('http' => array(
|
||||
'method' => 'POST',
|
||||
'content' => $sData,
|
||||
'header'=> "Content-type: application/x-www-form-urlencoded\r\nContent-Length: ".strlen($sData)."\r\n",
|
||||
));
|
||||
if ($sOptionnalHeaders !== null)
|
||||
{
|
||||
$aParams['http']['header'] .= $sOptionnalHeaders;
|
||||
}
|
||||
$ctx = stream_context_create($aParams);
|
||||
|
||||
$fp = @fopen($sUrl, 'rb', false, $ctx);
|
||||
if (!$fp)
|
||||
{
|
||||
global $php_errormsg;
|
||||
if (isset($php_errormsg))
|
||||
{
|
||||
throw new Exception("Wrong URL: $sUrl, $php_errormsg");
|
||||
}
|
||||
elseif ((strtolower(substr($sUrl, 0, 5)) == 'https') && !extension_loaded('openssl'))
|
||||
{
|
||||
throw new Exception("Cannot connect to $sUrl: missing module 'openssl'");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Wrong URL: $sUrl");
|
||||
}
|
||||
}
|
||||
$response = @stream_get_contents($fp);
|
||||
if ($response === false)
|
||||
{
|
||||
throw new Exception("Problem reading data from $sUrl, $php_errormsg");
|
||||
}
|
||||
if (is_array($aResponseHeaders))
|
||||
{
|
||||
$aMeta = stream_get_meta_data($fp);
|
||||
$aHeaders = $aMeta['wrapper_data'];
|
||||
foreach($aHeaders as $sHeaderString)
|
||||
{
|
||||
if(preg_match('/^([^:]+): (.+)$/', $sHeaderString, $aMatches))
|
||||
{
|
||||
$aResponseHeaders[$aMatches[1]] = trim($aMatches[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
?>
|
||||
?>
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// 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 WebPage
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
@@ -56,6 +57,7 @@ class WebPage implements Page
|
||||
protected $s_content;
|
||||
protected $s_deferred_content;
|
||||
protected $a_scripts;
|
||||
protected $a_dict_entries;
|
||||
protected $a_styles;
|
||||
protected $a_include_scripts;
|
||||
protected $a_include_stylesheets;
|
||||
@@ -75,6 +77,7 @@ class WebPage implements Page
|
||||
$this->s_content = "";
|
||||
$this->s_deferred_content = '';
|
||||
$this->a_scripts = array();
|
||||
$this->a_dict_entries = array();
|
||||
$this->a_styles = array();
|
||||
$this->a_linked_scripts = array();
|
||||
$this->a_linked_stylesheets = array();
|
||||
@@ -230,6 +233,16 @@ class WebPage implements Page
|
||||
{
|
||||
// Do nothing silently... this is not supported by this type of page...
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a dictionary entry for the Javascript side
|
||||
*/
|
||||
public function add_dict_entry($s_entryId)
|
||||
{
|
||||
$this->a_dict_entries[$s_entryId] = Dict::S($s_entryId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add some CSS definitions to the header of the page
|
||||
*/
|
||||
@@ -378,6 +391,25 @@ class WebPage implements Page
|
||||
return $sHTMLValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Discard unexpected output data
|
||||
* This is a MUST when the Page output is DATA (download of a document, download CSV export, download ...)
|
||||
*/
|
||||
public function TrashUnexpectedOutput()
|
||||
{
|
||||
// This protection is redundant with a protection implemented in MetaModel::IncludeModule
|
||||
// which detects such issues while loading module files
|
||||
// Here, the purpose is to detect and discard characters produced by the code execution (echo)
|
||||
$sPreviousContent = ob_get_clean();
|
||||
if (trim($sPreviousContent) != '')
|
||||
{
|
||||
if (Utils::GetConfig() && Utils::GetConfig()->Get('debug_report_spurious_chars'))
|
||||
{
|
||||
IssueLog::Error("Output already started before downloading file:\nContent was:'$sPreviousContent'\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs (via some echo) the complete HTML page by assembling all its elements
|
||||
*/
|
||||
@@ -418,6 +450,7 @@ class WebPage implements Page
|
||||
}
|
||||
echo "</script>\n";
|
||||
}
|
||||
$this->output_dict_entries();
|
||||
foreach($this->a_linked_stylesheets as $a_stylesheet)
|
||||
{
|
||||
if ($a_stylesheet['condition'] != "")
|
||||
@@ -454,18 +487,30 @@ class WebPage implements Page
|
||||
echo '<div id="at_the_end">'.self::FilterXSS($this->s_deferred_content).'</div>';
|
||||
echo "</body>\n";
|
||||
echo "</html>\n";
|
||||
|
||||
if (class_exists('MetaModel'))
|
||||
{
|
||||
MetaModel::RecordQueryTrace();
|
||||
}
|
||||
if (class_exists('ExecutionKPI'))
|
||||
{
|
||||
ExecutionKPI::ReportStats();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a series of hidden field[s] from an array
|
||||
*/
|
||||
// By Rom - je verrais bien une serie d'outils pour gerer des parametres que l'on retransmet entre pages d'un wizard...
|
||||
// ptet deriver webpage en webwizard
|
||||
public function add_input_hidden($sLabel, $aData)
|
||||
{
|
||||
foreach($aData as $sKey=>$sValue)
|
||||
foreach($aData as $sKey => $sValue)
|
||||
{
|
||||
$this->add("<input type=\"hidden\" name=\"".$sLabel."[$sKey]\" value=\"$sValue\">");
|
||||
// Note: protection added to protect against the Notice 'array to string conversion' that appeared with PHP 5.4
|
||||
// (this function seems unused though!)
|
||||
if (is_scalar($sValue))
|
||||
{
|
||||
$this->add("<input type=\"hidden\" name=\"".$sLabel."[$sKey]\" value=\"$sValue\">");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -612,7 +657,8 @@ class WebPage implements Page
|
||||
foreach ($aActions as $aAction)
|
||||
{
|
||||
$sClass = isset($aAction['class']) ? " class=\"{$aAction['class']}\"" : "";
|
||||
$sOnClick = isset($aAction['onclick']) ? " onclick=\"{$aAction['onclick']}\"" : "";
|
||||
$sOnClick = isset($aAction['onclick']) ? ' onclick="'.htmlspecialchars($aAction['onclick'], ENT_QUOTES, "UTF-8").'"' : '';
|
||||
$sTarget = isset($aAction['target']) ? " target=\"{$aAction['target']}\"" : "";
|
||||
if (empty($aAction['url']))
|
||||
{
|
||||
if ($sPrevUrl != '') // Don't output consecutively two separators...
|
||||
@@ -623,17 +669,340 @@ class WebPage implements Page
|
||||
}
|
||||
else
|
||||
{
|
||||
$sHtml .= "<li><a href=\"{$aAction['url']}\"$sClass $sOnClick>{$aAction['label']}</a></li>";
|
||||
$sHtml .= "<li><a $sTarget href=\"{$aAction['url']}\"$sClass $sOnClick>{$aAction['label']}</a></li>";
|
||||
$sPrevUrl = $aAction['url'];
|
||||
}
|
||||
}
|
||||
$sHtml .= "</ul></li></ul></div>";
|
||||
foreach(array_reverse($aFavoriteActions) as $aAction)
|
||||
{
|
||||
$sHtml .= "<div class=\"actions_button\"><a href='{$aAction['url']}'>{$aAction['label']}</a></div>";
|
||||
$sTarget = isset($aAction['target']) ? " target=\"{$aAction['target']}\"" : "";
|
||||
$sHtml .= "<div class=\"actions_button\"><a $sTarget href='{$aAction['url']}'>{$aAction['label']}</a></div>";
|
||||
}
|
||||
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
protected function output_dict_entries($bReturnOutput = false)
|
||||
{
|
||||
$sHtml = '';
|
||||
if (count($this->a_dict_entries)>0)
|
||||
{
|
||||
$sHtml .= "<script type=\"text/javascript\">\n";
|
||||
$sHtml .= "var Dict = {};\n";
|
||||
$sHtml .= "Dict._entries = {};\n";
|
||||
$sHtml .= "Dict.S = function(sEntry) {\n";
|
||||
$sHtml .= " if (sEntry in Dict._entries)\n";
|
||||
$sHtml .= " {\n";
|
||||
$sHtml .= " return Dict._entries[sEntry];\n";
|
||||
$sHtml .= " }\n";
|
||||
$sHtml .= " else\n";
|
||||
$sHtml .= " {\n";
|
||||
$sHtml .= " return sEntry;\n";
|
||||
$sHtml .= " }\n";
|
||||
$sHtml .= "};\n";
|
||||
foreach($this->a_dict_entries as $s_entry => $s_value)
|
||||
{
|
||||
$sHtml .= "Dict._entries['$s_entry'] = '".addslashes($s_value)."';\n";
|
||||
}
|
||||
$sHtml .= "</script>\n";
|
||||
}
|
||||
|
||||
if ($bReturnOutput)
|
||||
{
|
||||
return $sHtml;
|
||||
}
|
||||
else
|
||||
{
|
||||
echo $sHtml;
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
|
||||
interface iTabbedPage
|
||||
{
|
||||
public function AddTabContainer($sTabContainer, $sPrefix = '');
|
||||
|
||||
public function AddToTab($sTabContainer, $sTabLabel, $sHtml);
|
||||
|
||||
public function SetCurrentTabContainer($sTabContainer = '');
|
||||
|
||||
public function SetCurrentTab($sTabLabel = '');
|
||||
|
||||
/**
|
||||
* Add a tab which content will be loaded asynchronously via the supplied URL
|
||||
*
|
||||
* Limitations:
|
||||
* Cross site scripting is not not allowed for security reasons. Use a normal tab with an IFRAME if you want to pull content from another server.
|
||||
* Static content cannot be added inside such tabs.
|
||||
*
|
||||
* @param string $sTabLabel The (localised) label of the tab
|
||||
* @param string $sUrl The URL to load (on the same server)
|
||||
* @param boolean $bCache Whether or not to cache the content of the tab once it has been loaded. flase will cause the tab to be reloaded upon each activation.
|
||||
* @since 2.0.3
|
||||
*/
|
||||
public function AddAjaxTab($sTabLabel, $sUrl, $bCache = true);
|
||||
|
||||
public function GetCurrentTab();
|
||||
|
||||
public function RemoveTab($sTabLabel, $sTabContainer = null);
|
||||
|
||||
/**
|
||||
* Finds the tab whose title matches a given pattern
|
||||
* @return mixed The name of the tab as a string or false if not found
|
||||
*/
|
||||
public function FindTab($sPattern, $sTabContainer = null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class to implement JQueryUI tabs inside a page
|
||||
*/
|
||||
class TabManager
|
||||
{
|
||||
protected $m_aTabs;
|
||||
protected $m_sCurrentTabContainer;
|
||||
protected $m_sCurrentTab;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->m_aTabs = array();
|
||||
$this->m_sCurrentTabContainer = '';
|
||||
$this->m_sCurrentTab = '';
|
||||
}
|
||||
|
||||
public function AddTabContainer($sTabContainer, $sPrefix = '')
|
||||
{
|
||||
$this->m_aTabs[$sTabContainer] = array('prefix' => $sPrefix, 'tabs' => array());
|
||||
return "\$Tabs:$sTabContainer\$";
|
||||
}
|
||||
|
||||
public function AddToCurrentTab($sHtml)
|
||||
{
|
||||
$this->AddToTab($this->m_sCurrentTabContainer, $this->m_sCurrentTab, $sHtml);
|
||||
}
|
||||
|
||||
public function GetCurrentTabLength($sHtml)
|
||||
{
|
||||
$iLength = isset($this->m_aTabs[$this->m_sCurrentTabContainer]['tabs'][$this->m_sCurrentTab]['html']) ? strlen($this->m_aTabs[$this->m_sCurrentTabContainer]['tabs'][$this->m_sCurrentTab]['html']): 0;
|
||||
return $iLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncates the given tab to the specifed length and returns the truncated part
|
||||
* @param string $sTabContainer The tab container in which to truncate the tab
|
||||
* @param string $sTab The name/identifier of the tab to truncate
|
||||
* @param integer $iLength The length/offset at which to truncate the tab
|
||||
* @return string The truncated part
|
||||
*/
|
||||
public function TruncateTab($sTabContainer, $sTab, $iLength)
|
||||
{
|
||||
$sResult = substr($this->m_aTabs[$this->m_sCurrentTabContainer]['tabs'][$this->m_sCurrentTab]['html'], $iLength);
|
||||
$this->m_aTabs[$this->m_sCurrentTabContainer]['tabs'][$this->m_sCurrentTab]['html'] = substr($this->m_aTabs[$this->m_sCurrentTabContainer]['tabs'][$this->m_sCurrentTab]['html'], 0, $iLength);
|
||||
return $sResult;
|
||||
}
|
||||
|
||||
public function TabExists($sTabContainer, $sTab)
|
||||
{
|
||||
return isset($this->m_aTabs[$sTabContainer]['tabs'][$sTab]);
|
||||
}
|
||||
|
||||
public function TabsContainerCount()
|
||||
{
|
||||
return count($this->m_aTabs);
|
||||
}
|
||||
|
||||
public function AddToTab($sTabContainer, $sTabLabel, $sHtml)
|
||||
{
|
||||
if (!isset($this->m_aTabs[$sTabContainer]['tabs'][$sTabLabel]))
|
||||
{
|
||||
// Set the content of the tab
|
||||
$this->m_aTabs[$sTabContainer]['tabs'][$sTabLabel] = array(
|
||||
'type' => 'html',
|
||||
'html' => $sHtml,
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($this->m_aTabs[$sTabContainer]['tabs'][$sTabLabel]['type'] != 'html')
|
||||
{
|
||||
throw new Exception("Cannot add HTML content to the tab '$sTabLabel' of type '{$this->m_aTabs[$sTabContainer]['tabs'][$sTabLabel]['type']}'");
|
||||
}
|
||||
// Append to the content of the tab
|
||||
$this->m_aTabs[$sTabContainer]['tabs'][$sTabLabel]['html'] .= $sHtml;
|
||||
}
|
||||
return ''; // Nothing to add to the page for now
|
||||
}
|
||||
|
||||
public function SetCurrentTabContainer($sTabContainer = '')
|
||||
{
|
||||
$sPreviousTabContainer = $this->m_sCurrentTabContainer;
|
||||
$this->m_sCurrentTabContainer = $sTabContainer;
|
||||
return $sPreviousTabContainer;
|
||||
}
|
||||
|
||||
public function SetCurrentTab($sTabLabel = '')
|
||||
{
|
||||
$sPreviousTab = $this->m_sCurrentTab;
|
||||
$this->m_sCurrentTab = $sTabLabel;
|
||||
return $sPreviousTab;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a tab which content will be loaded asynchronously via the supplied URL
|
||||
*
|
||||
* Limitations:
|
||||
* Cross site scripting is not not allowed for security reasons. Use a normal tab with an IFRAME if you want to pull content from another server.
|
||||
* Static content cannot be added inside such tabs.
|
||||
*
|
||||
* @param string $sTabLabel The (localised) label of the tab
|
||||
* @param string $sUrl The URL to load (on the same server)
|
||||
* @param boolean $bCache Whether or not to cache the content of the tab once it has been loaded. flase will cause the tab to be reloaded upon each activation.
|
||||
* @since 2.0.3
|
||||
*/
|
||||
public function AddAjaxTab($sTabLabel, $sUrl, $bCache = true)
|
||||
{
|
||||
// Set the content of the tab
|
||||
$this->m_aTabs[$this->m_sCurrentTabContainer]['tabs'][$sTabLabel] = array(
|
||||
'type' => 'ajax',
|
||||
'url' => $sUrl,
|
||||
'cache' => $bCache,
|
||||
);
|
||||
return ''; // Nothing to add to the page for now
|
||||
}
|
||||
|
||||
|
||||
public function GetCurrentTabContainer()
|
||||
{
|
||||
return $this->m_sCurrentTabContainer;
|
||||
}
|
||||
|
||||
public function GetCurrentTab()
|
||||
{
|
||||
return $this->m_sCurrentTab;
|
||||
}
|
||||
|
||||
public function RemoveTab($sTabLabel, $sTabContainer = null)
|
||||
{
|
||||
if ($sTabContainer == null)
|
||||
{
|
||||
$sTabContainer = $this->m_sCurrentTabContainer;
|
||||
}
|
||||
if (isset($this->m_aTabs[$sTabContainer]['tabs'][$sTabLabel]))
|
||||
{
|
||||
// Delete the content of the tab
|
||||
unset($this->m_aTabs[$sTabContainer]['tabs'][$sTabLabel]);
|
||||
|
||||
// If we just removed the active tab, let's reset the active tab
|
||||
if (($this->m_sCurrentTabContainer == $sTabContainer) && ($this->m_sCurrentTab == $sTabLabel))
|
||||
{
|
||||
$this->m_sCurrentTab = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the tab whose title matches a given pattern
|
||||
* @return mixed The name of the tab as a string or false if not found
|
||||
*/
|
||||
public function FindTab($sPattern, $sTabContainer = null)
|
||||
{
|
||||
$return = false;
|
||||
if ($sTabContainer == null)
|
||||
{
|
||||
$sTabContainer = $this->m_sCurrentTabContainer;
|
||||
}
|
||||
foreach($this->m_aTabs[$sTabContainer]['tabs'] as $sTabLabel => $void)
|
||||
{
|
||||
if (preg_match($sPattern, $sTabLabel))
|
||||
{
|
||||
$result = $sTabLabel;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the given tab the active one, as if it were clicked
|
||||
* DOES NOT WORK: apparently in the *old* version of jquery
|
||||
* that we are using this is not supported... TO DO upgrade
|
||||
* the whole jquery bundle...
|
||||
*/
|
||||
public function SelectTab($sTabContainer, $sTabLabel)
|
||||
{
|
||||
$container_index = 0;
|
||||
$tab_index = 0;
|
||||
foreach($this->m_aTabs as $sCurrentTabContainerName => $aTabs)
|
||||
{
|
||||
if ($sTabContainer == $sCurrentTabContainerName)
|
||||
{
|
||||
foreach($aTabs['tabs'] as $sCurrentTabLabel => $void)
|
||||
{
|
||||
if ($sCurrentTabLabel == $sTabLabel)
|
||||
{
|
||||
break;
|
||||
}
|
||||
$tab_index++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
$container_index++;
|
||||
}
|
||||
$sSelector = '#tabbedContent_'.$container_index.' > ul';
|
||||
return "window.setTimeout(\"$('$sSelector').tabs('select', $tab_index);\", 100);"; // Let the time to the tabs widget to initialize
|
||||
}
|
||||
|
||||
public function RenderIntoContent($sContent)
|
||||
{
|
||||
// Render the tabs in the page (if any)
|
||||
foreach($this->m_aTabs as $sTabContainerName => $aTabs)
|
||||
{
|
||||
$sTabs = '';
|
||||
$sPrefix = $aTabs['prefix'];
|
||||
$container_index = 0;
|
||||
if (count($aTabs['tabs']) > 0)
|
||||
{
|
||||
$sTabs = "<!-- tabs -->\n<div id=\"tabbedContent_{$sPrefix}{$container_index}\" class=\"light\">\n";
|
||||
$sTabs .= "<ul>\n";
|
||||
// Display the unordered list that will be rendered as the tabs
|
||||
$i = 0;
|
||||
foreach($aTabs['tabs'] as $sTabName => $aTabData)
|
||||
{
|
||||
switch($aTabData['type'])
|
||||
{
|
||||
case 'ajax':
|
||||
$sTabs .= "<li data-cache=\"".($aTabData['cache'] ? 'true' : 'false')."\"><a href=\"{$aTabData['url']}\" class=\"tab\"><span>".htmlentities($sTabName, ENT_QUOTES, 'UTF-8')."</span></a></li>\n";
|
||||
break;
|
||||
|
||||
case 'html':
|
||||
default:
|
||||
$sTabs .= "<li><a href=\"#tab_{$sPrefix}{$container_index}$i\" class=\"tab\"><span>".htmlentities($sTabName, ENT_QUOTES, 'UTF-8')."</span></a></li>\n";
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
$sTabs .= "</ul>\n";
|
||||
// Now add the content of the tabs themselves
|
||||
$i = 0;
|
||||
foreach($aTabs['tabs'] as $sTabName => $aTabData)
|
||||
{
|
||||
switch($aTabData['type'])
|
||||
{
|
||||
case 'ajax':
|
||||
// Nothing to add
|
||||
break;
|
||||
|
||||
case 'html':
|
||||
default:
|
||||
$sTabs .= "<div id=\"tab_{$sPrefix}{$container_index}$i\">".$aTabData['html']."</div>\n";
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
$sTabs .= "</div>\n<!-- end of tabs-->\n";
|
||||
}
|
||||
$sContent = str_replace("\$Tabs:$sTabContainerName\$", $sTabs, $sContent);
|
||||
$container_index++;
|
||||
}
|
||||
return $sContent;
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// 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 WizardHelper
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
require_once(APPROOT.'/application/uiwizard.class.inc.php');
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// 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 XMLPage
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
require_once(APPROOT."/application/webpage.class.inc.php");
|
||||
@@ -36,65 +37,77 @@ class XMLPage extends WebPage
|
||||
var $m_bPassThrough;
|
||||
var $m_bHeaderSent;
|
||||
|
||||
function __construct($s_title, $bPassThrough = false)
|
||||
{
|
||||
parent::__construct($s_title);
|
||||
$this->m_bPassThrough = $bPassThrough;
|
||||
$this->m_bHeaderSent = false;
|
||||
$this->add_header("Content-type: text/xml; charset=utf-8");
|
||||
function __construct($s_title, $bPassThrough = false)
|
||||
{
|
||||
parent::__construct($s_title);
|
||||
$this->m_bPassThrough = $bPassThrough;
|
||||
$this->m_bHeaderSent = false;
|
||||
$this->add_header("Content-type: text/xml; charset=utf-8");
|
||||
$this->add_header("Cache-control: no-cache");
|
||||
$this->add_header("Content-location: export.xml");
|
||||
}
|
||||
}
|
||||
|
||||
public function output()
|
||||
{
|
||||
if (!$this->m_bPassThrough)
|
||||
{
|
||||
$this->add("<?xml version=\"1.0\" encoding=\"UTF-8\"?".">\n");
|
||||
$this->add_header("Content-Length: ".strlen(trim($this->s_content)));
|
||||
foreach($this->a_headers as $s_header)
|
||||
{
|
||||
header($s_header);
|
||||
}
|
||||
echo trim($this->s_content);
|
||||
}
|
||||
}
|
||||
|
||||
public function add($sText)
|
||||
{
|
||||
if (!$this->m_bPassThrough)
|
||||
{
|
||||
parent::add($sText);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($this->m_bHeaderSent)
|
||||
{
|
||||
echo $sText;
|
||||
}
|
||||
else
|
||||
{
|
||||
$s_captured_output = ob_get_contents();
|
||||
ob_end_clean();
|
||||
foreach($this->a_headers as $s_header)
|
||||
{
|
||||
header($s_header);
|
||||
}
|
||||
echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?".">\n";
|
||||
echo trim($s_captured_output);
|
||||
echo trim($this->s_content);
|
||||
echo $sText;
|
||||
$this->m_bHeaderSent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function small_p($sText)
|
||||
{
|
||||
public function output()
|
||||
{
|
||||
if (!$this->m_bPassThrough)
|
||||
{
|
||||
$this->s_content = "<?xml version=\"1.0\" encoding=\"UTF-8\"?".">\n".trim($this->s_content);
|
||||
$this->add_header("Content-Length: ".strlen($this->s_content));
|
||||
foreach($this->a_headers as $s_header)
|
||||
{
|
||||
header($s_header);
|
||||
}
|
||||
echo $this->s_content;
|
||||
}
|
||||
if (class_exists('MetaModel'))
|
||||
{
|
||||
MetaModel::RecordQueryTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function add($sText)
|
||||
{
|
||||
if (!$this->m_bPassThrough)
|
||||
{
|
||||
parent::add($sText);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($this->m_bHeaderSent)
|
||||
{
|
||||
echo $sText;
|
||||
}
|
||||
else
|
||||
{
|
||||
$s_captured_output = ob_get_contents();
|
||||
ob_end_clean();
|
||||
foreach($this->a_headers as $s_header)
|
||||
{
|
||||
header($s_header);
|
||||
}
|
||||
echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?".">\n";
|
||||
echo trim($s_captured_output);
|
||||
echo trim($this->s_content);
|
||||
echo $sText;
|
||||
$this->m_bHeaderSent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function small_p($sText)
|
||||
{
|
||||
}
|
||||
|
||||
public function table($aConfig, $aData, $aParams = array())
|
||||
{
|
||||
}
|
||||
|
||||
public function TrashUnexpectedOutput()
|
||||
{
|
||||
if (!$this->m_bPassThrough)
|
||||
{
|
||||
parent::TrashUnexpectedOutput();
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Various dev/debug helpers
|
||||
* TODO: cleanup or at least re-organize
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
@@ -218,7 +219,7 @@ class MyHelpers
|
||||
}
|
||||
}
|
||||
|
||||
public static function get_callstack_html($iLevelsToIgnore = 0, $aCallStack = null)
|
||||
public static function get_callstack($iLevelsToIgnore = 0, $aCallStack = null)
|
||||
{
|
||||
if ($aCallStack == null) $aCallStack = debug_backtrace();
|
||||
|
||||
@@ -230,6 +231,16 @@ class MyHelpers
|
||||
{
|
||||
$sLine = empty($aCallInfo['line']) ? "" : $aCallInfo['line'];
|
||||
$sFile = empty($aCallInfo['file']) ? "" : $aCallInfo['file'];
|
||||
if ($sFile != '')
|
||||
{
|
||||
$sFile = str_replace('\\', '/', $sFile);
|
||||
$sAppRoot = str_replace('\\', '/', APPROOT);
|
||||
$iPos = strpos($sFile, $sAppRoot);
|
||||
if ($iPos !== false)
|
||||
{
|
||||
$sFile = substr($sFile, strlen($sAppRoot));
|
||||
}
|
||||
}
|
||||
$sClass = empty($aCallInfo['class']) ? "" : $aCallInfo['class'];
|
||||
$sType = empty($aCallInfo['type']) ? "" : $aCallInfo['type'];
|
||||
$sFunction = empty($aCallInfo['function']) ? "" : $aCallInfo['function'];
|
||||
@@ -258,11 +269,11 @@ class MyHelpers
|
||||
$args .= $a;
|
||||
break;
|
||||
case 'string':
|
||||
$a = Str::pure2html(self::beautifulstr($a, 1024, true, true));
|
||||
$a = Str::pure2html(self::beautifulstr($a, 64, true, false));
|
||||
$args .= "\"$a\"";
|
||||
break;
|
||||
case 'array':
|
||||
$args .= 'Array('.count($a).')';
|
||||
$args .= 'array('.count($a).')';
|
||||
break;
|
||||
case 'object':
|
||||
$args .= 'Object('.get_class($a).')';
|
||||
@@ -271,19 +282,25 @@ class MyHelpers
|
||||
$args .= 'Resource('.strstr($a, '#').')';
|
||||
break;
|
||||
case 'boolean':
|
||||
$args .= $a ? 'True' : 'False';
|
||||
$args .= $a ? 'true' : 'false';
|
||||
break;
|
||||
case 'NULL':
|
||||
$args .= 'Null';
|
||||
$args .= 'null';
|
||||
break;
|
||||
default:
|
||||
$args .= 'Unknown';
|
||||
}
|
||||
}
|
||||
$sFunctionInfo = "$sClass $sType $sFunction($args)";
|
||||
$sFunctionInfo = "$sClass$sType$sFunction($args)";
|
||||
}
|
||||
$aDigestCallStack[] = array('File'=>$sFile, 'Line'=>$sLine, 'Function'=>$sFunctionInfo);
|
||||
}
|
||||
return $aDigestCallStack;
|
||||
}
|
||||
|
||||
public static function get_callstack_html($iLevelsToIgnore = 0, $aCallStack = null)
|
||||
{
|
||||
$aDigestCallStack = self::get_callstack($iLevelsToIgnore, $aCallStack);
|
||||
return self::make_table_from_assoc_array($aDigestCallStack);
|
||||
}
|
||||
|
||||
@@ -292,6 +309,17 @@ class MyHelpers
|
||||
return self::get_callstack_html($iLevelsToIgnore, $aCallStack);
|
||||
}
|
||||
|
||||
public static function get_callstack_text($iLevelsToIgnore = 0, $aCallStack = null)
|
||||
{
|
||||
$aDigestCallStack = self::get_callstack($iLevelsToIgnore, $aCallStack);
|
||||
$aRes = array();
|
||||
foreach ($aDigestCallStack as $aCall)
|
||||
{
|
||||
$aRes[] = $aCall['File'].' at '.$aCall['Line'].', '.$aCall['Function'];
|
||||
}
|
||||
return implode("\n", $aRes);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Source: New
|
||||
// Last modif: 2004/12/20 RQU
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Persistent classes (internal): user defined actions
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
@@ -212,8 +213,12 @@ class ActionEmail extends ActionNotification
|
||||
$aRecipients = array();
|
||||
while ($oObj = $oSet->Fetch())
|
||||
{
|
||||
$aRecipients[] = $oObj->Get($sEmailAttCode);
|
||||
$this->m_iRecipients++;
|
||||
$sAddress = trim($oObj->Get($sEmailAttCode));
|
||||
if (strlen($sAddress) > 0)
|
||||
{
|
||||
$aRecipients[] = $sAddress;
|
||||
$this->m_iRecipients++;
|
||||
}
|
||||
}
|
||||
return implode(', ', $aRecipients);
|
||||
}
|
||||
@@ -288,8 +293,8 @@ class ActionEmail extends ActionNotification
|
||||
$sCC = $this->FindRecipients('cc', $aContextArgs);
|
||||
$sBCC = $this->FindRecipients('bcc', $aContextArgs);
|
||||
|
||||
$sFrom = $this->Get('from');
|
||||
$sReplyTo = $this->Get('reply_to');
|
||||
$sFrom = MetaModel::ApplyParams($this->Get('from'), $aContextArgs);
|
||||
$sReplyTo = MetaModel::ApplyParams($this->Get('reply_to'), $aContextArgs);
|
||||
|
||||
$sSubject = MetaModel::ApplyParams($this->Get('subject'), $aContextArgs);
|
||||
$sBody = MetaModel::ApplyParams($this->Get('body'), $aContextArgs);
|
||||
@@ -356,6 +361,17 @@ class ActionEmail extends ActionNotification
|
||||
$oEmail->SetMessageId($sMessageId);
|
||||
}
|
||||
|
||||
if (isset($aContextArgs['attachments']))
|
||||
{
|
||||
$aAttachmentReport = array();
|
||||
foreach($aContextArgs['attachments'] as $oDocument)
|
||||
{
|
||||
$oEmail->AddAttachment($oDocument->GetData(), $oDocument->GetFileName(), $oDocument->GetMimeType());
|
||||
$aAttachmentReport[] = array($oDocument->GetFileName(), $oDocument->GetMimeType(), strlen($oDocument->GetData()));
|
||||
}
|
||||
$oLog->Set('attachments', $aAttachmentReport);
|
||||
}
|
||||
|
||||
if (empty($this->m_aMailErrors))
|
||||
{
|
||||
if ($this->m_iRecipients == 0)
|
||||
@@ -392,4 +408,4 @@ class ActionEmail extends ActionNotification
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
?>
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Utility to import/export the DB from/to a ZIP file
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Persistent classes (internal): user defined actions
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
@@ -33,27 +34,27 @@ class ExecAsyncTask implements iBackgroundProcess
|
||||
|
||||
public function Process($iTimeLimit)
|
||||
{
|
||||
$sOQL = "SELECT AsyncTask WHERE ISNULL(started) AND (ISNULL(planned) OR (planned < NOW()))";
|
||||
$oSet = new CMDBObjectSet(DBObjectSearch::FromOQL($sOQL), array('created' => true) /* order by*/, array());
|
||||
$sNow = date('Y-m-d H:i:s');
|
||||
// Criteria: planned, and expected to occur... ASAP or in the past
|
||||
$sOQL = "SELECT AsyncTask WHERE (status = 'planned') AND (ISNULL(planned) OR (planned < '$sNow'))";
|
||||
$iProcessed = 0;
|
||||
while ((time() < $iTimeLimit) && ($oTask = $oSet->Fetch()))
|
||||
while (time() < $iTimeLimit)
|
||||
{
|
||||
$oTask->Set('started', time());
|
||||
$oTask->DBUpdate();
|
||||
|
||||
$oTask->Process();
|
||||
// Next one ?
|
||||
$oSet = new CMDBObjectSet(DBObjectSearch::FromOQL($sOQL), array('created' => true) /* order by*/, array(), null, 1 /* limit count */);
|
||||
$oTask = $oSet->Fetch();
|
||||
if (is_null($oTask))
|
||||
{
|
||||
// Nothing to be done
|
||||
break;
|
||||
}
|
||||
$iProcessed++;
|
||||
|
||||
$oTask->DBDelete();
|
||||
}
|
||||
if ($iProcessed == $oSet->Count())
|
||||
{
|
||||
return "processed $iProcessed tasks";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "processed $iProcessed tasks (remaining: ".($oSet->Count() - $iProcessed).")";
|
||||
if ($oTask->Process())
|
||||
{
|
||||
$oTask->DBDelete();
|
||||
}
|
||||
}
|
||||
return "processed $iProcessed tasks";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,36 +80,141 @@ abstract class AsyncTask extends DBObject
|
||||
"display_template" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
// MetaModel::Init_AddAttribute(new AttributeString("name", array("allowed_values"=>null, "sql"=>"name", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
|
||||
// Null is allowed to ease the migration from iTop 2.0.2 and earlier, when the status did not exist, and because the default value is not taken into account in the SQL definition
|
||||
// The value is set from null to planned in the setup program
|
||||
MetaModel::Init_AddAttribute(new AttributeEnum("status", array("allowed_values"=>new ValueSetEnum('planned,running,idle,error'), "sql"=>"status", "default_value"=>"planned", "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeDateTime("created", array("allowed_values"=>null, "sql"=>"created", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeDateTime("started", array("allowed_values"=>null, "sql"=>"started", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
// planned... still not used - reserved for timer management
|
||||
MetaModel::Init_AddAttribute(new AttributeDateTime("planned", array("allowed_values"=>null, "sql"=>"planned", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("event_id", array("targetclass"=>"Event", "jointype"=> "", "allowed_values"=>null, "sql"=>"event_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_SILENT, "depends_on"=>array())));
|
||||
|
||||
// Display lists
|
||||
// MetaModel::Init_SetZListItems('details', array()); // Attributes to be displayed for the complete details
|
||||
// MetaModel::Init_SetZListItems('list', array()); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
|
||||
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
|
||||
MetaModel::Init_AddAttribute(new AttributeInteger("remaining_retries", array("allowed_values"=>null, "sql"=>"remaining_retries", "default_value"=>0, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Every is fine
|
||||
*/
|
||||
const OK = 0;
|
||||
/**
|
||||
* The task no longer exists
|
||||
*/
|
||||
const DELETED = 1;
|
||||
/**
|
||||
* The task is already being executed
|
||||
*/
|
||||
const ALREADY_RUNNING = 2;
|
||||
|
||||
/**
|
||||
* The current process requests the ownership on the task.
|
||||
* In case the task can be accessed concurrently, this function can be overloaded to add a critical section.
|
||||
* The function must not block the caller if another process is already owning the task
|
||||
*
|
||||
* @return integer A code among OK/DELETED/ALREADY_RUNNING.
|
||||
*/
|
||||
public function MarkAsRunning()
|
||||
{
|
||||
try
|
||||
{
|
||||
$this->Set('status', 'running');
|
||||
$this->Set('started', time());
|
||||
$this->DBUpdate();
|
||||
return self::OK;
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
// Corrupted task !! (for example: "Failed to reload object")
|
||||
IssueLog::Error('Failed to process async task #'.$this->GetKey().' - reason: '.$e->getMessage().' - fatal error, deleting the task.');
|
||||
if ($this->Get('event_id') != 0)
|
||||
{
|
||||
$oEventLog = MetaModel::GetObject('Event', $this->Get('event_id'));
|
||||
$oEventLog->Set('message', 'Failed, corrupted data: '.$e->getMessage());
|
||||
$oEventLog->DBUpdate();
|
||||
}
|
||||
$this->DBDelete();
|
||||
return self::DELETED;
|
||||
}
|
||||
}
|
||||
|
||||
public function GetRetryDelay()
|
||||
{
|
||||
$iRetryDelay = 600;
|
||||
$aRetries = MetaModel::GetConfig()->Get('async_task_retries', array());
|
||||
if (is_array($aRetries) && array_key_exists(get_class($this), $aRetries))
|
||||
{
|
||||
$aConfig = $aRetries[get_class($this)];
|
||||
$iRetryDelay = $aConfig['retry_delay'];
|
||||
}
|
||||
return $iRetryDelay;
|
||||
}
|
||||
|
||||
public function GetMaxRetries()
|
||||
{
|
||||
$iMaxRetries = 0;
|
||||
$aRetries = MetaModel::GetConfig()->Get('async_task_retries', array());
|
||||
if (is_array($aRetries) && array_key_exists(get_class($this), $aRetries))
|
||||
{
|
||||
$aConfig = $aRetries[get_class($this)];
|
||||
$iMaxRetries = $aConfig['max_retries'];
|
||||
}
|
||||
}
|
||||
|
||||
protected function OnInsert()
|
||||
{
|
||||
$this->Set('created', time());
|
||||
$this->Set('remaining_retries', $this->GetMaxRetries());
|
||||
}
|
||||
|
||||
public function Process()
|
||||
/**
|
||||
* @return boolean True if the task record can be deleted
|
||||
*/
|
||||
public function Process()
|
||||
{
|
||||
$sStatus = $this->DoProcess();
|
||||
if ($this->Get('event_id') != 0)
|
||||
{
|
||||
$oEventLog = MetaModel::GetObject('Event', $this->Get('event_id'));
|
||||
$oEventLog->Set('message', $sStatus);
|
||||
$oEventLog->DBUpdate();
|
||||
// By default: consider that the task is not completed
|
||||
$bRet = false;
|
||||
|
||||
// Attempt to take the ownership
|
||||
$iStatus = $this->MarkAsRunning();
|
||||
if ($iStatus == self::OK)
|
||||
{
|
||||
try
|
||||
{
|
||||
$sStatus = $this->DoProcess();
|
||||
if ($this->Get('event_id') != 0)
|
||||
{
|
||||
$oEventLog = MetaModel::GetObject('Event', $this->Get('event_id'));
|
||||
$oEventLog->Set('message', $sStatus);
|
||||
$oEventLog->DBUpdate();
|
||||
}
|
||||
$bRet = true;
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
$iRemaining = $this->Get('remaining_retries');
|
||||
if ($iRemaining > 0)
|
||||
{
|
||||
$iRetryDelay = $this->GetRetryDelay();
|
||||
IssueLog::Info('Failed to process async task #'.$this->GetKey().' - reason: '.$e->getMessage().' - remaining retries: '.$iRemaining.' - next retry in '.$iRetryDelay.'s');
|
||||
|
||||
$this->Set('remaining_retries', $iRemaining - 1);
|
||||
$this->Set('status', 'planned');
|
||||
$this->Set('started', null);
|
||||
$this->Set('planned', time() + $iRetryDelay);
|
||||
$this->DBUpdate();
|
||||
}
|
||||
else
|
||||
{
|
||||
IssueLog::Error('Failed to process async task #'.$this->GetKey().' - reason: '.$e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Already done or being handled by another process... skip...
|
||||
$bRet = false;
|
||||
}
|
||||
return $bRet;
|
||||
}
|
||||
|
||||
abstract public function DoProcess();
|
||||
@@ -138,7 +244,8 @@ class AsyncSendEmail extends AsyncTask
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeText("to", array("allowed_values"=>null, "sql"=>"to", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeInteger("version", array("allowed_values"=>null, "sql"=>"version", "default_value"=>Email::ORIGINAL_FORMAT, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeText("to", array("allowed_values"=>null, "sql"=>"to", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeText("subject", array("allowed_values"=>null, "sql"=>"subject", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeLongText("message", array("allowed_values"=>null, "sql"=>"message", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
|
||||
@@ -160,7 +267,10 @@ class AsyncSendEmail extends AsyncTask
|
||||
$oNew->Set('to', $oEMail->GetRecipientTO(true /* string */));
|
||||
$oNew->Set('subject', $oEMail->GetSubject());
|
||||
|
||||
$sMessage = serialize($oEMail);
|
||||
// $oNew->Set('version', 1);
|
||||
// $sMessage = serialize($oEMail);
|
||||
$oNew->Set('version', 2);
|
||||
$sMessage = $oEMail->SerializeV2();
|
||||
$oNew->Set('message', $sMessage);
|
||||
$oNew->DBInsert();
|
||||
}
|
||||
@@ -168,7 +278,20 @@ class AsyncSendEmail extends AsyncTask
|
||||
public function DoProcess()
|
||||
{
|
||||
$sMessage = $this->Get('message');
|
||||
$oEMail = unserialize($sMessage);
|
||||
$iVersion = (int) $this->Get('version');
|
||||
switch($iVersion)
|
||||
{
|
||||
case Email::FORMAT_V2:
|
||||
$oEMail = Email::UnSerializeV2($sMessage);
|
||||
break;
|
||||
|
||||
case Email::ORIGINAL_FORMAT:
|
||||
$oEMail = unserialize($sMessage);
|
||||
break;
|
||||
|
||||
default:
|
||||
return 'Unknown version of the serialization format: '.$iVersion;
|
||||
}
|
||||
$iRes = $oEMail->Send($aIssues, true /* force synchro !!!!! */);
|
||||
switch ($iRes)
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,33 +1,67 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// 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 BackgroundProcess
|
||||
* Any extension that must be called regularly to be executed in the background
|
||||
* interface iProcess
|
||||
* Something that can be executed
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
interface iBackgroundProcess
|
||||
interface iProcess
|
||||
{
|
||||
public function GetPeriodicity();
|
||||
public function Process($iUnixTimeLimit);
|
||||
}
|
||||
|
||||
/**
|
||||
* interface iBackgroundProcess
|
||||
* Any extension that must be called regularly to be executed in the background
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
interface iBackgroundProcess extends iProcess
|
||||
{
|
||||
/*
|
||||
Gives the repetition rate in seconds
|
||||
@returns integer
|
||||
*/
|
||||
public function GetPeriodicity();
|
||||
}
|
||||
|
||||
/**
|
||||
* interface iScheduledProcess
|
||||
* A variant of process that must be called at specific times
|
||||
*
|
||||
* @copyright Copyright (C) 2013 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
interface iScheduledProcess extends iProcess
|
||||
{
|
||||
/*
|
||||
Gives the exact time at which the process must be run next time
|
||||
@returns DateTime
|
||||
*/
|
||||
public function GetNextOccurrence();
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
76
core/backgroundtask.class.inc.php
Normal file
76
core/backgroundtask.class.inc.php
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
// Copyright (C) 2013 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 BackgroundTask
|
||||
* A class to record information about the execution of background processes
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
class BackgroundTask extends DBObject
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "class_name",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_backgroundtask",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"display_template" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeString("class_name", array("allowed_values"=>null, "sql"=>"class_name", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeDateTime("first_run_date", array("allowed_values"=>null, "sql"=>"first_run_date", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeDateTime("latest_run_date", array("allowed_values"=>null, "sql"=>"latest_run_date", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeDateTime("next_run_date", array("allowed_values"=>null, "sql"=>"next_run_date", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeInteger("total_exec_count", array("allowed_values"=>null, "sql"=>"total_exec_count", "default_value"=>"0", "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeDecimal("latest_run_duration", array("allowed_values"=>null, "sql"=>"latest_run_duration", "digits"=> 8, "decimals"=> 3, "default_value"=>"0", "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeDecimal("min_run_duration", array("allowed_values"=>null, "sql"=>"min_run_duration", "digits"=> 8, "decimals"=> 3, "default_value"=>"0", "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeDecimal("max_run_duration", array("allowed_values"=>null, "sql"=>"max_run_duration", "digits"=> 8, "decimals"=> 3, "default_value"=>"0", "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeDecimal("average_run_duration", array("allowed_values"=>null, "sql"=>"average_run_duration", "digits"=> 8, "decimals"=> 3, "default_value"=>"0", "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeBoolean("running", array("allowed_values"=>null, "sql"=>"running", "default_value"=>false, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeEnum("status", array("allowed_values"=>new ValueSetEnum('active,paused'), "sql"=>"status", "default_value"=>'active', "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
}
|
||||
|
||||
public function ComputeDurations($fLatestDuration)
|
||||
{
|
||||
$iTotalRun = $this->Get('total_exec_count');
|
||||
$fAverageDuration = ($this->Get('average_run_duration') * $iTotalRun + $fLatestDuration) / (1+$iTotalRun);
|
||||
$this->Set('average_run_duration', sprintf('%.3f',$fAverageDuration));
|
||||
$this->Set('total_exec_count', 1+$iTotalRun);
|
||||
if ($fLatestDuration < $this->Get('min_run_duration'))
|
||||
{
|
||||
$this->Set('min_run_duration', sprintf('%.3f',$fLatestDuration));
|
||||
}
|
||||
if ($fLatestDuration > $this->Get('max_run_duration'))
|
||||
{
|
||||
$this->Set('max_run_duration', sprintf('%.3f',$fLatestDuration));
|
||||
}
|
||||
$this->Set('latest_run_duration', sprintf('%.3f',$fLatestDuration));
|
||||
}
|
||||
}
|
||||
@@ -1,29 +1,34 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Bulk change facility (common to interactive and batch usages)
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
// The BOM is added at the head of exported UTF-8 CSV data, and removed (if present) from input UTF-8 data.
|
||||
// This helps MS-Excel (Version > 2007, Windows only) in changing its interpretation of a CSV file (by default Excel reads data as ISO-8859-1 -not 100% sure!)
|
||||
define('UTF8_BOM', chr(239).chr(187).chr(191)); // 0xEF, 0xBB, 0xBF
|
||||
|
||||
/**
|
||||
* BulkChange
|
||||
* Interpret a given data set and update the DB accordingly (fake mode avail.)
|
||||
@@ -84,15 +89,16 @@ class CellStatus_Modify extends CellChangeSpec
|
||||
{
|
||||
protected $m_previousValue;
|
||||
|
||||
public function __construct($proposedValue, $previousValue)
|
||||
public function __construct($proposedValue, $previousValue = null)
|
||||
{
|
||||
$this->m_previousValue = $previousValue;
|
||||
// Unused (could be costly to know -see the case of reconciliation on ext keys)
|
||||
//$this->m_previousValue = $previousValue;
|
||||
parent::__construct($proposedValue);
|
||||
}
|
||||
|
||||
public function GetDescription()
|
||||
{
|
||||
return 'Modified';
|
||||
return Dict::S('UI:CSVReport-Value-Modified');
|
||||
}
|
||||
|
||||
//public function GetPreviousValue()
|
||||
@@ -115,9 +121,9 @@ class CellStatus_Issue extends CellStatus_Modify
|
||||
{
|
||||
if (is_null($this->m_proposedValue))
|
||||
{
|
||||
return 'Could not be changed - reason: '.$this->m_sReason;
|
||||
return Dict::Format('UI:CSVReport-Value-SetIssue', $this->m_sReason);
|
||||
}
|
||||
return 'Could not be changed to '.$this->m_proposedValue.' - reason: '.$this->m_sReason;
|
||||
return Dict::Format('UI:CSVReport-Value-ChangeIssue', $this->m_proposedValue, $this->m_sReason);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +136,7 @@ class CellStatus_SearchIssue extends CellStatus_Issue
|
||||
|
||||
public function GetDescription()
|
||||
{
|
||||
return 'No match';
|
||||
return Dict::S('UI:CSVReport-Value-NoMatch');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +149,7 @@ class CellStatus_NullIssue extends CellStatus_Issue
|
||||
|
||||
public function GetDescription()
|
||||
{
|
||||
return 'Missing mandatory value';
|
||||
return Dict::S('UI:CSVReport-Value-Missing');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,7 +168,7 @@ class CellStatus_Ambiguous extends CellStatus_Issue
|
||||
public function GetDescription()
|
||||
{
|
||||
$sCount = $this->m_iCount;
|
||||
return "Ambiguous: found $sCount objects";
|
||||
return Dict::Format('UI:CSVReport-Value-Ambiguous', $sCount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,7 +192,7 @@ class RowStatus_NoChange extends RowStatus
|
||||
{
|
||||
public function GetDescription()
|
||||
{
|
||||
return "unchanged";
|
||||
return Dict::S('UI:CSVReport-Row-Unchanged');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,7 +200,7 @@ class RowStatus_NewObj extends RowStatus
|
||||
{
|
||||
public function GetDescription()
|
||||
{
|
||||
return "created";
|
||||
return Dict::S('UI:CSVReport-Row-Created');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,7 +215,7 @@ class RowStatus_Modify extends RowStatus
|
||||
|
||||
public function GetDescription()
|
||||
{
|
||||
return "updated ".$this->m_iChanged." cols";
|
||||
return Dict::Format('UI:CSVReport-Row-Updated', $this->m_iChanged);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,7 +223,7 @@ class RowStatus_Disappeared extends RowStatus_Modify
|
||||
{
|
||||
public function GetDescription()
|
||||
{
|
||||
return "disappeared, changed ".$this->m_iChanged." cols";
|
||||
return Dict::Format('UI:CSVReport-Row-Disappeared', $this->m_iChanged);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,7 +238,7 @@ class RowStatus_Issue extends RowStatus
|
||||
|
||||
public function GetDescription()
|
||||
{
|
||||
return 'Issue: '.$this->m_sReason;
|
||||
return Dict::Format('UI:CSVReport-Row-Issue', $this->m_sReason);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,8 +259,10 @@ class BulkChange
|
||||
protected $m_sSynchroScope; // OQL - if specified, then the missing items will be reported
|
||||
protected $m_aOnDisappear; // array of attcode => value, values to be set when an object gets out of scope (ignored if no scope has been defined)
|
||||
protected $m_sDateFormat; // Date format specification, see utils::StringToTime()
|
||||
protected $m_bLocalizedValues; // Values in the data set are localized (see AttributeEnum)
|
||||
protected $m_aExtKeysMappingCache; // Cache for resolving external keys based on the given search criterias
|
||||
|
||||
public function __construct($sClass, $aData, $aAttList, $aExtKeys, $aReconcilKeys, $sSynchroScope = null, $aOnDisappear = null, $sDateFormat = null)
|
||||
public function __construct($sClass, $aData, $aAttList, $aExtKeys, $aReconcilKeys, $sSynchroScope = null, $aOnDisappear = null, $sDateFormat = null, $bLocalize = false)
|
||||
{
|
||||
$this->m_sClass = $sClass;
|
||||
$this->m_aData = $aData;
|
||||
@@ -264,6 +272,8 @@ class BulkChange
|
||||
$this->m_sSynchroScope = $sSynchroScope;
|
||||
$this->m_aOnDisappear = $aOnDisappear;
|
||||
$this->m_sDateFormat = $sDateFormat;
|
||||
$this->m_bLocalizedValues = $bLocalize;
|
||||
$this->m_aExtKeysMappingCache = array();
|
||||
}
|
||||
|
||||
protected $m_bReportHtml = false;
|
||||
@@ -288,8 +298,17 @@ class BulkChange
|
||||
$oReconFilter = new CMDBSearchFilter($oExtKey->GetTargetClass());
|
||||
foreach ($this->m_aExtKeys[$sAttCode] as $sForeignAttCode => $iCol)
|
||||
{
|
||||
// The foreign attribute is one of our reconciliation key
|
||||
$oReconFilter->AddCondition($sForeignAttCode, $aRowData[$iCol], '=');
|
||||
if ($sForeignAttCode == 'id')
|
||||
{
|
||||
$value = (int) $aRowData[$iCol];
|
||||
}
|
||||
else
|
||||
{
|
||||
// The foreign attribute is one of our reconciliation key
|
||||
$oForeignAtt = MetaModel::GetAttributeDef($oExtKey->GetTargetClass(), $sForeignAttCode);
|
||||
$value = $oForeignAtt->MakeValueFromString($aRowData[$iCol], $this->m_bLocalizedValues);
|
||||
}
|
||||
$oReconFilter->AddCondition($sForeignAttCode, $value, '=');
|
||||
$aResults[$iCol] = new CellStatus_Void($aRowData[$iCol]);
|
||||
}
|
||||
|
||||
@@ -331,6 +350,7 @@ class BulkChange
|
||||
{
|
||||
foreach ($aKeyConfig as $sForeignAttCode => $iCol)
|
||||
{
|
||||
// Default reporting
|
||||
$aResults[$iCol] = new CellStatus_Void($aRowData[$iCol]);
|
||||
}
|
||||
if ($oExtKey->IsNullAllowed())
|
||||
@@ -340,34 +360,80 @@ class BulkChange
|
||||
}
|
||||
else
|
||||
{
|
||||
$aErrors[$sAttCode] = "Null not allowed";
|
||||
$aResults[$sAttCode]= new CellStatus_Issue(null, $oTargetObj->Get($sAttCode), 'Null not allowed');
|
||||
$aErrors[$sAttCode] = Dict::S('UI:CSVReport-Value-Issue-Null');
|
||||
$aResults[$sAttCode]= new CellStatus_Issue(null, $oTargetObj->Get($sAttCode), Dict::S('UI:CSVReport-Value-Issue-Null'));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$oReconFilter = new CMDBSearchFilter($oExtKey->GetTargetClass());
|
||||
$aCacheKeys = array();
|
||||
foreach ($aKeyConfig as $sForeignAttCode => $iCol)
|
||||
{
|
||||
// The foreign attribute is one of our reconciliation key
|
||||
$oReconFilter->AddCondition($sForeignAttCode, $aRowData[$iCol], '=');
|
||||
if ($sForeignAttCode == 'id')
|
||||
{
|
||||
$value = $aRowData[$iCol];
|
||||
}
|
||||
else
|
||||
{
|
||||
$oForeignAtt = MetaModel::GetAttributeDef($oExtKey->GetTargetClass(), $sForeignAttCode);
|
||||
$value = $oForeignAtt->MakeValueFromString($aRowData[$iCol], $this->m_bLocalizedValues);
|
||||
}
|
||||
$aCacheKeys[] = $value;
|
||||
$oReconFilter->AddCondition($sForeignAttCode, $value, '=');
|
||||
$aResults[$iCol] = new CellStatus_Void($aRowData[$iCol]);
|
||||
}
|
||||
$oExtObjects = new CMDBObjectSet($oReconFilter);
|
||||
switch($oExtObjects->Count())
|
||||
$sCacheKey = implode('_|_', $aCacheKeys); // Unique key for this query...
|
||||
$iCount = 0;
|
||||
$iForeignKey = null;
|
||||
$sOQL = '';
|
||||
// TODO: check if *too long* keys can lead to collisions... and skip the cache in such a case...
|
||||
if (!array_key_exists($sAttCode, $this->m_aExtKeysMappingCache))
|
||||
{
|
||||
case 0:
|
||||
$aErrors[$sAttCode] = "Object not found";
|
||||
$this->m_aExtKeysMappingCache[$sAttCode] = array();
|
||||
}
|
||||
if (array_key_exists($sCacheKey, $this->m_aExtKeysMappingCache[$sAttCode]))
|
||||
{
|
||||
// Cache hit
|
||||
$iCount = $this->m_aExtKeysMappingCache[$sAttCode][$sCacheKey]['c'];
|
||||
$iForeignKey = $this->m_aExtKeysMappingCache[$sAttCode][$sCacheKey]['k'];
|
||||
$sOQL = $this->m_aExtKeysMappingCache[$sAttCode][$sCacheKey]['oql'];
|
||||
// Record the hit
|
||||
$this->m_aExtKeysMappingCache[$sAttCode][$sCacheKey]['h']++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Cache miss, let's initialize it
|
||||
$oExtObjects = new CMDBObjectSet($oReconFilter);
|
||||
$iCount = $oExtObjects->Count();
|
||||
if ($iCount == 1)
|
||||
{
|
||||
$oForeignObj = $oExtObjects->Fetch();
|
||||
$iForeignKey = $oForeignObj->GetKey();
|
||||
}
|
||||
$this->m_aExtKeysMappingCache[$sAttCode][$sCacheKey] = array(
|
||||
'c' => $iCount,
|
||||
'k' => $iForeignKey,
|
||||
'oql' => $oReconFilter->ToOql(),
|
||||
'h' => 0, // number of hits on this cache entry
|
||||
);
|
||||
}
|
||||
switch($iCount)
|
||||
{
|
||||
case 0:
|
||||
$aErrors[$sAttCode] = Dict::S('UI:CSVReport-Value-Issue-NotFound');
|
||||
$aResults[$sAttCode]= new CellStatus_SearchIssue();
|
||||
break;
|
||||
case 1:
|
||||
|
||||
case 1:
|
||||
// Do change the external key attribute
|
||||
$oForeignObj = $oExtObjects->Fetch();
|
||||
$oTargetObj->Set($sAttCode, $oForeignObj->GetKey());
|
||||
$oTargetObj->Set($sAttCode, $iForeignKey);
|
||||
break;
|
||||
default:
|
||||
$aErrors[$sAttCode] = "Found ".$oExtObjects->Count()." matches";
|
||||
$aResults[$sAttCode]= new CellStatus_Ambiguous($oTargetObj->Get($sAttCode), $oExtObjects->Count(), $oReconFilter->ToOql());
|
||||
|
||||
default:
|
||||
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-FoundMany', $oExtObjects->Count());
|
||||
$aResults[$sAttCode]= new CellStatus_Ambiguous($oTargetObj->Get($sAttCode), $iCount, $sOQL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -384,6 +450,11 @@ class BulkChange
|
||||
else
|
||||
{
|
||||
$aResults[$sAttCode]= new CellStatus_Modify($iForeignObj, $oTargetObj->GetOriginal($sAttCode));
|
||||
foreach ($aKeyConfig as $sForeignAttCode => $iCol)
|
||||
{
|
||||
// Report the change on reconciliation values as well
|
||||
$aResults[$iCol] = new CellStatus_Modify($aRowData[$iCol]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -405,31 +476,39 @@ class BulkChange
|
||||
$iFlags = $oTargetObj->GetAttributeFlags($sAttCode, $aReasons);
|
||||
if ( (($iFlags & OPT_ATT_READONLY) == OPT_ATT_READONLY) && ( $oTargetObj->Get($sAttCode) != $aRowData[$iCol]) )
|
||||
{
|
||||
$aErrors[$sAttCode] = "the attribute '$sAttCode' is read-only and cannot be modified (current value: ".$oTargetObj->Get($sAttCode).", proposed value: {$aRowData[$iCol]}).";
|
||||
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-Readonly', $sAttCode, $oTargetObj->Get($sAttCode), $aRowData[$iCol]);
|
||||
}
|
||||
else if ($oAttDef->IsLinkSet() && $oAttDef->IsIndirect())
|
||||
{
|
||||
try
|
||||
{
|
||||
$oSet = $oAttDef->MakeValueFromString($aRowData[$iCol]);
|
||||
$oSet = $oAttDef->MakeValueFromString($aRowData[$iCol], $this->m_bLocalizedValues);
|
||||
$oTargetObj->Set($sAttCode, $oSet);
|
||||
}
|
||||
catch(CoreException $e)
|
||||
{
|
||||
$aErrors[$sAttCode] = "Failed to process input: ".$e->getMessage();
|
||||
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-Format', $e->getMessage());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$res = $oTargetObj->CheckValue($sAttCode, $aRowData[$iCol]);
|
||||
if ($res === true)
|
||||
$value = $oAttDef->MakeValueFromString($aRowData[$iCol], $this->m_bLocalizedValues);
|
||||
if (is_null($value) && (strlen($aRowData[$iCol]) > 0))
|
||||
{
|
||||
$oTargetObj->Set($sAttCode, $aRowData[$iCol]);
|
||||
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-NoMatch', $sAttCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
// $res is a string with the error description
|
||||
$aErrors[$sAttCode] = "Unexpected value for attribute '$sAttCode': $res";
|
||||
$res = $oTargetObj->CheckValue($sAttCode, $value);
|
||||
if ($res === true)
|
||||
{
|
||||
$oTargetObj->Set($sAttCode, $value);
|
||||
}
|
||||
else
|
||||
{
|
||||
// $res is a string with the error description
|
||||
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-Unknown', $sAttCode, $res);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -447,17 +526,19 @@ class BulkChange
|
||||
{
|
||||
if ($this->m_bReportHtml)
|
||||
{
|
||||
$sCurValue = $oTargetObj->GetAsHTML($sAttCode);
|
||||
$sOrigValue = $oTargetObj->GetOriginalAsHTML($sAttCode);
|
||||
$sCurValue = $oTargetObj->GetAsHTML($sAttCode, $this->m_bLocalizedValues);
|
||||
$sOrigValue = $oTargetObj->GetOriginalAsHTML($sAttCode, $this->m_bLocalizedValues);
|
||||
$sInput = htmlentities($aRowData[$iCol], ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
else
|
||||
{
|
||||
$sCurValue = $oTargetObj->GetAsCSV($sAttCode, $this->m_sReportCsvSep, $this->m_sReportCsvDelimiter);
|
||||
$sOrigValue = $oTargetObj->GetOriginalAsCSV($sAttCode, $this->m_sReportCsvSep, $this->m_sReportCsvDelimiter);
|
||||
$sCurValue = $oTargetObj->GetAsCSV($sAttCode, $this->m_sReportCsvSep, $this->m_sReportCsvDelimiter, $this->m_bLocalizedValues);
|
||||
$sOrigValue = $oTargetObj->GetOriginalAsCSV($sAttCode, $this->m_sReportCsvSep, $this->m_sReportCsvDelimiter, $this->m_bLocalizedValues);
|
||||
$sInput = $aRowData[$iCol];
|
||||
}
|
||||
if (isset($aErrors[$sAttCode]))
|
||||
{
|
||||
$aResults[$iCol]= new CellStatus_Issue($sCurValue, $sOrigValue, $aErrors[$sAttCode]);
|
||||
$aResults[$iCol]= new CellStatus_Issue($aRowData[$iCol], $sOrigValue, $aErrors[$sAttCode]);
|
||||
}
|
||||
elseif (array_key_exists($sAttCode, $aChangedFields))
|
||||
{
|
||||
@@ -484,7 +565,7 @@ class BulkChange
|
||||
if ($res !== true)
|
||||
{
|
||||
// $res contains the error description
|
||||
$aErrors["GLOBAL"] = "Attributes not consistent with each others: $res";
|
||||
$aErrors["GLOBAL"] = Dict::Format('UI:CSVReport-Row-Issue-Inconsistent', $res);
|
||||
}
|
||||
return $aResults;
|
||||
}
|
||||
@@ -548,7 +629,7 @@ class BulkChange
|
||||
if ($res !== true)
|
||||
{
|
||||
// $res contains the error description
|
||||
$aErrors["GLOBAL"] = "Attributes not consistent with each others: $res";
|
||||
$aErrors["GLOBAL"] = Dict::Format('UI:CSVReport-Row-Issue-Inconsistent', $res);
|
||||
}
|
||||
return $aResults;
|
||||
}
|
||||
@@ -562,7 +643,7 @@ class BulkChange
|
||||
if (count($aErrors) > 0)
|
||||
{
|
||||
$sErrors = implode(', ', $aErrors);
|
||||
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue("Unexpected attribute value(s)");
|
||||
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-Attribute'));
|
||||
return $oTargetObj;
|
||||
}
|
||||
|
||||
@@ -581,7 +662,7 @@ class BulkChange
|
||||
if (count($aMissingKeys) > 0)
|
||||
{
|
||||
$sMissingKeys = implode(', ', $aMissingKeys);
|
||||
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue("Could not be created, due to missing external key(s): $sMissingKeys");
|
||||
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue(Dict::Format('UI:CSVReport-Row-Issue-MissingExtKey', $sMissingKeys));
|
||||
return $oTargetObj;
|
||||
}
|
||||
|
||||
@@ -615,7 +696,7 @@ class BulkChange
|
||||
if (count($aErrors) > 0)
|
||||
{
|
||||
$sErrors = implode(', ', $aErrors);
|
||||
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue("Unexpected attribute value(s)");
|
||||
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-Attribute'));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -656,7 +737,7 @@ class BulkChange
|
||||
if (count($aErrors) > 0)
|
||||
{
|
||||
$sErrors = implode(', ', $aErrors);
|
||||
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue("Unexpected attribute value(s)");
|
||||
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-Attribute'));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -732,8 +813,8 @@ class BulkChange
|
||||
else
|
||||
{
|
||||
// Leave the cell unchanged
|
||||
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue("wrong date format");
|
||||
$aResult[$iRow][$sAttCode] = new CellStatus_Issue(null, $this->m_aData[$iRow][$iCol], 'Wrong date format');
|
||||
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-DateFormat'));
|
||||
$aResult[$iRow][$sAttCode] = new CellStatus_Issue(null, $this->m_aData[$iRow][$iCol], Dict::S('UI:CSVReport-Row-Issue-DateFormat'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -746,98 +827,116 @@ class BulkChange
|
||||
{
|
||||
$aVisited = array();
|
||||
}
|
||||
$iPreviousTimeLimit = ini_get('max_execution_time');
|
||||
$iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop');
|
||||
foreach($this->m_aData as $iRow => $aRowData)
|
||||
{
|
||||
set_time_limit($iLoopTimeLimit);
|
||||
if (isset($aResult[$iRow]["__STATUS__"]))
|
||||
{
|
||||
// An issue at the earlier steps - skip the rest
|
||||
continue;
|
||||
}
|
||||
$oReconciliationFilter = new CMDBSearchFilter($this->m_sClass);
|
||||
$bSkipQuery = false;
|
||||
foreach($this->m_aReconcilKeys as $sAttCode)
|
||||
try
|
||||
{
|
||||
$valuecondition = null;
|
||||
if (array_key_exists($sAttCode, $this->m_aExtKeys))
|
||||
$oReconciliationFilter = new CMDBSearchFilter($this->m_sClass);
|
||||
$bSkipQuery = false;
|
||||
foreach($this->m_aReconcilKeys as $sAttCode)
|
||||
{
|
||||
if ($this->IsNullExternalKeySpec($aRowData, $sAttCode))
|
||||
$valuecondition = null;
|
||||
if (array_key_exists($sAttCode, $this->m_aExtKeys))
|
||||
{
|
||||
$oExtKey = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
|
||||
if ($oExtKey->IsNullAllowed())
|
||||
if ($this->IsNullExternalKeySpec($aRowData, $sAttCode))
|
||||
{
|
||||
$valuecondition = $oExtKey->GetNullValue();
|
||||
$aResult[$iRow][$sAttCode] = new CellStatus_Void($oExtKey->GetNullValue());
|
||||
$oExtKey = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
|
||||
if ($oExtKey->IsNullAllowed())
|
||||
{
|
||||
$valuecondition = $oExtKey->GetNullValue();
|
||||
$aResult[$iRow][$sAttCode] = new CellStatus_Void($oExtKey->GetNullValue());
|
||||
}
|
||||
else
|
||||
{
|
||||
$aResult[$iRow][$sAttCode] = new CellStatus_NullIssue();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$aResult[$iRow][$sAttCode] = new CellStatus_NullIssue();
|
||||
}
|
||||
// The value has to be found or verified
|
||||
list($sQuery, $aMatches) = $this->ResolveExternalKey($aRowData, $sAttCode, $aResult[$iRow]);
|
||||
|
||||
if (count($aMatches) == 1)
|
||||
{
|
||||
$oRemoteObj = reset($aMatches); // first item
|
||||
$valuecondition = $oRemoteObj->GetKey();
|
||||
$aResult[$iRow][$sAttCode] = new CellStatus_Void($oRemoteObj->GetKey());
|
||||
}
|
||||
elseif (count($aMatches) == 0)
|
||||
{
|
||||
$aResult[$iRow][$sAttCode] = new CellStatus_SearchIssue();
|
||||
}
|
||||
else
|
||||
{
|
||||
$aResult[$iRow][$sAttCode] = new CellStatus_Ambiguous(null, count($aMatches), $sQuery);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The value has to be found or verified
|
||||
list($sQuery, $aMatches) = $this->ResolveExternalKey($aRowData, $sAttCode, $aResult[$iRow]);
|
||||
|
||||
if (count($aMatches) == 1)
|
||||
// The value is given in the data row
|
||||
$iCol = $this->m_aAttList[$sAttCode];
|
||||
if ($sAttCode == 'id')
|
||||
{
|
||||
$oRemoteObj = reset($aMatches); // first item
|
||||
$valuecondition = $oRemoteObj->GetKey();
|
||||
$aResult[$iRow][$sAttCode] = new CellStatus_Void($oRemoteObj->GetKey());
|
||||
}
|
||||
elseif (count($aMatches) == 0)
|
||||
{
|
||||
$aResult[$iRow][$sAttCode] = new CellStatus_SearchIssue();
|
||||
}
|
||||
$valuecondition = $aRowData[$iCol];
|
||||
}
|
||||
else
|
||||
{
|
||||
$aResult[$iRow][$sAttCode] = new CellStatus_Ambiguous(null, count($aMatches), $sQuery);
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
|
||||
$valuecondition = $oAttDef->MakeValueFromString($aRowData[$iCol], $this->m_bLocalizedValues);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The value is given in the data row
|
||||
$iCol = $this->m_aAttList[$sAttCode];
|
||||
$valuecondition = $aRowData[$iCol];
|
||||
}
|
||||
if (is_null($valuecondition))
|
||||
{
|
||||
$bSkipQuery = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oReconciliationFilter->AddCondition($sAttCode, $valuecondition, '=');
|
||||
}
|
||||
}
|
||||
if ($bSkipQuery)
|
||||
{
|
||||
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue("failed to reconcile");
|
||||
}
|
||||
else
|
||||
{
|
||||
$oReconciliationSet = new CMDBObjectSet($oReconciliationFilter);
|
||||
switch($oReconciliationSet->Count())
|
||||
{
|
||||
case 0:
|
||||
$oTargetObj = $this->CreateObject($aResult, $iRow, $aRowData, $oChange);
|
||||
// $aResult[$iRow]["__STATUS__"]=> set in CreateObject
|
||||
$aVisited[] = $oTargetObj->GetKey();
|
||||
break;
|
||||
case 1:
|
||||
$oTargetObj = $oReconciliationSet->Fetch();
|
||||
$this->UpdateObject($aResult, $iRow, $oTargetObj, $aRowData, $oChange);
|
||||
// $aResult[$iRow]["__STATUS__"]=> set in UpdateObject
|
||||
if (!is_null($this->m_sSynchroScope))
|
||||
{
|
||||
$aVisited[] = $oTargetObj->GetKey();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Found several matches, ambiguous
|
||||
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue("ambiguous reconciliation");
|
||||
$aResult[$iRow]["id"]= new CellStatus_Ambiguous(0, $oReconciliationSet->Count(), $oReconciliationFilter->ToOql());
|
||||
$aResult[$iRow]["finalclass"]= 'n/a';
|
||||
if (is_null($valuecondition))
|
||||
{
|
||||
$bSkipQuery = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oReconciliationFilter->AddCondition($sAttCode, $valuecondition, '=');
|
||||
}
|
||||
}
|
||||
if ($bSkipQuery)
|
||||
{
|
||||
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-Reconciliation'));
|
||||
}
|
||||
else
|
||||
{
|
||||
$oReconciliationSet = new CMDBObjectSet($oReconciliationFilter);
|
||||
switch($oReconciliationSet->Count())
|
||||
{
|
||||
case 0:
|
||||
$oTargetObj = $this->CreateObject($aResult, $iRow, $aRowData, $oChange);
|
||||
// $aResult[$iRow]["__STATUS__"]=> set in CreateObject
|
||||
$aVisited[] = $oTargetObj->GetKey();
|
||||
break;
|
||||
case 1:
|
||||
$oTargetObj = $oReconciliationSet->Fetch();
|
||||
$this->UpdateObject($aResult, $iRow, $oTargetObj, $aRowData, $oChange);
|
||||
// $aResult[$iRow]["__STATUS__"]=> set in UpdateObject
|
||||
if (!is_null($this->m_sSynchroScope))
|
||||
{
|
||||
$aVisited[] = $oTargetObj->GetKey();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Found several matches, ambiguous
|
||||
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-Ambiguous'));
|
||||
$aResult[$iRow]["id"]= new CellStatus_Ambiguous(0, $oReconciliationSet->Count(), $oReconciliationFilter->ToOql());
|
||||
$aResult[$iRow]["finalclass"]= 'n/a';
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue(Dict::Format('UI:CSVReport-Row-Issue-Internal', get_class($e), $e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -851,11 +950,13 @@ class BulkChange
|
||||
$iObj = $oObj->GetKey();
|
||||
if (!in_array($iObj, $aVisited))
|
||||
{
|
||||
set_time_limit($iLoopTimeLimit);
|
||||
$iRow++;
|
||||
$this->UpdateMissingObject($aResult, $iRow, $oObj, $oChange);
|
||||
}
|
||||
}
|
||||
}
|
||||
set_time_limit($iPreviousTimeLimit);
|
||||
|
||||
// Fill in the blanks - the result matrix is expected to be 100% complete
|
||||
//
|
||||
@@ -899,17 +1000,17 @@ class BulkChange
|
||||
$oPage->add('<div id="'.$sAjaxDivId.'">');
|
||||
}
|
||||
|
||||
$oPage->p(Dict::S('UI:History:BulkImports+'));
|
||||
$oPage->p(Dict::S('UI:History:BulkImports+').' <span id="csv_history_reload"></span>');
|
||||
|
||||
$oBulkChangeSearch = DBObjectSearch::FromOQL("SELECT CMDBChange WHERE userinfo LIKE '%(CSV)'");
|
||||
$oBulkChangeSearch = DBObjectSearch::FromOQL("SELECT CMDBChange WHERE origin IN ('csv-interactive', 'csv-import.php')");
|
||||
|
||||
$iQueryLimit = $bShowAll ? 0 : MetaModel::GetConfig()->GetMaxDisplayLimit() + 1;
|
||||
$iQueryLimit = $bShowAll ? 0 : appUserPreferences::GetPref('default_page_size', MetaModel::GetConfig()->GetMinDisplayLimit());
|
||||
$oBulkChanges = new DBObjectSet($oBulkChangeSearch, array('date' => false), array(), null, $iQueryLimit);
|
||||
|
||||
$oAppContext = new ApplicationContext();
|
||||
|
||||
$bLimitExceeded = false;
|
||||
if ($oBulkChanges->Count() > MetaModel::GetConfig()->GetMaxDisplayLimit())
|
||||
if ($oBulkChanges->Count() > (appUserPreferences::GetPref('default_page_size', MetaModel::GetConfig()->GetMinDisplayLimit())))
|
||||
{
|
||||
$bLimitExceeded = true;
|
||||
if (!$bShowAll)
|
||||
@@ -967,7 +1068,6 @@ class BulkChange
|
||||
{
|
||||
$aDetails[] = array('date' => $sDate, 'user' => $sUser, 'class' => $sClass, 'created' => $iCreated, 'modified' => $iModified);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$aConfig = array( 'date' => array('label' => Dict::S('UI:History:Date'), 'description' => Dict::S('UI:History:Date+')),
|
||||
@@ -987,7 +1087,7 @@ class BulkChange
|
||||
else
|
||||
{
|
||||
// Truncated list
|
||||
$iMinDisplayLimit = MetaModel::GetConfig()->GetMinDisplayLimit();
|
||||
$iMinDisplayLimit = appUserPreferences::GetPref('default_page_size', MetaModel::GetConfig()->GetMinDisplayLimit());
|
||||
$sCollapsedLabel = Dict::Format('UI:TruncatedResults', $iMinDisplayLimit, $oBulkChanges->Count());
|
||||
$sLinkLabel = Dict::S('UI:DisplayAll');
|
||||
$oPage->add('<p>'.$sCollapsedLabel.' <a class="truncated" onclick="OnTruncatedHistoryToggle(true);">'.$sLinkLabel.'</p>');
|
||||
@@ -1005,6 +1105,7 @@ EOF
|
||||
<<<EOF
|
||||
function OnTruncatedHistoryToggle(bShowAll)
|
||||
{
|
||||
$('#csv_history_reload').html('<img src="../images/indicator.gif"/>');
|
||||
$.get(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php?{$sAppContext}', {operation: 'displayCSVHistory', showall: bShowAll}, function(data)
|
||||
{
|
||||
$('#$sAjaxDivId').html(data);
|
||||
@@ -1166,49 +1267,6 @@ EOF
|
||||
}
|
||||
$oPage->table($aConfig, $aDetails);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user friendly name for an 'extended' attribute code i.e 'name', becomes 'Name' and 'org_id->name' becomes 'Organization->Name'
|
||||
* @param string $sClassName The name of the class
|
||||
* @param string $sAttCodeEx Either an attribute code or ext_key_name->att_code
|
||||
* @return string A user friendly format of the string: AttributeName or AttributeName->ExtAttributeName
|
||||
*/
|
||||
public static function GetFriendlyAttCodeName($sClassName, $sAttCodeEx)
|
||||
{
|
||||
$sFriendlyName = '';
|
||||
if (preg_match('/(.+)->(.+)/', $sAttCodeEx, $aMatches) > 0)
|
||||
{
|
||||
$sAttribute = $aMatches[1];
|
||||
$sField = $aMatches[2];
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClassName, $sAttribute);
|
||||
if ($oAttDef->IsExternalKey())
|
||||
{
|
||||
$sTargetClass = $oAttDef->GetTargetClass();
|
||||
$oTargetAttDef = MetaModel::GetAttributeDef($sTargetClass, $sField);
|
||||
$sFriendlyName = $oAttDef->GetLabel().'->'.$oTargetAttDef->GetLabel();
|
||||
}
|
||||
else
|
||||
{
|
||||
// hum, hum... should never happen, we'd better raise an exception
|
||||
throw(new Exception(Dict::Format('UI:CSVImport:ErrorExtendedAttCode', $sAttCodeEx, $sAttribute, $sClassName)));
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($sAttCodeEx == 'id')
|
||||
{
|
||||
$sFriendlyName = Dict::S('UI:CSVImport:idField');
|
||||
}
|
||||
else
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClassName, $sAttCodeEx);
|
||||
$sFriendlyName = $oAttDef->GetLabel();
|
||||
}
|
||||
}
|
||||
return $sFriendlyName;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Persistent class (internal) cmdbChange
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
@@ -43,11 +44,15 @@ class CMDBChange extends DBObject
|
||||
"db_table" => "priv_change",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
'indexes' => array(
|
||||
array('origin'),
|
||||
)
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeDateTime("date", array("allowed_values"=>null, "sql"=>"date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("userinfo", array("allowed_values"=>null, "sql"=>"userinfo", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeEnum("origin", array("allowed_values"=>new ValueSetEnum('interactive,csv-interactive,csv-import.php,webservice-soap,webservice-rest,synchro-data-source,email-processing,custom-extension'), "sql"=>"origin", "default_value"=>"interactive", "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
}
|
||||
|
||||
// Helper to keep track of the author of a given change,
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Persistent classes (internal) : cmdbChangeOp and derived
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
@@ -44,6 +45,9 @@ class CMDBChangeOp extends DBObject
|
||||
"db_table" => "priv_changeop",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "optype",
|
||||
'indexes' => array(
|
||||
array('objclass', 'objkey'),
|
||||
)
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
@@ -64,6 +68,19 @@ class CMDBChangeOp extends DBObject
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Safety net: in case the change is not given, let's guarantee that it will
|
||||
* be set to the current ongoing change (or create a new one)
|
||||
*/
|
||||
protected function OnInsert()
|
||||
{
|
||||
if ($this->Get('change') <= 0)
|
||||
{
|
||||
$this->Set('change', CMDBObject::GetCurrentChange());
|
||||
}
|
||||
parent::OnInsert();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -124,6 +141,11 @@ class CMDBChangeOpDelete extends CMDBChangeOp
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
|
||||
// Final class of the object (objclass must be set to the root class for efficiency purposes)
|
||||
MetaModel::Init_AddAttribute(new AttributeString("fclass", array("allowed_values"=>null, "sql"=>"fclass", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
// Last friendly name of the object
|
||||
MetaModel::Init_AddAttribute(new AttributeString("fname", array("allowed_values"=>null, "sql"=>"fname", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
}
|
||||
/**
|
||||
* Describe (as a text string) the modifications corresponding to this change
|
||||
@@ -267,8 +289,16 @@ class CMDBChangeOpSetAttributeBlob extends CMDBChangeOpSetAttribute
|
||||
$oMonoObjectSet = new DBObjectSet($oTargetSearch);
|
||||
if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
|
||||
$sAttName = $oAttDef->GetLabel();
|
||||
if (MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode')))
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
|
||||
$sAttName = $oAttDef->GetLabel();
|
||||
}
|
||||
else
|
||||
{
|
||||
// The attribute was renamed or removed from the object ?
|
||||
$sAttName = $this->Get('attcode');
|
||||
}
|
||||
$oPrevDoc = $this->Get('prevdata');
|
||||
$sDocView = $oPrevDoc->GetAsHtml();
|
||||
$sDocView .= "<br/>".Dict::Format('UI:OpenDocumentInNewWindow_',$oPrevDoc->GetDisplayLink(get_class($this), $this->GetKey(), 'prevdata')).", \n";
|
||||
@@ -323,8 +353,16 @@ class CMDBChangeOpSetAttributeOneWayPassword extends CMDBChangeOpSetAttribute
|
||||
$oMonoObjectSet = new DBObjectSet($oTargetSearch);
|
||||
if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
|
||||
$sAttName = $oAttDef->GetLabel();
|
||||
if (MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode')))
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
|
||||
$sAttName = $oAttDef->GetLabel();
|
||||
}
|
||||
else
|
||||
{
|
||||
// The attribute was renamed or removed from the object ?
|
||||
$sAttName = $this->Get('attcode');
|
||||
}
|
||||
$sResult = Dict::Format('Change:AttName_Changed', $sAttName);
|
||||
}
|
||||
return $sResult;
|
||||
@@ -375,8 +413,16 @@ class CMDBChangeOpSetAttributeEncrypted extends CMDBChangeOpSetAttribute
|
||||
$oMonoObjectSet = new DBObjectSet($oTargetSearch);
|
||||
if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
|
||||
$sAttName = $oAttDef->GetLabel();
|
||||
if (MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode')))
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
|
||||
$sAttName = $oAttDef->GetLabel();
|
||||
}
|
||||
else
|
||||
{
|
||||
// The attribute was renamed or removed from the object ?
|
||||
$sAttName = $this->Get('attcode');
|
||||
}
|
||||
$sPrevString = $this->Get('prevstring');
|
||||
$sResult = Dict::Format('Change:AttName_Changed_PreviousValue_OldValue', $sAttName, $sPrevString);
|
||||
}
|
||||
@@ -430,8 +476,81 @@ class CMDBChangeOpSetAttributeText extends CMDBChangeOpSetAttribute
|
||||
$oMonoObjectSet = new DBObjectSet($oTargetSearch);
|
||||
if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
|
||||
$sAttName = $oAttDef->GetLabel();
|
||||
if (MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode')))
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
|
||||
$sAttName = $oAttDef->GetLabel();
|
||||
}
|
||||
else
|
||||
{
|
||||
// The attribute was renamed or removed from the object ?
|
||||
$sAttName = $this->Get('attcode');
|
||||
}
|
||||
$sTextView = '<div>'.$this->GetAsHtml('prevdata').'</div>';
|
||||
|
||||
//$sDocView = $oPrevDoc->GetDisplayInline(get_class($this), $this->GetKey(), 'prevdata');
|
||||
$sResult = Dict::Format('Change:AttName_Changed_PreviousValue_OldValue', $sAttName, $sTextView);
|
||||
}
|
||||
return $sResult;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Record the modification of a multiline string (text)
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class CMDBChangeOpSetAttributeLongText extends CMDBChangeOpSetAttribute
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_setatt_longtext",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeLongText("prevdata", array("allowed_values"=>null, "sql"=>"prevdata", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'attcode')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'attcode')); // Attributes to be displayed for a list
|
||||
}
|
||||
|
||||
/**
|
||||
* Describe (as a text string) the modifications corresponding to this change
|
||||
*/
|
||||
public function GetDescription()
|
||||
{
|
||||
// Temporary, until we change the options of GetDescription() -needs a more global revision
|
||||
$bIsHtml = true;
|
||||
|
||||
$sResult = '';
|
||||
$oTargetObjectClass = $this->Get('objclass');
|
||||
$oTargetObjectKey = $this->Get('objkey');
|
||||
$oTargetSearch = new DBObjectSearch($oTargetObjectClass);
|
||||
$oTargetSearch->AddCondition('id', $oTargetObjectKey, '=');
|
||||
|
||||
$oMonoObjectSet = new DBObjectSet($oTargetSearch);
|
||||
if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES)
|
||||
{
|
||||
if (MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode')))
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
|
||||
$sAttName = $oAttDef->GetLabel();
|
||||
}
|
||||
else
|
||||
{
|
||||
// The attribute was renamed or removed from the object ?
|
||||
$sAttName = $this->Get('attcode');
|
||||
}
|
||||
$sTextView = '<div>'.$this->GetAsHtml('prevdata').'</div>';
|
||||
|
||||
//$sDocView = $oPrevDoc->GetDisplayInline(get_class($this), $this->GetKey(), 'prevdata');
|
||||
@@ -490,8 +609,16 @@ class CMDBChangeOpSetAttributeCaseLog extends CMDBChangeOpSetAttribute
|
||||
$oMonoObjectSet = new DBObjectSet($oTargetSearch);
|
||||
if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
|
||||
$sAttName = $oAttDef->GetLabel();
|
||||
if (MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode')))
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
|
||||
$sAttName = $oAttDef->GetLabel();
|
||||
}
|
||||
else
|
||||
{
|
||||
// The attribute was renamed or removed from the object ?
|
||||
$sAttName = $this->Get('attcode');
|
||||
}
|
||||
$sResult = Dict::Format('Change:AttName_EntryAdded', $sAttName);
|
||||
}
|
||||
return $sResult;
|
||||
@@ -535,4 +662,185 @@ class CMDBChangeOpPlugin extends CMDBChangeOp
|
||||
return $this->Get('description');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Record added/removed objects from within a link set
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
abstract class CMDBChangeOpSetAttributeLinks extends CMDBChangeOpSetAttribute
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_links",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
|
||||
// Note: item class/id points to the link class itself in case of a direct link set (e.g. Server::interface_list => Interface)
|
||||
// item class/id points to the remote class in case of a indirect link set (e.g. Server::contract_list => Contract)
|
||||
MetaModel::Init_AddAttribute(new AttributeString("item_class", array("allowed_values"=>null, "sql"=>"item_class", "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeInteger("item_id", array("allowed_values"=>null, "sql"=>"item_id", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Record added/removed objects from within a link set
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class CMDBChangeOpSetAttributeLinksAddRemove extends CMDBChangeOpSetAttributeLinks
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_links_addremove",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeEnum("type", array("allowed_values"=>new ValueSetEnum('added,removed'), "sql"=>"type", "default_value"=>"added", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Describe (as a text string) the modifications corresponding to this change
|
||||
*/
|
||||
public function GetDescription()
|
||||
{
|
||||
$sResult = '';
|
||||
$oTargetObjectClass = $this->Get('objclass');
|
||||
$oTargetObjectKey = $this->Get('objkey');
|
||||
$oTargetSearch = new DBObjectSearch($oTargetObjectClass);
|
||||
$oTargetSearch->AddCondition('id', $oTargetObjectKey, '=');
|
||||
|
||||
$oMonoObjectSet = new DBObjectSet($oTargetSearch);
|
||||
if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES)
|
||||
{
|
||||
if (!MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode'))) return ''; // Protects against renamed attributes...
|
||||
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
|
||||
$sAttName = $oAttDef->GetLabel();
|
||||
|
||||
$sItemDesc = MetaModel::GetHyperLink($this->Get('item_class'), $this->Get('item_id'));
|
||||
|
||||
$sResult = $sAttName.' - ';
|
||||
switch ($this->Get('type'))
|
||||
{
|
||||
case 'added':
|
||||
$sResult .= Dict::Format('Change:LinkSet:Added', $sItemDesc);
|
||||
break;
|
||||
|
||||
case 'removed':
|
||||
$sResult .= Dict::Format('Change:LinkSet:Removed', $sItemDesc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $sResult;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Record attribute changes from within a link set
|
||||
* A single record redirects to the modifications made within the same change
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class CMDBChangeOpSetAttributeLinksTune extends CMDBChangeOpSetAttributeLinks
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_links_tune",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeInteger("link_id", array("allowed_values"=>null, "sql"=>"link_id", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Describe (as a text string) the modifications corresponding to this change
|
||||
*/
|
||||
public function GetDescription()
|
||||
{
|
||||
$sResult = '';
|
||||
$oTargetObjectClass = $this->Get('objclass');
|
||||
$oTargetObjectKey = $this->Get('objkey');
|
||||
$oTargetSearch = new DBObjectSearch($oTargetObjectClass);
|
||||
$oTargetSearch->AddCondition('id', $oTargetObjectKey, '=');
|
||||
|
||||
$oMonoObjectSet = new DBObjectSet($oTargetSearch);
|
||||
if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES)
|
||||
{
|
||||
if (!MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode'))) return ''; // Protects against renamed attributes...
|
||||
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
|
||||
$sAttName = $oAttDef->GetLabel();
|
||||
|
||||
$sLinkClass = $oAttDef->GetLinkedClass();
|
||||
$aLinkClasses = MetaModel::EnumChildClasses($sLinkClass, ENUM_CHILD_CLASSES_ALL);
|
||||
|
||||
// Search for changes on the corresponding link
|
||||
//
|
||||
$oSearch = new DBObjectSearch('CMDBChangeOpSetAttribute');
|
||||
$oSearch->AddCondition('change', $this->Get('change'), '=');
|
||||
$oSearch->AddCondition('objkey', $this->Get('link_id'), '=');
|
||||
if (count($aLinkClasses) == 1)
|
||||
{
|
||||
// Faster than the whole building of the expression below for just one value ??
|
||||
$oSearch->AddCondition('objclass', $sLinkClass, '=');
|
||||
}
|
||||
else
|
||||
{
|
||||
$oField = new FieldExpression('objclass', $oSearch->GetClassAlias());
|
||||
$sListExpr = '('.implode(', ', CMDBSource::Quote($aLinkClasses)).')';
|
||||
$sOQLCondition = $oField->Render()." IN $sListExpr";
|
||||
$oNewCondition = Expression::FromOQL($sOQLCondition);
|
||||
$oSearch->AddConditionExpression($oNewCondition);
|
||||
}
|
||||
$oSet = new DBObjectSet($oSearch);
|
||||
$aChanges = array();
|
||||
while ($oChangeOp = $oSet->Fetch())
|
||||
{
|
||||
$aChanges[] = $oChangeOp->GetDescription();
|
||||
}
|
||||
if (count($aChanges) == 0)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
$sItemDesc = MetaModel::GetHyperLink($this->Get('item_class'), $this->Get('item_id'));
|
||||
|
||||
$sResult = $sAttName.' - ';
|
||||
$sResult .= Dict::Format('Change:LinkSet:Modified', $sItemDesc);
|
||||
$sResult .= ' : '.implode(', ', $aChanges);
|
||||
}
|
||||
return $sResult;
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// 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 cmdbObject
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
@@ -91,8 +92,12 @@ abstract class CMDBObject extends DBObject
|
||||
protected $m_datUpdated;
|
||||
// Note: this value is static, but that could be changed because it is sometimes a real issue (see update of interfaces / connected_to
|
||||
protected static $m_oCurrChange = null;
|
||||
|
||||
|
||||
protected static $m_sInfo = null; // null => the information is built in a standard way
|
||||
protected static $m_sOrigin = null; // null => the origin is 'interactive'
|
||||
|
||||
/**
|
||||
* Specify another change (this is mainly for backward compatibility)
|
||||
*/
|
||||
public static function SetCurrentChange(CMDBChange $oChange)
|
||||
{
|
||||
self::$m_oCurrChange = $oChange;
|
||||
@@ -100,41 +105,142 @@ abstract class CMDBObject extends DBObject
|
||||
|
||||
//
|
||||
// Todo: simplify the APIs and do not pass the current change as an argument anymore
|
||||
// SetCurrentChange to be invoked in very few cases (UI.php, CSV import, Data synchro)
|
||||
// SetTrackInfo to be invoked in very few cases (UI.php, CSV import, Data synchro)
|
||||
// SetCurrentChange is an alternative to SetTrackInfo (csv ?)
|
||||
// GetCurrentChange to be called ONCE (!) by CMDBChangeOp::OnInsert ($this->Set('change', ..GetCurrentChange())
|
||||
// GetCurrentChange to create a default change if not already done in the current context
|
||||
//
|
||||
public static function GetCurrentChange()
|
||||
/**
|
||||
* Get a change record (create it if not existing)
|
||||
*/
|
||||
public static function GetCurrentChange($bAutoCreate = true)
|
||||
{
|
||||
if ($bAutoCreate && is_null(self::$m_oCurrChange))
|
||||
{
|
||||
self::CreateChange();
|
||||
}
|
||||
return self::$m_oCurrChange;
|
||||
}
|
||||
|
||||
|
||||
private function RecordObjCreation(CMDBChange $oChange)
|
||||
/**
|
||||
* Override the additional information (defaulting to user name)
|
||||
* A call to this verb should replace every occurence of
|
||||
* $oMyChange = MetaModel::NewObject("CMDBChange");
|
||||
* $oMyChange->Set("date", time());
|
||||
* $oMyChange->Set("userinfo", 'this is done by ... for ...');
|
||||
* $iChangeId = $oMyChange->DBInsert();
|
||||
*/
|
||||
public static function SetTrackInfo($sInfo)
|
||||
{
|
||||
self::$m_sInfo = $sInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides information about the origin of the change
|
||||
* @param $sOrigin String: one of: interactive, csv-interactive, csv-import.php, webservice-soap, webservice-rest, syncho-data-source, email-processing, custom-extension
|
||||
*/
|
||||
public static function SetTrackOrigin($sOrigin)
|
||||
{
|
||||
self::$m_sOrigin = $sOrigin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the additional information (defaulting to user name)
|
||||
*/
|
||||
protected static function GetTrackInfo()
|
||||
{
|
||||
if (is_null(self::$m_sInfo))
|
||||
{
|
||||
return CMDBChange::GetCurrentUserName();
|
||||
}
|
||||
else
|
||||
{
|
||||
return self::$m_sInfo;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the 'origin' information (defaulting to 'interactive')
|
||||
*/
|
||||
protected static function GetTrackOrigin()
|
||||
{
|
||||
if (is_null(self::$m_sOrigin))
|
||||
{
|
||||
return 'interactive';
|
||||
}
|
||||
else
|
||||
{
|
||||
return self::$m_sOrigin;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a standard change record (done here 99% of the time, and nearly once per page)
|
||||
*/
|
||||
protected static function CreateChange()
|
||||
{
|
||||
self::$m_oCurrChange = MetaModel::NewObject("CMDBChange");
|
||||
self::$m_oCurrChange->Set("date", time());
|
||||
self::$m_oCurrChange->Set("userinfo", self::GetTrackInfo());
|
||||
self::$m_oCurrChange->Set("origin", self::GetTrackOrigin());
|
||||
self::$m_oCurrChange->DBInsert();
|
||||
}
|
||||
|
||||
protected function RecordObjCreation()
|
||||
{
|
||||
// Delete any existing change tracking about the current object (IDs can be reused due to InnoDb bug; see TRAC #886)
|
||||
//
|
||||
// 1 - remove the deletion record(s)
|
||||
// Note that objclass contain the ROOT class
|
||||
$oFilter = new DBObjectSearch('CMDBChangeOpDelete');
|
||||
$oFilter->AddCondition('objclass', MetaModel::GetRootClass(get_class($this)), '=');
|
||||
$oFilter->AddCondition('objkey', $this->GetKey(), '=');
|
||||
MetaModel::PurgeData($oFilter);
|
||||
// 2 - any other change tracking information left prior to 2.0.3 (when the purge of the history has been implemented in RecordObjDeletion
|
||||
// In that case, objclass is the final class of the object
|
||||
$oFilter = new DBObjectSearch('CMDBChangeOp');
|
||||
$oFilter->AddCondition('objclass', get_class($this), '=');
|
||||
$oFilter->AddCondition('objkey', $this->GetKey(), '=');
|
||||
MetaModel::PurgeData($oFilter);
|
||||
|
||||
parent::RecordObjCreation();
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpCreate");
|
||||
$oMyChangeOp->Set("change", $oChange->GetKey());
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
private function RecordObjDeletion(CMDBChange $oChange, $objkey)
|
||||
|
||||
protected function RecordObjDeletion($objkey)
|
||||
{
|
||||
$sRootClass = MetaModel::GetRootClass(get_class($this));
|
||||
|
||||
// Delete any existing change tracking about the current object
|
||||
$oFilter = new DBObjectSearch('CMDBChangeOp');
|
||||
$oFilter->AddCondition('objclass', get_class($this), '=');
|
||||
$oFilter->AddCondition('objkey', $objkey, '=');
|
||||
MetaModel::PurgeData($oFilter);
|
||||
|
||||
parent::RecordObjDeletion($objkey);
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpDelete");
|
||||
$oMyChangeOp->Set("change", $oChange->GetKey());
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objclass", MetaModel::GetRootClass(get_class($this)));
|
||||
$oMyChangeOp->Set("objkey", $objkey);
|
||||
$oMyChangeOp->Set("fclass", get_class($this));
|
||||
$oMyChangeOp->Set("fname", substr($this->GetRawName(), 0, 255)); // Protect against very long friendly names
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
private function RecordAttChanges(CMDBChange $oChange, array $aValues, array $aOrigValues)
|
||||
|
||||
protected function RecordAttChanges(array $aValues, array $aOrigValues)
|
||||
{
|
||||
parent::RecordAttChanges($aValues, $aOrigValues);
|
||||
|
||||
// $aValues is an array of $sAttCode => $value
|
||||
//
|
||||
foreach ($aValues as $sAttCode=> $value)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
if ($oAttDef->IsExternalField()) continue; // #@# temporary
|
||||
if ($oAttDef->IsLinkSet()) continue; // #@# temporary
|
||||
if ($oAttDef->IsExternalField()) continue;
|
||||
if ($oAttDef->IsLinkSet()) continue;
|
||||
if ($oAttDef->GetTrackingLevel() == ATTRIBUTE_TRACKING_NONE) continue;
|
||||
|
||||
if (array_key_exists($sAttCode, $aOrigValues))
|
||||
{
|
||||
@@ -149,7 +255,6 @@ abstract class CMDBObject extends DBObject
|
||||
{
|
||||
// One Way encrypted passwords' history is stored -one way- encrypted
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeOneWayPassword");
|
||||
$oMyChangeOp->Set("change", $oChange->GetKey());
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
@@ -165,7 +270,6 @@ abstract class CMDBObject extends DBObject
|
||||
{
|
||||
// Encrypted string history is stored encrypted
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeEncrypted");
|
||||
$oMyChangeOp->Set("change", $oChange->GetKey());
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
@@ -181,7 +285,6 @@ abstract class CMDBObject extends DBObject
|
||||
{
|
||||
// Data blobs
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeBlob");
|
||||
$oMyChangeOp->Set("change", $oChange->GetKey());
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
@@ -209,7 +312,6 @@ abstract class CMDBObject extends DBObject
|
||||
if ($item_value != $item_original)
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
|
||||
$oMyChangeOp->Set("change", $oChange->GetKey());
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sSubItemAttCode);
|
||||
@@ -223,7 +325,6 @@ abstract class CMDBObject extends DBObject
|
||||
elseif ($oAttDef instanceOf AttributeCaseLog)
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeCaseLog");
|
||||
$oMyChangeOp->Set("change", $oChange->GetKey());
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
@@ -231,11 +332,10 @@ abstract class CMDBObject extends DBObject
|
||||
$oMyChangeOp->Set("lastentry", $value->GetLatestEntryIndex());
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeText)
|
||||
elseif ($oAttDef instanceOf AttributeLongText)
|
||||
{
|
||||
// Data blobs
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeText");
|
||||
$oMyChangeOp->Set("change", $oChange->GetKey());
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeLongText");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
@@ -247,12 +347,48 @@ abstract class CMDBObject extends DBObject
|
||||
$oMyChangeOp->Set("prevdata", $original);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeText)
|
||||
{
|
||||
// Data blobs
|
||||
$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();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Scalars
|
||||
//
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
|
||||
$oMyChangeOp->Set("change", $oChange->GetKey());
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
@@ -294,32 +430,24 @@ abstract class CMDBObject extends DBObject
|
||||
|
||||
public function DBInsert()
|
||||
{
|
||||
if(!is_object(self::$m_oCurrChange))
|
||||
{
|
||||
throw new CoreException("DBInsert() could not be used here, please use DBInsertTracked() instead");
|
||||
}
|
||||
return $this->DBInsertTracked_Internal();
|
||||
}
|
||||
|
||||
public function DBInsertTracked(CMDBChange $oChange, $bSkipStrongSecurity = null)
|
||||
{
|
||||
self::SetCurrentChange($oChange);
|
||||
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY);
|
||||
|
||||
$oPreviousChange = self::$m_oCurrChange;
|
||||
self::$m_oCurrChange = $oChange;
|
||||
$ret = $this->DBInsertTracked_Internal();
|
||||
self::$m_oCurrChange = $oPreviousChange;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function DBInsertTrackedNoReload(CMDBChange $oChange, $bSkipStrongSecurity = null)
|
||||
{
|
||||
self::SetCurrentChange($oChange);
|
||||
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY);
|
||||
|
||||
$oPreviousChange = self::$m_oCurrChange;
|
||||
self::$m_oCurrChange = $oChange;
|
||||
$ret = $this->DBInsertTracked_Internal(true);
|
||||
self::$m_oCurrChange = $oPreviousChange;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
@@ -333,25 +461,18 @@ abstract class CMDBObject extends DBObject
|
||||
{
|
||||
$ret = parent::DBInsert();
|
||||
}
|
||||
$this->RecordObjCreation(self::$m_oCurrChange);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function DBClone($newKey = null)
|
||||
{
|
||||
if(!self::$m_oCurrChange)
|
||||
{
|
||||
throw new CoreException("DBClone() could not be used here, please use DBCloneTracked() instead");
|
||||
}
|
||||
return $this->DBCloneTracked_Internal();
|
||||
}
|
||||
|
||||
public function DBCloneTracked(CMDBChange $oChange, $newKey = null)
|
||||
{
|
||||
$oPreviousChange = self::$m_oCurrChange;
|
||||
self::$m_oCurrChange = $oChange;
|
||||
self::SetCurrentChange($oChange);
|
||||
$this->DBCloneTracked_Internal($newKey);
|
||||
self::$m_oCurrChange = $oPreviousChange;
|
||||
}
|
||||
|
||||
protected function DBCloneTracked_Internal($newKey = null)
|
||||
@@ -359,128 +480,57 @@ abstract class CMDBObject extends DBObject
|
||||
$newKey = parent::DBClone($newKey);
|
||||
$oClone = MetaModel::GetObject(get_class($this), $newKey);
|
||||
|
||||
$oClone->RecordObjCreation(self::$m_oCurrChange);
|
||||
return $newKey;
|
||||
}
|
||||
|
||||
public function DBUpdate()
|
||||
{
|
||||
if(!self::$m_oCurrChange)
|
||||
{
|
||||
throw new CoreException("DBUpdate() could not be used here, please use DBUpdateTracked() instead");
|
||||
}
|
||||
return $this->DBUpdateTracked_internal();
|
||||
}
|
||||
|
||||
public function DBUpdateTracked(CMDBChange $oChange, $bSkipStrongSecurity = null)
|
||||
{
|
||||
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY);
|
||||
|
||||
$oPreviousChange = self::$m_oCurrChange;
|
||||
self::$m_oCurrChange = $oChange;
|
||||
$this->DBUpdateTracked_Internal();
|
||||
self::$m_oCurrChange = $oPreviousChange;
|
||||
}
|
||||
|
||||
protected function DBUpdateTracked_Internal()
|
||||
{
|
||||
// Copy the changes list before the update (the list should be reset afterwards)
|
||||
$aChanges = $this->ListChanges();
|
||||
if (count($aChanges) == 0)
|
||||
{
|
||||
//throw new CoreWarning("Attempting to update an unchanged object");
|
||||
return;
|
||||
}
|
||||
|
||||
// Save the original values (will be reset to the new values when the object get written to the DB)
|
||||
$aOriginalValues = $this->m_aOrigValues;
|
||||
$ret = parent::DBUpdate();
|
||||
$this->RecordAttChanges(self::$m_oCurrChange, $aChanges, $aOriginalValues);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function DBUpdateTracked(CMDBChange $oChange, $bSkipStrongSecurity = null)
|
||||
{
|
||||
self::SetCurrentChange($oChange);
|
||||
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY);
|
||||
$this->DBUpdate();
|
||||
}
|
||||
|
||||
public function DBDelete(&$oDeletionPlan = null)
|
||||
{
|
||||
if(!self::$m_oCurrChange)
|
||||
{
|
||||
throw new CoreException("DBDelete() could not be used here, please use DBDeleteTracked() instead");
|
||||
}
|
||||
return $this->DBDeleteTracked_Internal($oDeletionPlan);
|
||||
}
|
||||
|
||||
public function DBDeleteTracked(CMDBChange $oChange, $bSkipStrongSecurity = null, &$oDeletionPlan = null)
|
||||
{
|
||||
self::SetCurrentChange($oChange);
|
||||
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_DELETE);
|
||||
|
||||
$oPreviousChange = self::$m_oCurrChange;
|
||||
self::$m_oCurrChange = $oChange;
|
||||
$this->DBDeleteTracked_Internal($oDeletionPlan);
|
||||
self::$m_oCurrChange = $oPreviousChange;
|
||||
}
|
||||
|
||||
protected function DBDeleteTracked_Internal(&$oDeletionPlan = null)
|
||||
{
|
||||
$prevkey = $this->GetKey();
|
||||
$ret = parent::DBDelete($oDeletionPlan);
|
||||
$this->RecordObjDeletion(self::$m_oCurrChange, $prevkey);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public static function BulkDelete(DBObjectSearch $oFilter)
|
||||
{
|
||||
if(!self::$m_oCurrChange)
|
||||
{
|
||||
throw new CoreException("BulkDelete() could not be used here, please use BulkDeleteTracked() instead");
|
||||
}
|
||||
return $this->BulkDeleteTracked_Internal($oFilter);
|
||||
}
|
||||
|
||||
public static function BulkDeleteTracked(CMDBChange $oChange, DBObjectSearch $oFilter)
|
||||
{
|
||||
$oPreviousChange = self::$m_oCurrChange;
|
||||
self::$m_oCurrChange = $oChange;
|
||||
$this->BulkDeleteTracked_Internal($oFilter);
|
||||
self::$m_oCurrChange = $oPreviousChange;
|
||||
}
|
||||
|
||||
protected static function BulkDeleteTracked_Internal(DBObjectSearch $oFilter)
|
||||
{
|
||||
throw new CoreWarning("Change tracking not tested for bulk operations");
|
||||
|
||||
// Get the list of objects to delete (and record data before deleting the DB records)
|
||||
$oObjSet = new CMDBObjectSet($oFilter);
|
||||
$aObjAndKeys = array(); // array of id=>object
|
||||
while ($oItem = $oObjSet->Fetch())
|
||||
{
|
||||
$aObjAndKeys[$oItem->GetKey()] = $oItem;
|
||||
}
|
||||
$oObjSet->FreeResult();
|
||||
|
||||
// Delete in one single efficient query
|
||||
$ret = parent::BulkDelete($oFilter);
|
||||
// Record... in many queries !!!
|
||||
foreach($aObjAndKeys as $prevkey=>$oItem)
|
||||
{
|
||||
$oItem->RecordObjDeletion(self::$m_oCurrChange, $prevkey);
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public static function BulkUpdate(DBObjectSearch $oFilter, array $aValues)
|
||||
{
|
||||
if(!self::$m_oCurrChange)
|
||||
{
|
||||
throw new CoreException("BulkUpdate() could not be used here, please use BulkUpdateTracked() instead");
|
||||
}
|
||||
return $this->BulkUpdateTracked_Internal($oFilter, $aValues);
|
||||
}
|
||||
|
||||
public static function BulkUpdateTracked(CMDBChange $oChange, DBObjectSearch $oFilter, array $aValues)
|
||||
{
|
||||
$oPreviousChange = self::$m_oCurrChange;
|
||||
self::$m_oCurrChange = $oChange;
|
||||
self::SetCurrentChange($oChange);
|
||||
$this->BulkUpdateTracked_Internal($oFilter, $aValues);
|
||||
self::$m_oCurrChange = $oPreviousChange;
|
||||
}
|
||||
|
||||
protected static function BulkUpdateTracked_Internal(DBObjectSearch $oFilter, array $aValues)
|
||||
@@ -507,7 +557,7 @@ abstract class CMDBObject extends DBObject
|
||||
while ($oItem = $oObjSet->Fetch())
|
||||
{
|
||||
$aChangedValues = $oItem->ListChangedValues($aValues);
|
||||
$oItem->RecordAttChanges(self::$m_oCurrChange, $aChangedValues, $aOriginalValues[$oItem->GetKey()]);
|
||||
$oItem->RecordAttChanges($aChangedValues, $aOriginalValues[$oItem->GetKey()]);
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2014 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* DB Server abstraction
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
require_once('MyHelpers.class.inc.php');
|
||||
@@ -28,10 +29,18 @@ require_once(APPROOT.'core/kpi.class.inc.php');
|
||||
|
||||
class MySQLException extends CoreException
|
||||
{
|
||||
public function __construct($sIssue, $aContext)
|
||||
public function __construct($sIssue, $aContext, $oException = null)
|
||||
{
|
||||
$aContext['mysql_error'] = CMDBSource::GetError();
|
||||
$aContext['mysql_errno'] = CMDBSource::GetErrNo();;
|
||||
if ($oException != null)
|
||||
{
|
||||
$aContext['mysql_error'] = $oException->getCode();
|
||||
$aContext['mysql_errno'] = $oException->getMessage();
|
||||
}
|
||||
else
|
||||
{
|
||||
$aContext['mysql_error'] = CMDBSource::GetError();
|
||||
$aContext['mysql_errno'] = CMDBSource::GetErrNo();
|
||||
}
|
||||
parent::__construct($sIssue, $aContext);
|
||||
}
|
||||
}
|
||||
@@ -49,7 +58,7 @@ class CMDBSource
|
||||
protected static $m_sDBUser;
|
||||
protected static $m_sDBPwd;
|
||||
protected static $m_sDBName;
|
||||
protected static $m_resDBLink;
|
||||
protected static $m_oMysqli;
|
||||
|
||||
public static function Init($sServer, $sUser, $sPwd, $sSource = '')
|
||||
{
|
||||
@@ -57,15 +66,41 @@ class CMDBSource
|
||||
self::$m_sDBUser = $sUser;
|
||||
self::$m_sDBPwd = $sPwd;
|
||||
self::$m_sDBName = $sSource;
|
||||
if (!self::$m_resDBLink = @mysqli_connect($sServer, $sUser, $sPwd))
|
||||
self::$m_oMysqli = null;
|
||||
|
||||
mysqli_report(MYSQLI_REPORT_STRICT); // *some* errors (like connection errors) will throw mysqli_sql_exception instead
|
||||
// of generating warnings printed to the output but some other errors will still
|
||||
// cause the query() method to return false !!!
|
||||
try
|
||||
{
|
||||
throw new MySQLException('Could not connect to the DB server', array('host'=>$sServer, 'user'=>$sUser));
|
||||
$aConnectInfo = explode(':', self::$m_sDBHost);
|
||||
if (count($aConnectInfo) > 1)
|
||||
{
|
||||
// Override the default port
|
||||
$sServer = $aConnectInfo[0];
|
||||
$iPort = (int)$aConnectInfo[1];
|
||||
self::$m_oMysqli = new mysqli(self::$m_sDBHost, self::$m_sDBUser, self::$m_sDBPwd, '', $iPort);
|
||||
}
|
||||
else
|
||||
{
|
||||
self::$m_oMysqli = new mysqli(self::$m_sDBHost, self::$m_sDBUser, self::$m_sDBPwd);
|
||||
}
|
||||
}
|
||||
catch(mysqli_sql_exception $e)
|
||||
{
|
||||
throw new MySQLException('Could not connect to the DB server', array('host'=>self::$m_sDBHost, 'user'=>self::$m_sDBUser), $e);
|
||||
}
|
||||
|
||||
if (!empty($sSource))
|
||||
{
|
||||
if (!((bool)mysqli_query(self::$m_resDBLink, "USE $sSource")))
|
||||
try
|
||||
{
|
||||
throw new MySQLException('Could not select DB', array('host'=>$sServer, 'user'=>$sUser, 'db_name'=>$sSource));
|
||||
mysqli_report(MYSQLI_REPORT_STRICT); // Errors, in the next query, will throw mysqli_sql_exception
|
||||
self::$m_oMysqli->query("USE `$sSource`");
|
||||
}
|
||||
catch(mysqli_sql_exception $e)
|
||||
{
|
||||
throw new MySQLException('Could not select DB', array('host'=>self::$m_sDBHost, 'user'=>self::$m_sDBUser, 'db_name'=>self::$m_sDBName), $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -119,7 +154,7 @@ class CMDBSource
|
||||
{
|
||||
// In case we don't have rights to enumerate the databases
|
||||
// Let's try to connect directly
|
||||
return @((bool)mysqli_query(self::$m_resDBLink, "USE $sSource"));
|
||||
return @((bool)self::$m_oMysqli->query("USE `$sSource`"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -132,7 +167,7 @@ class CMDBSource
|
||||
|
||||
public static function SelectDB($sSource)
|
||||
{
|
||||
if (!((bool)mysqli_query(self::$m_resDBLink, "USE $sSource")))
|
||||
if (!((bool)self::$m_oMysqli->query("USE `$sSource`")))
|
||||
{
|
||||
throw new MySQLException('Could not select DB', array('db_name'=>$sSource));
|
||||
}
|
||||
@@ -174,25 +209,25 @@ class CMDBSource
|
||||
|
||||
public static function GetErrNo()
|
||||
{
|
||||
if (self::$m_resDBLink)
|
||||
if (self::$m_oMysqli->errno != 0)
|
||||
{
|
||||
return mysqli_errno(self::$m_resDBLink);
|
||||
return self::$m_oMysqli->errno;
|
||||
}
|
||||
else
|
||||
{
|
||||
return mysqli_connect_errno();
|
||||
return self::$m_oMysqli->connect_errno;
|
||||
}
|
||||
}
|
||||
|
||||
public static function GetError()
|
||||
{
|
||||
if (self::$m_resDBLink)
|
||||
if (self::$m_oMysqli->error != '')
|
||||
{
|
||||
return mysqli_error(self::$m_resDBLink);
|
||||
return self::$m_oMysqli->error;
|
||||
}
|
||||
else
|
||||
{
|
||||
return mysqli_connect_error();
|
||||
return self::$m_oMysqli->connect_error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,42 +268,43 @@ class CMDBSource
|
||||
// Quote if not a number or a numeric string
|
||||
if ($bAlways || is_string($value))
|
||||
{
|
||||
$value = $cQuoteStyle . mysqli_real_escape_string(self::$m_resDBLink, $value) . $cQuoteStyle;
|
||||
$value = $cQuoteStyle . self::$m_oMysqli->real_escape_string($value) . $cQuoteStyle;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
public static function Query($sSQLQuery)
|
||||
{
|
||||
// Add info into the query as a comment, for easier error tracking
|
||||
// disabled until we need it really!
|
||||
//
|
||||
//$aTraceInf['file'] = __FILE__;
|
||||
// $sSQLQuery .= MyHelpers::MakeSQLComment($aTraceInf);
|
||||
|
||||
$oKPI = new ExecutionKPI();
|
||||
$result = mysqli_query(self::$m_resDBLink, $sSQLQuery);
|
||||
if (!$result)
|
||||
try
|
||||
{
|
||||
$oResult = self::$m_oMysqli->query($sSQLQuery);
|
||||
}
|
||||
catch(mysqli_sql_exception $e)
|
||||
{
|
||||
throw new MySQLException('Failed to issue SQL query', array('query' => $sSQLQuery, $e));
|
||||
}
|
||||
$oKPI->ComputeStats('Query exec (mySQL)', $sSQLQuery);
|
||||
if ($oResult === false)
|
||||
{
|
||||
throw new MySQLException('Failed to issue SQL query', array('query' => $sSQLQuery));
|
||||
}
|
||||
$oKPI->ComputeStats('Query exec (mySQL)', $sSQLQuery);
|
||||
|
||||
return $result;
|
||||
return $oResult;
|
||||
}
|
||||
|
||||
public static function GetNextInsertId($sTable)
|
||||
{
|
||||
$sSQL = "SHOW TABLE STATUS LIKE '$sTable'";
|
||||
$result = self::Query($sSQL);
|
||||
$aRow = mysqli_fetch_assoc($result);
|
||||
$oResult = self::Query($sSQL);
|
||||
$aRow = $oResult->fetch_assoc();
|
||||
$iNextInsertId = $aRow['Auto_increment'];
|
||||
return $iNextInsertId;
|
||||
}
|
||||
|
||||
public static function GetInsertId()
|
||||
{
|
||||
$iRes = mysqli_insert_id(self::$m_resDBLink);
|
||||
$iRes = self::$m_oMysqli->insert_id;
|
||||
if (is_null($iRes))
|
||||
{
|
||||
return 0;
|
||||
@@ -292,37 +328,59 @@ class CMDBSource
|
||||
|
||||
public static function QueryToScalar($sSql)
|
||||
{
|
||||
$result = mysqli_query(self::$m_resDBLink, $sSql);
|
||||
if (!$result)
|
||||
$oKPI = new ExecutionKPI();
|
||||
try
|
||||
{
|
||||
$oResult = self::$m_oMysqli->query($sSql);
|
||||
}
|
||||
catch(mysqli_sql_exception $e)
|
||||
{
|
||||
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
|
||||
MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
|
||||
}
|
||||
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
|
||||
if ($oResult === false)
|
||||
{
|
||||
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql));
|
||||
}
|
||||
if ($aRow = mysqli_fetch_array($result, MYSQLI_BOTH))
|
||||
|
||||
if ($aRow = $oResult->fetch_array(MYSQLI_BOTH))
|
||||
{
|
||||
$res = $aRow[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
mysqli_free_result($result);
|
||||
$oResult->free();
|
||||
throw new MySQLException('Found no result for query', array('query' => $sSql));
|
||||
}
|
||||
mysqli_free_result($result);
|
||||
$oResult->free();
|
||||
return $res;
|
||||
}
|
||||
|
||||
public static function QueryToArray($sSql)
|
||||
{
|
||||
$aData = array();
|
||||
$result = mysqli_query(self::$m_resDBLink, $sSql);
|
||||
if (!$result)
|
||||
$oKPI = new ExecutionKPI();
|
||||
try
|
||||
{
|
||||
$oResult = self::$m_oMysqli->query($sSql);
|
||||
}
|
||||
catch(mysqli_sql_exception $e)
|
||||
{
|
||||
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
|
||||
MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
|
||||
}
|
||||
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
|
||||
if ($oResult === false)
|
||||
{
|
||||
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql));
|
||||
}
|
||||
while ($aRow = mysqli_fetch_array($result, MYSQLI_BOTH))
|
||||
|
||||
while ($aRow = $oResult->fetch_array(MYSQLI_BOTH))
|
||||
{
|
||||
$aData[] = $aRow;
|
||||
}
|
||||
mysqli_free_result($result);
|
||||
$oResult->free();
|
||||
return $aData;
|
||||
}
|
||||
|
||||
@@ -340,51 +398,73 @@ class CMDBSource
|
||||
public static function ExplainQuery($sSql)
|
||||
{
|
||||
$aData = array();
|
||||
$result = mysqli_query(self::$m_resDBLink, "EXPLAIN $sSql");
|
||||
if (!$result)
|
||||
try
|
||||
{
|
||||
$oResult = self::$m_oMysqli->query($sSql);
|
||||
}
|
||||
catch(mysqli_sql_exception $e)
|
||||
{
|
||||
MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
|
||||
}
|
||||
if ($oResult === false)
|
||||
{
|
||||
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql));
|
||||
}
|
||||
|
||||
$aNames = self::GetColumns($result);
|
||||
|
||||
$aNames = self::GetColumns($oResult);
|
||||
|
||||
$aData[] = $aNames;
|
||||
while ($aRow = mysqli_fetch_array($result, MYSQLI_ASSOC))
|
||||
while ($aRow = $oResult->fetch_array(MYSQLI_ASSOC))
|
||||
{
|
||||
$aData[] = $aRow;
|
||||
}
|
||||
mysqli_free_result($result);
|
||||
$oResult->free();
|
||||
return $aData;
|
||||
}
|
||||
|
||||
public static function TestQuery($sSql)
|
||||
{
|
||||
$result = mysqli_query(self::$m_resDBLink, "EXPLAIN $sSql");
|
||||
if (!$result)
|
||||
try
|
||||
{
|
||||
return self::GetError();
|
||||
$oResult = self::$m_oMysqli->query($sSql);
|
||||
}
|
||||
catch(mysqli_sql_exception $e)
|
||||
{
|
||||
MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
|
||||
}
|
||||
if ($oResult === false)
|
||||
{
|
||||
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql));
|
||||
}
|
||||
|
||||
mysqli_free_result($result);
|
||||
if (is_object($oResult))
|
||||
{
|
||||
$oResult->free();
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
public static function NbRows($result)
|
||||
public static function NbRows($oResult)
|
||||
{
|
||||
return mysqli_num_rows($result);
|
||||
return $oResult->num_rows;
|
||||
}
|
||||
|
||||
public static function FetchArray($result)
|
||||
public static function AffectedRows()
|
||||
{
|
||||
return mysqli_fetch_array($result, MYSQLI_ASSOC);
|
||||
return self::$m_oMysqli->affected_rows;
|
||||
}
|
||||
|
||||
public static function GetColumns($result)
|
||||
public static function FetchArray($oResult)
|
||||
{
|
||||
return $oResult->fetch_array(MYSQLI_ASSOC);
|
||||
}
|
||||
|
||||
public static function GetColumns($oResult)
|
||||
{
|
||||
$aNames = array();
|
||||
for ($i = 0; $i < (($___mysqli_tmp = mysqli_num_fields($result)) ? $___mysqli_tmp : 0) ; $i++)
|
||||
for ($i = 0; $i < (($___mysqli_tmp = $oResult->field_count) ? $___mysqli_tmp : 0) ; $i++)
|
||||
{
|
||||
$meta = mysqli_fetch_field_direct($result, $i);
|
||||
$meta = $oResult->fetch_field_direct($i);
|
||||
if (!$meta)
|
||||
{
|
||||
throw new MySQLException('mysql_fetch_field: No information available', array('query'=>$sSql, 'i'=>$i));
|
||||
@@ -397,14 +477,15 @@ class CMDBSource
|
||||
return $aNames;
|
||||
}
|
||||
|
||||
public static function Seek($result, $iRow)
|
||||
public static function Seek($oResult, $iRow)
|
||||
{
|
||||
return mysqli_data_seek($result, $iRow);
|
||||
return $oResult->data_seek($iRow);
|
||||
}
|
||||
|
||||
public static function FreeResult($result)
|
||||
public static function FreeResult($oResult)
|
||||
{
|
||||
return ((mysqli_free_result($result) || (is_object($result) && (get_class($result) == "mysqli_result"))) ? true : false);
|
||||
$oResult->free(); /* returns void */
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function IsTable($sTable)
|
||||
@@ -460,14 +541,23 @@ class CMDBSource
|
||||
return ($aFieldData["Type"]);
|
||||
}
|
||||
|
||||
public static function HasIndex($sTable, $sField)
|
||||
public static function HasIndex($sTable, $sIndexId, $aFields = null)
|
||||
{
|
||||
$aTableInfo = self::GetTableInfo($sTable);
|
||||
if (empty($aTableInfo)) return false;
|
||||
if (!array_key_exists($sField, $aTableInfo["Fields"])) return false;
|
||||
$aFieldData = $aTableInfo["Fields"][$sField];
|
||||
// $aFieldData could be 'PRI' for the primary key, or 'MUL', or ?
|
||||
return (strlen($aFieldData["Key"]) > 0);
|
||||
if (!array_key_exists($sIndexId, $aTableInfo['Indexes'])) return false;
|
||||
|
||||
if ($aFields == null)
|
||||
{
|
||||
// Just searching for the name
|
||||
return true;
|
||||
}
|
||||
|
||||
// Compare the columns
|
||||
$sSearchedIndex = implode(',', $aFields);
|
||||
$sExistingIndex = implode(',', $aTableInfo['Indexes'][$sIndexId]);
|
||||
|
||||
return ($sSearchedIndex == $sExistingIndex);
|
||||
}
|
||||
|
||||
// Returns an array of (fieldname => array of field info)
|
||||
@@ -517,6 +607,17 @@ class CMDBSource
|
||||
// Table does not exist
|
||||
self::$m_aTablesInfo[strtolower($sTableName)] = null;
|
||||
}
|
||||
|
||||
if (!is_null(self::$m_aTablesInfo[strtolower($sTableName)]))
|
||||
{
|
||||
$aIndexes = self::QueryToArray("SHOW INDEXES FROM `$sTableName`");
|
||||
$aMyIndexes = array();
|
||||
foreach ($aIndexes as $aIndexColumn)
|
||||
{
|
||||
$aMyIndexes[$aIndexColumn['Key_name']][$aIndexColumn['Seq_in_index']-1] = $aIndexColumn['Column_name'];
|
||||
}
|
||||
self::$m_aTablesInfo[strtolower($sTableName)]["Indexes"] = $aMyIndexes;
|
||||
}
|
||||
}
|
||||
//public static function EnumTables()
|
||||
//{
|
||||
@@ -542,18 +643,25 @@ class CMDBSource
|
||||
public static function DumpTable($sTable)
|
||||
{
|
||||
$sSql = "SELECT * FROM `$sTable`";
|
||||
$result = mysqli_query(self::$m_resDBLink, $sSql);
|
||||
if (!$result)
|
||||
try
|
||||
{
|
||||
$oResult = self::$m_oMysqli->query($sSql);
|
||||
}
|
||||
catch(mysqli_sql_exception $e)
|
||||
{
|
||||
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql), $e);
|
||||
}
|
||||
if ($oResult === false)
|
||||
{
|
||||
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql));
|
||||
}
|
||||
|
||||
|
||||
$aRows = array();
|
||||
while ($aRow = mysqli_fetch_array($result, MYSQLI_ASSOC))
|
||||
while ($aRow = $oResult->fetch_array(MYSQLI_ASSOC))
|
||||
{
|
||||
$aRows[] = $aRow;
|
||||
}
|
||||
mysqli_free_result($result);
|
||||
$oResult->free();
|
||||
return $aRows;
|
||||
}
|
||||
|
||||
@@ -583,7 +691,7 @@ class CMDBSource
|
||||
{
|
||||
try
|
||||
{
|
||||
$result = self::Query('SHOW GRANTS'); // [ FOR CURRENT_USER()]
|
||||
$oResult = self::Query('SHOW GRANTS'); // [ FOR CURRENT_USER()]
|
||||
}
|
||||
catch(MySQLException $e)
|
||||
{
|
||||
@@ -591,12 +699,12 @@ class CMDBSource
|
||||
}
|
||||
|
||||
$aRes = array();
|
||||
while ($aRow = mysqli_fetch_array($result, MYSQLI_NUM))
|
||||
while ($aRow = $oResult->fetch_array(MYSQLI_NUM))
|
||||
{
|
||||
// so far, only one column...
|
||||
$aRes[] = implode('/', $aRow);
|
||||
}
|
||||
mysqli_free_result($result);
|
||||
$oResult->free();
|
||||
// so far, only one line...
|
||||
return implode(', ', $aRes);
|
||||
}
|
||||
@@ -609,21 +717,21 @@ class CMDBSource
|
||||
{
|
||||
try
|
||||
{
|
||||
$result = self::Query('SHOW SLAVE STATUS');
|
||||
$oResult = self::Query('SHOW SLAVE STATUS');
|
||||
}
|
||||
catch(MySQLException $e)
|
||||
{
|
||||
throw new CoreException("Current user not allowed to check the status", array('mysql_error' => $e->getMessage()));
|
||||
}
|
||||
|
||||
if (mysqli_num_rows($result) == 0)
|
||||
if ($oResult->num_rows == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns one single row anytime
|
||||
$aRow = mysqli_fetch_array($result, MYSQLI_ASSOC);
|
||||
mysqli_free_result($result);
|
||||
$aRow = $oResult->fetch_array(MYSQLI_ASSOC);
|
||||
$oResult->free();
|
||||
|
||||
if (!isset($aRow['Slave_IO_Running']))
|
||||
{
|
||||
@@ -645,7 +753,4 @@ class CMDBSource
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
}
|
||||
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Any extension to compute things like a stop watch deadline or working hours
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
define('ITOP_APPLICATION', 'iTop');
|
||||
define('ITOP_VERSION', '$ITOP_VERSION$');
|
||||
@@ -27,13 +30,12 @@ define('ACCESS_READONLY', 0);
|
||||
/**
|
||||
* Configuration read/write
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
require_once('coreexception.class.inc.php');
|
||||
require_once('attributedef.class.inc.php'); // For the defines
|
||||
|
||||
class ConfigException extends CoreException
|
||||
{
|
||||
@@ -46,9 +48,7 @@ define ('DEFAULT_LOG_GLOBAL', true);
|
||||
define ('DEFAULT_LOG_NOTIFICATION', true);
|
||||
define ('DEFAULT_LOG_ISSUE', true);
|
||||
define ('DEFAULT_LOG_WEB_SERVICE', true);
|
||||
define ('DEFAULT_LOG_KPI_DURATION', false);
|
||||
define ('DEFAULT_LOG_KPI_MEMORY', false);
|
||||
define ('DEFAULT_DEBUG_QUERIES', false);
|
||||
define ('DEFAULT_LOG_QUERIES', false);
|
||||
|
||||
define ('DEFAULT_QUERY_CACHE_ENABLED', true);
|
||||
|
||||
@@ -90,7 +90,7 @@ class Config
|
||||
'default' => '',
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'app_root_url' => array(
|
||||
'type' => 'string',
|
||||
@@ -100,6 +100,14 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
),
|
||||
'app_icon_url' => array(
|
||||
'type' => 'string',
|
||||
'description' => 'Hyperlink to redirect the user when clicking on the application icon (in the main window, or login/logoff pages)',
|
||||
'default' => 'http://www.combodo.com/itop',
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'skip_check_to_write' => array(
|
||||
'type' => 'bool',
|
||||
'description' => 'Disable data format and integrity checks to boost up data load (insert or update)',
|
||||
@@ -124,6 +132,30 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'query_optimization_enabled' => array(
|
||||
'type' => 'bool',
|
||||
'description' => 'The queries are optimized based on the assumption that the DB integrity has been preserved. By disabling the optimization one can ensure that the fetched data is clean... but this can be really slower or not usable at all (some queries will exceed the allowed number of joins in MySQL: 61!)',
|
||||
'default' => true,
|
||||
'value' => true,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'query_indentation_enabled' => array(
|
||||
'type' => 'bool',
|
||||
'description' => 'For developpers: format the SQL queries for human analysis',
|
||||
'default' => false,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'disable_mandatory_ext_keys' => array(
|
||||
'type' => 'bool',
|
||||
'description' => 'For developpers: allow every external keys to be undefined',
|
||||
'default' => false,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'graphviz_path' => array(
|
||||
'type' => 'string',
|
||||
'description' => 'Path to the Graphviz "dot" executable for graphing objects lifecycle',
|
||||
@@ -205,6 +237,14 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'csv_import_history_display' => array(
|
||||
'type' => 'bool',
|
||||
'description' => 'Display the history tab in the import wizard',
|
||||
'default' => false,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
),
|
||||
'access_mode' => array(
|
||||
'type' => 'integer',
|
||||
'description' => 'Combination of flags (ACCESS_USER_WRITE | ACCESS_ADMIN_WRITE, or ACCESS_FULL)',
|
||||
@@ -293,6 +333,14 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'async_task_retries' => array(
|
||||
'type' => 'array',
|
||||
'description' => 'Automatic retries of asynchronous tasks in case of failure (per class)',
|
||||
'default' => array('AsyncSendEmail' => array('max_retries' => 0, 'retry_delay' => 600)),
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'email_asynchronous' => array(
|
||||
'type' => 'bool',
|
||||
'description' => 'If set, the emails are sent off line, which requires cron.php to be activated. Exception: some features like the email test utility will force the serialized mode',
|
||||
@@ -391,7 +439,7 @@ class Config
|
||||
'default' => '2.0',
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'cas_host' => array(
|
||||
'type' => 'string',
|
||||
@@ -400,7 +448,7 @@ class Config
|
||||
'default' => '',
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'cas_port' => array(
|
||||
'type' => 'integer',
|
||||
@@ -409,7 +457,7 @@ class Config
|
||||
'default' => 443,
|
||||
'value' => 443,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'cas_context' => array(
|
||||
'type' => 'string',
|
||||
@@ -418,7 +466,7 @@ class Config
|
||||
'default' => '',
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'cas_server_ca_cert_path' => array(
|
||||
'type' => 'string',
|
||||
@@ -427,7 +475,7 @@ class Config
|
||||
'default' => '',
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'cas_logout_redirect_service' => array(
|
||||
'type' => 'string',
|
||||
@@ -436,7 +484,7 @@ class Config
|
||||
'default' => '',
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'cas_memberof' => array(
|
||||
'type' => 'string',
|
||||
@@ -445,7 +493,7 @@ class Config
|
||||
'default' => '',
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'cas_user_synchro' => array(
|
||||
'type' => 'bool',
|
||||
@@ -454,7 +502,7 @@ class Config
|
||||
'default' => 0,
|
||||
'value' => 0,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'cas_update_profiles' => array(
|
||||
'type' => 'bool',
|
||||
@@ -463,7 +511,7 @@ class Config
|
||||
'default' => 0,
|
||||
'value' => 0,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'cas_profile_pattern' => array(
|
||||
'type' => 'string',
|
||||
@@ -472,7 +520,7 @@ class Config
|
||||
'default' => '/^cn=([^,]+),/',
|
||||
'value' => '/^cn=([^,]+),/',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'cas_default_profiles' => array(
|
||||
'type' => 'string',
|
||||
@@ -481,7 +529,7 @@ class Config
|
||||
'default' => 'Portal user',
|
||||
'value' => 'Portal user',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'cas_debug' => array(
|
||||
'type' => 'bool',
|
||||
@@ -490,7 +538,25 @@ class Config
|
||||
'default' => false,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'forgot_password' => array(
|
||||
'type' => 'bool',
|
||||
'description' => 'Enable the "Forgot password" feature',
|
||||
// examples... not used (nor 'description')
|
||||
'default' => true,
|
||||
'value' => true,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'forgot_password_from' => array(
|
||||
'type' => 'string',
|
||||
'description' => 'Sender email address for the "forgot password" feature. If empty, defaults to the recipient\'s email address.',
|
||||
// examples... not used (nor 'description')
|
||||
'default' => '',
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'deadline_format' => array(
|
||||
'type' => 'string',
|
||||
@@ -519,6 +585,15 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
),
|
||||
'complex_actions_limit' => array(
|
||||
'type' => 'integer',
|
||||
'description' => 'Display the "actions" menu items that require long computation only if the list of objects is contains less objects than this number (0 means no limit)',
|
||||
// examples... not used
|
||||
'default' => 50,
|
||||
'value' => 50,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'synchro_prevent_delete_all' => array(
|
||||
'type' => 'bool',
|
||||
'description' => 'Stop the synchro if all the replicas of a data source become obsolete at the same time.',
|
||||
@@ -532,11 +607,168 @@ class Config
|
||||
'type' => 'string',
|
||||
'description' => 'Source directory for the datamodel files. (which gets compiled to env-production).',
|
||||
// examples... not used
|
||||
'default' => 'datamodels/latest',
|
||||
'default' => '',
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
),
|
||||
'csv_file_default_charset' => array(
|
||||
'type' => 'string',
|
||||
'description' => 'Character set used by default for downloading and uploading data as a CSV file. Warning: it is case sensitive (uppercase is preferable).',
|
||||
// examples... not used
|
||||
'default' => 'ISO-8859-1',
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
),
|
||||
'debug_report_spurious_chars' => array(
|
||||
'type' => 'bool',
|
||||
'description' => 'Report, in the error log, the characters found in the output buffer, echoed by mistake in the loaded modules, and potentially corrupting the output',
|
||||
// examples... not used
|
||||
'default' => false,
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'impact_analysis_first_tab' => array(
|
||||
'type' => 'string',
|
||||
'description' => 'Which tab to display first in the impact analysis view: list or graphics. Graphics are nicer but slower to display when there are many objects',
|
||||
// examples... not used
|
||||
'default' => 'graphics',
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'url_validation_pattern' => array(
|
||||
'type' => 'string',
|
||||
'description' => 'Regular expression to validate/detect the format of an URL (URL attributes and Wiki formatting for Text attributes)',
|
||||
'default' => '(https?|ftp)\://([a-zA-Z0-9+!*(),;?&=\$_.-]+(\:[a-zA-Z0-9+!*(),;?&=\$_.-]+)?@)?([a-zA-Z0-9-.]{3,})(\:[0-9]{2,5})?(/([a-zA-Z0-9%+\$_-]\.?)+)*/?(\?[a-zA-Z+&\$_.-][a-zA-Z0-9;:[\]@&%=+/\$_.-]*)?(#[a-zA-Z_.-][a-zA-Z0-9+\$_.-]*)?',
|
||||
// SHEME.......... USER....................... PASSWORD.......................... HOST/IP........... PORT.......... PATH........................ GET............................................ ANCHOR............................
|
||||
// Example: http://User:passWord@127.0.0.1:8888/patH/Page.php?arrayArgument[2]=something:blah20#myAnchor
|
||||
// Origin of this regexp: http://www.php.net/manual/fr/function.preg-match.php#93824
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
),
|
||||
'email_validation_pattern' => array(
|
||||
'type' => 'string',
|
||||
'description' => 'Regular expression to validate/detect the format of an eMail address',
|
||||
'default' => "[a-zA-Z0-9._&'-]+@[a-zA-Z0-9.-]+\.[a-zA-Z0-9-]{2,}",
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
),
|
||||
'log_kpi_duration' => array(
|
||||
'type' => 'integer',
|
||||
'description' => 'Level of logging for troubleshooting performance issues (1 to enable, 2 +blame callers)',
|
||||
// examples... not used
|
||||
'default' => 0,
|
||||
'value' => 0,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'log_kpi_memory' => array(
|
||||
'type' => 'integer',
|
||||
'description' => 'Level of logging for troubleshooting memory limit issues',
|
||||
// examples... not used
|
||||
'default' => 0,
|
||||
'value' => 0,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'log_kpi_user_id' => array(
|
||||
'type' => 'string',
|
||||
'description' => 'Limit the scope of users to the given user id (* means no limit)',
|
||||
// examples... not used
|
||||
'default' => '*',
|
||||
'value' => '*',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'max_linkset_output' => array(
|
||||
'type' => 'integer',
|
||||
'description' => 'Maximum number of items shown when getting a list of related items in an email, using the form $this->some_list$. 0 means no limit.',
|
||||
'default' => 100,
|
||||
'value' => 100,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
),
|
||||
'demo_mode' => array(
|
||||
'type' => 'bool',
|
||||
'description' => 'Set to true to prevent users from changing passwords/languages',
|
||||
'default' => false,
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'portal_tickets' => array(
|
||||
'type' => 'string',
|
||||
'description' => 'CSV list of classes supported in the portal',
|
||||
// examples... not used
|
||||
'default' => 'UserRequest',
|
||||
'value' => 'UserRequest',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
),
|
||||
'max_execution_time_per_loop' => array(
|
||||
'type' => 'integer',
|
||||
'description' => 'Maximum execution time requested, per loop, during bulk operations. Zero means no limit.',
|
||||
// examples... not used
|
||||
'default' => 30,
|
||||
'value' => 30,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'max_history_length' => array(
|
||||
'type' => 'integer',
|
||||
'description' => 'Maximum length of the history table (in the "History" tab on each object) before it gets truncated. Latest modifications are displayed first.',
|
||||
// examples... not used
|
||||
'default' => 50,
|
||||
'value' => 50,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'full_text_chunk_duration' => array(
|
||||
'type' => 'integer',
|
||||
'description' => 'Delay after which the results are displayed.',
|
||||
// examples... not used
|
||||
'default' => 2,
|
||||
'value' => 2,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'full_text_accelerators' => array(
|
||||
'type' => 'array',
|
||||
'description' => 'Specifies classes to be searched at first (and the subset of data) when running the full text search.',
|
||||
'default' => array(),
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'full_text_needle_min' => array(
|
||||
'type' => 'integer',
|
||||
'description' => 'Minimum size of the full text needle.',
|
||||
'default' => 3,
|
||||
'value' => 3,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'tracking_level_linked_set_default' => array(
|
||||
'type' => 'integer',
|
||||
'description' => 'Default tracking level if not explicitely set at the attribute level, for AttributeLinkedSet (defaults to NONE in case of a fresh install, LIST otherwise - this to preserve backward compatibility while upgrading from a version older than 2.0.3 - see TRAC #936)',
|
||||
'default' => LINKSET_TRACKING_LIST,
|
||||
'value' => LINKSET_TRACKING_LIST,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'tracking_level_linked_set_indirect_default' => array(
|
||||
'type' => 'integer',
|
||||
'description' => 'Default tracking level if not explicitely set at the attribute level, for AttributeLinkedSetIndirect',
|
||||
'default' => LINKSET_TRACKING_ALL,
|
||||
'value' => LINKSET_TRACKING_ALL,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
);
|
||||
|
||||
public function IsProperty($sPropCode)
|
||||
@@ -565,6 +797,8 @@ class Config
|
||||
case 'float':
|
||||
$value = (float) $value;
|
||||
break;
|
||||
case 'array':
|
||||
break;
|
||||
default:
|
||||
throw new CoreException('Unknown type for setting', array('property' => $sPropCode, 'type' => $sType));
|
||||
}
|
||||
@@ -595,9 +829,7 @@ class Config
|
||||
protected $m_bLogNotification;
|
||||
protected $m_bLogIssue;
|
||||
protected $m_bLogWebService;
|
||||
protected $m_bLogKPIDuration; // private setting
|
||||
protected $m_bLogKPIMemory; // private setting
|
||||
protected $m_bDebugQueries; // private setting
|
||||
protected $m_bLogQueries; // private setting
|
||||
protected $m_bQueryCacheEnabled; // private setting
|
||||
|
||||
/**
|
||||
@@ -678,6 +910,7 @@ class Config
|
||||
'core/action.class.inc.php',
|
||||
'core/trigger.class.inc.php',
|
||||
'synchro/synchrodatasource.class.inc.php',
|
||||
'core/backgroundtask.class.inc.php',
|
||||
);
|
||||
$this->m_aDataModels = array();
|
||||
$this->m_aWebServiceCategories = array(
|
||||
@@ -705,8 +938,6 @@ class Config
|
||||
$this->m_bLogNotification = DEFAULT_LOG_NOTIFICATION;
|
||||
$this->m_bLogIssue = DEFAULT_LOG_ISSUE;
|
||||
$this->m_bLogWebService = DEFAULT_LOG_WEB_SERVICE;
|
||||
$this->m_bLogKPIDuration = DEFAULT_LOG_KPI_DURATION;
|
||||
$this->m_bLogKPIDuration = DEFAULT_LOG_KPI_DURATION;
|
||||
$this->m_iMinDisplayLimit = DEFAULT_MIN_DISPLAY_LIMIT;
|
||||
$this->m_iMaxDisplayLimit = DEFAULT_MAX_DISPLAY_LIMIT;
|
||||
$this->m_iStandardReloadInterval = DEFAULT_STANDARD_RELOAD_INTERVAL;
|
||||
@@ -824,7 +1055,14 @@ class Config
|
||||
{
|
||||
if ($this->IsProperty($sPropCode))
|
||||
{
|
||||
$value = trim($rawvalue);
|
||||
if (is_string($rawvalue))
|
||||
{
|
||||
$value = trim($rawvalue);
|
||||
}
|
||||
else
|
||||
{
|
||||
$value = $rawvalue;
|
||||
}
|
||||
$this->Set($sPropCode, $value, $sConfigFile);
|
||||
}
|
||||
}
|
||||
@@ -842,9 +1080,7 @@ 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_bLogKPIDuration = isset($MySettings['log_kpi_duration']) ? (bool) trim($MySettings['log_kpi_duration']) : DEFAULT_LOG_KPI_DURATION;
|
||||
$this->m_bLogKPIMemory = isset($MySettings['log_kpi_memory']) ? (bool) trim($MySettings['log_kpi_memory']) : DEFAULT_LOG_KPI_MEMORY;
|
||||
$this->m_bDebugQueries = isset($MySettings['debug_queries']) ? (bool) trim($MySettings['debug_queries']) : DEFAULT_DEBUG_QUERIES;
|
||||
$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;
|
||||
@@ -982,19 +1218,9 @@ class Config
|
||||
return $this->m_bLogWebService;
|
||||
}
|
||||
|
||||
public function GetLogKPIDuration()
|
||||
public function GetLogQueries()
|
||||
{
|
||||
return $this->m_bLogKPIDuration;
|
||||
}
|
||||
|
||||
public function GetLogKPIMemory()
|
||||
{
|
||||
return $this->m_bLogKPIMemory;
|
||||
}
|
||||
|
||||
public function GetDebugQueries()
|
||||
{
|
||||
return $this->m_bDebugQueries;
|
||||
return $this->m_bLogQueries;
|
||||
}
|
||||
|
||||
public function GetQueryCacheEnabled()
|
||||
@@ -1278,8 +1504,24 @@ class Config
|
||||
'value' => $bValue,
|
||||
);
|
||||
}
|
||||
|
||||
// Old fashioned non boolean values
|
||||
|
||||
// Old fashioned integer settings
|
||||
$aIntValues = array(
|
||||
'fast_reload_interval' => $this->m_iFastReloadInterval,
|
||||
'max_display_limit' => $this->m_iMaxDisplayLimit,
|
||||
'min_display_limit' => $this->m_iMinDisplayLimit,
|
||||
'standard_reload_interval' => $this->m_iStandardReloadInterval,
|
||||
);
|
||||
foreach($aIntValues as $sKey => $iValue)
|
||||
{
|
||||
$aConfigSettings[$sKey] = array(
|
||||
'show_in_conf_sample' => true,
|
||||
'type' => 'integer',
|
||||
'value' => $iValue,
|
||||
);
|
||||
}
|
||||
|
||||
// Old fashioned remaining values
|
||||
$aOtherValues = array(
|
||||
'db_host' => $this->m_sDBHost,
|
||||
'db_user' => $this->m_sDBUser,
|
||||
@@ -1306,7 +1548,8 @@ class Config
|
||||
fwrite($hFile, "\$MySettings = array(\n");
|
||||
foreach($aConfigSettings as $sPropCode => $aSettingInfo)
|
||||
{
|
||||
if ($aSettingInfo['show_in_conf_sample'])
|
||||
// Write all values that are either always visible or present in the cloned config file
|
||||
if ($aSettingInfo['show_in_conf_sample'] || (!empty($aSettingInfo['source_of_value']) && ($aSettingInfo['source_of_value'] != 'unknown')) )
|
||||
{
|
||||
$sType = $aSettingInfo['type'];
|
||||
switch($sType)
|
||||
@@ -1420,7 +1663,7 @@ class Config
|
||||
/**
|
||||
* Helper function to initialize a configuration from the page arguments
|
||||
*/
|
||||
public function UpdateFromParams($aParamValues, $sModulesDir = null)
|
||||
public function UpdateFromParams($aParamValues, $sModulesDir = null, $bPreserveModuleSettings = false)
|
||||
{
|
||||
if (isset($aParamValues['application_path']))
|
||||
{
|
||||
@@ -1469,7 +1712,7 @@ class Config
|
||||
// Merge the values with the ones provided by the modules
|
||||
// Make sure when don't load the same file twice...
|
||||
|
||||
$aModules = ModuleDiscovery::GetAvailableModules(APPROOT, $sModulesDir);
|
||||
$aModules = ModuleDiscovery::GetAvailableModules(array(APPROOT.$sModulesDir));
|
||||
foreach($aModules as $sModuleId => $aModuleInfo)
|
||||
{
|
||||
list($sModuleName, $sModuleVersion) = ModuleDiscovery::GetModuleName($sModuleId);
|
||||
@@ -1483,16 +1726,19 @@ class Config
|
||||
{
|
||||
$aWebServiceCategories = array_unique(array_merge($aWebServiceCategories, $aModuleInfo['webservice']));
|
||||
}
|
||||
if (isset($aModuleInfo['dictionary']))
|
||||
{
|
||||
$aDictionaries = array_unique(array_merge($aDictionaries, $aModuleInfo['dictionary']));
|
||||
}
|
||||
if (isset($aModuleInfo['settings']))
|
||||
{
|
||||
list($sName, $sVersion) = ModuleDiscovery::GetModuleName($sModuleId);
|
||||
foreach($aModuleInfo['settings'] as $sProperty => $value)
|
||||
{
|
||||
list($sName, $sVersion) = ModuleDiscovery::GetModuleName($sModuleId);
|
||||
$this->SetModuleSetting($sName, $sProperty, $value);
|
||||
if ($bPreserveModuleSettings && isset($this->m_aModuleSettings[$sName][$sProperty]))
|
||||
{
|
||||
// Do nothing keep the original value
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->SetModuleSetting($sName, $sProperty, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isset($aModuleInfo['installer']))
|
||||
@@ -1515,6 +1761,17 @@ class Config
|
||||
$this->SetAppModules($aAppModules);
|
||||
$this->SetDataModels($aDataModels);
|
||||
$this->SetWebServiceCategories($aWebServiceCategories);
|
||||
|
||||
// Scan dictionaries
|
||||
//
|
||||
if (!is_null($sModulesDir))
|
||||
{
|
||||
foreach(glob(APPROOT.$sModulesDir.'/dictionaries/*.dict.php') as $sFilePath)
|
||||
{
|
||||
$sFile = basename($sFilePath);
|
||||
$aDictionaries[] = $sModulesDir.'/dictionaries/'.$sFile;
|
||||
}
|
||||
}
|
||||
$this->SetDictionaries($aDictionaries);
|
||||
}
|
||||
}
|
||||
@@ -1548,14 +1805,14 @@ class Config
|
||||
/**
|
||||
* Pretty format a var_export'ed value so that (if possible) the identation is preserved on every line
|
||||
* @param mixed $value The value to export
|
||||
* @param string $sIdentation The string to use to indent the text
|
||||
* @param string $sIndentation The string to use to indent the text
|
||||
* @param bool $bForceIndentation Forces the identation (enven if it breaks/changes an eval, for example to ouput a value inside a comment)
|
||||
* @return string The indented export string
|
||||
*/
|
||||
protected static function PrettyVarExport($value, $sIdentation, $bForceIndentation = false)
|
||||
protected static function PrettyVarExport($value, $sIndentation, $bForceIndentation = false)
|
||||
{
|
||||
$sExport = var_export($value, true);
|
||||
$sNiceExport = trim(preg_replace("/^/m", "\t\t\t", $sExport));
|
||||
$sNiceExport = str_replace(array("\r\n", "\n", "\r"), "\n".$sIndentation, trim($sExport));
|
||||
if (!$bForceIndentation)
|
||||
{
|
||||
eval('$aImported='.$sNiceExport.';');
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Exception management
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* CSV parser
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* data generator
|
||||
* helps the consultants in creating dummy data sets, for various test purposes (validation, usability, scalability)
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Define filters for a given class of objects (formerly named "filter")
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
define('TREE_OPERATOR_EQUALS', 0);
|
||||
@@ -70,6 +71,14 @@ class DBObjectSearch
|
||||
$this->m_aModifierProperties = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a deep clone (as opposed to "clone" which does copy a reference to the underlying objects)
|
||||
**/
|
||||
public function DeepClone()
|
||||
{
|
||||
return unserialize(serialize($this)); // Beware this serializes/unserializes the search and its parameters as well
|
||||
}
|
||||
|
||||
public function AllowAllData() {$this->m_bAllowAllData = true;}
|
||||
public function IsAllDataAllowed() {return $this->m_bAllowAllData;}
|
||||
public function IsDataFiltered() {return $this->m_bDataFiltered; }
|
||||
@@ -109,6 +118,60 @@ class DBObjectSearch
|
||||
return key($this->m_aClasses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the class (only subclasses are supported as of now, because the conditions must fit the new class)
|
||||
* Defaults to the first selected class (most of the time it is also the first joined class
|
||||
*/
|
||||
public function ChangeClass($sNewClass, $sAlias = null)
|
||||
{
|
||||
if (is_null($sAlias))
|
||||
{
|
||||
$sAlias = $this->GetClassAlias();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!array_key_exists($sAlias, $this->m_aClasses))
|
||||
{
|
||||
// discard silently - necessary when recursing on the related nodes (see code below)
|
||||
return;
|
||||
}
|
||||
}
|
||||
$sCurrClass = $this->GetClassName($sAlias);
|
||||
if (!MetaModel::IsParentClass($sCurrClass, $sNewClass))
|
||||
{
|
||||
throw new Exception("Could not change the search class from '$sCurrClass' to '$sNewClass'. Only child classes are permitted.");
|
||||
}
|
||||
|
||||
// Change for this node
|
||||
//
|
||||
$this->m_aSelectedClasses[$sAlias] = $sNewClass;
|
||||
$this->m_aClasses[$sAlias] = $sNewClass;
|
||||
|
||||
// Change for all the related node (yes, this was necessary with some queries - strange effects otherwise)
|
||||
//
|
||||
foreach($this->m_aRelatedTo as $aRelatedTo)
|
||||
{
|
||||
$aRelatedTo['flt']->ChangeClass($sNewClass, $sAlias);
|
||||
}
|
||||
foreach($this->m_aPointingTo as $sExtKeyAttCode=>$aPointingTo)
|
||||
{
|
||||
foreach($aPointingTo as $iOperatorCode => $aFilter)
|
||||
{
|
||||
foreach($aFilter as $oExtFilter)
|
||||
{
|
||||
$oExtFilter->ChangeClass($sNewClass, $sAlias);
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach($this->m_aReferencedBy as $sForeignClass => $aReferences)
|
||||
{
|
||||
foreach($aReferences as $sForeignExtKeyAttCode => $oForeignFilter)
|
||||
{
|
||||
$oForeignFilter->ChangeClass($sNewClass, $sAlias);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function SetSelectedClasses($aNewSet)
|
||||
{
|
||||
$this->m_aSelectedClasses = array();
|
||||
@@ -339,7 +402,7 @@ class DBObjectSearch
|
||||
{
|
||||
foreach($aPointingTo as $iOperatorCode => $aFilter)
|
||||
{
|
||||
foreach($aFilter as $sAlias => $oExtFilter)
|
||||
foreach($aFilter as $oExtFilter)
|
||||
{
|
||||
$oExtFilter->RenameParam($sOldName, $sNewName);
|
||||
}
|
||||
@@ -474,7 +537,7 @@ class DBObjectSearch
|
||||
* Specify a condition on external keys or link sets
|
||||
* @param sAttSpec Can be either an attribute code or extkey->[sAttSpec] or linkset->[sAttSpec] and so on, recursively
|
||||
* Example: infra_list->ci_id->location_id->country
|
||||
* @param value The value to match
|
||||
* @param value The value to match (can be an array => IN(val1, val2...)
|
||||
* @return void
|
||||
*/
|
||||
public function AddConditionAdvanced($sAttSpec, $value)
|
||||
@@ -521,7 +584,18 @@ class DBObjectSearch
|
||||
{
|
||||
// $sAttSpec is an attribute code
|
||||
//
|
||||
$this->AddCondition($sAttSpec, $value);
|
||||
if (is_array($value))
|
||||
{
|
||||
$oField = new FieldExpression($sAttSpec, $this->GetClass());
|
||||
$oListExpr = ListExpression::FromScalars($value);
|
||||
$oInValues = new BinaryExpression($oField, 'IN', $oListExpr);
|
||||
|
||||
$this->AddConditionExpression($oInValues);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->AddCondition($sAttSpec, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -586,7 +660,7 @@ class DBObjectSearch
|
||||
{
|
||||
foreach($aPointingTo as $iOperatorCode => $aFilter)
|
||||
{
|
||||
foreach($aFilter as $sAlias => $oFilter)
|
||||
foreach($aFilter as $oFilter)
|
||||
{
|
||||
$oFilter->AddToNameSpace($aClassAliases, $aAliasTranslation);
|
||||
}
|
||||
@@ -602,19 +676,53 @@ class DBObjectSearch
|
||||
}
|
||||
}
|
||||
|
||||
public function AddCondition_PointingTo(DBObjectSearch $oFilter, $sExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS)
|
||||
|
||||
// Browse the tree nodes recursively
|
||||
//
|
||||
protected function GetNode($sAlias)
|
||||
{
|
||||
$aAliasTranslation = array();
|
||||
$res = $this->AddCondition_PointingTo_InNameSpace($oFilter, $sExtKeyAttCode, $this->m_aClasses, $aAliasTranslation, $iOperatorCode);
|
||||
$this->TransferConditionExpression($oFilter, $aAliasTranslation);
|
||||
return $res;
|
||||
if ($this->GetFirstJoinedClassAlias() == $sAlias)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach($this->m_aPointingTo as $sExtKeyAttCode=>$aPointingTo)
|
||||
{
|
||||
foreach($aPointingTo as $iOperatorCode => $aFilter)
|
||||
{
|
||||
foreach($aFilter as $oFilter)
|
||||
{
|
||||
$ret = $oFilter->GetNode($sAlias);
|
||||
if (is_object($ret))
|
||||
{
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach($this->m_aReferencedBy as $sForeignClass=>$aReferences)
|
||||
{
|
||||
foreach($aReferences as $sForeignExtKeyAttCode=>$oForeignFilter)
|
||||
{
|
||||
$ret = $oForeignFilter->GetNode($sAlias);
|
||||
if (is_object($ret))
|
||||
{
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Not found
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function AddCondition_PointingTo_InNameSpace(DBObjectSearch $oFilter, $sExtKeyAttCode, &$aClassAliases, &$aAliasTranslation, $iOperatorCode)
|
||||
|
||||
public function AddCondition_PointingTo(DBObjectSearch $oFilter, $sExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS)
|
||||
{
|
||||
if (!MetaModel::IsValidKeyAttCode($this->GetClass(), $sExtKeyAttCode))
|
||||
{
|
||||
throw new CoreWarning("The attribute code '$sExtKeyAttCode' is not an external key of the class '{$this->GetClass()}' - the condition will be ignored");
|
||||
throw new CoreWarning("The attribute code '$sExtKeyAttCode' is not an external key of the class '{$this->GetClass()}'");
|
||||
}
|
||||
$oAttExtKey = MetaModel::GetAttributeDef($this->GetClass(), $sExtKeyAttCode);
|
||||
if(!MetaModel::IsSameFamilyBranch($oFilter->GetClass(), $oAttExtKey->GetTargetClass()))
|
||||
@@ -625,34 +733,46 @@ class DBObjectSearch
|
||||
{
|
||||
throw new CoreException("The specified tree operator $iOperatorCode is not applicable to the key '{$this->GetClass()}::$sExtKeyAttCode', which is not a HierarchicalKey");
|
||||
}
|
||||
// Note: though it seems to be a good practice to clone the given source filter
|
||||
// (as it was done and fixed an issue in MergeWith())
|
||||
// this was not implemented here because it was causing a regression (login as admin, select an org, click on any badge)
|
||||
// root cause: FromOQL relies on the fact that the passed filter can be modified later
|
||||
// NO: $oFilter = $oFilter->DeepClone();
|
||||
// See also: Trac #639, and self::AddCondition_ReferencedBy()
|
||||
$aAliasTranslation = array();
|
||||
$res = $this->AddCondition_PointingTo_InNameSpace($oFilter, $sExtKeyAttCode, $this->m_aClasses, $aAliasTranslation, $iOperatorCode);
|
||||
$this->TransferConditionExpression($oFilter, $aAliasTranslation);
|
||||
return $res;
|
||||
}
|
||||
|
||||
$bSamePointingTo = false;
|
||||
if (array_key_exists($sExtKeyAttCode, $this->m_aPointingTo))
|
||||
{
|
||||
if (array_key_exists($iOperatorCode, $this->m_aPointingTo[$sExtKeyAttCode]))
|
||||
{
|
||||
if (array_key_exists($oFilter->GetClassAlias(), $this->m_aPointingTo[$sExtKeyAttCode][$iOperatorCode]))
|
||||
{
|
||||
$bSamePointingTo = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
protected function AddCondition_PointingTo_InNameSpace(DBObjectSearch $oFilter, $sExtKeyAttCode, &$aClassAliases, &$aAliasTranslation, $iOperatorCode)
|
||||
{
|
||||
// Find the node on which the new tree must be attached (most of the time it is "this")
|
||||
$oReceivingFilter = $this->GetNode($this->GetClassAlias());
|
||||
|
||||
if ($bSamePointingTo)
|
||||
{
|
||||
// Same ext key, alias and same operator, merge the filters together
|
||||
$oFilter->AddToNamespace($aClassAliases, $aAliasTranslation, true /* Don't translate the main alias */);
|
||||
$this->m_aPointingTo[$sExtKeyAttCode][$iOperatorCode][$oFilter->GetClassAlias()] = $oFilter;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oFilter->AddToNamespace($aClassAliases, $aAliasTranslation);
|
||||
$this->m_aPointingTo[$sExtKeyAttCode][$iOperatorCode][$oFilter->GetClassAlias()] = $oFilter;
|
||||
}
|
||||
$oFilter->AddToNamespace($aClassAliases, $aAliasTranslation);
|
||||
$oReceivingFilter->m_aPointingTo[$sExtKeyAttCode][$iOperatorCode][] = $oFilter;
|
||||
}
|
||||
|
||||
public function AddCondition_ReferencedBy(DBObjectSearch $oFilter, $sForeignExtKeyAttCode)
|
||||
{
|
||||
$sForeignClass = $oFilter->GetClass();
|
||||
if (!MetaModel::IsValidKeyAttCode($sForeignClass, $sForeignExtKeyAttCode))
|
||||
{
|
||||
throw new CoreException("The attribute code '$sForeignExtKeyAttCode' is not an external key of the class '{$sForeignClass}'");
|
||||
}
|
||||
$oAttExtKey = MetaModel::GetAttributeDef($sForeignClass, $sForeignExtKeyAttCode);
|
||||
if(!MetaModel::IsSameFamilyBranch($this->GetClass(), $oAttExtKey->GetTargetClass()))
|
||||
{
|
||||
// à refaire en spécifique dans FromOQL
|
||||
throw new CoreException("The specified filter (objects referencing an object of class {$this->GetClass()}) is not compatible with the key '{$sForeignClass}::$sForeignExtKeyAttCode', which is pointing to {$oAttExtKey->GetTargetClass()}");
|
||||
}
|
||||
// Note: though it seems to be a good practice to clone the given source filter
|
||||
// (as it was done and fixed an issue in MergeWith())
|
||||
// this was not implemented here because it was causing a regression (login as admin, select an org, click on any badge)
|
||||
// root cause: FromOQL relies on the fact that the passed filter can be modified later
|
||||
// NO: $oFilter = $oFilter->DeepClone();
|
||||
// See also: Trac #639, and self::AddCondition_PointingTo()
|
||||
$aAliasTranslation = array();
|
||||
$res = $this->AddCondition_ReferencedBy_InNameSpace($oFilter, $sForeignExtKeyAttCode, $this->m_aClasses, $aAliasTranslation);
|
||||
$this->TransferConditionExpression($oFilter, $aAliasTranslation);
|
||||
@@ -662,41 +782,26 @@ class DBObjectSearch
|
||||
protected function AddCondition_ReferencedBy_InNameSpace(DBObjectSearch $oFilter, $sForeignExtKeyAttCode, &$aClassAliases, &$aAliasTranslation)
|
||||
{
|
||||
$sForeignClass = $oFilter->GetClass();
|
||||
$sForeignClassAlias = $oFilter->GetClassAlias();
|
||||
if (!MetaModel::IsValidKeyAttCode($sForeignClass, $sForeignExtKeyAttCode))
|
||||
{
|
||||
throw new CoreException("The attribute code '$sForeignExtKeyAttCode' is not an external key of the class '{$sForeignClass}' - the condition will be ignored");
|
||||
}
|
||||
$oAttExtKey = MetaModel::GetAttributeDef($sForeignClass, $sForeignExtKeyAttCode);
|
||||
if(!MetaModel::IsSameFamilyBranch($this->GetClass(), $oAttExtKey->GetTargetClass()))
|
||||
{
|
||||
throw new CoreException("The specified filter (objects referencing an object of class {$this->GetClass()}) is not compatible with the key '{$sForeignClass}::$sForeignExtKeyAttCode', which is pointing to {$oAttExtKey->GetTargetClass()}");
|
||||
}
|
||||
|
||||
// Find the node on which the new tree must be attached (most of the time it is "this")
|
||||
$oReceivingFilter = $this->GetNode($this->GetClassAlias());
|
||||
|
||||
if (array_key_exists($sForeignClass, $this->m_aReferencedBy) && array_key_exists($sForeignExtKeyAttCode, $this->m_aReferencedBy[$sForeignClass]))
|
||||
{
|
||||
$this->m_aReferencedBy[$sForeignClass][$sForeignExtKeyAttCode]->MergeWith_InNamespace($oFilter, $aClassAliases, $aAliasTranslation);
|
||||
$oReceivingFilter->m_aReferencedBy[$sForeignClass][$sForeignExtKeyAttCode]->MergeWith_InNamespace($oFilter, $aClassAliases, $aAliasTranslation);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oFilter->AddToNamespace($aClassAliases, $aAliasTranslation);
|
||||
|
||||
// #@# The condition expression found in that filter should not be used - could be another kind of structure like a join spec tree !!!!
|
||||
//$oNewFilter = clone $oFilter;
|
||||
//$oNewFilter = $oFilter->DeepClone();
|
||||
//$oNewFilter->ResetCondition();
|
||||
|
||||
$this->m_aReferencedBy[$sForeignClass][$sForeignExtKeyAttCode]= $oFilter;
|
||||
$oReceivingFilter->m_aReferencedBy[$sForeignClass][$sForeignExtKeyAttCode]= $oFilter;
|
||||
}
|
||||
}
|
||||
|
||||
public function AddCondition_LinkedTo(DBObjectSearch $oLinkFilter, $sExtKeyAttCodeToMe, $sExtKeyAttCodeTarget, DBObjectSearch $oFilterTarget)
|
||||
{
|
||||
$oLinkFilterFinal = clone $oLinkFilter;
|
||||
// todo : new function prototype
|
||||
$oLinkFilterFinal->AddCondition_PointingTo($sExtKeyAttCodeToMe);
|
||||
|
||||
$this->AddCondition_ReferencedBy($oLinkFilterFinal, $sExtKeyAttCodeToMe);
|
||||
}
|
||||
|
||||
public function AddCondition_RelatedTo(DBObjectSearch $oFilter, $sRelCode, $iMaxDepth)
|
||||
{
|
||||
MyHelpers::CheckValueInArray('relation code', $sRelCode, MetaModel::EnumRelations());
|
||||
@@ -705,6 +810,7 @@ class DBObjectSearch
|
||||
|
||||
public function MergeWith($oFilter)
|
||||
{
|
||||
$oFilter = $oFilter->DeepClone();
|
||||
$aAliasTranslation = array();
|
||||
$res = $this->MergeWith_InNamespace($oFilter, $this->m_aClasses, $aAliasTranslation);
|
||||
$this->TransferConditionExpression($oFilter, $aAliasTranslation);
|
||||
@@ -728,7 +834,7 @@ class DBObjectSearch
|
||||
{
|
||||
foreach($aPointingTo as $iOperatorCode => $aFilter)
|
||||
{
|
||||
foreach($aFilter as $sAlias => $oExtFilter)
|
||||
foreach($aFilter as $oExtFilter)
|
||||
{
|
||||
$this->AddCondition_PointingTo_InNamespace($oExtFilter, $sExtKeyAttCode, $aClassAliases, $aAliasTranslation, $iOperatorCode);
|
||||
}
|
||||
@@ -951,7 +1057,7 @@ class DBObjectSearch
|
||||
{
|
||||
foreach($aPointingTo as $iOperatorCode => $aFilter)
|
||||
{
|
||||
foreach($aFilter as $sAlias => $oFilter)
|
||||
foreach($aFilter as $oFilter)
|
||||
{
|
||||
switch($iOperatorCode)
|
||||
{
|
||||
@@ -1023,7 +1129,7 @@ class DBObjectSearch
|
||||
$sFltCode = $oExpression->GetName();
|
||||
if (empty($sClassAlias))
|
||||
{
|
||||
// Try to find an alias
|
||||
// Need to find the right alias
|
||||
// Build an array of field => array of aliases
|
||||
$aFieldClasses = array();
|
||||
foreach($aClassAliases as $sAlias => $sReal)
|
||||
@@ -1033,29 +1139,8 @@ class DBObjectSearch
|
||||
$aFieldClasses[$sAnFltCode][] = $sAlias;
|
||||
}
|
||||
}
|
||||
if (!array_key_exists($sFltCode, $aFieldClasses))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown filter code', $sQuery, $oExpression->GetNameDetails(), array_keys($aFieldClasses));
|
||||
}
|
||||
if (count($aFieldClasses[$sFltCode]) > 1)
|
||||
{
|
||||
throw new OqlNormalizeException('Ambiguous filter code', $sQuery, $oExpression->GetNameDetails());
|
||||
}
|
||||
$sClassAlias = $aFieldClasses[$sFltCode][0];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!array_key_exists($sClassAlias, $aClassAliases))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown class [alias]', $sQuery, $oExpression->GetParentDetails(), array_keys($aClassAliases));
|
||||
}
|
||||
$sClass = $aClassAliases[$sClassAlias];
|
||||
if (!MetaModel::IsValidFilterCode($sClass, $sFltCode))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown filter code', $sQuery, $oExpression->GetNameDetails(), MetaModel::GetFiltersList($sClass));
|
||||
}
|
||||
}
|
||||
|
||||
return new FieldExpression($sFltCode, $sClassAlias);
|
||||
}
|
||||
elseif ($oExpression instanceof VariableOqlExpression)
|
||||
@@ -1126,20 +1211,23 @@ class DBObjectSearch
|
||||
if ($bOQLCacheEnabled && array_key_exists($sQuery, self::$m_aOQLQueries))
|
||||
{
|
||||
// hit!
|
||||
return clone self::$m_aOQLQueries[$sQuery];
|
||||
$oClone = self::$m_aOQLQueries[$sQuery]->DeepClone();
|
||||
if (!is_null($aParams))
|
||||
{
|
||||
$oClone->m_aParams = $aParams;
|
||||
}
|
||||
return $oClone;
|
||||
}
|
||||
|
||||
$oOql = new OqlInterpreter($sQuery);
|
||||
$oOqlQuery = $oOql->ParseObjectQuery();
|
||||
|
||||
|
||||
$oMetaModel = new ModelReflectionRuntime();
|
||||
$oOqlQuery->Check($oMetaModel, $sQuery); // Exceptions thrown in case of issue
|
||||
|
||||
$sClass = $oOqlQuery->GetClass();
|
||||
$sClassAlias = $oOqlQuery->GetClassAlias();
|
||||
|
||||
if (!MetaModel::IsValidClass($sClass))
|
||||
{
|
||||
throw new UnknownClassOqlException($sQuery, $oOqlQuery->GetClassDetails(), MetaModel::GetClasses());
|
||||
}
|
||||
|
||||
$oResultFilter = new DBObjectSearch($sClass, $sClassAlias);
|
||||
$aAliases = array($sClassAlias => $sClass);
|
||||
|
||||
@@ -1155,21 +1243,6 @@ class DBObjectSearch
|
||||
{
|
||||
$sJoinClass = $oJoinSpec->GetClass();
|
||||
$sJoinClassAlias = $oJoinSpec->GetClassAlias();
|
||||
if (!MetaModel::IsValidClass($sJoinClass))
|
||||
{
|
||||
throw new UnknownClassOqlException($sQuery, $oJoinSpec->GetClassDetails(), MetaModel::GetClasses());
|
||||
}
|
||||
if (array_key_exists($sJoinClassAlias, $aAliases))
|
||||
{
|
||||
if ($sJoinClassAlias != $sJoinClass)
|
||||
{
|
||||
throw new OqlNormalizeException('Duplicate class alias', $sQuery, $oJoinSpec->GetClassAliasDetails());
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new OqlNormalizeException('Duplicate class name', $sQuery, $oJoinSpec->GetClassDetails());
|
||||
}
|
||||
}
|
||||
|
||||
// Assumption: ext key on the left only !!!
|
||||
// normalization should take care of this
|
||||
@@ -1179,32 +1252,17 @@ class DBObjectSearch
|
||||
|
||||
$oRightField = $oJoinSpec->GetRightField();
|
||||
$sToClass = $oRightField->GetParent();
|
||||
$sPKeyDescriptor = $oRightField->GetName();
|
||||
if ($sPKeyDescriptor != 'id')
|
||||
{
|
||||
throw new OqlNormalizeException('Wrong format for Join clause (right hand), expecting an id', $sQuery, $oRightField->GetNameDetails(), array('id'));
|
||||
}
|
||||
|
||||
$aAliases[$sJoinClassAlias] = $sJoinClass;
|
||||
$aJoinItems[$sJoinClassAlias] = new DBObjectSearch($sJoinClass, $sJoinClassAlias);
|
||||
|
||||
if (!array_key_exists($sFromClass, $aJoinItems))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown class in join condition (left expression)', $sQuery, $oLeftField->GetParentDetails(), array_keys($aJoinItems));
|
||||
}
|
||||
if (!array_key_exists($sToClass, $aJoinItems))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown class in join condition (right expression)', $sQuery, $oRightField->GetParentDetails(), array_keys($aJoinItems));
|
||||
}
|
||||
$aExtKeys = array_keys(MetaModel::GetExternalKeys($aAliases[$sFromClass]));
|
||||
if (!in_array($sExtKeyAttCode, $aExtKeys))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown external key in join condition (left expression)', $sQuery, $oLeftField->GetNameDetails(), $aExtKeys);
|
||||
}
|
||||
|
||||
if ($sFromClass == $sJoinClassAlias)
|
||||
{
|
||||
$aJoinItems[$sToClass]->AddCondition_ReferencedBy($aJoinItems[$sFromClass], $sExtKeyAttCode);
|
||||
$oReceiver = $aJoinItems[$sToClass];
|
||||
$oNewComer = $aJoinItems[$sFromClass];
|
||||
|
||||
$aAliasTranslation = array();
|
||||
$oReceiver->AddCondition_ReferencedBy_InNameSpace($oNewComer, $sExtKeyAttCode, $oReceiver->m_aClasses, $aAliasTranslation);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1239,7 +1297,11 @@ class DBObjectSearch
|
||||
$iOperatorCode = TREE_OPERATOR_NOT_ABOVE_STRICT;
|
||||
break;
|
||||
}
|
||||
$aJoinItems[$sFromClass]->AddCondition_PointingTo($aJoinItems[$sToClass], $sExtKeyAttCode, $iOperatorCode);
|
||||
$oReceiver = $aJoinItems[$sFromClass];
|
||||
$oNewComer = $aJoinItems[$sToClass];
|
||||
|
||||
$aAliasTranslation = array();
|
||||
$oReceiver->AddCondition_PointingTo_InNameSpace($oNewComer, $sExtKeyAttCode, $oReceiver->m_aClasses, $aAliasTranslation, $iOperatorCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1249,10 +1311,6 @@ class DBObjectSearch
|
||||
foreach ($oOqlQuery->GetSelectedClasses() as $oClassDetails)
|
||||
{
|
||||
$sClassToSelect = $oClassDetails->GetValue();
|
||||
if (!array_key_exists($sClassToSelect, $aAliases))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown class [alias]', $sQuery, $oClassDetails, array_keys($aAliases));
|
||||
}
|
||||
$aSelected[$sClassToSelect] = $aAliases[$sClassToSelect];
|
||||
}
|
||||
$oResultFilter->m_aClasses = $aAliases;
|
||||
@@ -1271,7 +1329,7 @@ class DBObjectSearch
|
||||
|
||||
if ($bOQLCacheEnabled)
|
||||
{
|
||||
self::$m_aOQLQueries[$sQuery] = clone $oResultFilter;
|
||||
self::$m_aOQLQueries[$sQuery] = $oResultFilter->DeepClone();
|
||||
}
|
||||
|
||||
return $oResultFilter;
|
||||
@@ -1288,4 +1346,4 @@ class DBObjectSearch
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
?>
|
||||
|
||||
@@ -1,47 +1,62 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2014 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Object set management
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* A set of persistent objects, could be heterogeneous
|
||||
* A set of persistent objects, could be heterogeneous as long as the objects in the set have a common ancestor class
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class DBObjectSet
|
||||
{
|
||||
private $m_oFilter;
|
||||
private $m_aAddedIds; // Ids of objects added (discrete lists)
|
||||
private $m_aOrderBy;
|
||||
protected $m_aAddedIds; // Ids of objects added (discrete lists)
|
||||
protected $m_aAddedObjects;
|
||||
protected $m_aArgs;
|
||||
protected $m_aAttToLoad;
|
||||
protected $m_aOrderBy;
|
||||
public $m_bLoaded;
|
||||
private $m_aData;
|
||||
private $m_aId2Row;
|
||||
private $m_iCurrRow;
|
||||
protected $m_iNumTotalDBRows;
|
||||
protected $m_iNumLoadedDBRows;
|
||||
protected $m_iCurrRow;
|
||||
protected $m_oFilter;
|
||||
protected $m_oSQLResult;
|
||||
|
||||
/**
|
||||
* Create a new set based on a Search definition.
|
||||
*
|
||||
* @param DBObjectSearch $oFilter The search filter defining the objects which are part of the set (multiple columns/objects per row are supported)
|
||||
* @param hash $aOrderBy
|
||||
* @param hash $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)
|
||||
*/
|
||||
public function __construct(DBObjectSearch $oFilter, $aOrderBy = array(), $aArgs = array(), $aExtendedDataSpec = null, $iLimitCount = 0, $iLimitStart = 0)
|
||||
{
|
||||
$this->m_oFilter = $oFilter;
|
||||
$this->m_oFilter = $oFilter->DeepClone();
|
||||
$this->m_aAddedIds = array();
|
||||
$this->m_aOrderBy = $aOrderBy;
|
||||
$this->m_aArgs = $aArgs;
|
||||
@@ -50,15 +65,20 @@ class DBObjectSet
|
||||
$this->m_iLimitCount = $iLimitCount;
|
||||
$this->m_iLimitStart = $iLimitStart;
|
||||
|
||||
$this->m_iCount = null; // null if unknown yet
|
||||
$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_aData = array(); // array of (row => array of (classalias) => object/null)
|
||||
$this->m_aId2Row = array(); // array of (pkey => index in m_aData)
|
||||
$this->m_aAddedObjects = array(); // array of (row => array of (classalias) => object/null) storing the objects added "in memory"
|
||||
$this->m_iCurrRow = 0;
|
||||
$this->m_oSQLResult = null;
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
if (is_object($this->m_oSQLResult))
|
||||
{
|
||||
$this->m_oSQLResult->free();
|
||||
}
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
@@ -81,6 +101,35 @@ class DBObjectSet
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
public function __clone()
|
||||
{
|
||||
$this->m_oFilter = $this->m_oFilter->DeepClone();
|
||||
|
||||
$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_iCurrRow = 0;
|
||||
$this->m_oSQLResult = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when unserializing a DBObjectSet
|
||||
*/
|
||||
public function __wakeup()
|
||||
{
|
||||
$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_iCurrRow = 0;
|
||||
$this->m_oSQLResult = null;
|
||||
}
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
if (is_null($aAttToLoad))
|
||||
@@ -91,19 +140,22 @@ class DBObjectSet
|
||||
{
|
||||
// Complete the attribute list with the attribute codes
|
||||
$aAttToLoadWithAttDef = array();
|
||||
foreach($aAttToLoad as $sClassAlias => $aAttList)
|
||||
foreach($this->m_oFilter->GetSelectedClasses() as $sClassAlias => $sClass)
|
||||
{
|
||||
$aSelectedClasses = $this->m_oFilter->GetSelectedClasses();
|
||||
$sClass = $aSelectedClasses[$sClassAlias];
|
||||
foreach($aAttList as $sAttToLoad)
|
||||
$aAttToLoadWithAttDef[$sClassAlias] = array();
|
||||
if (array_key_exists($sClassAlias, $aAttToLoad))
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttToLoad);
|
||||
$aAttToLoadWithAttDef[$sClassAlias][$sAttToLoad] = $oAttDef;
|
||||
if ($oAttDef->IsExternalKey())
|
||||
$aAttList = $aAttToLoad[$sClassAlias];
|
||||
foreach($aAttList as $sAttToLoad)
|
||||
{
|
||||
// Add the external key friendly name anytime
|
||||
$oFriendlyNameAttDef = MetaModel::GetAttributeDef($sClass, $sAttToLoad.'_friendlyname');
|
||||
$aAttToLoadWithAttDef[$sClassAlias][$sAttToLoad.'_friendlyname'] = $oFriendlyNameAttDef;
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttToLoad);
|
||||
$aAttToLoadWithAttDef[$sClassAlias][$sAttToLoad] = $oAttDef;
|
||||
if ($oAttDef->IsExternalKey())
|
||||
{
|
||||
// Add the external key friendly name anytime
|
||||
$oFriendlyNameAttDef = MetaModel::GetAttributeDef($sClass, $sAttToLoad.'_friendlyname');
|
||||
$aAttToLoadWithAttDef[$sClassAlias][$sAttToLoad.'_friendlyname'] = $oFriendlyNameAttDef;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Add the friendly name anytime
|
||||
@@ -111,7 +163,7 @@ class DBObjectSet
|
||||
$aAttToLoadWithAttDef[$sClassAlias]['friendlyname'] = $oFriendlyNameAttDef;
|
||||
|
||||
// Make sure that the final class is requested anytime, whatever the specification (needed for object construction!)
|
||||
if (!MetaModel::IsStandaloneClass($sClass) && !array_key_exists('finalclass', $aAttList))
|
||||
if (!MetaModel::IsStandaloneClass($sClass) && !array_key_exists('finalclass', $aAttToLoadWithAttDef[$sClassAlias]))
|
||||
{
|
||||
$aAttToLoadWithAttDef[$sClassAlias]['finalclass'] = MetaModel::GetAttributeDef($sClass, 'finalclass');
|
||||
}
|
||||
@@ -121,6 +173,13 @@ class DBObjectSet
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a set (in-memory) containing just the given object
|
||||
*
|
||||
* @param DBobject $oObject
|
||||
*
|
||||
* @return DBObjectSet The singleton set
|
||||
*/
|
||||
static public function FromObject($oObject)
|
||||
{
|
||||
$oRetSet = self::FromScratch(get_class($oObject));
|
||||
@@ -128,17 +187,31 @@ class DBObjectSet
|
||||
return $oRetSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an empty set (in-memory), for the given class (and its subclasses) of objects
|
||||
*
|
||||
* @param string $sClass The class (or an ancestor) for the objects to be added in this set
|
||||
*
|
||||
* @return DBObject The empty set
|
||||
*/
|
||||
static public function FromScratch($sClass)
|
||||
{
|
||||
$oFilter = new DBObjectSearch($sClass);
|
||||
$oFilter->AddConditionExpression(new FalseExpression());
|
||||
$oRetSet = new self($oFilter);
|
||||
$oRetSet->m_bLoaded = true; // no DB load
|
||||
$oRetSet->m_iNumTotalDBRows = 0; // Nothing from the DB
|
||||
return $oRetSet;
|
||||
}
|
||||
|
||||
// create an object set ex nihilo
|
||||
// input = array of objects
|
||||
/**
|
||||
* Create a set (in-memory) with just one column (i.e. one object per row) and filled with the given array of objects
|
||||
*
|
||||
* @param string $sClass The class of the objects (must be a common ancestor to all objects in the set)
|
||||
* @param array $aObjects The list of objects to add into the set
|
||||
*
|
||||
* @return DBObjectSet
|
||||
*/
|
||||
static public function FromArray($sClass, $aObjects)
|
||||
{
|
||||
$oRetSet = self::FromScratch($sClass);
|
||||
@@ -146,21 +219,30 @@ class DBObjectSet
|
||||
return $oRetSet;
|
||||
}
|
||||
|
||||
// create an object set ex nihilo
|
||||
// aClasses = array of (alias => class)
|
||||
// input = array of (array of (classalias => object))
|
||||
/**
|
||||
* Create a set in-memory with several classes of objects per row (with one alias per "column")
|
||||
*
|
||||
* Limitation:
|
||||
* The filter/OQL query representing such a set can not be rebuilt (only the first column will be taken into account)
|
||||
*
|
||||
* @param hash $aClasses Format: array of (alias => class)
|
||||
* @param hash $aObjects Format: array of (array of (classalias => object))
|
||||
*
|
||||
* @return DBObjectSet
|
||||
*/
|
||||
static public function FromArrayAssoc($aClasses, $aObjects)
|
||||
{
|
||||
// In a perfect world, we should create a complete tree of DBObjectSearch,
|
||||
// but as we lack most of the information related to the objects,
|
||||
// let's create one search definition
|
||||
// let's create one search definition corresponding only to the first column
|
||||
$sClass = reset($aClasses);
|
||||
$sAlias = key($aClasses);
|
||||
$oFilter = new CMDBSearchFilter($sClass, $sAlias);
|
||||
|
||||
$oRetSet = new self($oFilter);
|
||||
$oRetSet->m_bLoaded = true; // no DB load
|
||||
|
||||
$oRetSet->m_iNumTotalDBRows = 0; // Nothing from the DB
|
||||
|
||||
foreach($aObjects as $rowIndex => $aObjectsByClassAlias)
|
||||
{
|
||||
$oRetSet->AddObjectExtended($aObjectsByClassAlias);
|
||||
@@ -205,11 +287,13 @@ class DBObjectSet
|
||||
public function ToArrayOfValues()
|
||||
{
|
||||
if (!$this->m_bLoaded) $this->Load();
|
||||
$this->Rewind();
|
||||
|
||||
$aSelectedClasses = $this->m_oFilter->GetSelectedClasses();
|
||||
|
||||
$aRet = array();
|
||||
foreach($this->m_aData as $iRow => $aObjects)
|
||||
$iRow = 0;
|
||||
while($aObjects = $this->FetchAssoc())
|
||||
{
|
||||
foreach($aObjects as $sClassAlias => $oObject)
|
||||
{
|
||||
@@ -245,6 +329,7 @@ class DBObjectSet
|
||||
}
|
||||
}
|
||||
}
|
||||
$iRow++;
|
||||
}
|
||||
return $aRet;
|
||||
}
|
||||
@@ -267,16 +352,28 @@ class DBObjectSet
|
||||
return $aRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the DBObjectSearch corresponding to the objects present in this set
|
||||
*
|
||||
* Limitation:
|
||||
* This method will NOT work for sets with several columns (i.e. several objects per row)
|
||||
*
|
||||
* @return DBObjectSearch
|
||||
*/
|
||||
public function GetFilter()
|
||||
{
|
||||
// Make sure that we carry on the parameters of the set with the filter
|
||||
$oFilter = $this->m_oFilter->DeepClone();
|
||||
// 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(), MetaModel::PrepareQueryArguments($this->m_aArgs)));
|
||||
|
||||
if (count($this->m_aAddedIds) == 0)
|
||||
{
|
||||
return $this->m_oFilter;
|
||||
return $oFilter;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oFilter = clone $this->m_oFilter;
|
||||
|
||||
$oIdListExpr = ListExpression::FromScalars(array_keys($this->m_aAddedIds));
|
||||
$oIdExpr = new FieldExpression('id', $oFilter->GetClassAlias());
|
||||
$oIdInList = new BinaryExpression($oIdExpr, 'IN', $oIdListExpr);
|
||||
@@ -285,37 +382,72 @@ class DBObjectSet
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The (common ancestor) class of the objects in the first column of this set
|
||||
*
|
||||
* @return string The class of the objects in the first column
|
||||
*/
|
||||
public function GetClass()
|
||||
{
|
||||
return $this->m_oFilter->GetClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* The alias for the class of the objects in the first column of this set
|
||||
*
|
||||
* @return string The alias of the class in the first column
|
||||
*/
|
||||
public function GetClassAlias()
|
||||
{
|
||||
return $this->m_oFilter->GetClassAlias();
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of all classes (one per column) which are part of this set
|
||||
*
|
||||
* @return hash Format: alias => class
|
||||
*/
|
||||
public function GetSelectedClasses()
|
||||
{
|
||||
return $this->m_oFilter->GetSelectedClasses();
|
||||
}
|
||||
|
||||
/**
|
||||
* The root class (i.e. highest ancestor in the MeaModel class hierarchy) for the first column on this set
|
||||
*
|
||||
* @return string The root class for the objects in the first column of the set
|
||||
*/
|
||||
public function GetRootClass()
|
||||
{
|
||||
return MetaModel::GetRootClass($this->GetClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* The arguments used for building this set
|
||||
*
|
||||
* @return hash Format: parameter_name => value
|
||||
*/
|
||||
public function GetArgs()
|
||||
{
|
||||
return $this->m_aArgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the limits for loading the rows from the DB. Equivalent to MySQL's LIMIT start,count clause.
|
||||
* @param int $iLimitCount The number of rows to load
|
||||
* @param int $iLimitStart The index of the first row to load
|
||||
*/
|
||||
public function SetLimit($iLimitCount, $iLimitStart = 0)
|
||||
{
|
||||
$this->m_iLimitCount = $iLimitCount;
|
||||
$this->m_iLimitStart = $iLimitStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the sort order for loading the rows from the DB. Changing the order by causes a Reload.
|
||||
*
|
||||
* @param hash $aOrderBy Format: field_code => boolean (true = ascending, false = descending)
|
||||
*/
|
||||
public function SetOrderBy($aOrderBy)
|
||||
{
|
||||
if ($this->m_aOrderBy != $aOrderBy)
|
||||
@@ -329,16 +461,33 @@ class DBObjectSet
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the 'count' limit for loading the rows from the DB
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function GetLimitCount()
|
||||
{
|
||||
return $this->m_iLimitCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the 'start' limit for loading the rows from the DB
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function GetLimitStart()
|
||||
{
|
||||
return $this->m_iLimitStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sort order used for loading this set from the database
|
||||
*
|
||||
* Limitation: the sort order has no effect on objects added in-memory
|
||||
*
|
||||
* @return hash Format: field_code => boolean (true = ascending, false = descending)
|
||||
*/
|
||||
public function GetRealSortOrder()
|
||||
{
|
||||
// Get the class default sort order if not specified with the API
|
||||
@@ -353,6 +502,9 @@ class DBObjectSet
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the set from the database. Actually performs the SQL query to retrieve the records from the DB.
|
||||
*/
|
||||
public function Load()
|
||||
{
|
||||
if ($this->m_bLoaded) return;
|
||||
@@ -367,87 +519,150 @@ class DBObjectSet
|
||||
{
|
||||
$sSQL = MetaModel::MakeSelectQuery($this->m_oFilter, $this->GetRealSortOrder(), $this->m_aArgs, $this->m_aAttToLoad, $this->m_aExtendedDataSpec);
|
||||
}
|
||||
$resQuery = CMDBSource::Query($sSQL);
|
||||
if (!$resQuery) return;
|
||||
|
||||
$sClass = $this->m_oFilter->GetClass();
|
||||
while ($aRow = CMDBSource::FetchArray($resQuery))
|
||||
|
||||
if (is_object($this->m_oSQLResult))
|
||||
{
|
||||
$aObjects = array();
|
||||
foreach ($this->m_oFilter->GetSelectedClasses() as $sClassAlias => $sClass)
|
||||
{
|
||||
if (is_null($aRow[$sClassAlias.'id']))
|
||||
{
|
||||
$oObject = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oObject = MetaModel::GetObjectByRow($sClass, $aRow, $sClassAlias, $this->m_aAttToLoad, $this->m_aExtendedDataSpec);
|
||||
}
|
||||
|
||||
$aObjects[$sClassAlias] = $oObject;
|
||||
}
|
||||
$this->AddObjectExtended($aObjects, true /* internal load */);
|
||||
// Free previous resultset if any
|
||||
$this->m_oSQLResult->free();
|
||||
$this->m_oSQLResult = null;
|
||||
}
|
||||
CMDBSource::FreeResult($resQuery);
|
||||
$this->m_iNumTotalDBRows = null;
|
||||
|
||||
$this->m_oSQLResult = CMDBSource::Query($sSQL);
|
||||
if ($this->m_oSQLResult === false) return;
|
||||
|
||||
if (($this->m_iLimitCount == 0) && ($this->m_iLimitStart == 0))
|
||||
{
|
||||
$this->m_iNumTotalDBRows = $this->m_oSQLResult->num_rows;
|
||||
}
|
||||
$this->m_iNumLoadedDBRows = $this->m_oSQLResult->num_rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public function Count()
|
||||
{
|
||||
if ($this->m_bLoaded && ($this->m_iLimitCount == 0) && ($this->m_iLimitStart == 0))
|
||||
if (is_null($this->m_iNumTotalDBRows))
|
||||
{
|
||||
return count($this->m_aData);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (is_null($this->m_iCount))
|
||||
{
|
||||
$sSQL = MetaModel::MakeSelectQuery($this->m_oFilter, array(), $this->m_aArgs, null, null, 0, 0, true);
|
||||
$resQuery = CMDBSource::Query($sSQL);
|
||||
if (!$resQuery) return 0;
|
||||
|
||||
$aRow = CMDBSource::FetchArray($resQuery);
|
||||
CMDBSource::FreeResult($resQuery);
|
||||
$this->m_iCount = $aRow['COUNT'];
|
||||
}
|
||||
return $this->m_iCount;
|
||||
$sSQL = MetaModel::MakeSelectQuery($this->m_oFilter, array(), $this->m_aArgs, null, null, 0, 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 ??
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of rows available in memory (loaded from DB + added in memory)
|
||||
*
|
||||
* @return number The number of rows available for Fetch'ing
|
||||
*/
|
||||
protected function CountLoaded()
|
||||
{
|
||||
return $this->m_iNumLoadedDBRows + count($this->m_aAddedObjects);
|
||||
}
|
||||
|
||||
public function Fetch($sClassAlias = '')
|
||||
/**
|
||||
* Fetch the object (with the given class alias) at the current position in the set and move the cursor to the next position.
|
||||
*
|
||||
* @param string $sRequestedClassAlias The class alias to fetch (if there are several objects/classes per row)
|
||||
* @return DBObject The fetched object or null when at the end
|
||||
*/
|
||||
public function Fetch($sRequestedClassAlias = '')
|
||||
{
|
||||
if (!$this->m_bLoaded) $this->Load();
|
||||
|
||||
if ($this->m_iCurrRow >= count($this->m_aData))
|
||||
if ($this->m_iCurrRow >= $this->CountLoaded())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (strlen($sClassAlias) == 0)
|
||||
if (strlen($sRequestedClassAlias) == 0)
|
||||
{
|
||||
$sClassAlias = $this->m_oFilter->GetClassAlias();
|
||||
$sRequestedClassAlias = $this->m_oFilter->GetClassAlias();
|
||||
}
|
||||
|
||||
if ($this->m_iCurrRow < $this->m_iNumLoadedDBRows)
|
||||
{
|
||||
// Pick the row from the database
|
||||
$aRow = CMDBSource::FetchArray($this->m_oSQLResult);
|
||||
foreach ($this->m_oFilter->GetSelectedClasses() as $sClassAlias => $sClass)
|
||||
{
|
||||
if ($sRequestedClassAlias == $sClassAlias)
|
||||
{
|
||||
if (is_null($aRow[$sClassAlias.'id']))
|
||||
{
|
||||
$oRetObj = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oRetObj = MetaModel::GetObjectByRow($sClass, $aRow, $sClassAlias, $this->m_aAttToLoad, $this->m_aExtendedDataSpec);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Pick the row from the objects added *in memory*
|
||||
$oRetObj = $this->m_aAddedObjects[$this->m_iCurrRow - $this->m_iNumLoadedDBRows][$sRequestedClassAlias];
|
||||
}
|
||||
$oRetObj = $this->m_aData[$this->m_iCurrRow][$sClassAlias];
|
||||
$this->m_iCurrRow++;
|
||||
return $oRetObj;
|
||||
}
|
||||
|
||||
// Return the whole line if several classes have been specified in the query
|
||||
//
|
||||
/**
|
||||
* Fetch the whole row of objects (if several classes have been specified in the query) and move the cursor to the next position
|
||||
*
|
||||
* @return hash A hash with the format 'classAlias' => $oObj representing the current row of the set. Returns null when at the end.
|
||||
*/
|
||||
public function FetchAssoc()
|
||||
{
|
||||
if (!$this->m_bLoaded) $this->Load();
|
||||
|
||||
if ($this->m_iCurrRow >= count($this->m_aData))
|
||||
if ($this->m_iCurrRow >= $this->CountLoaded())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
$aRetObjects = $this->m_aData[$this->m_iCurrRow];
|
||||
if ($this->m_iCurrRow < $this->m_iNumLoadedDBRows)
|
||||
{
|
||||
// Pick the row from the database
|
||||
$aRow = CMDBSource::FetchArray($this->m_oSQLResult);
|
||||
$aRetObjects = array();
|
||||
foreach ($this->m_oFilter->GetSelectedClasses() as $sClassAlias => $sClass)
|
||||
{
|
||||
if (is_null($aRow[$sClassAlias.'id']))
|
||||
{
|
||||
$oObj = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oObj = MetaModel::GetObjectByRow($sClass, $aRow, $sClassAlias, $this->m_aAttToLoad, $this->m_aExtendedDataSpec);
|
||||
}
|
||||
$aRetObjects[$sClassAlias] = $oObj;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Pick the row from the objects added *in memory*
|
||||
$oRetObj = $this->m_aAddedObjects[$this->m_iCurrRow - $this->m_iNumLoadedDBRows][$sRequestedClassAlias];
|
||||
}
|
||||
$this->m_iCurrRow++;
|
||||
return $aRetObjects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Position the cursor (for iterating in the set) to the first position (equivalent to Seek(0))
|
||||
*/
|
||||
public function Rewind()
|
||||
{
|
||||
if ($this->m_bLoaded)
|
||||
@@ -456,14 +671,32 @@ class DBObjectSet
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Position the cursor (for iterating in the set) to the given position
|
||||
*
|
||||
* @param int $iRow
|
||||
*/
|
||||
public function Seek($iRow)
|
||||
{
|
||||
if (!$this->m_bLoaded) $this->Load();
|
||||
|
||||
$this->m_iCurrRow = min($iRow, count($this->m_aData));
|
||||
$this->m_iCurrRow = min($iRow, $this->Count());
|
||||
if ($this->m_iCurrRow < $this->m_iNumLoadedDBRows)
|
||||
{
|
||||
$this->m_oSQLResult->data_seek($this->m_iCurrRow);
|
||||
}
|
||||
return $this->m_iCurrRow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an object to the current set (in-memory only, nothing is written to the database)
|
||||
*
|
||||
* Limitation:
|
||||
* Sets with several objects per row are NOT supported
|
||||
*
|
||||
* @param DBObject $oObject The object to add
|
||||
* @param string $sClassAlias The alias for the class of the object
|
||||
*/
|
||||
public function AddObject($oObject, $sClassAlias = '')
|
||||
{
|
||||
if (!$this->m_bLoaded) $this->Load();
|
||||
@@ -473,35 +706,52 @@ class DBObjectSet
|
||||
$sClassAlias = $this->m_oFilter->GetClassAlias();
|
||||
}
|
||||
|
||||
$iNextPos = count($this->m_aData);
|
||||
$this->m_aData[$iNextPos][$sClassAlias] = $oObject;
|
||||
$iNextPos = count($this->m_aAddedObjects);
|
||||
$this->m_aAddedObjects[$iNextPos][$sClassAlias] = $oObject;
|
||||
if (!is_null($oObject))
|
||||
{
|
||||
$this->m_aId2Row[$sClassAlias][$oObject->GetKey()] = $iNextPos;
|
||||
$this->m_aAddedIds[$oObject->GetKey()] = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected function AddObjectExtended($aObjectArray, $bInternalLoad = false)
|
||||
/**
|
||||
* Add a hash containig objects into the current set.
|
||||
*
|
||||
* The expected format for the hash is: $aObjectArray[$idx][$sClassAlias] => $oObject
|
||||
* Limitation:
|
||||
* The aliases MUST match the ones used in the current set
|
||||
* Only the ID of the objects associated to the first alias (column) is remembered.. in case we have to rebuild a filter
|
||||
*
|
||||
* @param hash $aObjectArray
|
||||
*/
|
||||
protected function AddObjectExtended($aObjectArray)
|
||||
{
|
||||
if (!$this->m_bLoaded) $this->Load();
|
||||
|
||||
$iNextPos = count($this->m_aData);
|
||||
$iNextPos = count($this->m_aAddedObjects);
|
||||
|
||||
$sFirstAlias = $this->m_oFilter->GetClassAlias();
|
||||
|
||||
foreach ($aObjectArray as $sClassAlias => $oObject)
|
||||
{
|
||||
$this->m_aData[$iNextPos][$sClassAlias] = $oObject;
|
||||
if (!is_null($oObject))
|
||||
$this->m_aAddedObjects[$iNextPos][$sClassAlias] = $oObject;
|
||||
|
||||
if (!is_null($oObject) && ($sFirstAlias == $sClassAlias))
|
||||
{
|
||||
$this->m_aId2Row[$sClassAlias][$oObject->GetKey()] = $iNextPos;
|
||||
if (!$bInternalLoad)
|
||||
{
|
||||
$this->m_aAddedIds[$oObject->GetKey()] = true;
|
||||
}
|
||||
$this->m_aAddedIds[$oObject->GetKey()] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an array of objects into the current set
|
||||
*
|
||||
* Limitation:
|
||||
* Sets with several classes per row are not supported (use AddObjectExtended instead)
|
||||
*
|
||||
* @param array $aObjects The array of objects to add
|
||||
* @param string $sClassAlias The Alias of the class for the added objects
|
||||
*/
|
||||
public function AddObjectArray($aObjects, $sClassAlias = '')
|
||||
{
|
||||
if (!$this->m_bLoaded) $this->Load();
|
||||
@@ -513,7 +763,16 @@ class DBObjectSet
|
||||
}
|
||||
}
|
||||
|
||||
public function Merge($oObjectSet)
|
||||
/**
|
||||
* Append a given set to the current object. (This method used to be named Merge)
|
||||
*
|
||||
* Limitation:
|
||||
* The added objects are not checked for duplicates (i.e. one cann add several times the same object, or add an object already present in the set).
|
||||
*
|
||||
* @param DBObjectSet $oObjectSet The set to append
|
||||
* @throws CoreException
|
||||
*/
|
||||
public function Append(DBObjectSet $oObjectSet)
|
||||
{
|
||||
if ($this->GetRootClass() != $oObjectSet->GetRootClass())
|
||||
{
|
||||
@@ -528,7 +787,18 @@ class DBObjectSet
|
||||
}
|
||||
}
|
||||
|
||||
public function CreateIntersect($oObjectSet)
|
||||
/**
|
||||
* Create a set containing the objects present in both the current set and another specified set
|
||||
*
|
||||
* Limitations:
|
||||
* Will NOT work if only a subset of the sets was loaded with SetLimit.
|
||||
* Works only with sets made of objects loaded from the database since the comparison is based on the objects identifiers
|
||||
*
|
||||
* @param DBObjectSet $oObjectSet The set to intersect with. The current position inside the set will be lost (= at the end)
|
||||
* @throws CoreException
|
||||
* @return DBObjectSet A new set of objects, containing the objects present in both sets (based on their identifier)
|
||||
*/
|
||||
public function CreateIntersect(DBObjectSet $oObjectSet)
|
||||
{
|
||||
if ($this->GetRootClass() != $oObjectSet->GetRootClass())
|
||||
{
|
||||
@@ -536,58 +806,132 @@ class DBObjectSet
|
||||
}
|
||||
if (!$this->m_bLoaded) $this->Load();
|
||||
|
||||
$aId2Row = array();
|
||||
$iCurrPos = $this->m_iCurrRow; // Save the cursor
|
||||
$idx = 0;
|
||||
while($oObj = $this->Fetch())
|
||||
{
|
||||
$aId2Row[$oObj->GetKey()] = $idx;
|
||||
$idx++;
|
||||
}
|
||||
|
||||
$oNewSet = DBObjectSet::FromScratch($this->GetClass());
|
||||
|
||||
$sClassAlias = $this->m_oFilter->GetClassAlias();
|
||||
$oObjectSet->Seek(0);
|
||||
while ($oObject = $oObjectSet->Fetch())
|
||||
{
|
||||
if (array_key_exists($oObject->GetKey(), $this->m_aId2Row[$sClassAlias]))
|
||||
if (array_key_exists($oObject->GetKey(), $aId2Row))
|
||||
{
|
||||
$oNewSet->AddObject($oObject);
|
||||
}
|
||||
}
|
||||
$this->Seek($iCurrPos); // Restore the cursor
|
||||
return $oNewSet;
|
||||
}
|
||||
|
||||
// Note: This verb works only with objects existing in the database
|
||||
//
|
||||
public function HasSameContents($oObjectSet)
|
||||
/**
|
||||
* Compare two sets of objects to determine if their content is identical or not.
|
||||
*
|
||||
* Limitation:
|
||||
* Works only on objects written to the DB, since we rely on their identifiers
|
||||
*
|
||||
* @param DBObjectSet $oObjectSet
|
||||
* @return boolean True if the sets are identical, false otherwise
|
||||
*/
|
||||
public function HasSameContents(DBObjectSet $oObjectSet)
|
||||
{
|
||||
if ($this->GetRootClass() != $oObjectSet->GetRootClass())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!$this->m_bLoaded) $this->Load();
|
||||
|
||||
if ($this->Count() != $oObjectSet->Count())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
$sClassAlias = $this->m_oFilter->GetClassAlias();
|
||||
|
||||
$aId2Row = array();
|
||||
$bRet = true;
|
||||
$iCurrPos = $this->m_iCurrRow; // Save the cursor
|
||||
$idx = 0;
|
||||
|
||||
// Optimization: we retain the first $iMaxObjects objects in memory
|
||||
// to speed up the comparison of small sets (see below: $oObject->Equals($oSibling))
|
||||
$iMaxObjects = 20;
|
||||
$aCachedObjects = array();
|
||||
while($oObj = $this->Fetch())
|
||||
{
|
||||
$aId2Row[$oObj->GetKey()] = $idx;
|
||||
if ($idx <= $iMaxObjects)
|
||||
{
|
||||
$aCachedObjects[$idx] = $oObj;
|
||||
}
|
||||
$idx++;
|
||||
}
|
||||
|
||||
$oObjectSet->Rewind();
|
||||
while ($oObject = $oObjectSet->Fetch())
|
||||
{
|
||||
$iObjectKey = $oObject->GetKey();
|
||||
if ($iObjectKey < 0)
|
||||
{
|
||||
return false;
|
||||
$bRet = false;
|
||||
break;
|
||||
}
|
||||
if (!array_key_exists($iObjectKey, $this->m_aId2Row[$sClassAlias]))
|
||||
if (!array_key_exists($iObjectKey, $aId2Row))
|
||||
{
|
||||
return false;
|
||||
$bRet = false;
|
||||
break;
|
||||
}
|
||||
$iRow = $aId2Row[$iObjectKey];
|
||||
if (array_key_exists($iRow, $aCachedObjects))
|
||||
{
|
||||
// Cache hit
|
||||
$oSibling = $aCachedObjects[$iRow];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Go fetch it from the DB, unless it's an object added in-memory
|
||||
$oSibling = $this->GetObjectAt($iRow);
|
||||
}
|
||||
$iRow = $this->m_aId2Row[$sClassAlias][$iObjectKey];
|
||||
$oSibling = $this->m_aData[$iRow][$sClassAlias];
|
||||
if (!$oObject->Equals($oSibling))
|
||||
{
|
||||
return false;
|
||||
$bRet = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
$this->Seek($iCurrPos); // Restore the cursor
|
||||
return $bRet;
|
||||
}
|
||||
|
||||
public function CreateDelta($oObjectSet)
|
||||
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
|
||||
*
|
||||
* Limitations:
|
||||
* The objects inside the set must be written in the database since the comparison is based on their identifiers
|
||||
* Sets with several objects per row are NOT supported
|
||||
*
|
||||
* @param DBObjectSet $oObjectSet
|
||||
* @throws CoreException
|
||||
*
|
||||
* @return DBObjectSet The "delta" set.
|
||||
*/
|
||||
public function CreateDelta(DBObjectSet $oObjectSet)
|
||||
{
|
||||
if ($this->GetRootClass() != $oObjectSet->GetRootClass())
|
||||
{
|
||||
@@ -595,20 +939,37 @@ class DBObjectSet
|
||||
}
|
||||
if (!$this->m_bLoaded) $this->Load();
|
||||
|
||||
$aId2Row = array();
|
||||
$iCurrPos = $this->m_iCurrRow; // Save the cursor
|
||||
$idx = 0;
|
||||
while($oObj = $this->Fetch())
|
||||
{
|
||||
$aId2Row[$oObj->GetKey()] = $idx;
|
||||
$idx++;
|
||||
}
|
||||
|
||||
$oNewSet = DBObjectSet::FromScratch($this->GetClass());
|
||||
|
||||
$sClassAlias = $this->m_oFilter->GetClassAlias();
|
||||
$oObjectSet->Seek(0);
|
||||
while ($oObject = $oObjectSet->Fetch())
|
||||
{
|
||||
if (!array_key_exists($oObject->GetKey(), $this->m_aId2Row[$sClassAlias]))
|
||||
if (!array_key_exists($oObject->GetKey(), $aId2Row))
|
||||
{
|
||||
$oNewSet->AddObject($oObject);
|
||||
}
|
||||
}
|
||||
$this->Seek($iCurrPos); // Restore the cursor
|
||||
return $oNewSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the "RelatedObjects" (for the given relation, as defined by MetaModel::GetRelatedObjects) for a whole set of DBObjects
|
||||
*
|
||||
* @param string $sRelCode The code of the relation to use for the computation
|
||||
* @param int $iMaxDepth Teh maximum recursion depth
|
||||
*
|
||||
* @return Array An array containg all the "related" objects
|
||||
*/
|
||||
public function GetRelatedObjects($sRelCode, $iMaxDepth = 99)
|
||||
{
|
||||
$aRelatedObjs = array();
|
||||
@@ -637,7 +998,7 @@ class DBObjectSet
|
||||
* in the set. If for a given attribute, objects in the set have various values
|
||||
* then the resulting object will contain null for this value.
|
||||
* @param $aValues Hash Output: the distribution of the values, in the set, for each attribute
|
||||
* @return Object
|
||||
* @return DBObject The object with the common values
|
||||
*/
|
||||
public function ComputeCommonObject(&$aValues)
|
||||
{
|
||||
@@ -733,14 +1094,8 @@ class DBObjectSet
|
||||
{
|
||||
foreach($aVals as $sCode => $oExpr)
|
||||
{
|
||||
if ($oExpr instanceof ScalarExpression)
|
||||
{
|
||||
$aConst[$sClassAlias][$sCode] = $oExpr->GetValue();
|
||||
}
|
||||
else //Variable
|
||||
{
|
||||
$aConst[$sClassAlias][$sCode] = $aScalarArgs[$oExpr->GetName()];
|
||||
}
|
||||
$oScalarExpr = $oExpr->GetAsScalar($aScalarArgs);
|
||||
$aConst[$sClassAlias][$sCode] = $oScalarExpr->GetValue();
|
||||
}
|
||||
}
|
||||
return $aConst;
|
||||
@@ -753,11 +1108,23 @@ class DBObjectSet
|
||||
{
|
||||
if (MetaModel::IsValidObject($value))
|
||||
{
|
||||
$aScalarArgs = array_merge($aScalarArgs, $value->ToArgs($sArgName));
|
||||
if (strpos($sArgName, '->object()') === false)
|
||||
{
|
||||
// Lazy syntax - develop the object contextual parameters
|
||||
$aScalarArgs = array_merge($aScalarArgs, $value->ToArgsForQuery($sArgName));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Leave as is
|
||||
$aScalarArgs[$sArgName] = $value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$aScalarArgs[$sArgName] = (string) $value;
|
||||
if (!is_array($value)) // Sometimes ExtraParams contains a mix (like defaults[]) so non scalar parameters are ignored
|
||||
{
|
||||
$aScalarArgs[$sArgName] = (string) $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
$aScalarArgs['current_contact_id'] = UserRights::GetContactId();
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Database properties - manage database instances in a complex installation
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// 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 dbObject: the root of persistent classes
|
||||
* Algorithm to delete object(s) and maintain data integrity
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2013 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
class DeleteException extends CoreException
|
||||
@@ -81,6 +82,9 @@ class DeletionPlan
|
||||
|
||||
public function ComputeResults()
|
||||
{
|
||||
$this->m_iToDelete = 0;
|
||||
$this->m_iToUpdate = 0;
|
||||
|
||||
foreach($this->m_aToDelete as $sClass => $aToDelete)
|
||||
{
|
||||
foreach($aToDelete as $iId => $aData)
|
||||
@@ -103,10 +107,15 @@ class DeletionPlan
|
||||
}
|
||||
}
|
||||
|
||||
// Getting and setting time limit are not symetric:
|
||||
// www.php.net/manual/fr/function.set-time-limit.php#72305
|
||||
$iPreviousTimeLimit = ini_get('max_execution_time');
|
||||
$iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop');
|
||||
foreach($this->m_aToUpdate as $sClass => $aToUpdate)
|
||||
{
|
||||
foreach($aToUpdate as $iId => $aData)
|
||||
{
|
||||
set_time_limit($iLoopTimeLimit);
|
||||
$this->m_iToUpdate++;
|
||||
|
||||
$oObject = $aData['to_reset'];
|
||||
@@ -130,9 +139,9 @@ class DeletionPlan
|
||||
$this->m_bFoundSecurityIssue = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
set_time_limit($iPreviousTimeLimit);
|
||||
}
|
||||
|
||||
public function GetIssues()
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// 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 Dict
|
||||
* Management of localizable strings
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
class DictException extends CoreException
|
||||
@@ -120,7 +120,7 @@ class Dict
|
||||
}
|
||||
|
||||
|
||||
public static function S($sStringCode, $sDefault = null)
|
||||
public static function S($sStringCode, $sDefault = null, $bUserLanguageOnly = false)
|
||||
{
|
||||
// Attempt to find the string in the user language
|
||||
//
|
||||
@@ -134,19 +134,22 @@ class Dict
|
||||
{
|
||||
return $aCurrentDictionary[$sStringCode];
|
||||
}
|
||||
// Attempt to find the string in the default language
|
||||
//
|
||||
$aDefaultDictionary = self::$m_aData[self::$m_sDefaultLanguage];
|
||||
if (array_key_exists($sStringCode, $aDefaultDictionary))
|
||||
if (!$bUserLanguageOnly)
|
||||
{
|
||||
return $aDefaultDictionary[$sStringCode];
|
||||
}
|
||||
// Attempt to find the string in english
|
||||
//
|
||||
$aDefaultDictionary = self::$m_aData['EN US'];
|
||||
if (array_key_exists($sStringCode, $aDefaultDictionary))
|
||||
{
|
||||
return $aDefaultDictionary[$sStringCode];
|
||||
// Attempt to find the string in the default language
|
||||
//
|
||||
$aDefaultDictionary = self::$m_aData[self::$m_sDefaultLanguage];
|
||||
if (array_key_exists($sStringCode, $aDefaultDictionary))
|
||||
{
|
||||
return $aDefaultDictionary[$sStringCode];
|
||||
}
|
||||
// Attempt to find the string in english
|
||||
//
|
||||
$aDefaultDictionary = self::$m_aData['EN US'];
|
||||
if (array_key_exists($sStringCode, $aDefaultDictionary))
|
||||
{
|
||||
return $aDefaultDictionary[$sStringCode];
|
||||
}
|
||||
}
|
||||
// Could not find the string...
|
||||
//
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Send an email (abstraction for synchronous/asynchronous modes)
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
require_once(APPROOT.'/lib/swiftmailer/lib/swift_required.php');
|
||||
@@ -32,10 +33,15 @@ define ('EMAIL_SEND_OK', 0);
|
||||
define ('EMAIL_SEND_PENDING', 1);
|
||||
define ('EMAIL_SEND_ERROR', 2);
|
||||
|
||||
|
||||
class EMail
|
||||
{
|
||||
// Serialization formats
|
||||
const ORIGINAL_FORMAT = 1; // Original format, consisting in serializing the whole object, inculding the Swift Mailer's object.
|
||||
// Did not work with attachements since their binary representation cannot be stored as a valid UTF-8 string
|
||||
const FORMAT_V2 = 2; // New format, only the raw data are serialized (base64 encoded if needed)
|
||||
|
||||
protected static $m_oConfig = null;
|
||||
protected $m_aData; // For storing data to serialize
|
||||
|
||||
public function LoadConfig($sConfigFile = ITOP_DEFAULT_CONFIG_FILE)
|
||||
{
|
||||
@@ -45,17 +51,95 @@ class EMail
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected $m_oMessage;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->m_aData = array();
|
||||
$this->m_oMessage = Swift_Message::newInstance();
|
||||
|
||||
$oEncoder = new Swift_Mime_ContentEncoder_PlainContentEncoder('8bit');
|
||||
$this->m_oMessage->setEncoder($oEncoder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom serialization method
|
||||
* No longer use the brute force "serialize" method since
|
||||
* 1) It does not work with binary attachments (since they cannot be stored in a UTF-8 text field)
|
||||
* 2) The size tends to be quite big (sometimes ten times the size of the email)
|
||||
*/
|
||||
public function SerializeV2()
|
||||
{
|
||||
return serialize($this->m_aData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom de-serialization method
|
||||
* @param string $sSerializedMessage The serialized representation of the message
|
||||
*/
|
||||
static public function UnSerializeV2($sSerializedMessage)
|
||||
{
|
||||
$aData = unserialize($sSerializedMessage);
|
||||
$oMessage = new Email();
|
||||
|
||||
if (array_key_exists('body', $aData))
|
||||
{
|
||||
$oMessage->SetBody($aData['body']['body'], $aData['body']['mimeType']);
|
||||
}
|
||||
if (array_key_exists('message_id', $aData))
|
||||
{
|
||||
$oMessage->SetMessageId($aData['message_id']);
|
||||
}
|
||||
if (array_key_exists('bcc', $aData))
|
||||
{
|
||||
$oMessage->SetRecipientBCC($aData['bcc']);
|
||||
}
|
||||
if (array_key_exists('cc', $aData))
|
||||
{
|
||||
$oMessage->SetRecipientCC($aData['cc']);
|
||||
}
|
||||
if (array_key_exists('from', $aData))
|
||||
{
|
||||
$oMessage->SetRecipientFrom($aData['from']['address'], $aData['from']['label']);
|
||||
}
|
||||
if (array_key_exists('reply_to', $aData))
|
||||
{
|
||||
$oMessage->SetRecipientReplyTo($aData['reply_to']);
|
||||
}
|
||||
if (array_key_exists('to', $aData))
|
||||
{
|
||||
$oMessage->SetRecipientTO($aData['to']);
|
||||
}
|
||||
if (array_key_exists('subject', $aData))
|
||||
{
|
||||
$oMessage->SetSubject($aData['subject']);
|
||||
}
|
||||
|
||||
|
||||
if (array_key_exists('headers', $aData))
|
||||
{
|
||||
foreach($aData['headers'] as $sKey => $sValue)
|
||||
{
|
||||
$oMessage->AddToHeader($sKey, $sValue);
|
||||
}
|
||||
}
|
||||
if (array_key_exists('parts', $aData))
|
||||
{
|
||||
foreach($aData['parts'] as $aPart)
|
||||
{
|
||||
$oMessage->AddPart($aPart['text'], $aPart['mimeType']);
|
||||
}
|
||||
}
|
||||
if (array_key_exists('attachments', $aData))
|
||||
{
|
||||
foreach($aData['attachments'] as $aAttachment)
|
||||
{
|
||||
$oMessage->AddAttachment(base64_decode($aAttachment['data']), $aAttachment['filename'], $aAttachment['mimeType']);
|
||||
}
|
||||
}
|
||||
return $oMessage;
|
||||
}
|
||||
|
||||
protected function SendAsynchronous(&$aIssues, $oLog = null)
|
||||
{
|
||||
try
|
||||
@@ -100,10 +184,13 @@ class EMail
|
||||
|
||||
$oMailer = Swift_Mailer::newInstance($oTransport);
|
||||
|
||||
$iSent = $oMailer->send($this->m_oMessage);
|
||||
if ($iSent === false)
|
||||
$aFailedRecipients = array();
|
||||
$iSent = $oMailer->send($this->m_oMessage, $aFailedRecipients);
|
||||
if ($iSent === 0)
|
||||
{
|
||||
$aIssues = 'une erreur s\'est produite... mais quoi !!!';
|
||||
// Beware: it seems that $aFailedRecipients sometimes contains the recipients that actually received the message !!!
|
||||
IssueLog::Warning('Email sending failed: Some recipients were invalid, aFailedRecipients contains: '.implode(', ', $aFailedRecipients));
|
||||
$aIssues = array('Some recipients were invalid.');
|
||||
return EMAIL_SEND_ERROR;
|
||||
}
|
||||
else
|
||||
@@ -135,27 +222,17 @@ class EMail
|
||||
|
||||
public function AddToHeader($sKey, $sValue)
|
||||
{
|
||||
if (!array_key_exists('headers', $this->m_aData))
|
||||
{
|
||||
$this->m_aData['headers'] = array();
|
||||
}
|
||||
$this->m_aData['headers'][$sKey] = $sValue;
|
||||
|
||||
if (strlen($sValue) > 0)
|
||||
{
|
||||
$oHeaders = $this->m_oMessage->getHeaders();
|
||||
switch(strtolower($sKey))
|
||||
{
|
||||
case 'from':
|
||||
case 'cc':
|
||||
case 'bcc':
|
||||
$aMatches = array();
|
||||
// Header may be in the form: John Doe <jd@company.com>
|
||||
if (preg_match('/^([^<]+) <([^>]+)>$/', $sValue, $aMatches))
|
||||
{
|
||||
$aHeader = array($aMatches[2] => $aMatches[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
$aHeader = array($sValue);
|
||||
}
|
||||
$oHeaders->addMailboxHeader($sKey, $aHeader);
|
||||
break;
|
||||
|
||||
default:
|
||||
$oHeaders->addTextHeader($sKey, $sValue);
|
||||
}
|
||||
@@ -164,6 +241,8 @@ class EMail
|
||||
|
||||
public function SetMessageId($sId)
|
||||
{
|
||||
$this->m_aData['message_id'] = $sId;
|
||||
|
||||
// Note: Swift will add the angle brackets for you
|
||||
// so let's remove the angle brackets if present, for historical reasons
|
||||
$sId = str_replace(array('<', '>'), '', $sId);
|
||||
@@ -179,22 +258,34 @@ class EMail
|
||||
|
||||
public function SetBody($sBody, $sMimeType = 'text/html')
|
||||
{
|
||||
$this->m_aData['body'] = array('body' => $sBody, 'mimeType' => $sMimeType);
|
||||
$this->m_oMessage->setBody($sBody, $sMimeType);
|
||||
}
|
||||
|
||||
public function AddPart($sText, $sMimeType = 'text/html')
|
||||
{
|
||||
if (!array_key_exists('parts', $this->m_aData))
|
||||
{
|
||||
$this->m_aData['parts'] = array();
|
||||
}
|
||||
$this->m_aData['parts'][] = array('text' => $sText, 'mimeType' => $sMimeType);
|
||||
$this->m_oMessage->addPart($sText, $sMimeType);
|
||||
}
|
||||
|
||||
public function AddAttachment($data, $sFileName, $sMimeType)
|
||||
{
|
||||
if (!array_key_exists('attachments', $this->m_aData))
|
||||
{
|
||||
$this->m_aData['attachments'] = array();
|
||||
}
|
||||
$this->m_aData['attachments'][] = array('data' => base64_encode($data), 'filename' => $sFileName, 'mimeType' => $sMimeType);
|
||||
$this->m_oMessage->attach(Swift_Attachment::newInstance($data, $sFileName, $sMimeType));
|
||||
}
|
||||
|
||||
public function SetSubject($aSubject)
|
||||
public function SetSubject($sSubject)
|
||||
{
|
||||
$this->m_oMessage->setSubject($aSubject);
|
||||
$this->m_aData['subject'] = $sSubject;
|
||||
$this->m_oMessage->setSubject($sSubject);
|
||||
}
|
||||
|
||||
public function GetSubject()
|
||||
@@ -202,14 +293,42 @@ class EMail
|
||||
return $this->m_oMessage->getSubject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to transform and sanitize addresses
|
||||
* - get rid of empty addresses
|
||||
*/
|
||||
protected function AddressStringToArray($sAddressCSVList)
|
||||
{
|
||||
$aAddresses = array();
|
||||
foreach(explode(',', $sAddressCSVList) as $sAddress)
|
||||
{
|
||||
$sAddress = trim($sAddress);
|
||||
if (strlen($sAddress) > 0)
|
||||
{
|
||||
$aAddresses[] = $sAddress;
|
||||
}
|
||||
}
|
||||
return $aAddresses;
|
||||
}
|
||||
|
||||
public function SetRecipientTO($sAddress)
|
||||
{
|
||||
$this->m_oMessage->setTo($sAddress);
|
||||
$this->m_aData['to'] = $sAddress;
|
||||
if (!empty($sAddress))
|
||||
{
|
||||
$aAddresses = $this->AddressStringToArray($sAddress);
|
||||
$this->m_oMessage->setTo($aAddresses);
|
||||
}
|
||||
}
|
||||
|
||||
public function GetRecipientTO($bAsString = false)
|
||||
{
|
||||
$aRes = $this->m_oMessage->getTo();
|
||||
if ($aRes === null)
|
||||
{
|
||||
// There is no "To" header field
|
||||
$aRes = array();
|
||||
}
|
||||
if ($bAsString)
|
||||
{
|
||||
$aStrings = array();
|
||||
@@ -235,21 +354,32 @@ class EMail
|
||||
|
||||
public function SetRecipientCC($sAddress)
|
||||
{
|
||||
$this->AddToHeader('Cc', $sAddress);
|
||||
$this->m_aData['cc'] = $sAddress;
|
||||
if (!empty($sAddress))
|
||||
{
|
||||
$aAddresses = $this->AddressStringToArray($sAddress);
|
||||
$this->m_oMessage->setCc($aAddresses);
|
||||
}
|
||||
}
|
||||
|
||||
public function SetRecipientBCC($sAddress)
|
||||
{
|
||||
$this->AddToHeader('Bcc', $sAddress);
|
||||
$this->m_aData['bcc'] = $sAddress;
|
||||
if (!empty($sAddress))
|
||||
{
|
||||
$aAddresses = $this->AddressStringToArray($sAddress);
|
||||
$this->m_oMessage->setBcc($aAddresses);
|
||||
}
|
||||
}
|
||||
|
||||
public function SetRecipientFrom($sAddress, $sLabel = '')
|
||||
{
|
||||
$this->m_aData['from'] = array('address' => $sAddress, 'label' => $sLabel);
|
||||
if ($sLabel != '')
|
||||
{
|
||||
$this->m_oMessage->setFrom(array($sAddress => $sLabel));
|
||||
}
|
||||
else
|
||||
else if (!empty($sAddress))
|
||||
{
|
||||
$this->m_oMessage->setFrom($sAddress);
|
||||
}
|
||||
@@ -257,7 +387,11 @@ class EMail
|
||||
|
||||
public function SetRecipientReplyTo($sAddress)
|
||||
{
|
||||
$this->AddToHeader('Reply-To', $sAddress);
|
||||
$this->m_aData['reply_to'] = $sAddress;
|
||||
if (!empty($sAddress))
|
||||
{
|
||||
$this->m_oMessage->setReplyTo($sAddress);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,28 +1,29 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Persistent class Event and derived
|
||||
* Application internal events
|
||||
* There is also a file log
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
class Event extends DBObject implements iDisplay
|
||||
@@ -40,6 +41,7 @@ class Event extends DBObject implements iDisplay
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "realclass",
|
||||
"display_template" => "",
|
||||
"order_by_default" => array('date' => false)
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
@@ -105,7 +107,7 @@ class Event extends DBObject implements iDisplay
|
||||
|
||||
function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array())
|
||||
{
|
||||
if ($bEditMode) return; // Not editable
|
||||
if ($bEditMode) return array(); // Not editable
|
||||
|
||||
$aDetails = array();
|
||||
$sClass = get_class($this);
|
||||
@@ -116,6 +118,7 @@ class Event extends DBObject implements iDisplay
|
||||
$aDetails[] = array('label' => '<span title="'.MetaModel::GetDescription($sClass, $sAttCode).'">'.MetaModel::GetLabel($sClass, $sAttCode).'</span>', 'value' => $sDisplayValue);
|
||||
}
|
||||
$oPage->Details($aDetails);
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,6 +137,10 @@ class EventNotification extends Event
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"display_template" => "",
|
||||
"order_by_default" => array('date' => false),
|
||||
'indexes' => array(
|
||||
array('object_id'),
|
||||
)
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
@@ -166,6 +173,7 @@ class EventNotificationEmail extends EventNotification
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"display_template" => "",
|
||||
"order_by_default" => array('date' => false)
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
@@ -175,10 +183,11 @@ class EventNotificationEmail extends EventNotification
|
||||
MetaModel::Init_AddAttribute(new AttributeText("from", array("allowed_values"=>null, "sql"=>"from", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeText("subject", array("allowed_values"=>null, "sql"=>"subject", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeHTML("body", array("allowed_values"=>null, "sql"=>"body", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeTable("attachments", array("allowed_values"=>null, "sql"=>"attachments", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'message', 'trigger_id', 'action_id', 'object_id', 'to', 'cc', 'bcc', 'from', 'subject', 'body')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('date', 'message', 'to', 'subject')); // Attributes to be displayed for a list
|
||||
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'message', 'trigger_id', 'action_id', 'object_id', 'to', 'cc', 'bcc', 'from', 'subject', 'body', 'attachments')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('date', 'message', 'to', 'subject', 'attachments')); // Attributes to be displayed for a list
|
||||
|
||||
// Search criteria
|
||||
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
|
||||
@@ -202,6 +211,7 @@ class EventIssue extends Event
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"display_template" => "",
|
||||
"order_by_default" => array('date' => false)
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
@@ -301,6 +311,7 @@ class EventWebService extends Event
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"display_template" => "",
|
||||
"order_by_default" => array('date' => false)
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
@@ -335,6 +346,7 @@ class EventLoginUsage extends Event
|
||||
"db_table" => "priv_event_loginusage",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"order_by_default" => array('date' => false)
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// 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)
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
class MissingQueryArgument extends CoreException
|
||||
@@ -41,6 +42,9 @@ abstract class Expression
|
||||
// recursively builds an array of class => fieldname
|
||||
abstract public function ListRequiredFields();
|
||||
|
||||
// recursively list field parents ($aTable = array of sParent => dummy)
|
||||
abstract public function CollectUsedParents(&$aTable);
|
||||
|
||||
abstract public function IsTrue();
|
||||
|
||||
// recursively builds an array of [classAlias][fieldName] => value
|
||||
@@ -143,6 +147,10 @@ class SQLExpression extends Expression
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
public function CollectUsedParents(&$aTable)
|
||||
{
|
||||
}
|
||||
|
||||
public function ListConstantFields()
|
||||
{
|
||||
@@ -259,7 +267,12 @@ class BinaryExpression extends Expression
|
||||
$aRight = $this->GetRightExpr()->ListRequiredFields();
|
||||
return array_merge($aLeft, $aRight);
|
||||
}
|
||||
|
||||
|
||||
public function CollectUsedParents(&$aTable)
|
||||
{
|
||||
$this->GetLeftExpr()->CollectUsedParents($aTable);
|
||||
$this->GetRightExpr()->CollectUsedParents($aTable);
|
||||
}
|
||||
|
||||
/**
|
||||
* List all constant expression of the form <field> = <scalar> or <field> = :<variable>
|
||||
@@ -350,6 +363,10 @@ class UnaryExpression extends Expression
|
||||
return array();
|
||||
}
|
||||
|
||||
public function CollectUsedParents(&$aTable)
|
||||
{
|
||||
}
|
||||
|
||||
public function ListConstantFields()
|
||||
{
|
||||
return array();
|
||||
@@ -366,7 +383,7 @@ class ScalarExpression extends UnaryExpression
|
||||
{
|
||||
public function __construct($value)
|
||||
{
|
||||
if (!is_scalar($value) && !is_null($value))
|
||||
if (!is_scalar($value) && !is_null($value) && (!$value instanceof OqlHexValue))
|
||||
{
|
||||
throw new CoreException('Attempt to create a scalar expression from a non scalar', array('var_type'=>gettype($value)));
|
||||
}
|
||||
@@ -386,6 +403,11 @@ class ScalarExpression extends UnaryExpression
|
||||
}
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
public function GetAsScalar($aArgs)
|
||||
{
|
||||
return clone $this;
|
||||
}
|
||||
}
|
||||
|
||||
class TrueExpression extends ScalarExpression
|
||||
@@ -451,6 +473,11 @@ class FieldExpression extends UnaryExpression
|
||||
return array($this->m_sParent.'.'.$this->m_sName);
|
||||
}
|
||||
|
||||
public function CollectUsedParents(&$aTable)
|
||||
{
|
||||
$aTable[$this->m_sParent] = true;
|
||||
}
|
||||
|
||||
public function GetUnresolvedFields($sAlias, &$aUnresolved)
|
||||
{
|
||||
if ($this->m_sParent == $sAlias)
|
||||
@@ -589,14 +616,29 @@ class VariableExpression extends UnaryExpression
|
||||
{
|
||||
return CMDBSource::Quote($aArgs[$this->m_sName]);
|
||||
}
|
||||
elseif ($bRetrofitParams)
|
||||
elseif (($iPos = strpos($this->m_sName, '->')) !== false)
|
||||
{
|
||||
$sParamName = substr($this->m_sName, 0, $iPos);
|
||||
if (array_key_exists($sParamName.'->object()', $aArgs))
|
||||
{
|
||||
$sAttCode = substr($this->m_sName, $iPos + 2);
|
||||
$oObj = $aArgs[$sParamName.'->object()'];
|
||||
if ($sAttCode == 'id')
|
||||
{
|
||||
return CMDBSource::Quote($oObj->GetKey());
|
||||
}
|
||||
return CMDBSource::Quote($oObj->Get($sAttCode));
|
||||
}
|
||||
}
|
||||
|
||||
if ($bRetrofitParams)
|
||||
{
|
||||
$aArgs[$this->m_sName] = null;
|
||||
return ':'.$this->m_sName;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new MissingQueryArgument('Missing query argument', array('expecting'=>$this->m_sName, 'available'=>$aArgs));
|
||||
throw new MissingQueryArgument('Missing query argument', array('expecting'=>$this->m_sName, 'available'=>array_keys($aArgs)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -610,12 +652,29 @@ class VariableExpression extends UnaryExpression
|
||||
|
||||
public function GetAsScalar($aArgs)
|
||||
{
|
||||
$value = '';
|
||||
$value = null;
|
||||
if (array_key_exists($this->m_sName, $aArgs))
|
||||
{
|
||||
$value = $aArgs[$this->m_sName];
|
||||
}
|
||||
else
|
||||
elseif (($iPos = strpos($this->m_sName, '->')) !== false)
|
||||
{
|
||||
$sParamName = substr($this->m_sName, 0, $iPos);
|
||||
if (array_key_exists($sParamName.'->object()', $aArgs))
|
||||
{
|
||||
$sAttCode = substr($this->m_sName, $iPos + 2);
|
||||
$oObj = $aArgs[$sParamName.'->object()'];
|
||||
if ($sAttCode == 'id')
|
||||
{
|
||||
$value = $oObj->GetKey();
|
||||
}
|
||||
else
|
||||
{
|
||||
$value = $oObj->Get($sAttCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (is_null($value))
|
||||
{
|
||||
throw new MissingQueryArgument('Missing query argument', array('expecting'=>$this->m_sName, 'available'=>array_keys($aArgs)));
|
||||
}
|
||||
@@ -710,6 +769,14 @@ class ListExpression extends Expression
|
||||
return $aRes;
|
||||
}
|
||||
|
||||
public function CollectUsedParents(&$aTable)
|
||||
{
|
||||
foreach ($this->m_aExpressions as $oExpr)
|
||||
{
|
||||
$oExpr->CollectUsedParents($aTable);
|
||||
}
|
||||
}
|
||||
|
||||
public function ListConstantFields()
|
||||
{
|
||||
$aRes = array();
|
||||
@@ -813,6 +880,14 @@ class FunctionExpression extends Expression
|
||||
return $aRes;
|
||||
}
|
||||
|
||||
public function CollectUsedParents(&$aTable)
|
||||
{
|
||||
foreach ($this->m_aArgs as $oExpr)
|
||||
{
|
||||
$oExpr->CollectUsedParents($aTable);
|
||||
}
|
||||
}
|
||||
|
||||
public function ListConstantFields()
|
||||
{
|
||||
$aRes = array();
|
||||
@@ -841,31 +916,63 @@ class FunctionExpression extends Expression
|
||||
*/
|
||||
public function MakeValueLabel($oFilter, $sValue, $sDefault)
|
||||
{
|
||||
static $aWeekDayToString = null;
|
||||
if (is_null($aWeekDayToString))
|
||||
{
|
||||
// Init the correspondance table
|
||||
$aWeekDayToString = array(
|
||||
0 => Dict::S('DayOfWeek-Sunday'),
|
||||
1 => Dict::S('DayOfWeek-Monday'),
|
||||
2 => Dict::S('DayOfWeek-Tuesday'),
|
||||
3 => Dict::S('DayOfWeek-Wednesday'),
|
||||
4 => Dict::S('DayOfWeek-Thursday'),
|
||||
5 => Dict::S('DayOfWeek-Friday'),
|
||||
6 => Dict::S('DayOfWeek-Saturday')
|
||||
);
|
||||
}
|
||||
static $aMonthToString = null;
|
||||
if (is_null($aMonthToString))
|
||||
{
|
||||
// Init the correspondance table
|
||||
$aMonthToString = array(
|
||||
1 => Dict::S('Month-01'),
|
||||
2 => Dict::S('Month-02'),
|
||||
3 => Dict::S('Month-03'),
|
||||
4 => Dict::S('Month-04'),
|
||||
5 => Dict::S('Month-05'),
|
||||
6 => Dict::S('Month-06'),
|
||||
7 => Dict::S('Month-07'),
|
||||
8 => Dict::S('Month-08'),
|
||||
9 => Dict::S('Month-09'),
|
||||
10 => Dict::S('Month-10'),
|
||||
11 => Dict::S('Month-11'),
|
||||
12 => Dict::S('Month-12'),
|
||||
);
|
||||
}
|
||||
|
||||
$sRes = $sDefault;
|
||||
if (strtolower($this->m_sVerb) == 'date_format')
|
||||
{
|
||||
$oFormatExpr = $this->m_aArgs[1];
|
||||
if ($oFormatExpr->Render() == "'%w'")
|
||||
{
|
||||
static $aWeekDayToString = null;
|
||||
if (is_null($aWeekDayToString))
|
||||
{
|
||||
// Init the correspondance table
|
||||
$aWeekDayToString = array(
|
||||
0 => Dict::S('DayOfWeek-Sunday'),
|
||||
1 => Dict::S('DayOfWeek-Monday'),
|
||||
2 => Dict::S('DayOfWeek-Tuesday'),
|
||||
3 => Dict::S('DayOfWeek-Wednesday'),
|
||||
4 => Dict::S('DayOfWeek-Thursday'),
|
||||
5 => Dict::S('DayOfWeek-Friday'),
|
||||
6 => Dict::S('DayOfWeek-Saturday')
|
||||
);
|
||||
}
|
||||
if (isset($aWeekDayToString[(int)$sValue]))
|
||||
{
|
||||
$sRes = $aWeekDayToString[(int)$sValue];
|
||||
}
|
||||
}
|
||||
elseif ($oFormatExpr->Render() == "'%Y-%m'")
|
||||
{
|
||||
// yyyy-mm => "yyyy month"
|
||||
$iMonth = (int) substr($sValue, -2); // the two last chars
|
||||
$sRes = substr($sValue, 0, 4).' '.$aMonthToString[$iMonth];
|
||||
}
|
||||
elseif ($oFormatExpr->Render() == "'%Y-%m-%d'")
|
||||
{
|
||||
// yyyy-mm-dd => "month d"
|
||||
$iMonth = (int) substr($sValue, 5, 2);
|
||||
$sRes = $aMonthToString[$iMonth].' '.(int)substr($sValue, -2);
|
||||
}
|
||||
}
|
||||
return $sRes;
|
||||
}
|
||||
@@ -931,6 +1038,10 @@ class IntervalExpression extends Expression
|
||||
return array();
|
||||
}
|
||||
|
||||
public function CollectUsedParents(&$aTable)
|
||||
{
|
||||
}
|
||||
|
||||
public function ListConstantFields()
|
||||
{
|
||||
return array();
|
||||
@@ -1019,6 +1130,14 @@ class CharConcatExpression extends Expression
|
||||
return $aRes;
|
||||
}
|
||||
|
||||
public function CollectUsedParents(&$aTable)
|
||||
{
|
||||
foreach ($this->m_aExpressions as $oExpr)
|
||||
{
|
||||
$oExpr->CollectUsedParents($aTable);
|
||||
}
|
||||
}
|
||||
|
||||
public function ListConstantFields()
|
||||
{
|
||||
$aRes = array();
|
||||
@@ -1062,6 +1181,16 @@ class CharConcatWSExpression extends CharConcatExpression
|
||||
$sSep = CMDBSource::Quote($this->m_separator);
|
||||
return "CAST(CONCAT_WS($sSep, ".implode(', ', $aRes).") AS CHAR)";
|
||||
}
|
||||
|
||||
public function Translate($aTranslationData, $bMatchAll = true, $bMarkFieldsAsResolved = true)
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($this->m_aExpressions as $oExpr)
|
||||
{
|
||||
$aRes[] = $oExpr->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
|
||||
}
|
||||
return new CharConcatWSExpression($this->m_separator, $aRes);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1071,13 +1200,20 @@ class QueryBuilderExpressions
|
||||
protected $m_aSelectExpr;
|
||||
protected $m_aGroupByExpr;
|
||||
protected $m_aJoinFields;
|
||||
protected $m_aClassIds;
|
||||
|
||||
public function __construct($oCondition, $aGroupByExpr = null)
|
||||
public function __construct($oSearch, $aGroupByExpr = null)
|
||||
{
|
||||
$this->m_oConditionExpr = $oCondition;
|
||||
$this->m_oConditionExpr = $oSearch->GetCriteria();
|
||||
$this->m_aSelectExpr = array();
|
||||
$this->m_aGroupByExpr = $aGroupByExpr;
|
||||
$this->m_aJoinFields = array();
|
||||
|
||||
$this->m_aClassIds = array();
|
||||
foreach($oSearch->GetJoinedClasses() as $sClassAlias => $sClass)
|
||||
{
|
||||
$this->m_aClassIds[$sClassAlias] = new FieldExpression('id', $sClassAlias);
|
||||
}
|
||||
}
|
||||
|
||||
public function GetSelect()
|
||||
@@ -1116,6 +1252,20 @@ class QueryBuilderExpressions
|
||||
array_push($this->m_aJoinFields, $oExpression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tables representing the queried objects
|
||||
* Could be further optimized: when the first join is an outer join, then the rest can be omitted
|
||||
*/
|
||||
public function GetMandatoryTables(&$aTables = null)
|
||||
{
|
||||
if (is_null($aTables)) $aTables = array();
|
||||
|
||||
foreach($this->m_aClassIds as $sClass => $oExpression)
|
||||
{
|
||||
$oExpression->CollectUsedParents($aTables);
|
||||
}
|
||||
}
|
||||
|
||||
public function GetUnresolvedFields($sAlias, &$aUnresolved)
|
||||
{
|
||||
$this->m_oConditionExpr->GetUnresolvedFields($sAlias, $aUnresolved);
|
||||
@@ -1154,6 +1304,11 @@ class QueryBuilderExpressions
|
||||
{
|
||||
$this->m_aJoinFields[$index] = $oExpression->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
|
||||
}
|
||||
|
||||
foreach($this->m_aClassIds as $sClass => $oExpression)
|
||||
{
|
||||
$this->m_aClassIds[$sClass] = $oExpression->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
|
||||
}
|
||||
}
|
||||
|
||||
public function RenameParam($sOldName, $sNewName)
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Definition of a filter
|
||||
* Most of the time, a filter corresponds to an attribute, but we could imagine other search criteria
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -1,96 +1,256 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Measures operations duration, memory usage, etc. (and some other KPIs)
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
class ExecutionKPI
|
||||
{
|
||||
static protected $m_bEnabled_Duration = false;
|
||||
static protected $m_bEnabled_Memory = false;
|
||||
static protected $m_bBlameCaller = false;
|
||||
static protected $m_sAllowedUser = '*';
|
||||
|
||||
static protected $m_aStats = array();
|
||||
static protected $m_aStats = array(); // Recurrent operations
|
||||
static protected $m_aExecData = array(); // One shot operations
|
||||
|
||||
protected $m_fStarted = null;
|
||||
protected $m_iInitialMemory = null;
|
||||
|
||||
static public function EnableDuration()
|
||||
static public function EnableDuration($iLevel)
|
||||
{
|
||||
self::$m_bEnabled_Duration = true;
|
||||
if ($iLevel > 0)
|
||||
{
|
||||
self::$m_bEnabled_Duration = true;
|
||||
if ($iLevel > 1)
|
||||
{
|
||||
self::$m_bBlameCaller = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static public function EnableMemory()
|
||||
static public function EnableMemory($iLevel)
|
||||
{
|
||||
self::$m_bEnabled_Memory = true;
|
||||
if ($iLevel > 0)
|
||||
{
|
||||
self::$m_bEnabled_Memory = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string sUser A user login or * for all users
|
||||
*/
|
||||
static public function SetAllowedUser($sUser)
|
||||
{
|
||||
self::$m_sAllowedUser = $sUser;
|
||||
}
|
||||
|
||||
static public function IsEnabled()
|
||||
{
|
||||
if (self::$m_bEnabled_Duration || self::$m_bEnabled_Memory)
|
||||
{
|
||||
if ((self::$m_sAllowedUser == '*') || (UserRights::GetUser() == trim(self::$m_sAllowedUser)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static public function GetDescription()
|
||||
{
|
||||
$aFeatures = array();
|
||||
if (self::$m_bEnabled_Duration) $aFeatures[] = 'Duration';
|
||||
if (self::$m_bEnabled_Memory) $aFeatures[] = 'Memory usage';
|
||||
$sFeatures = implode(', ', $aFeatures);
|
||||
$sFor = self::$m_sAllowedUser == '*' ? 'EVERYBODY' : "'".trim(self::$m_sAllowedUser)."'";
|
||||
return "KPI logging is active for $sFor. Measures: $sFeatures";
|
||||
}
|
||||
|
||||
static public function ReportStats()
|
||||
{
|
||||
if (!self::IsEnabled()) return;
|
||||
|
||||
global $fItopStarted;
|
||||
$sExecId = microtime(); // id to differentiate the hrefs!
|
||||
|
||||
$aBeginTimes = array();
|
||||
foreach (self::$m_aExecData as $aOpStats)
|
||||
{
|
||||
$aBeginTimes[] = $aOpStats['time_begin'];
|
||||
}
|
||||
array_multisort($aBeginTimes, self::$m_aExecData);
|
||||
|
||||
$sTableStyle = 'background-color: #ccc; margin: 10px;';
|
||||
|
||||
self::Report("<hr/>");
|
||||
self::Report("<div style=\"background-color: grey; padding: 10px;\">");
|
||||
self::Report("<h3><a name=\"".md5($sExecId)."\">KPIs</a> - ".$_SERVER['REQUEST_URI']." (".$_SERVER['REQUEST_METHOD'].")</h3>");
|
||||
self::Report("<p>".date('Y-m-d H:i:s', $fItopStarted)."</p>");
|
||||
self::Report("<p>log_kpi_user_id: ".MetaModel::GetConfig()->Get('log_kpi_user_id')."</p>");
|
||||
self::Report("<div>");
|
||||
self::Report("<table border=\"1\" style=\"$sTableStyle\">");
|
||||
self::Report("<thead>");
|
||||
self::Report(" <th>Operation</th><th>Begin</th><th>End</th><th>Duration</th><th>Memory start</th><th>Memory end</th><th>Memory peak</th>");
|
||||
self::Report("</thead>");
|
||||
foreach (self::$m_aExecData as $aOpStats)
|
||||
{
|
||||
$sOperation = $aOpStats['op'];
|
||||
$sBegin = $sEnd = $sDuration = $sMemBegin = $sMemEnd = $sMemPeak = '?';
|
||||
|
||||
$sBegin = round($aOpStats['time_begin'], 3);
|
||||
$sEnd = round($aOpStats['time_end'], 3);
|
||||
$fDuration = $aOpStats['time_end'] - $aOpStats['time_begin'];
|
||||
$sDuration = round($fDuration, 3);
|
||||
|
||||
if (isset($aOpStats['mem_begin']))
|
||||
{
|
||||
$sMemBegin = self::MemStr($aOpStats['mem_begin']);
|
||||
$sMemEnd = self::MemStr($aOpStats['mem_end']);
|
||||
if (isset($aOpStats['mem_peak']))
|
||||
{
|
||||
$sMemPeak = self::MemStr($aOpStats['mem_peak']);
|
||||
}
|
||||
}
|
||||
|
||||
self::Report("<tr>");
|
||||
self::Report(" <td>$sOperation</td><td>$sBegin</td><td>$sEnd</td><td>$sDuration</td><td>$sMemBegin</td><td>$sMemEnd</td><td>$sMemPeak</td>");
|
||||
self::Report("</tr>");
|
||||
}
|
||||
self::Report("</table>");
|
||||
self::Report("</div>");
|
||||
|
||||
$aConsolidatedStats = array();
|
||||
foreach (self::$m_aStats as $sOperation => $aOpStats)
|
||||
{
|
||||
echo "<h2>KPIs for $sOperation</h2>\n";
|
||||
$fTotalOp = 0;
|
||||
$iTotalOp = 0;
|
||||
$fMinOp = null;
|
||||
$fMaxOp = 0;
|
||||
echo "<ul>\n";
|
||||
$sMaxOpArguments = null;
|
||||
foreach ($aOpStats as $sArguments => $aEvents)
|
||||
{
|
||||
foreach ($aEvents as $aEventData)
|
||||
{
|
||||
$fDuration = $aEventData['time'];
|
||||
$fTotalOp += $fDuration;
|
||||
$iTotalOp++;
|
||||
|
||||
$fMinOp = is_null($fMinOp) ? $fDuration : min($fMinOp, $fDuration);
|
||||
if ($fDuration > $fMaxOp)
|
||||
{
|
||||
$sMaxOpArguments = $sArguments;
|
||||
$fMaxOp = $fDuration;
|
||||
}
|
||||
}
|
||||
}
|
||||
$aConsolidatedStats[$sOperation] = array(
|
||||
'count' => $iTotalOp,
|
||||
'duration' => $fTotalOp,
|
||||
'min' => $fMinOp,
|
||||
'max' => $fMaxOp,
|
||||
'avg' => $fTotalOp / $iTotalOp,
|
||||
'max_args' => $sMaxOpArguments
|
||||
);
|
||||
}
|
||||
|
||||
self::Report("<div>");
|
||||
self::Report("<table border=\"1\" style=\"$sTableStyle\">");
|
||||
self::Report("<thead>");
|
||||
self::Report(" <th>Operation</th><th>Count</th><th>Duration</th><th>Min</th><th>Max</th><th>Avg</th>");
|
||||
self::Report("</thead>");
|
||||
foreach ($aConsolidatedStats as $sOperation => $aOpStats)
|
||||
{
|
||||
$sOperation = '<a href="#'.md5($sExecId.$sOperation).'">'.$sOperation.'</a>';
|
||||
$sCount = $aOpStats['count'];
|
||||
$sDuration = round($aOpStats['duration'], 3);
|
||||
$sMin = round($aOpStats['min'], 3);
|
||||
$sMax = '<a href="#'.md5($sExecId.$aOpStats['max_args']).'">'.round($aOpStats['max'], 3).'</a>';
|
||||
$sAvg = round($aOpStats['avg'], 3);
|
||||
|
||||
self::Report("<tr>");
|
||||
self::Report(" <td>$sOperation</td><td>$sCount</td><td>$sDuration</td><td>$sMin</td><td>$sMax</td><td>$sAvg</td>");
|
||||
self::Report("</tr>");
|
||||
}
|
||||
self::Report("</table>");
|
||||
self::Report("</div>");
|
||||
|
||||
self::Report("</div>");
|
||||
|
||||
// Report operation details
|
||||
foreach (self::$m_aStats as $sOperation => $aOpStats)
|
||||
{
|
||||
$sOperationHtml = '<a name="'.md5($sExecId.$sOperation).'">'.$sOperation.'</a>';
|
||||
self::Report("<h4>$sOperationHtml</h4>");
|
||||
self::Report("<p><a href=\"#".md5($sExecId)."\">Back to page stats</a></p>");
|
||||
self::Report("<table border=\"1\" style=\"$sTableStyle\">");
|
||||
self::Report("<thead>");
|
||||
self::Report(" <th>Operation details (+ blame caller if log_kpi_duration = 2)</th><th>Count</th><th>Duration</th><th>Min</th><th>Max</th>");
|
||||
self::Report("</thead>");
|
||||
foreach ($aOpStats as $sArguments => $aEvents)
|
||||
{
|
||||
$sHtmlArguments = '<a name="'.md5($sExecId.$sArguments).'"><div style="white-space: pre-wrap;">'.$sArguments.'</div></a>';
|
||||
if ($aConsolidatedStats[$sOperation]['max_args'] == $sArguments)
|
||||
{
|
||||
$sHtmlArguments = '<span style="color: red;">'.$sHtmlArguments.'</span>';
|
||||
}
|
||||
if (isset($aEvents[0]['callers']))
|
||||
{
|
||||
$sHtmlArguments .= '<div style="padding: 10px;">';
|
||||
$sHtmlArguments .= '<table border="1" bgcolor="#cfc">';
|
||||
$sHtmlArguments .= '<tr><td colspan="2" bgcolor="#e9b96">Call stack for the <b>FIRST</b> caller</td></tr>';
|
||||
|
||||
foreach ($aEvents[0]['callers'] as $aCall)
|
||||
{
|
||||
$sHtmlArguments .= '<tr>';
|
||||
$sHtmlArguments .= '<td>'.$aCall['Function'].'</td>';
|
||||
$sHtmlArguments .= '<td>'.$aCall['File'].':'.$aCall['Line'].'</td>';
|
||||
$sHtmlArguments .= '</tr>';
|
||||
}
|
||||
$sHtmlArguments .= '</table>';
|
||||
$sHtmlArguments .= '</div>';
|
||||
}
|
||||
|
||||
$fTotalInter = 0;
|
||||
$fMinInter = null;
|
||||
$fMaxInter = 0;
|
||||
foreach ($aEvents as $fDuration)
|
||||
foreach ($aEvents as $aEventData)
|
||||
{
|
||||
$fDuration = $aEventData['time'];
|
||||
$fTotalInter += $fDuration;
|
||||
$fMinInter = is_null($fMinInter) ? $fDuration : min($fMinInter, $fDuration);
|
||||
$fMaxInter = max($fMaxInter, $fDuration);
|
||||
|
||||
$fMinOp = is_null($fMinOp) ? $fDuration : min($fMinOp, $fDuration);
|
||||
$fMaxOp = max($fMaxOp, $fDuration);
|
||||
}
|
||||
$fTotalOp += $fTotalInter;
|
||||
$iTotalOp++;
|
||||
|
||||
$iCountInter = count($aEvents);
|
||||
$sTotalInter = round($fTotalInter, 3)."s";
|
||||
if ($iCountInter > 1)
|
||||
{
|
||||
$sMinInter = round($fMinInter, 3)."s";
|
||||
$sMaxInter = round($fMaxInter, 3)."s";
|
||||
$sTimeDesc = "$sTotalInter (from $sMinInter to $sMaxInter) in $iCountInter times";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sTimeDesc = "$sTotalInter";
|
||||
}
|
||||
echo "<li>Spent $sTimeDesc, on: <span style=\"font-size:60%\">$sArguments</span></li>\n";
|
||||
$sTotalInter = round($fTotalInter, 3);
|
||||
$sMinInter = round($fMinInter, 3);
|
||||
$sMaxInter = round($fMaxInter, 3);
|
||||
self::Report("<tr>");
|
||||
self::Report(" <td>$sHtmlArguments</td><td>$iCountInter</td><td>$sTotalInter</td><td>$sMinInter</td><td>$sMaxInter</td>");
|
||||
self::Report("</tr>");
|
||||
}
|
||||
echo "</ul>\n";
|
||||
echo "<ul>Sumary for $sOperation\n";
|
||||
echo "<li>Total: $iTotalOp (".round($fTotalOp, 3).")</li>\n";
|
||||
echo "<li>Min: ".round($fMinOp, 3)."</li>\n";
|
||||
echo "<li>Max: ".round($fMaxOp, 3)."</li>\n";
|
||||
echo "<li>Avg: ".round($fTotalOp / $iTotalOp, 3)."</li>\n";
|
||||
echo "</ul>\n";
|
||||
self::Report("</table>");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,25 +264,43 @@ class ExecutionKPI
|
||||
//
|
||||
public function ComputeAndReport($sOperationDesc)
|
||||
{
|
||||
global $fItopStarted;
|
||||
|
||||
$aNewEntry = null;
|
||||
|
||||
if (self::$m_bEnabled_Duration)
|
||||
{
|
||||
$fStopped = MyHelpers::getmicrotime();
|
||||
$fDuration = $fStopped - $this->m_fStarted;
|
||||
$this->Report($sOperationDesc.' / duration: '.round($fDuration, 3));
|
||||
$aNewEntry = array(
|
||||
'op' => $sOperationDesc,
|
||||
'time_begin' => $this->m_fStarted - $fItopStarted,
|
||||
'time_end' => $fStopped - $fItopStarted,
|
||||
);
|
||||
// Reset for the next operation (if the object is recycled)
|
||||
$this->m_fStarted = $fStopped;
|
||||
}
|
||||
|
||||
if (self::$m_bEnabled_Memory)
|
||||
{
|
||||
$iMemory = self::memory_get_usage();
|
||||
$iMemoryUsed = $iMemory - $this->m_iInitialMemory;
|
||||
$this->Report($sOperationDesc.' / memory: '.self::MemStr($iMemoryUsed).' (Total: '.self::MemStr($iMemory).')');
|
||||
$iCurrentMemory = self::memory_get_usage();
|
||||
if (is_null($aNewEntry))
|
||||
{
|
||||
$aNewEntry = array('op' => $sOperationDesc);
|
||||
}
|
||||
$aNewEntry['mem_begin'] = $this->m_iInitialMemory;
|
||||
$aNewEntry['mem_end'] = $iCurrentMemory;
|
||||
if (function_exists('memory_get_peak_usage'))
|
||||
{
|
||||
$iMemoryPeak = memory_get_peak_usage();
|
||||
$this->Report($sOperationDesc.' / memory peak: '.self::MemStr($iMemoryPeak));
|
||||
$aNewEntry['mem_peak'] = memory_get_peak_usage();
|
||||
}
|
||||
// Reset for the next operation (if the object is recycled)
|
||||
$this->m_iInitialMemory = $iCurrentMemory;
|
||||
}
|
||||
|
||||
if (!is_null($aNewEntry))
|
||||
{
|
||||
self::$m_aExecData[] = $aNewEntry;
|
||||
}
|
||||
$this->ResetCounters();
|
||||
}
|
||||
|
||||
@@ -132,7 +310,19 @@ class ExecutionKPI
|
||||
{
|
||||
$fStopped = MyHelpers::getmicrotime();
|
||||
$fDuration = $fStopped - $this->m_fStarted;
|
||||
self::$m_aStats[$sOperation][$sArguments][] = $fDuration;
|
||||
if (self::$m_bBlameCaller)
|
||||
{
|
||||
self::$m_aStats[$sOperation][$sArguments][] = array(
|
||||
'time' => $fDuration,
|
||||
'callers' => MyHelpers::get_callstack(1),
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
self::$m_aStats[$sOperation][$sArguments][] = array(
|
||||
'time' => $fDuration
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,9 +339,11 @@ class ExecutionKPI
|
||||
}
|
||||
}
|
||||
|
||||
protected function Report($sText)
|
||||
const HtmlReportFile = 'log/kpi.html';
|
||||
|
||||
static protected function Report($sText)
|
||||
{
|
||||
echo "$sText<br/>\n";
|
||||
file_put_contents(APPROOT.self::HtmlReportFile, "$sText\n", FILE_APPEND | LOCK_EX);
|
||||
}
|
||||
|
||||
static protected function MemStr($iMemory)
|
||||
@@ -203,13 +395,3 @@ class ExecutionKPI
|
||||
}
|
||||
}
|
||||
|
||||
class ApplicationStartupKPI extends ExecutionKPI
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
global $fItopStarted;
|
||||
$this->m_fStarted = $fItopStarted;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// 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 logging
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
class FileLog
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Any extension to hook the initialization of the metamodel
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
interface iOnClassInitialization
|
||||
|
||||
261
core/modelreflection.class.inc.php
Normal file
261
core/modelreflection.class.inc.php
Normal file
@@ -0,0 +1,261 @@
|
||||
<?php
|
||||
// Copyright (C) 2013 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/>
|
||||
|
||||
|
||||
/**
|
||||
* Reflection API for the MetaModel (partial)
|
||||
*
|
||||
* @copyright Copyright (C) 2013 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
abstract class ModelReflection
|
||||
{
|
||||
abstract public function GetClassIcon($sClass, $bImgTag = true);
|
||||
abstract public function IsValidAttCode($sClass, $sAttCode);
|
||||
abstract public function GetName($sClass);
|
||||
abstract public function GetLabel($sClass, $sAttCodeEx);
|
||||
abstract public function GetValueLabel($sClass, $sAttCode, $sValue);
|
||||
abstract public function ListAttributes($sClass, $sScope = null);
|
||||
abstract public function GetAttributeProperty($sClass, $sAttCode, $sPropName, $default = null);
|
||||
abstract public function GetAllowedValues_att($sClass, $sAttCode);
|
||||
abstract public function HasChildrenClasses($sClass);
|
||||
abstract public function GetClasses($sCategories = '', $bExcludeLinks = false);
|
||||
abstract public function IsValidClass($sClass);
|
||||
abstract public function IsSameFamilyBranch($sClassA, $sClassB);
|
||||
abstract public function GetParentClass($sClass);
|
||||
abstract public function GetFiltersList($sClass);
|
||||
abstract public function IsValidFilterCode($sClass, $sFilterCode);
|
||||
|
||||
abstract public function GetQuery($sOQL);
|
||||
|
||||
abstract public function DictString($sStringCode, $sDefault = null, $bUserLanguageOnly = false);
|
||||
|
||||
public function DictFormat($sFormatCode /*, ... arguments ....*/)
|
||||
{
|
||||
$sLocalizedFormat = $this->DictString($sFormatCode);
|
||||
$aArguments = func_get_args();
|
||||
array_shift($aArguments);
|
||||
|
||||
if ($sLocalizedFormat == $sFormatCode)
|
||||
{
|
||||
// Make sure the information will be displayed (ex: an error occuring before the dictionary gets loaded)
|
||||
return $sFormatCode.' - '.implode(', ', $aArguments);
|
||||
}
|
||||
|
||||
return vsprintf($sLocalizedFormat, $aArguments);
|
||||
}
|
||||
|
||||
abstract public function GetIconSelectionField($sCode, $sLabel = '', $defaultValue = '');
|
||||
}
|
||||
|
||||
abstract class QueryReflection
|
||||
{
|
||||
/**
|
||||
* Throws an exception in case of an invalid syntax
|
||||
*/
|
||||
abstract public function __construct($sOQL);
|
||||
|
||||
abstract public function GetClass();
|
||||
abstract public function GetClassAlias();
|
||||
}
|
||||
|
||||
|
||||
class ModelReflectionRuntime extends ModelReflection
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public function GetClassIcon($sClass, $bImgTag = true)
|
||||
{
|
||||
return MetaModel::GetClassIcon($sClass, $bImgTag);
|
||||
}
|
||||
|
||||
public function IsValidAttCode($sClass, $sAttCode)
|
||||
{
|
||||
return MetaModel::IsValidAttCode($sClass, $sAttCode);
|
||||
}
|
||||
|
||||
public function GetName($sClass)
|
||||
{
|
||||
return MetaModel::GetName($sClass);
|
||||
}
|
||||
|
||||
public function GetLabel($sClass, $sAttCodeEx)
|
||||
{
|
||||
return MetaModel::GetLabel($sClass, $sAttCodeEx);
|
||||
}
|
||||
|
||||
public function GetValueLabel($sClass, $sAttCode, $sValue)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
return $oAttDef->GetValueLabel($sValue);
|
||||
}
|
||||
|
||||
public function ListAttributes($sClass, $sScope = null)
|
||||
{
|
||||
$aScope = null;
|
||||
if ($sScope != null)
|
||||
{
|
||||
$aScope = array();
|
||||
foreach (explode(',', $sScope) as $sScopeClass)
|
||||
{
|
||||
$aScope[] = trim($sScopeClass);
|
||||
}
|
||||
}
|
||||
$aAttributes = array();
|
||||
foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
|
||||
{
|
||||
$sAttributeClass = get_class($oAttDef);
|
||||
if ($aScope != null)
|
||||
{
|
||||
foreach ($aScope as $sScopeClass)
|
||||
{
|
||||
if (($sAttributeClass == $sScopeClass) || is_subclass_of($sAttributeClass, $sScopeClass))
|
||||
{
|
||||
$aAttributes[$sAttCode] = $sAttributeClass;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$aAttributes[$sAttCode] = $sAttributeClass;
|
||||
}
|
||||
}
|
||||
return $aAttributes;
|
||||
}
|
||||
|
||||
public function GetAttributeProperty($sClass, $sAttCode, $sPropName, $default = null)
|
||||
{
|
||||
$ret = $default;
|
||||
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
$aParams = $oAttDef->GetParams();
|
||||
if (array_key_exists($sPropName, $aParams))
|
||||
{
|
||||
$ret = $aParams[$sPropName];
|
||||
}
|
||||
|
||||
if ($oAttDef instanceof AttributeHierarchicalKey)
|
||||
{
|
||||
if ($sPropName == 'targetclass')
|
||||
{
|
||||
$ret = $sClass;
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function GetAllowedValues_att($sClass, $sAttCode)
|
||||
{
|
||||
return MetaModel::GetAllowedValues_att($sClass, $sAttCode);
|
||||
}
|
||||
|
||||
public function HasChildrenClasses($sClass)
|
||||
{
|
||||
return MetaModel::HasChildrenClasses($sClass);
|
||||
}
|
||||
|
||||
public function GetClasses($sCategories = '', $bExcludeLinks = false)
|
||||
{
|
||||
$aClasses = MetaModel::GetClasses($sCategories);
|
||||
if ($bExcludeLinks)
|
||||
{
|
||||
$aExcluded = MetaModel::GetLinkClasses();
|
||||
$aRes = array();
|
||||
foreach ($aClasses as $sClass)
|
||||
{
|
||||
if (!array_key_exists($sClass, $aExcluded))
|
||||
{
|
||||
$aRes[] = $sClass;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$aRes = $aClasses;
|
||||
}
|
||||
return $aRes;
|
||||
}
|
||||
|
||||
public function IsValidClass($sClass)
|
||||
{
|
||||
return MetaModel::IsValidClass($sClass);
|
||||
}
|
||||
|
||||
public function IsSameFamilyBranch($sClassA, $sClassB)
|
||||
{
|
||||
return MetaModel::IsSameFamilyBranch($sClassA, $sClassB);
|
||||
}
|
||||
|
||||
public function GetParentClass($sClass)
|
||||
{
|
||||
return MetaModel::GetParentClass($sClass);
|
||||
}
|
||||
|
||||
public function GetFiltersList($sClass)
|
||||
{
|
||||
return MetaModel::GetFiltersList($sClass);
|
||||
}
|
||||
|
||||
public function IsValidFilterCode($sClass, $sFilterCode)
|
||||
{
|
||||
return MetaModel::IsValidFilterCode($sClass, $sFilterCode);
|
||||
}
|
||||
|
||||
public function GetQuery($sOQL)
|
||||
{
|
||||
return new QueryReflectionRuntime($sOQL);
|
||||
}
|
||||
|
||||
public function DictString($sStringCode, $sDefault = null, $bUserLanguageOnly = false)
|
||||
{
|
||||
return Dict::S($sStringCode, $sDefault, $bUserLanguageOnly);
|
||||
}
|
||||
|
||||
public function GetIconSelectionField($sCode, $sLabel = '', $defaultValue = '')
|
||||
{
|
||||
return new RunTimeIconSelectionField($sCode, $sLabel, $defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class QueryReflectionRuntime extends QueryReflection
|
||||
{
|
||||
protected $oFilter;
|
||||
|
||||
/**
|
||||
* throws an exception in case of a wrong syntax
|
||||
*/
|
||||
public function __construct($sOQL)
|
||||
{
|
||||
$this->oFilter = DBObjectSearch::FromOQL($sOQL);
|
||||
}
|
||||
|
||||
public function GetClass()
|
||||
{
|
||||
return $this->oFilter->GetClass();
|
||||
}
|
||||
|
||||
public function GetClassAlias()
|
||||
{
|
||||
return $this->oFilter->GetClassAlias();
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,28 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// 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 ModuleHandler
|
||||
* Defines the API to implement module specific actions during page execution
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
abstract class ModuleHandlerAPI
|
||||
|
||||
193
core/mutex.class.inc.php
Normal file
193
core/mutex.class.inc.php
Normal file
@@ -0,0 +1,193 @@
|
||||
<?php
|
||||
// Copyright (C) 2013 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 iTopMutex
|
||||
* A class to serialize the execution of some code sections
|
||||
* Emulates the API of PECL Mutex class
|
||||
* Relies on MySQL locks because the API sem_get is not always present in the
|
||||
* installed PHP.
|
||||
*
|
||||
* @copyright Copyright (C) 2013 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
class iTopMutex
|
||||
{
|
||||
protected $sName;
|
||||
protected $hDBLink;
|
||||
protected $bLocked; // Whether or not this instance of the Mutex is locked
|
||||
static protected $aAcquiredLocks = array(); // Number of instances of the Mutex, having the lock, in this page
|
||||
|
||||
public function __construct($sName, $sDBHost = null, $sDBUser = null, $sDBPwd = null)
|
||||
{
|
||||
// Compute the name of a lock for mysql
|
||||
// Note: the name is server-wide!!!
|
||||
$this->sName = 'itop.'.$sName;
|
||||
$this->bLocked = false; // Not yet locked
|
||||
|
||||
if (!array_key_exists($this->sName, self::$aAcquiredLocks))
|
||||
{
|
||||
self::$aAcquiredLocks[$this->sName] = 0;
|
||||
}
|
||||
|
||||
// It is a MUST to create a dedicated session each time a lock is required, because
|
||||
// using GET_LOCK anytime on the same session will RELEASE the current and unique session lock (known issue)
|
||||
$oConfig = utils::GetConfig();
|
||||
$sDBHost = is_null($sDBHost) ? $oConfig->GetDBHost() : $sDBHost;
|
||||
$sDBUser = is_null($sDBUser) ? $oConfig->GetDBUser() : $sDBUser;
|
||||
$sDBPwd = is_null($sDBPwd) ? $oConfig->GetDBPwd() : $sDBPwd;
|
||||
$this->InitMySQLSession($sDBHost, $sDBUser, $sDBPwd);
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
if ($this->bLocked)
|
||||
{
|
||||
$this->Unlock();
|
||||
}
|
||||
mysqli_close($this->hDBLink);
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire the mutex
|
||||
*/
|
||||
public function Lock()
|
||||
{
|
||||
if ($this->bLocked)
|
||||
{
|
||||
// Lock already acquired
|
||||
return;
|
||||
}
|
||||
if (self::$aAcquiredLocks[$this->sName] == 0)
|
||||
{
|
||||
do
|
||||
{
|
||||
$res = $this->QueryToScalar("SELECT GET_LOCK('".$this->sName."', 3600)");
|
||||
if (is_null($res))
|
||||
{
|
||||
throw new Exception("Failed to acquire the lock '".$this->sName."'");
|
||||
}
|
||||
// $res === '1' means I hold the lock
|
||||
// $res === '0' means it timed out
|
||||
}
|
||||
while ($res !== '1');
|
||||
}
|
||||
$this->bLocked = true;
|
||||
self::$aAcquiredLocks[$this->sName]++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to acquire the mutex
|
||||
* @returns bool True if the mutex is acquired, false if already locked elsewhere
|
||||
*/
|
||||
public function TryLock()
|
||||
{
|
||||
if ($this->bLocked)
|
||||
{
|
||||
return true; // Already acquired
|
||||
}
|
||||
if (self::$aAcquiredLocks[$this->sName] > 0)
|
||||
{
|
||||
self::$aAcquiredLocks[$this->sName]++;
|
||||
$this->bLocked = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
$res = $this->QueryToScalar("SELECT GET_LOCK('".$this->sName."', 0)");
|
||||
if (is_null($res))
|
||||
{
|
||||
throw new Exception("Failed to acquire the lock '".$this->sName."'");
|
||||
}
|
||||
// $res === '1' means I hold the lock
|
||||
// $res === '0' means it timed out
|
||||
if ($res === '1')
|
||||
{
|
||||
$this->bLocked = true;
|
||||
self::$aAcquiredLocks[$this->sName]++;
|
||||
}
|
||||
return ($res === '1');
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the mutex
|
||||
*/
|
||||
public function Unlock()
|
||||
{
|
||||
if (!$this->bLocked)
|
||||
{
|
||||
// ??? the lock is not acquired, exit
|
||||
return;
|
||||
}
|
||||
if (self::$aAcquiredLocks[$this->sName] == 0)
|
||||
{
|
||||
return; // Safety net
|
||||
}
|
||||
|
||||
if (self::$aAcquiredLocks[$this->sName] == 1)
|
||||
{
|
||||
$res = $this->QueryToScalar("SELECT RELEASE_LOCK('".$this->sName."')");
|
||||
}
|
||||
$this->bLocked = false;
|
||||
self::$aAcquiredLocks[$this->sName]--;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function InitMySQLSession($sHost, $sUser, $sPwd)
|
||||
{
|
||||
$aConnectInfo = explode(':', $sHost);
|
||||
if (count($aConnectInfo) > 1)
|
||||
{
|
||||
// Override the default port
|
||||
$sServer = $aConnectInfo[0];
|
||||
$iPort = $aConnectInfo[1];
|
||||
$this->hDBLink = @mysqli_connect($sServer, $sUser, $sPwd, '', $iPort);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->hDBLink = @mysqli_connect($sHost, $sUser, $sPwd);
|
||||
}
|
||||
|
||||
if (!$this->hDBLink)
|
||||
{
|
||||
throw new Exception("Could not connect to the DB server (host=$sHost, user=$sUser): ".mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno().')');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function QueryToScalar($sSql)
|
||||
{
|
||||
$result = mysqli_query($this->hDBLink, $sSql);
|
||||
if (!$result)
|
||||
{
|
||||
throw new Exception("Failed to issue MySQL query '".$sSql."': ".mysqli_error($this->hDBLink).' (mysql errno: '.mysqli_errno($this->hDBLink).')');
|
||||
}
|
||||
if ($aRow = mysqli_fetch_array($result, MYSQLI_BOTH))
|
||||
{
|
||||
$res = $aRow[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
mysqli_free_result($result);
|
||||
throw new Exception("No result for query '".$sSql."'");
|
||||
}
|
||||
mysqli_free_result($result);
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,28 @@
|
||||
<?php
|
||||
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* OQL syntax analyzer, to be used prior to run the lexical analyzer
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
// Notes (from the source file: oql-lexer.plex) - Romain
|
||||
@@ -119,6 +120,11 @@ class OQLLexerRaw
|
||||
'/\G-/ ',
|
||||
'/\GAND/ ',
|
||||
'/\GOR/ ',
|
||||
'/\G\\|/ ',
|
||||
'/\G&/ ',
|
||||
'/\G\\^/ ',
|
||||
'/\G<</ ',
|
||||
'/\G>>/ ',
|
||||
'/\G,/ ',
|
||||
'/\G\\(/ ',
|
||||
'/\G\\)/ ',
|
||||
@@ -168,7 +174,8 @@ class OQLLexerRaw
|
||||
'/\GABOVE STRICT/ ',
|
||||
'/\GNOT ABOVE/ ',
|
||||
'/\GNOT ABOVE STRICT/ ',
|
||||
'/\G(0x[0-9a-fA-F]+|[0-9]+)/ ',
|
||||
'/\G(0x[0-9a-fA-F]+)/ ',
|
||||
'/\G([0-9]+)/ ',
|
||||
'/\G\"([^\\\\\"]|\\\\\"|\\\\\\\\)*\"|'.chr(94).chr(39).'([^\\\\'.chr(39).']|\\\\'.chr(39).'|\\\\\\\\)*'.chr(39).'/ ',
|
||||
'/\G([_a-zA-Z][_a-zA-Z0-9]*|`[^`]+`)/ ',
|
||||
'/\G:([_a-zA-Z][_a-zA-Z0-9]*->[_a-zA-Z][_a-zA-Z0-9]*|[_a-zA-Z][_a-zA-Z0-9]*)/ ',
|
||||
@@ -333,269 +340,299 @@ class OQLLexerRaw
|
||||
function yy_r1_13($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::COMA;
|
||||
$this->token = OQLParser::BITWISE_OR;
|
||||
}
|
||||
function yy_r1_14($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::PAR_OPEN;
|
||||
$this->token = OQLParser::BITWISE_AND;
|
||||
}
|
||||
function yy_r1_15($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::PAR_CLOSE;
|
||||
$this->token = OQLParser::BITWISE_XOR;
|
||||
}
|
||||
function yy_r1_16($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::REGEXP;
|
||||
$this->token = OQLParser::BITWISE_LEFT_SHIFT;
|
||||
}
|
||||
function yy_r1_17($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::EQ;
|
||||
$this->token = OQLParser::BITWISE_RIGHT_SHIFT;
|
||||
}
|
||||
function yy_r1_18($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NOT_EQ;
|
||||
$this->token = OQLParser::COMA;
|
||||
}
|
||||
function yy_r1_19($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::GT;
|
||||
$this->token = OQLParser::PAR_OPEN;
|
||||
}
|
||||
function yy_r1_20($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::LT;
|
||||
$this->token = OQLParser::PAR_CLOSE;
|
||||
}
|
||||
function yy_r1_21($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::GE;
|
||||
$this->token = OQLParser::REGEXP;
|
||||
}
|
||||
function yy_r1_22($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::LE;
|
||||
$this->token = OQLParser::EQ;
|
||||
}
|
||||
function yy_r1_23($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::LIKE;
|
||||
$this->token = OQLParser::NOT_EQ;
|
||||
}
|
||||
function yy_r1_24($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NOT_LIKE;
|
||||
$this->token = OQLParser::GT;
|
||||
}
|
||||
function yy_r1_25($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::IN;
|
||||
$this->token = OQLParser::LT;
|
||||
}
|
||||
function yy_r1_26($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NOT_IN;
|
||||
$this->token = OQLParser::GE;
|
||||
}
|
||||
function yy_r1_27($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::INTERVAL;
|
||||
$this->token = OQLParser::LE;
|
||||
}
|
||||
function yy_r1_28($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_IF;
|
||||
$this->token = OQLParser::LIKE;
|
||||
}
|
||||
function yy_r1_29($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_ELT;
|
||||
$this->token = OQLParser::NOT_LIKE;
|
||||
}
|
||||
function yy_r1_30($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_COALESCE;
|
||||
$this->token = OQLParser::IN;
|
||||
}
|
||||
function yy_r1_31($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_ISNULL;
|
||||
$this->token = OQLParser::NOT_IN;
|
||||
}
|
||||
function yy_r1_32($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_CONCAT;
|
||||
$this->token = OQLParser::INTERVAL;
|
||||
}
|
||||
function yy_r1_33($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_SUBSTR;
|
||||
$this->token = OQLParser::F_IF;
|
||||
}
|
||||
function yy_r1_34($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_TRIM;
|
||||
$this->token = OQLParser::F_ELT;
|
||||
}
|
||||
function yy_r1_35($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_DATE;
|
||||
$this->token = OQLParser::F_COALESCE;
|
||||
}
|
||||
function yy_r1_36($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_DATE_FORMAT;
|
||||
$this->token = OQLParser::F_ISNULL;
|
||||
}
|
||||
function yy_r1_37($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_CURRENT_DATE;
|
||||
$this->token = OQLParser::F_CONCAT;
|
||||
}
|
||||
function yy_r1_38($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_NOW;
|
||||
$this->token = OQLParser::F_SUBSTR;
|
||||
}
|
||||
function yy_r1_39($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_TIME;
|
||||
$this->token = OQLParser::F_TRIM;
|
||||
}
|
||||
function yy_r1_40($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_TO_DAYS;
|
||||
$this->token = OQLParser::F_DATE;
|
||||
}
|
||||
function yy_r1_41($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_FROM_DAYS;
|
||||
$this->token = OQLParser::F_DATE_FORMAT;
|
||||
}
|
||||
function yy_r1_42($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_YEAR;
|
||||
$this->token = OQLParser::F_CURRENT_DATE;
|
||||
}
|
||||
function yy_r1_43($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_MONTH;
|
||||
$this->token = OQLParser::F_NOW;
|
||||
}
|
||||
function yy_r1_44($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_DAY;
|
||||
$this->token = OQLParser::F_TIME;
|
||||
}
|
||||
function yy_r1_45($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_HOUR;
|
||||
$this->token = OQLParser::F_TO_DAYS;
|
||||
}
|
||||
function yy_r1_46($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_MINUTE;
|
||||
$this->token = OQLParser::F_FROM_DAYS;
|
||||
}
|
||||
function yy_r1_47($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_SECOND;
|
||||
$this->token = OQLParser::F_YEAR;
|
||||
}
|
||||
function yy_r1_48($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_DATE_ADD;
|
||||
$this->token = OQLParser::F_MONTH;
|
||||
}
|
||||
function yy_r1_49($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_DATE_SUB;
|
||||
$this->token = OQLParser::F_DAY;
|
||||
}
|
||||
function yy_r1_50($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_ROUND;
|
||||
$this->token = OQLParser::F_HOUR;
|
||||
}
|
||||
function yy_r1_51($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_FLOOR;
|
||||
$this->token = OQLParser::F_MINUTE;
|
||||
}
|
||||
function yy_r1_52($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_INET_ATON;
|
||||
$this->token = OQLParser::F_SECOND;
|
||||
}
|
||||
function yy_r1_53($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_INET_NTOA;
|
||||
$this->token = OQLParser::F_DATE_ADD;
|
||||
}
|
||||
function yy_r1_54($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::BELOW;
|
||||
$this->token = OQLParser::F_DATE_SUB;
|
||||
}
|
||||
function yy_r1_55($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::BELOW_STRICT;
|
||||
$this->token = OQLParser::F_ROUND;
|
||||
}
|
||||
function yy_r1_56($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NOT_BELOW;
|
||||
$this->token = OQLParser::F_FLOOR;
|
||||
}
|
||||
function yy_r1_57($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NOT_BELOW_STRICT;
|
||||
$this->token = OQLParser::F_INET_ATON;
|
||||
}
|
||||
function yy_r1_58($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::ABOVE;
|
||||
$this->token = OQLParser::F_INET_NTOA;
|
||||
}
|
||||
function yy_r1_59($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::ABOVE_STRICT;
|
||||
$this->token = OQLParser::BELOW;
|
||||
}
|
||||
function yy_r1_60($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NOT_ABOVE;
|
||||
$this->token = OQLParser::BELOW_STRICT;
|
||||
}
|
||||
function yy_r1_61($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NOT_ABOVE_STRICT;
|
||||
$this->token = OQLParser::NOT_BELOW;
|
||||
}
|
||||
function yy_r1_62($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NUMVAL;
|
||||
$this->token = OQLParser::NOT_BELOW_STRICT;
|
||||
}
|
||||
function yy_r1_63($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::STRVAL;
|
||||
$this->token = OQLParser::ABOVE;
|
||||
}
|
||||
function yy_r1_64($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NAME;
|
||||
$this->token = OQLParser::ABOVE_STRICT;
|
||||
}
|
||||
function yy_r1_65($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::VARNAME;
|
||||
$this->token = OQLParser::NOT_ABOVE;
|
||||
}
|
||||
function yy_r1_66($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NOT_ABOVE_STRICT;
|
||||
}
|
||||
function yy_r1_67($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::HEXVAL;
|
||||
}
|
||||
function yy_r1_68($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NUMVAL;
|
||||
}
|
||||
function yy_r1_69($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::STRVAL;
|
||||
}
|
||||
function yy_r1_70($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NAME;
|
||||
}
|
||||
function yy_r1_71($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::VARNAME;
|
||||
}
|
||||
function yy_r1_72($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::DOT;
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
<?php
|
||||
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* OQL syntax analyzer, to be used prior to run the lexical analyzer
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
// Notes (from the source file: oql-lexer.plex) - Romain
|
||||
@@ -94,6 +95,11 @@ math_plus = "+"
|
||||
math_minus = "-"
|
||||
log_and = "AND"
|
||||
log_or = "OR"
|
||||
bitwise_and = "&"
|
||||
bitwise_or = "|"
|
||||
bitwise_xor = "^"
|
||||
bitwise_leftshift = "<<"
|
||||
bitwise_rightshift = ">>"
|
||||
regexp = "REGEXP"
|
||||
eq = "="
|
||||
not_eq = "!="
|
||||
@@ -155,8 +161,11 @@ not_above_strict = "NOT ABOVE STRICT"
|
||||
//
|
||||
// numval = /([0-9]+|0x[0-9a-fA-F]+)/
|
||||
// Does not work either, the hexadecimal numbers are not matched properly
|
||||
// The following seems to work...
|
||||
numval = /(0x[0-9a-fA-F]+|[0-9]+)/
|
||||
// Anyhow let's distinguish the hexadecimal values from decimal integers, hex numbers will be stored as strings
|
||||
// and passed as-is to MySQL which enables us to pass 64-bit values without messing with them in PHP
|
||||
//
|
||||
hexval = /(0x[0-9a-fA-F]+)/
|
||||
numval = /([0-9]+)/
|
||||
strval = /"([^\\"]|\\"|\\\\)*"|'.chr(94).chr(39).'([^\\'.chr(39).']|\\'.chr(39).'|\\\\)*'.chr(39).'/
|
||||
name = /([_a-zA-Z][_a-zA-Z0-9]*|`[^`]+`)/
|
||||
varname = /:([_a-zA-Z][_a-zA-Z0-9]*->[_a-zA-Z][_a-zA-Z0-9]*|[_a-zA-Z][_a-zA-Z0-9]*)/
|
||||
@@ -203,6 +212,21 @@ log_and {
|
||||
log_or {
|
||||
$this->token = OQLParser::LOG_OR;
|
||||
}
|
||||
bitwise_or {
|
||||
$this->token = OQLParser::BITWISE_OR;
|
||||
}
|
||||
bitwise_and {
|
||||
$this->token = OQLParser::BITWISE_AND;
|
||||
}
|
||||
bitwise_xor {
|
||||
$this->token = OQLParser::BITWISE_XOR;
|
||||
}
|
||||
bitwise_leftshift {
|
||||
$this->token = OQLParser::BITWISE_LEFT_SHIFT;
|
||||
}
|
||||
bitwise_rightshift {
|
||||
$this->token = OQLParser::BITWISE_RIGHT_SHIFT;
|
||||
}
|
||||
coma {
|
||||
$this->token = OQLParser::COMA;
|
||||
}
|
||||
@@ -350,6 +374,9 @@ not_above {
|
||||
not_above_strict {
|
||||
$this->token = OQLParser::NOT_ABOVE_STRICT;
|
||||
}
|
||||
hexval {
|
||||
$this->token = OQLParser::HEXVAL;
|
||||
}
|
||||
numval {
|
||||
$this->token = OQLParser::NUMVAL;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -164,19 +164,23 @@ name(A) ::= NAME(X). {
|
||||
}
|
||||
A = new OqlName($name, $this->m_iColPrev);
|
||||
}
|
||||
|
||||
num_value(A) ::= NUMVAL(X). {A=X;}
|
||||
num_value(A) ::= NUMVAL(X). {A=(int)X;}
|
||||
num_value(A) ::= MATH_MINUS NUMVAL(X). {A=(int)-X;}
|
||||
num_value(A) ::= HEXVAL(X). {A=new OqlHexValue(X);}
|
||||
str_value(A) ::= STRVAL(X). {A=stripslashes(substr(X, 1, strlen(X) - 2));}
|
||||
|
||||
|
||||
operator1(A) ::= num_operator1(X). {A=X;}
|
||||
operator1(A) ::= bitwise_operator1(X). {A=X;}
|
||||
operator2(A) ::= num_operator2(X). {A=X;}
|
||||
operator2(A) ::= str_operator(X). {A=X;}
|
||||
operator2(A) ::= REGEXP(X). {A=X;}
|
||||
operator2(A) ::= EQ(X). {A=X;}
|
||||
operator2(A) ::= NOT_EQ(X). {A=X;}
|
||||
operator3(A) ::= LOG_AND(X). {A=X;}
|
||||
operator3(A) ::= bitwise_operator3(X). {A=X;}
|
||||
operator4(A) ::= LOG_OR(X). {A=X;}
|
||||
operator4(A) ::= bitwise_operator4(X). {A=X;}
|
||||
|
||||
num_operator1(A) ::= MATH_DIV(X). {A=X;}
|
||||
num_operator1(A) ::= MATH_MULT(X). {A=X;}
|
||||
@@ -190,6 +194,12 @@ num_operator2(A) ::= LE(X). {A=X;}
|
||||
str_operator(A) ::= LIKE(X). {A=X;}
|
||||
str_operator(A) ::= NOT_LIKE(X). {A=X;}
|
||||
|
||||
bitwise_operator1(A) ::= BITWISE_LEFT_SHIFT(X). {A=X;}
|
||||
bitwise_operator1(A) ::= BITWISE_RIGHT_SHIFT(X). {A=X;}
|
||||
bitwise_operator3(A) ::= BITWISE_AND(X). {A=X;}
|
||||
bitwise_operator4(A) ::= BITWISE_OR(X). {A=X;}
|
||||
bitwise_operator4(A) ::= BITWISE_XOR(X). {A=X;}
|
||||
|
||||
list_operator(A) ::= IN(X). {A=X;}
|
||||
list_operator(A) ::= NOT_IN(X). {A=X;}
|
||||
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
/**
|
||||
* Special handling for OQL syntax errors
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
@@ -65,8 +65,11 @@ class OQLException extends CoreException
|
||||
|
||||
if (!is_null($this->m_aExpecting) && (count($this->m_aExpecting) > 0))
|
||||
{
|
||||
$sExpectations = '{'.implode(', ', $this->m_aExpecting).'}';
|
||||
$sRet .= ", expecting ".htmlentities($sExpectations, ENT_QUOTES, 'UTF-8');
|
||||
if (count($this->m_aExpecting) < 30)
|
||||
{
|
||||
$sExpectations = '{'.implode(', ', $this->m_aExpecting).'}';
|
||||
$sRet .= ", expecting ".htmlentities($sExpectations, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
$sSuggest = self::FindClosestString($this->m_sUnexpected, $this->m_aExpecting);
|
||||
if (strlen($sSuggest) > 0)
|
||||
{
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Wrapper to execute the parser, lexical analyzer and normalization of an OQL query
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Classes defined for lexical analyze (see oql-parser.y)
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
// Position a string within an OQL query
|
||||
@@ -53,6 +54,27 @@ class OqlName
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Store hexadecimal values as strings so that we can support 64-bit values
|
||||
*
|
||||
*/
|
||||
class OqlHexValue
|
||||
{
|
||||
protected $m_sValue;
|
||||
|
||||
public function __construct($sValue)
|
||||
{
|
||||
$this->m_sValue = $sValue;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->m_sValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class OqlJoinSpec
|
||||
{
|
||||
protected $m_oClass;
|
||||
@@ -105,15 +127,38 @@ class OqlJoinSpec
|
||||
}
|
||||
}
|
||||
|
||||
class BinaryOqlExpression extends BinaryExpression
|
||||
interface CheckableExpression
|
||||
{
|
||||
/**
|
||||
* Check the validity of the expression with regard to the data model
|
||||
* and the query in which it is used
|
||||
*
|
||||
* @param ModelReflection $oModelReflection MetaModel to consider
|
||||
* @param array $aAliases Aliases to class names (for the current query)
|
||||
* @param string $sSourceQuery For the reporting
|
||||
* @throws OqlNormalizeException
|
||||
*/
|
||||
public function Check(ModelReflection $oModelReflection, $aAliases, $sSourceQuery);
|
||||
}
|
||||
|
||||
class ScalarOqlExpression extends ScalarExpression
|
||||
class BinaryOqlExpression extends BinaryExpression implements CheckableExpression
|
||||
{
|
||||
public function Check(ModelReflection $oModelReflection, $aAliases, $sSourceQuery)
|
||||
{
|
||||
$this->m_oLeftExpr->Check($oModelReflection, $aAliases, $sSourceQuery);
|
||||
$this->m_oRightExpr->Check($oModelReflection, $aAliases, $sSourceQuery);
|
||||
}
|
||||
}
|
||||
|
||||
class FieldOqlExpression extends FieldExpression
|
||||
class ScalarOqlExpression extends ScalarExpression implements CheckableExpression
|
||||
{
|
||||
public function Check(ModelReflection $oModelReflection, $aAliases, $sSourceQuery)
|
||||
{
|
||||
// a scalar is always fine
|
||||
}
|
||||
}
|
||||
|
||||
class FieldOqlExpression extends FieldExpression implements CheckableExpression
|
||||
{
|
||||
protected $m_oParent;
|
||||
protected $m_oName;
|
||||
@@ -139,22 +184,84 @@ class FieldOqlExpression extends FieldExpression
|
||||
{
|
||||
return $this->m_oName;
|
||||
}
|
||||
|
||||
public function Check(ModelReflection $oModelReflection, $aAliases, $sSourceQuery)
|
||||
{
|
||||
$sClassAlias = $this->GetParent();
|
||||
$sFltCode = $this->GetName();
|
||||
if (empty($sClassAlias))
|
||||
{
|
||||
// Try to find an alias
|
||||
// Build an array of field => array of aliases
|
||||
$aFieldClasses = array();
|
||||
foreach($aAliases as $sAlias => $sReal)
|
||||
{
|
||||
foreach($oModelReflection->GetFiltersList($sReal) as $sAnFltCode)
|
||||
{
|
||||
$aFieldClasses[$sAnFltCode][] = $sAlias;
|
||||
}
|
||||
}
|
||||
if (!array_key_exists($sFltCode, $aFieldClasses))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown filter code', $sSourceQuery, $this->GetNameDetails(), array_keys($aFieldClasses));
|
||||
}
|
||||
if (count($aFieldClasses[$sFltCode]) > 1)
|
||||
{
|
||||
throw new OqlNormalizeException('Ambiguous filter code', $sSourceQuery, $this->GetNameDetails());
|
||||
}
|
||||
$sClassAlias = $aFieldClasses[$sFltCode][0];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!array_key_exists($sClassAlias, $aAliases))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown class [alias]', $sSourceQuery, $this->GetParentDetails(), array_keys($aAliases));
|
||||
}
|
||||
$sClass = $aAliases[$sClassAlias];
|
||||
if (!$oModelReflection->IsValidFilterCode($sClass, $sFltCode))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown filter code', $sSourceQuery, $this->GetNameDetails(), $oModelReflection->GetFiltersList($sClass));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class VariableOqlExpression extends VariableExpression
|
||||
class VariableOqlExpression extends VariableExpression implements CheckableExpression
|
||||
{
|
||||
public function Check(ModelReflection $oModelReflection, $aAliases, $sSourceQuery)
|
||||
{
|
||||
// a scalar is always fine
|
||||
}
|
||||
}
|
||||
|
||||
class ListOqlExpression extends ListExpression
|
||||
class ListOqlExpression extends ListExpression implements CheckableExpression
|
||||
{
|
||||
public function Check(ModelReflection $oModelReflection, $aAliases, $sSourceQuery)
|
||||
{
|
||||
foreach ($this->GetItems() as $oItemExpression)
|
||||
{
|
||||
$oItemExpression->Check($oModelReflection, $aAliases, $sSourceQuery);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FunctionOqlExpression extends FunctionExpression
|
||||
class FunctionOqlExpression extends FunctionExpression implements CheckableExpression
|
||||
{
|
||||
public function Check(ModelReflection $oModelReflection, $aAliases, $sSourceQuery)
|
||||
{
|
||||
foreach ($this->GetArgs() as $oArgExpression)
|
||||
{
|
||||
$oArgExpression->Check($oModelReflection, $aAliases, $sSourceQuery);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class IntervalOqlExpression extends IntervalExpression
|
||||
class IntervalOqlExpression extends IntervalExpression implements CheckableExpression
|
||||
{
|
||||
public function Check(ModelReflection $oModelReflection, $aAliases, $sSourceQuery)
|
||||
{
|
||||
// an interval is always fine (made of a scalar and unit)
|
||||
}
|
||||
}
|
||||
|
||||
abstract class OqlQuery
|
||||
@@ -213,6 +320,155 @@ class OqlObjectQuery extends OqlQuery
|
||||
{
|
||||
return $this->m_oClassAlias;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively check the validity of the expression with regard to the data model
|
||||
* and the query in which it is used
|
||||
*
|
||||
* @param ModelReflection $oModelReflection MetaModel to consider
|
||||
* @throws OqlNormalizeException
|
||||
*/
|
||||
public function Check(ModelReflection $oModelReflection, $sSourceQuery)
|
||||
{
|
||||
$sClass = $this->GetClass();
|
||||
$sClassAlias = $this->GetClassAlias();
|
||||
|
||||
if (!$oModelReflection->IsValidClass($sClass))
|
||||
{
|
||||
throw new UnknownClassOqlException($sSourceQuery, $this->GetClassDetails(), $oModelReflection->GetClasses());
|
||||
}
|
||||
|
||||
$aAliases = array($sClassAlias => $sClass);
|
||||
|
||||
$aJoinSpecs = $this->GetJoins();
|
||||
if (is_array($aJoinSpecs))
|
||||
{
|
||||
foreach ($aJoinSpecs as $oJoinSpec)
|
||||
{
|
||||
$sJoinClass = $oJoinSpec->GetClass();
|
||||
$sJoinClassAlias = $oJoinSpec->GetClassAlias();
|
||||
if (!$oModelReflection->IsValidClass($sJoinClass))
|
||||
{
|
||||
throw new UnknownClassOqlException($sSourceQuery, $oJoinSpec->GetClassDetails(), $oModelReflection->GetClasses());
|
||||
}
|
||||
if (array_key_exists($sJoinClassAlias, $aAliases))
|
||||
{
|
||||
if ($sJoinClassAlias != $sJoinClass)
|
||||
{
|
||||
throw new OqlNormalizeException('Duplicate class alias', $sSourceQuery, $oJoinSpec->GetClassAliasDetails());
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new OqlNormalizeException('Duplicate class name', $sSourceQuery, $oJoinSpec->GetClassDetails());
|
||||
}
|
||||
}
|
||||
|
||||
// Assumption: ext key on the left only !!!
|
||||
// normalization should take care of this
|
||||
$oLeftField = $oJoinSpec->GetLeftField();
|
||||
$sFromClass = $oLeftField->GetParent();
|
||||
$sExtKeyAttCode = $oLeftField->GetName();
|
||||
|
||||
$oRightField = $oJoinSpec->GetRightField();
|
||||
$sToClass = $oRightField->GetParent();
|
||||
$sPKeyDescriptor = $oRightField->GetName();
|
||||
if ($sPKeyDescriptor != 'id')
|
||||
{
|
||||
throw new OqlNormalizeException('Wrong format for Join clause (right hand), expecting an id', $sSourceQuery, $oRightField->GetNameDetails(), array('id'));
|
||||
}
|
||||
|
||||
$aAliases[$sJoinClassAlias] = $sJoinClass;
|
||||
|
||||
if (!array_key_exists($sFromClass, $aAliases))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown class in join condition (left expression)', $sSourceQuery, $oLeftField->GetParentDetails(), array_keys($aAliases));
|
||||
}
|
||||
if (!array_key_exists($sToClass, $aAliases))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown class in join condition (right expression)', $sSourceQuery, $oRightField->GetParentDetails(), array_keys($aAliases));
|
||||
}
|
||||
$aExtKeys = $oModelReflection->ListAttributes($aAliases[$sFromClass], 'AttributeExternalKey');
|
||||
if (!array_key_exists($sExtKeyAttCode, $aExtKeys))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown external key in join condition (left expression)', $sSourceQuery, $oLeftField->GetNameDetails(), array_keys($aExtKeys));
|
||||
}
|
||||
|
||||
if ($sFromClass == $sJoinClassAlias)
|
||||
{
|
||||
$sTargetClass = $oModelReflection->GetAttributeProperty($aAliases[$sFromClass], $sExtKeyAttCode, 'targetclass');
|
||||
if(!$oModelReflection->IsSameFamilyBranch($aAliases[$sToClass], $sTargetClass))
|
||||
{
|
||||
throw new OqlNormalizeException("The joined class ($aAliases[$sFromClass]) is not compatible with the external key, which is pointing to $sTargetClass", $sSourceQuery, $oLeftField->GetNameDetails());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$sOperator = $oJoinSpec->GetOperator();
|
||||
switch($sOperator)
|
||||
{
|
||||
case '=':
|
||||
$iOperatorCode = TREE_OPERATOR_EQUALS;
|
||||
break;
|
||||
case 'BELOW':
|
||||
$iOperatorCode = TREE_OPERATOR_BELOW;
|
||||
break;
|
||||
case 'BELOW_STRICT':
|
||||
$iOperatorCode = TREE_OPERATOR_BELOW_STRICT;
|
||||
break;
|
||||
case 'NOT_BELOW':
|
||||
$iOperatorCode = TREE_OPERATOR_NOT_BELOW;
|
||||
break;
|
||||
case 'NOT_BELOW_STRICT':
|
||||
$iOperatorCode = TREE_OPERATOR_NOT_BELOW_STRICT;
|
||||
break;
|
||||
case 'ABOVE':
|
||||
$iOperatorCode = TREE_OPERATOR_ABOVE;
|
||||
break;
|
||||
case 'ABOVE_STRICT':
|
||||
$iOperatorCode = TREE_OPERATOR_ABOVE_STRICT;
|
||||
break;
|
||||
case 'NOT_ABOVE':
|
||||
$iOperatorCode = TREE_OPERATOR_NOT_ABOVE;
|
||||
break;
|
||||
case 'NOT_ABOVE_STRICT':
|
||||
$iOperatorCode = TREE_OPERATOR_NOT_ABOVE_STRICT;
|
||||
break;
|
||||
}
|
||||
$sTargetClass = $oModelReflection->GetAttributeProperty($aAliases[$sFromClass], $sExtKeyAttCode, 'targetclass');
|
||||
if(!$oModelReflection->IsSameFamilyBranch($aAliases[$sToClass], $sTargetClass))
|
||||
{
|
||||
throw new OqlNormalizeException("The joined class ($aAliases[$sToClass]) is not compatible with the external key, which is pointing to $sTargetClass", $sSourceQuery, $oLeftField->GetNameDetails());
|
||||
}
|
||||
$aAttList = $oModelReflection->ListAttributes($aAliases[$sFromClass]);
|
||||
$sAttType = $aAttList[$sExtKeyAttCode];
|
||||
if(($iOperatorCode != TREE_OPERATOR_EQUALS) && !is_subclass_of($sAttType, 'AttributeHierarchicalKey') && ($sAttType != 'AttributeHierarchicalKey'))
|
||||
{
|
||||
throw new OqlNormalizeException("The specified tree operator $sOperator is not applicable to the key", $sSourceQuery, $oLeftField->GetNameDetails());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check the select information
|
||||
//
|
||||
$aSelected = array();
|
||||
foreach ($this->GetSelectedClasses() as $oClassDetails)
|
||||
{
|
||||
$sClassToSelect = $oClassDetails->GetValue();
|
||||
if (!array_key_exists($sClassToSelect, $aAliases))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown class [alias]', $sSourceQuery, $oClassDetails, array_keys($aAliases));
|
||||
}
|
||||
$aSelected[$sClassToSelect] = $aAliases[$sClassToSelect];
|
||||
}
|
||||
|
||||
// Check the condition tree
|
||||
//
|
||||
if ($this->m_oCondition instanceof Expression)
|
||||
{
|
||||
$this->m_oCondition->Check($oModelReflection, $aAliases, $sSourceQuery);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
@@ -1,27 +1,30 @@
|
||||
<?php
|
||||
// Copyright (C) 2011-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
define('CASELOG_VISIBLE_ITEMS', 2);
|
||||
define('CASELOG_SEPARATOR', "\n".'========== %1$s : %2$s (%3$d) ============'."\n\n");
|
||||
|
||||
//require_once(APPROOT.'/core/userrights.class.inc.php');
|
||||
//require_once(APPROOT.'/application/webpage.class.inc.php');
|
||||
|
||||
/**
|
||||
* Class to store a "case log" in a structured way, keeping track of its successive entries
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
class ormCaseLog {
|
||||
protected $m_sLog;
|
||||
@@ -45,6 +48,80 @@ class ormCaseLog {
|
||||
return $this->m_sLog;
|
||||
}
|
||||
|
||||
public static function FromJSON($oJson)
|
||||
{
|
||||
if (!isset($oJson->items))
|
||||
{
|
||||
throw new Exception("Missing 'items' elements");
|
||||
}
|
||||
$oCaseLog = new ormCaseLog();
|
||||
foreach($oJson->items as $oItem)
|
||||
{
|
||||
$oCaseLog->AddLogEntryFromJSON($oItem);
|
||||
}
|
||||
return $oCaseLog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a value that will be further JSON encoded
|
||||
*/
|
||||
public function GetForJSON()
|
||||
{
|
||||
$aEntries = array();
|
||||
$iPos = 0;
|
||||
for($index=count($this->m_aIndex)-1 ; $index >= 0 ; $index--)
|
||||
{
|
||||
$iPos += $this->m_aIndex[$index]['separator_length'];
|
||||
$sTextEntry = substr($this->m_sLog, $iPos, $this->m_aIndex[$index]['text_length']);
|
||||
$iPos += $this->m_aIndex[$index]['text_length'];
|
||||
|
||||
// Workaround: PHP < 5.3 cannot unserialize correctly DateTime objects,
|
||||
// therefore we have changed the format. To preserve the compatibility with existing
|
||||
// installations of iTop, both format are allowed:
|
||||
// the 'date' item is either a DateTime object, or a unix timestamp
|
||||
if (is_int($this->m_aIndex[$index]['date']))
|
||||
{
|
||||
// Unix timestamp
|
||||
$sDate = date(Dict::S('UI:CaseLog:DateFormat'),$this->m_aIndex[$index]['date']);
|
||||
}
|
||||
elseif (is_object($this->m_aIndex[$index]['date']))
|
||||
{
|
||||
if (version_compare(phpversion(), '5.3.0', '>='))
|
||||
{
|
||||
// DateTime
|
||||
$sDate = $this->m_aIndex[$index]['date']->format(Dict::S('UI:CaseLog:DateFormat'));
|
||||
}
|
||||
else
|
||||
{
|
||||
// No Warning... but the date is unknown
|
||||
$sDate = '';
|
||||
}
|
||||
}
|
||||
$aEntries[] = array(
|
||||
'date' => $sDate,
|
||||
'user_login' => $this->m_aIndex[$index]['user_name'],
|
||||
'user_id' => $this->m_aIndex[$index]['user_id'],
|
||||
'message' => $sTextEntry
|
||||
);
|
||||
}
|
||||
|
||||
// Process the case of an eventual remainder (quick migration of AttributeText fields)
|
||||
if ($iPos < (strlen($this->m_sLog) - 1))
|
||||
{
|
||||
$sTextEntry = substr($this->m_sLog, $iPos);
|
||||
|
||||
$aEntries[] = array(
|
||||
'date' => '',
|
||||
'user_login' => '',
|
||||
'message' => $sTextEntry
|
||||
);
|
||||
}
|
||||
|
||||
// Order by ascending date
|
||||
$aRet = array('entries' => array_reverse($aEntries));
|
||||
return $aRet;
|
||||
}
|
||||
|
||||
public function GetIndex()
|
||||
{
|
||||
return $this->m_aIndex;
|
||||
@@ -59,7 +136,85 @@ class ormCaseLog {
|
||||
{
|
||||
$this->m_bModified = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Produces an HTML representation, aimed at being used within an email
|
||||
*/
|
||||
public function GetAsEmailHtml()
|
||||
{
|
||||
$sStyleCaseLogHeader = '';
|
||||
$sStyleCaseLogEntry = '';
|
||||
|
||||
$sHtml = '<table style="width:100%;table-layout:fixed"><tr><td>'; // Use table-layout:fixed to force the with to be independent from the actual content
|
||||
$iPos = 0;
|
||||
$aIndex = $this->m_aIndex;
|
||||
for($index=count($aIndex)-1 ; $index >= 0 ; $index--)
|
||||
{
|
||||
$iPos += $aIndex[$index]['separator_length'];
|
||||
$sTextEntry = substr($this->m_sLog, $iPos, $aIndex[$index]['text_length']);
|
||||
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
|
||||
$iPos += $aIndex[$index]['text_length'];
|
||||
|
||||
$sEntry = '<div class="caselog_header" style="'.$sStyleCaseLogHeader.'">';
|
||||
// Workaround: PHP < 5.3 cannot unserialize correctly DateTime objects,
|
||||
// therefore we have changed the format. To preserve the compatibility with existing
|
||||
// installations of iTop, both format are allowed:
|
||||
// the 'date' item is either a DateTime object, or a unix timestamp
|
||||
if (is_int($aIndex[$index]['date']))
|
||||
{
|
||||
// Unix timestamp
|
||||
$sDate = date(Dict::S('UI:CaseLog:DateFormat'),$aIndex[$index]['date']);
|
||||
}
|
||||
elseif (is_object($aIndex[$index]['date']))
|
||||
{
|
||||
if (version_compare(phpversion(), '5.3.0', '>='))
|
||||
{
|
||||
// DateTime
|
||||
$sDate = $aIndex[$index]['date']->format(Dict::S('UI:CaseLog:DateFormat'));
|
||||
}
|
||||
else
|
||||
{
|
||||
// No Warning... but the date is unknown
|
||||
$sDate = '';
|
||||
}
|
||||
}
|
||||
$sEntry .= sprintf(Dict::S('UI:CaseLog:Header_Date_UserName'), '<span class="caselog_header_date">'.$sDate.'</span>', '<span class="caselog_header_user">'.$aIndex[$index]['user_name'].'</span>');
|
||||
$sEntry .= '</div>';
|
||||
$sEntry .= '<div class="caselog_entry" style="'.$sStyleCaseLogEntry.'">';
|
||||
$sEntry .= $sTextEntry;
|
||||
$sEntry .= '</div>';
|
||||
$sHtml = $sHtml.$sEntry;
|
||||
}
|
||||
|
||||
// Process the case of an eventual remainder (quick migration of AttributeText fields)
|
||||
if ($iPos < (strlen($this->m_sLog) - 1))
|
||||
{
|
||||
$sTextEntry = substr($this->m_sLog, $iPos);
|
||||
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
|
||||
|
||||
if (count($this->m_aIndex) == 0)
|
||||
{
|
||||
$sHtml .= '<div class="caselog_entry" style="'.$sStyleCaseLogEntry.'"">';
|
||||
$sHtml .= $sTextEntry;
|
||||
$sHtml .= '</div>';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sHtml .= '<div class="caselog_header" style="'.$sStyleCaseLogHeader.'">';
|
||||
$sHtml .= Dict::S('UI:CaseLog:InitialValue');
|
||||
$sHtml .= '</div>';
|
||||
$sHtml .= '<div class="caselog_entry" style="'.$sStyleCaseLogEntry.'">';
|
||||
$sHtml .= $sTextEntry;
|
||||
$sHtml .= '</div>';
|
||||
}
|
||||
}
|
||||
$sHtml .= '</td></tr></table>';
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Produces an HTML representation, aimed at being used within the iTop framework
|
||||
*/
|
||||
public function GetAsHTML(WebPage $oP = null, $bEditMode = false, $aTransfoHandler = null)
|
||||
{
|
||||
$sHtml = '<table style="width:100%;table-layout:fixed"><tr><td>'; // Use table-layout:fixed to force the with to be independent from the actual content
|
||||
@@ -227,7 +382,62 @@ class ormCaseLog {
|
||||
}
|
||||
$this->m_bModified = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function AddLogEntryFromJSON($oJson)
|
||||
{
|
||||
$sText = isset($oJson->message) ? $oJson->message : '';
|
||||
|
||||
if (isset($oJson->user_id))
|
||||
{
|
||||
if (!UserRights::IsAdministrator())
|
||||
{
|
||||
throw new Exception("Only administrators can set the user id", RestResult::UNAUTHORIZED);
|
||||
}
|
||||
try
|
||||
{
|
||||
$oUser = RestUtils::FindObjectFromKey('User', $oJson->user_id);
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
throw new Exception('user_id: '.$e->getMessage(), $e->getCode());
|
||||
}
|
||||
$iUserId = $oUser->GetKey();
|
||||
$sOnBehalfOf = $oUser->GetFriendlyName();
|
||||
}
|
||||
else
|
||||
{
|
||||
$iUserId = UserRights::GetUserId();
|
||||
$sOnBehalfOf = UserRights::GetUserFriendlyName();
|
||||
}
|
||||
|
||||
if (isset($oJson->date))
|
||||
{
|
||||
$oDate = new DateTime($oJson->date);
|
||||
$iDate = (int) $oDate->format('U');
|
||||
}
|
||||
else
|
||||
{
|
||||
$iDate = time();
|
||||
}
|
||||
$sDate = date(Dict::S('UI:CaseLog:DateFormat'), $iDate);
|
||||
|
||||
$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' => $iDate,
|
||||
'text_length' => $iTextlength,
|
||||
'separator_length' => $iSepLength,
|
||||
);
|
||||
|
||||
$this->m_bModified = true;
|
||||
}
|
||||
|
||||
|
||||
public function GetModifiedEntry()
|
||||
{
|
||||
$sModifiedEntry = '';
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* ormDocument
|
||||
* encapsulate the behavior of a binary data set that will be stored an attribute of class AttributeBlob
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
@@ -116,5 +117,32 @@ class ormDocument
|
||||
{
|
||||
return "<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=download_document&class=$sClass&id=$Id&field=$sAttCode\">".$this->GetFileName()."</a>\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.render.php?operation=download_document&class=$sClass&id=$Id&field=$sAttCode&s=$sSignature&cache=86400";
|
||||
}
|
||||
|
||||
|
||||
public function IsPreviewAvailable()
|
||||
{
|
||||
$bRet = false;
|
||||
switch($this->GetMimeType())
|
||||
{
|
||||
case 'image/png':
|
||||
case 'image/jpg':
|
||||
case 'image/jpeg':
|
||||
case 'image/gif':
|
||||
$bRet = true;
|
||||
break;
|
||||
}
|
||||
return $bRet;
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
require_once(APPROOT.'/core/simplecrypt.class.inc.php');
|
||||
|
||||
@@ -23,10 +26,8 @@ require_once(APPROOT.'/core/simplecrypt.class.inc.php');
|
||||
* If a cryptographic random number generator is available (on Linux or Windows)
|
||||
* it will be used for generating the salt.
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @package itopORM
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// 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('backgroundprocess.inc.php');
|
||||
|
||||
@@ -20,10 +22,8 @@ require_once('backgroundprocess.inc.php');
|
||||
* ormStopWatch
|
||||
* encapsulate the behavior of a stop watch that will be stored as an attribute of class AttributeStopWatch
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
@@ -66,7 +66,6 @@ class ormStopWatch
|
||||
{
|
||||
$this->aThresholds[$iPercent] = array(
|
||||
'deadline' => $tDeadline, // unix time (seconds)
|
||||
'passed' => $bPassed,
|
||||
'triggered' => $bTriggered,
|
||||
'overrun' => $iOverrun
|
||||
);
|
||||
@@ -122,14 +121,16 @@ class ormStopWatch
|
||||
}
|
||||
public function IsThresholdPassed($iPercent)
|
||||
{
|
||||
$bRet = false;
|
||||
if (array_key_exists($iPercent, $this->aThresholds))
|
||||
{
|
||||
return $this->aThresholds[$iPercent]['passed'];
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
$aThresholdData = $this->aThresholds[$iPercent];
|
||||
if (!is_null($aThresholdData['deadline']) && ($aThresholdData['deadline'] <= time()))
|
||||
{
|
||||
$bRet = true;
|
||||
}
|
||||
}
|
||||
return $bRet;
|
||||
}
|
||||
public function IsThresholdTriggered($iPercent)
|
||||
{
|
||||
@@ -162,8 +163,7 @@ class ormStopWatch
|
||||
}
|
||||
else
|
||||
{
|
||||
$iElapsedTemp = ''; //$this->ComputeDuration($oHostObject, $oAttDef, $this->iLastStart, time());
|
||||
$aProperties['Elapsed'] = $this->iTimeSpent.' + '.$iElapsedTemp.' s + <img src="../images/indicator.gif">';
|
||||
$aProperties['Elapsed'] = 'running <img src="../images/indicator.gif">';
|
||||
}
|
||||
|
||||
$aProperties['Started'] = $oAttDef->SecondsToDate($this->iStarted);
|
||||
@@ -183,7 +183,7 @@ class ormStopWatch
|
||||
}
|
||||
$aProperties[$iPercent.'%'] = $sThresholdDesc;
|
||||
}
|
||||
$sRes = "<TABLE class=\"listResults\">";
|
||||
$sRes = "<TABLE>";
|
||||
$sRes .= "<TBODY>";
|
||||
foreach ($aProperties as $sProperty => $sValue)
|
||||
{
|
||||
@@ -213,6 +213,10 @@ class ormStopWatch
|
||||
protected function ComputeDeadline($oObject, $oAttDef, $iStartTime, $iDurationSec)
|
||||
{
|
||||
$sWorkingTimeComputer = $oAttDef->Get('working_time_computing');
|
||||
if ($sWorkingTimeComputer == '')
|
||||
{
|
||||
$sWorkingTimeComputer = class_exists('SLAComputation') ? 'SLAComputation' : 'DefaultWorkingTimeComputer';
|
||||
}
|
||||
$aCallSpec = array($sWorkingTimeComputer, '__construct');
|
||||
if (!is_callable($aCallSpec))
|
||||
{
|
||||
@@ -234,6 +238,10 @@ class ormStopWatch
|
||||
protected function ComputeDuration($oObject, $oAttDef, $iStartTime, $iEndTime)
|
||||
{
|
||||
$sWorkingTimeComputer = $oAttDef->Get('working_time_computing');
|
||||
if ($sWorkingTimeComputer == '')
|
||||
{
|
||||
$sWorkingTimeComputer = class_exists('SLAComputation') ? 'SLAComputation' : 'DefaultWorkingTimeComputer';
|
||||
}
|
||||
$oComputer = new $sWorkingTimeComputer();
|
||||
$aCallSpec = array($oComputer, 'GetOpenDuration');
|
||||
if (!is_callable($aCallSpec))
|
||||
@@ -250,17 +258,22 @@ class ormStopWatch
|
||||
public function Reset($oObject, $oAttDef)
|
||||
{
|
||||
$this->iTimeSpent = 0;
|
||||
$this->iStarted = null;
|
||||
$this->iLastStart = null;
|
||||
$this->iStopped = null;
|
||||
$this->iStarted = null;
|
||||
|
||||
foreach ($this->aThresholds as $iPercent => &$aThresholdData)
|
||||
{
|
||||
$aThresholdData['passed'] = false;
|
||||
$aThresholdData['triggered'] = false;
|
||||
$aThresholdData['deadline'] = null;
|
||||
$aThresholdData['overrun'] = null;
|
||||
}
|
||||
|
||||
if (!is_null($this->iLastStart))
|
||||
{
|
||||
// Currently running... starting again from now!
|
||||
$this->iStarted = time();
|
||||
$this->iLastStart = time();
|
||||
$this->ComputeDeadlines($oObject, $oAttDef);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -315,14 +328,12 @@ class ormStopWatch
|
||||
if (is_null($aThresholdData['deadline']) || ($aThresholdData['deadline'] > time()))
|
||||
{
|
||||
// The threshold is in the future, reset
|
||||
$aThresholdData['passed'] = false;
|
||||
$aThresholdData['triggered'] = false;
|
||||
$aThresholdData['overrun'] = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The new threshold is in the past
|
||||
$aThresholdData['passed'] = true;
|
||||
// Note: the overrun can be wrong, but the correct algorithm to compute
|
||||
// the overrun of a deadline in the past requires that the ormStopWatch keeps track of all its history!!!
|
||||
}
|
||||
@@ -360,7 +371,6 @@ class ormStopWatch
|
||||
$iOverrun = $this->ComputeDuration($oObject, $oAttDef, $aThresholdData['deadline'], time());
|
||||
$aThresholdData['overrun'] = $iOverrun;
|
||||
}
|
||||
$aThresholdData['passed'] = true;
|
||||
}
|
||||
$aThresholdData['deadline'] = null;
|
||||
}
|
||||
@@ -387,6 +397,7 @@ class CheckStopWatchThresholds implements iBackgroundProcess
|
||||
|
||||
public function Process($iTimeLimit)
|
||||
{
|
||||
$aList = array();
|
||||
foreach (MetaModel::GetClasses() as $sClass)
|
||||
{
|
||||
foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
|
||||
@@ -397,17 +408,15 @@ class CheckStopWatchThresholds implements iBackgroundProcess
|
||||
{
|
||||
$iPercent = $aThresholdData['percent']; // could be different than the index !
|
||||
|
||||
$sExpression = "SELECT $sClass WHERE {$sAttCode}_laststart AND {$sAttCode}_{$iThreshold}_triggered = 0 AND {$sAttCode}_{$iThreshold}_deadline < NOW()";
|
||||
//echo $sExpression."<br/>\n";
|
||||
$sNow = date('Y-m-d H:i:s');
|
||||
$sExpression = "SELECT $sClass WHERE {$sAttCode}_laststart AND {$sAttCode}_{$iThreshold}_triggered = 0 AND {$sAttCode}_{$iThreshold}_deadline < '$sNow'";
|
||||
$oFilter = DBObjectSearch::FromOQL($sExpression);
|
||||
$aList = array();
|
||||
$oSet = new DBObjectSet($oFilter);
|
||||
while ((time() < $iTimeLimit) && ($oObj = $oSet->Fetch()))
|
||||
{
|
||||
$sClass = get_class($oObj);
|
||||
|
||||
$aList[] = $sClass.'::'.$oObj->GetKey().' '.$sAttCode.' '.$iThreshold;
|
||||
//echo $sClass.'::'.$oObj->GetKey().' '.$sAttCode.' '.$iThreshold."\n";
|
||||
|
||||
// Execute planned actions
|
||||
//
|
||||
@@ -416,7 +425,6 @@ class CheckStopWatchThresholds implements iBackgroundProcess
|
||||
$sVerb = $aActionData['verb'];
|
||||
$aParams = $aActionData['params'];
|
||||
$sParams = implode(', ', $aParams);
|
||||
//echo "Calling: $sVerb($sParams)<br/>\n";
|
||||
$aCallSpec = array($oObj, $sVerb);
|
||||
call_user_func_array($aCallSpec, $aParams);
|
||||
}
|
||||
@@ -429,24 +437,21 @@ class CheckStopWatchThresholds implements iBackgroundProcess
|
||||
|
||||
if($oObj->IsModified())
|
||||
{
|
||||
// Todo - factorize so that only one single change will be instantiated
|
||||
$oMyChange = new CMDBChange();
|
||||
$oMyChange->Set("date", time());
|
||||
$oMyChange->Set("userinfo", "Automatic - threshold triggered");
|
||||
$iChangeId = $oMyChange->DBInsertNoReload();
|
||||
|
||||
CMDBObject::SetTrackInfo("Automatic - threshold triggered");
|
||||
|
||||
$oMyChange = CMDBObject::GetCurrentChange();
|
||||
$oObj->DBUpdateTracked($oMyChange, true /*skip security*/);
|
||||
}
|
||||
|
||||
// Activate any existing trigger
|
||||
//
|
||||
$sClassList = implode("', '", MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL));
|
||||
$oSet = new DBObjectSet(
|
||||
$oTriggerSet = new DBObjectSet(
|
||||
DBObjectSearch::FromOQL("SELECT TriggerOnThresholdReached AS t WHERE t.target_class IN ('$sClassList') AND stop_watch_code=:stop_watch_code AND threshold_index = :threshold_index"),
|
||||
array(), // order by
|
||||
array('stop_watch_code' => $sAttCode, 'threshold_index' => $iThreshold)
|
||||
);
|
||||
while ($oTrigger = $oSet->Fetch())
|
||||
while ($oTrigger = $oTriggerSet->Fetch())
|
||||
{
|
||||
$oTrigger->DoActivate($oObj->ToArgs('this'));
|
||||
}
|
||||
@@ -457,9 +462,6 @@ class CheckStopWatchThresholds implements iBackgroundProcess
|
||||
}
|
||||
|
||||
$iProcessed = count($aList);
|
||||
return "Triggered $iProcessed threshold(s)";
|
||||
return "Triggered $iProcessed threshold(s):".implode(", ", $aList);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
/**
|
||||
* Associated with the metamodel -> MakeQuery/MakeQuerySingleTable
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
class QueryBuilderContext
|
||||
@@ -35,7 +35,7 @@ class QueryBuilderContext
|
||||
public function __construct($oFilter, $aModifierProperties, $aGroupByExpr = null)
|
||||
{
|
||||
$this->m_oRootFilter = $oFilter;
|
||||
$this->m_oQBExpressions = new QueryBuilderExpressions($oFilter->GetCriteria(), $aGroupByExpr);
|
||||
$this->m_oQBExpressions = new QueryBuilderExpressions($oFilter, $aGroupByExpr);
|
||||
|
||||
$this->m_aClassAliases = $oFilter->GetJoinedClasses();
|
||||
$this->m_aTableAliases = array();
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Interface iQueryModifier
|
||||
* Defines the API to tweak queries (e.g. translate data on the fly)
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
interface iQueryModifier
|
||||
|
||||
643
core/restservices.class.inc.php
Normal file
643
core/restservices.class.inc.php
Normal file
@@ -0,0 +1,643 @@
|
||||
<?php
|
||||
// Copyright (C) 2013 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/>
|
||||
|
||||
/**
|
||||
* REST/json services
|
||||
*
|
||||
* Definition of common structures + the very minimum service provider (manage objects)
|
||||
*
|
||||
* @package REST Services
|
||||
* @copyright Copyright (C) 2013 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @api
|
||||
*/
|
||||
|
||||
/**
|
||||
* Element of the response formed by RestResultWithObjects
|
||||
*
|
||||
* @package REST Services
|
||||
*/
|
||||
class ObjectResult
|
||||
{
|
||||
public $code;
|
||||
public $message;
|
||||
public $class;
|
||||
public $key;
|
||||
public $fields;
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public function __construct($sClass = null, $iId = null)
|
||||
{
|
||||
$this->code = RestResult::OK;
|
||||
$this->message = '';
|
||||
$this->class = $sClass;
|
||||
$this->key = $iId;
|
||||
$this->fields = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to make an output value for a given attribute
|
||||
*
|
||||
* @param DBObject $oObject The object being reported
|
||||
* @param string $sAttCode The attribute code (must be valid)
|
||||
* @param boolean $bExtendedOutput Output all of the link set attributes ?
|
||||
* @return string A scalar representation of the value
|
||||
*/
|
||||
protected function MakeResultValue(DBObject $oObject, $sAttCode, $bExtendedOutput = false)
|
||||
{
|
||||
if ($sAttCode == 'id')
|
||||
{
|
||||
$value = $oObject->GetKey();
|
||||
}
|
||||
else
|
||||
{
|
||||
$sClass = get_class($oObject);
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
if ($oAttDef instanceof AttributeLinkedSet)
|
||||
{
|
||||
// Iterate on the set and build an array of array of attcode=>value
|
||||
$oSet = $oObject->Get($sAttCode);
|
||||
$value = array();
|
||||
while ($oLnk = $oSet->Fetch())
|
||||
{
|
||||
$sLnkRefClass = $bExtendedOutput ? get_class($oLnk) : $oAttDef->GetLinkedClass();
|
||||
|
||||
$aLnkValues = array();
|
||||
foreach (MetaModel::ListAttributeDefs($sLnkRefClass) as $sLnkAttCode => $oLnkAttDef)
|
||||
{
|
||||
// Skip attributes pointing to the current object (redundant data)
|
||||
if ($sLnkAttCode == $oAttDef->GetExtKeyToMe())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// Skip any attribute of the link that points to the current object
|
||||
$oLnkAttDef = MetaModel::GetAttributeDef($sLnkRefClass, $sLnkAttCode);
|
||||
if (method_exists($oLnkAttDef, 'GetKeyAttCode'))
|
||||
{
|
||||
if ($oLnkAttDef->GetKeyAttCode() == $oAttDef->GetExtKeyToMe())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$aLnkValues[$sLnkAttCode] = $this->MakeResultValue($oLnk, $sLnkAttCode, $bExtendedOutput);
|
||||
}
|
||||
$value[] = $aLnkValues;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$value = $oAttDef->GetForJSON($oObject->Get($sAttCode));
|
||||
}
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Report the value for the given object attribute
|
||||
*
|
||||
* @param DBObject $oObject The object being reported
|
||||
* @param string $sAttCode The attribute code (must be valid)
|
||||
* @param boolean $bExtendedOutput Output all of the link set attributes ?
|
||||
* @return void
|
||||
*/
|
||||
public function AddField(DBObject $oObject, $sAttCode, $bExtendedOutput = false)
|
||||
{
|
||||
$this->fields[$sAttCode] = $this->MakeResultValue($oObject, $sAttCode, $bExtendedOutput);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* REST response for services managing objects. Derive this structure to add information and/or constants
|
||||
*
|
||||
* @package Extensibility
|
||||
* @package REST Services
|
||||
* @api
|
||||
*/
|
||||
class RestResultWithObjects extends RestResult
|
||||
{
|
||||
public $objects;
|
||||
|
||||
/**
|
||||
* Report the given object
|
||||
*
|
||||
* @param int An error code (RestResult::OK is no issue has been found)
|
||||
* @param string $sMessage Description of the error if any, an empty string otherwise
|
||||
* @param DBObject $oObject The object being reported
|
||||
* @param array $aFieldSpec An array of class => attribute codes (Cf. RestUtils::GetFieldList). List of the attributes to be reported.
|
||||
* @param boolean $bExtendedOutput Output all of the link set attributes ?
|
||||
* @return void
|
||||
*/
|
||||
public function AddObject($iCode, $sMessage, $oObject, $aFieldSpec = null, $bExtendedOutput = false)
|
||||
{
|
||||
$sClass = get_class($oObject);
|
||||
$oObjRes = new ObjectResult($sClass, $oObject->GetKey());
|
||||
$oObjRes->code = $iCode;
|
||||
$oObjRes->message = $sMessage;
|
||||
|
||||
$aFields = null;
|
||||
if (!is_null($aFieldSpec))
|
||||
{
|
||||
// Enum all classes in the hierarchy, starting with the current one
|
||||
foreach (MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL, false) as $sRefClass)
|
||||
{
|
||||
if (array_key_exists($sRefClass, $aFieldSpec))
|
||||
{
|
||||
$aFields = $aFieldSpec[$sRefClass];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (is_null($aFields))
|
||||
{
|
||||
// No fieldspec given, or not found...
|
||||
$aFields = array('id', 'friendlyname');
|
||||
}
|
||||
|
||||
foreach ($aFields as $sAttCode)
|
||||
{
|
||||
$oObjRes->AddField($oObject, $sAttCode, $bExtendedOutput);
|
||||
}
|
||||
|
||||
$sObjKey = get_class($oObject).'::'.$oObject->GetKey();
|
||||
$this->objects[$sObjKey] = $oObjRes;
|
||||
}
|
||||
}
|
||||
|
||||
class RestResultWithRelations extends RestResultWithObjects
|
||||
{
|
||||
public $relations;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->relations = array();
|
||||
}
|
||||
|
||||
public function AddRelation($sSrcKey, $sDestKey)
|
||||
{
|
||||
if (!array_key_exists($sSrcKey, $this->relations))
|
||||
{
|
||||
$this->relations[$sSrcKey] = array();
|
||||
}
|
||||
$this->relations[$sSrcKey][] = array('key' => $sDestKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletion result codes for a target object (either deleted or updated)
|
||||
*
|
||||
* @package Extensibility
|
||||
* @api
|
||||
* @since 2.0.1
|
||||
*/
|
||||
class RestDelete
|
||||
{
|
||||
/**
|
||||
* Result: Object deleted as per the initial request
|
||||
*/
|
||||
const OK = 0;
|
||||
/**
|
||||
* Result: general issue (user rights or ... ?)
|
||||
*/
|
||||
const ISSUE = 1;
|
||||
/**
|
||||
* Result: Must be deleted to preserve database integrity
|
||||
*/
|
||||
const AUTO_DELETE = 2;
|
||||
/**
|
||||
* Result: Must be deleted to preserve database integrity, but that is NOT possible
|
||||
*/
|
||||
const AUTO_DELETE_ISSUE = 3;
|
||||
/**
|
||||
* Result: Must be deleted to preserve database integrity, but this must be requested explicitely
|
||||
*/
|
||||
const REQUEST_EXPLICITELY = 4;
|
||||
/**
|
||||
* Result: Must be updated to preserve database integrity
|
||||
*/
|
||||
const AUTO_UPDATE = 5;
|
||||
/**
|
||||
* Result: Must be updated to preserve database integrity, but that is NOT possible
|
||||
*/
|
||||
const AUTO_UPDATE_ISSUE = 6;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of core REST services (create/get/update... objects)
|
||||
*
|
||||
* @package Core
|
||||
*/
|
||||
class CoreServices implements iRestServiceProvider
|
||||
{
|
||||
/**
|
||||
* Enumerate services delivered by this class
|
||||
*
|
||||
* @param string $sVersion The version (e.g. 1.0) supported by the services
|
||||
* @return array An array of hash 'verb' => verb, 'description' => description
|
||||
*/
|
||||
public function ListOperations($sVersion)
|
||||
{
|
||||
// 1.1 - In the reply, objects have a 'key' entry so that it is no more necessary to split class::key programmaticaly
|
||||
//
|
||||
$aOps = array();
|
||||
if (in_array($sVersion, array('1.0', '1.1')))
|
||||
{
|
||||
$aOps[] = array(
|
||||
'verb' => 'core/create',
|
||||
'description' => 'Create an object'
|
||||
);
|
||||
$aOps[] = array(
|
||||
'verb' => 'core/update',
|
||||
'description' => 'Update an object'
|
||||
);
|
||||
$aOps[] = array(
|
||||
'verb' => 'core/apply_stimulus',
|
||||
'description' => 'Apply a stimulus to change the state of an object'
|
||||
);
|
||||
$aOps[] = array(
|
||||
'verb' => 'core/get',
|
||||
'description' => 'Search for objects'
|
||||
);
|
||||
$aOps[] = array(
|
||||
'verb' => 'core/delete',
|
||||
'description' => 'Delete objects'
|
||||
);
|
||||
$aOps[] = array(
|
||||
'verb' => 'core/get_related',
|
||||
'description' => 'Get related objects through the specified relation'
|
||||
);
|
||||
$aOps[] = array(
|
||||
'verb' => 'core/check_credentials',
|
||||
'description' => 'Check user credentials'
|
||||
);
|
||||
}
|
||||
return $aOps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerate services delivered by this class
|
||||
* @param string $sVersion The version (e.g. 1.0) supported by the services
|
||||
* @return RestResult The standardized result structure (at least a message)
|
||||
* @throws Exception in case of internal failure.
|
||||
*/
|
||||
public function ExecOperation($sVersion, $sVerb, $aParams)
|
||||
{
|
||||
$oResult = new RestResultWithObjects();
|
||||
switch ($sVerb)
|
||||
{
|
||||
case 'core/create':
|
||||
RestUtils::InitTrackingComment($aParams);
|
||||
$sClass = RestUtils::GetClass($aParams, 'class');
|
||||
$aFields = RestUtils::GetMandatoryParam($aParams, 'fields');
|
||||
$aShowFields = RestUtils::GetFieldList($sClass, $aParams, 'output_fields');
|
||||
$bExtendedOutput = (RestUtils::GetOptionalParam($aParams, 'output_fields', '*') == '*+');
|
||||
|
||||
$oObject = RestUtils::MakeObjectFromFields($sClass, $aFields);
|
||||
$oObject->DBInsert();
|
||||
|
||||
$oResult->AddObject(0, 'created', $oObject, $aShowFields, $bExtendedOutput);
|
||||
break;
|
||||
|
||||
case 'core/update':
|
||||
RestUtils::InitTrackingComment($aParams);
|
||||
$sClass = RestUtils::GetClass($aParams, 'class');
|
||||
$key = RestUtils::GetMandatoryParam($aParams, 'key');
|
||||
$aFields = RestUtils::GetMandatoryParam($aParams, 'fields');
|
||||
$aShowFields = RestUtils::GetFieldList($sClass, $aParams, 'output_fields');
|
||||
$bExtendedOutput = (RestUtils::GetOptionalParam($aParams, 'output_fields', '*') == '*+');
|
||||
|
||||
$oObject = RestUtils::FindObjectFromKey($sClass, $key);
|
||||
RestUtils::UpdateObjectFromFields($oObject, $aFields);
|
||||
$oObject->DBUpdate();
|
||||
|
||||
$oResult->AddObject(0, 'updated', $oObject, $aShowFields, $bExtendedOutput);
|
||||
break;
|
||||
|
||||
case 'core/apply_stimulus':
|
||||
RestUtils::InitTrackingComment($aParams);
|
||||
$sClass = RestUtils::GetClass($aParams, 'class');
|
||||
$key = RestUtils::GetMandatoryParam($aParams, 'key');
|
||||
$aFields = RestUtils::GetMandatoryParam($aParams, 'fields');
|
||||
$aShowFields = RestUtils::GetFieldList($sClass, $aParams, 'output_fields');
|
||||
$bExtendedOutput = (RestUtils::GetOptionalParam($aParams, 'output_fields', '*') == '*+');
|
||||
$sStimulus = RestUtils::GetMandatoryParam($aParams, 'stimulus');
|
||||
|
||||
$oObject = RestUtils::FindObjectFromKey($sClass, $key);
|
||||
RestUtils::UpdateObjectFromFields($oObject, $aFields);
|
||||
|
||||
$aTransitions = $oObject->EnumTransitions();
|
||||
$aStimuli = MetaModel::EnumStimuli(get_class($oObject));
|
||||
if (!isset($aTransitions[$sStimulus]))
|
||||
{
|
||||
// Invalid stimulus
|
||||
$oResult->code = RestResult::INTERNAL_ERROR;
|
||||
$oResult->message = "Invalid stimulus: '$sStimulus' on the object ".$oObject->GetName()." in state '".$oObject->GetState()."'";
|
||||
}
|
||||
else
|
||||
{
|
||||
$aTransition = $aTransitions[$sStimulus];
|
||||
$sTargetState = $aTransition['target_state'];
|
||||
$aStates = MetaModel::EnumStates($sClass);
|
||||
$aTargetStateDef = $aStates[$sTargetState];
|
||||
$aExpectedAttributes = $aTargetStateDef['attribute_list'];
|
||||
|
||||
$aMissingMandatory = array();
|
||||
foreach($aExpectedAttributes as $sAttCode => $iExpectCode)
|
||||
{
|
||||
if ( ($iExpectCode & OPT_ATT_MANDATORY) && ($oObject->Get($sAttCode) == ''))
|
||||
{
|
||||
$aMissingMandatory[] = $sAttCode;
|
||||
}
|
||||
}
|
||||
if (count($aMissingMandatory) == 0)
|
||||
{
|
||||
// If all the mandatory fields are already present, just apply the transition silently...
|
||||
if ($oObject->ApplyStimulus($sStimulus))
|
||||
{
|
||||
$oObject->DBUpdate();
|
||||
$oResult->AddObject(0, 'updated', $oObject, $aShowFields, $bExtendedOutput);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Missing mandatory attributes for the transition
|
||||
$oResult->code = RestResult::INTERNAL_ERROR;
|
||||
$oResult->message = 'Missing mandatory attribute(s) for applying the stimulus: '.implode(', ', $aMissingMandatory).'.';
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'core/get':
|
||||
$sClass = RestUtils::GetClass($aParams, 'class');
|
||||
$key = RestUtils::GetMandatoryParam($aParams, 'key');
|
||||
$aShowFields = RestUtils::GetFieldList($sClass, $aParams, 'output_fields');
|
||||
$bExtendedOutput = (RestUtils::GetOptionalParam($aParams, 'output_fields', '*') == '*+');
|
||||
|
||||
$oObjectSet = RestUtils::GetObjectSetFromKey($sClass, $key);
|
||||
while ($oObject = $oObjectSet->Fetch())
|
||||
{
|
||||
$oResult->AddObject(0, '', $oObject, $aShowFields, $bExtendedOutput);
|
||||
}
|
||||
$oResult->message = "Found: ".$oObjectSet->Count();
|
||||
break;
|
||||
|
||||
case 'core/delete':
|
||||
$sClass = RestUtils::GetClass($aParams, 'class');
|
||||
$key = RestUtils::GetMandatoryParam($aParams, 'key');
|
||||
$bSimulate = RestUtils::GetOptionalParam($aParams, 'simulate', false);
|
||||
|
||||
$oObjectSet = RestUtils::GetObjectSetFromKey($sClass, $key);
|
||||
$aObjects = $oObjectSet->ToArray();
|
||||
$this->DeleteObjects($oResult, $aObjects, $bSimulate);
|
||||
break;
|
||||
|
||||
case 'core/get_related':
|
||||
$oResult = new RestResultWithRelations();
|
||||
$sClass = RestUtils::GetClass($aParams, 'class');
|
||||
$key = RestUtils::GetMandatoryParam($aParams, 'key');
|
||||
$sRelation = RestUtils::GetMandatoryParam($aParams, 'relation');
|
||||
$iMaxRecursionDepth = RestUtils::GetOptionalParam($aParams, 'depth', 20 /* = MAX_RECURSION_DEPTH */);
|
||||
|
||||
$oObjectSet = RestUtils::GetObjectSetFromKey($sClass, $key);
|
||||
$aIndexByClass = array();
|
||||
while ($oObject = $oObjectSet->Fetch())
|
||||
{
|
||||
$aRelated = array();
|
||||
$aGraph = array();
|
||||
$aIndexByClass[get_class($oObject)][$oObject->GetKey()] = null;
|
||||
$oResult->AddObject(0, '', $oObject);
|
||||
$this->GetRelatedObjects($oObject, $sRelation, $iMaxRecursionDepth, $aRelated, $aGraph);
|
||||
|
||||
foreach($aRelated as $sClass => $aObjects)
|
||||
{
|
||||
foreach($aObjects as $oRelatedObj)
|
||||
{
|
||||
$aIndexByClass[get_class($oRelatedObj)][$oRelatedObj->GetKey()] = null;
|
||||
$oResult->AddObject(0, '', $oRelatedObj);
|
||||
}
|
||||
}
|
||||
foreach($aGraph as $sSrcKey => $aDestinations)
|
||||
{
|
||||
foreach ($aDestinations as $sDestKey)
|
||||
{
|
||||
$oResult->AddRelation($sSrcKey, $sDestKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (count($aIndexByClass) > 0)
|
||||
{
|
||||
$aStats = array();
|
||||
foreach ($aIndexByClass as $sClass => $aIds)
|
||||
{
|
||||
$aStats[] = $sClass.'= '.count($aIds);
|
||||
}
|
||||
$oResult->message = "Scope: ".$oObjectSet->Count()."; Related objects: ".implode(', ', $aStats);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oResult->message = "Nothing found";
|
||||
}
|
||||
break;
|
||||
|
||||
case 'core/check_credentials':
|
||||
$oResult = new RestResult();
|
||||
$sUser = RestUtils::GetMandatoryParam($aParams, 'user');
|
||||
$sPassword = RestUtils::GetMandatoryParam($aParams, 'password');
|
||||
|
||||
if (UserRights::CheckCredentials($sUser, $sPassword) !== true)
|
||||
{
|
||||
$oResult->authorized = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oResult->authorized = true;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// unknown operation: handled at a higher level
|
||||
}
|
||||
return $oResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for object deletion
|
||||
*/
|
||||
public function DeleteObjects($oResult, $aObjects, $bSimulate)
|
||||
{
|
||||
$oDeletionPlan = new DeletionPlan();
|
||||
foreach($aObjects as $oObj)
|
||||
{
|
||||
if ($bSimulate)
|
||||
{
|
||||
$oObj->CheckToDelete($oDeletionPlan);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oObj->DBDelete($oDeletionPlan);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($oDeletionPlan->ListDeletes() as $sTargetClass => $aDeletes)
|
||||
{
|
||||
foreach ($aDeletes as $iId => $aData)
|
||||
{
|
||||
$oToDelete = $aData['to_delete'];
|
||||
$bAutoDel = (($aData['mode'] == DEL_SILENT) || ($aData['mode'] == DEL_AUTO));
|
||||
if (array_key_exists('issue', $aData))
|
||||
{
|
||||
if ($bAutoDel)
|
||||
{
|
||||
if (isset($aData['requested_explicitely'])) // i.e. in the initial list of objects to delete
|
||||
{
|
||||
$iCode = RestDelete::ISSUE;
|
||||
$sPlanned = 'Cannot be deleted: '.$aData['issue'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$iCode = RestDelete::AUTO_DELETE_ISSUE;
|
||||
$sPlanned = 'Should be deleted automatically... but: '.$aData['issue'];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$iCode = RestDelete::REQUEST_EXPLICITELY;
|
||||
$sPlanned = 'Must be deleted explicitely... but: '.$aData['issue'];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($bAutoDel)
|
||||
{
|
||||
if (isset($aData['requested_explicitely']))
|
||||
{
|
||||
$iCode = RestDelete::OK;
|
||||
$sPlanned = '';
|
||||
}
|
||||
else
|
||||
{
|
||||
$iCode = RestDelete::AUTO_DELETE;
|
||||
$sPlanned = 'Deleted automatically';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$iCode = RestDelete::REQUEST_EXPLICITELY;
|
||||
$sPlanned = 'Must be deleted explicitely';
|
||||
}
|
||||
}
|
||||
$oResult->AddObject($iCode, $sPlanned, $oToDelete);
|
||||
}
|
||||
}
|
||||
foreach ($oDeletionPlan->ListUpdates() as $sRemoteClass => $aToUpdate)
|
||||
{
|
||||
foreach ($aToUpdate as $iId => $aData)
|
||||
{
|
||||
$oToUpdate = $aData['to_reset'];
|
||||
if (array_key_exists('issue', $aData))
|
||||
{
|
||||
$iCode = RestDelete::AUTO_UPDATE_ISSUE;
|
||||
$sPlanned = 'Should be updated automatically... but: '.$aData['issue'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$iCode = RestDelete::AUTO_UPDATE;
|
||||
$sPlanned = 'Reset external keys: '.$aData['attributes_list'];
|
||||
}
|
||||
$oResult->AddObject($iCode, $sPlanned, $oToUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
if ($oDeletionPlan->FoundStopper())
|
||||
{
|
||||
if ($oDeletionPlan->FoundSecurityIssue())
|
||||
{
|
||||
$iRes = RestResult::UNAUTHORIZED;
|
||||
$sRes = 'Deletion not allowed on some objects';
|
||||
}
|
||||
elseif ($oDeletionPlan->FoundManualOperation())
|
||||
{
|
||||
$iRes = RestResult::UNSAFE;
|
||||
$sRes = 'The deletion requires that other objects be deleted/updated, and those operations must be requested explicitely';
|
||||
}
|
||||
else
|
||||
{
|
||||
$iRes = RestResult::INTERNAL_ERROR;
|
||||
$sRes = 'Some issues have been encountered. See the list of planned changes for more information about the issue(s).';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$iRes = RestResult::OK;
|
||||
$sRes = 'Deleted: '.count($aObjects);
|
||||
$iIndirect = $oDeletionPlan->GetTargetCount() - count($aObjects);
|
||||
if ($iIndirect > 0)
|
||||
{
|
||||
$sRes .= ' plus (for DB integrity) '.$iIndirect;
|
||||
}
|
||||
}
|
||||
$oResult->code = $iRes;
|
||||
if ($bSimulate)
|
||||
{
|
||||
$oResult->message = 'SIMULATING: '.$sRes;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oResult->message = $sRes;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get the related objects up to the given depth along with the "graph" of the relation
|
||||
* @param DBObject $oObject Starting point of the computation
|
||||
* @param string $sRelation Code of the relation (i.e; 'impact', 'depends on'...)
|
||||
* @param integer $iMaxRecursionDepth Maximum level of recursion
|
||||
* @param Hash $aRelated Two dimensions hash of the already related objects: array( 'class' => array(key => ))
|
||||
* @param Hash $aGraph Hash array for the topology of the relation: source => related: array('class:key' => array( DBObjects ))
|
||||
* @param integer $iRecursionDepth Current level of recursion
|
||||
*/
|
||||
protected function GetRelatedObjects(DBObject $oObject, $sRelation, $iMaxRecursionDepth, &$aRelated, &$aGraph, $iRecursionDepth = 1)
|
||||
{
|
||||
// Avoid loops
|
||||
if ((array_key_exists(get_class($oObject), $aRelated)) && (array_key_exists($oObject->GetKey(), $aRelated[get_class($oObject)]))) return;
|
||||
// Stop at maximum recursion level
|
||||
if ($iRecursionDepth > $iMaxRecursionDepth) return;
|
||||
|
||||
$sSrcKey = get_class($oObject).'::'.$oObject->GetKey();
|
||||
$aNewRelated = array();
|
||||
$oObject->GetRelatedObjects($sRelation, 1, $aNewRelated);
|
||||
foreach($aNewRelated as $sClass => $aObjects)
|
||||
{
|
||||
if (!array_key_exists($sSrcKey, $aGraph))
|
||||
{
|
||||
$aGraph[$sSrcKey] = array();
|
||||
}
|
||||
foreach($aObjects as $oRelatedObject)
|
||||
{
|
||||
$aRelated[$sClass][$oRelatedObject->GetKey()] = $oRelatedObject;
|
||||
$aGraph[$sSrcKey][] = get_class($oRelatedObject).'::'.$oRelatedObject->GetKey();
|
||||
$this->GetRelatedObjects($oRelatedObject, $sRelation, $iMaxRecursionDepth, $aRelated, $aGraph, $iRecursionDepth+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,20 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
/**
|
||||
* SimpleCrypt Class - crypto helpers
|
||||
@@ -34,12 +36,9 @@
|
||||
* A string encrypted with one engine can't be decrypted with
|
||||
* a different one even if the key is the same.
|
||||
*
|
||||
* @author Miguel Ros <rossoft@gmail.com>
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @version 0.3
|
||||
* @license GPL
|
||||
* @author Miguel Ros <rossoft@gmail.com>
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
class SimpleCrypt
|
||||
@@ -221,9 +220,16 @@ class SimpleCryptMcryptEngine implements CryptEngine
|
||||
{
|
||||
$iv = substr($encrypted_data, 0, mcrypt_enc_get_iv_size($this->td));
|
||||
$string = substr($encrypted_data, mcrypt_enc_get_iv_size($this->td));
|
||||
mcrypt_generic_init($this->td, $key, $iv);
|
||||
$decrypted_data = rtrim(mdecrypt_generic($this->td, $string), "\0");
|
||||
mcrypt_generic_deinit($this->td);
|
||||
$r = mcrypt_generic_init($this->td, $key, $iv);
|
||||
if (($r < 0) || ($r === false))
|
||||
{
|
||||
$decrypted_data = '** decryption error **';
|
||||
}
|
||||
else
|
||||
{
|
||||
$decrypted_data = rtrim(mdecrypt_generic($this->td, $string), "\0");
|
||||
mcrypt_generic_deinit($this->td);
|
||||
}
|
||||
return $decrypted_data;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* SQLQuery
|
||||
* build an mySQL compatible SQL query
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
@@ -47,6 +48,7 @@ class 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;
|
||||
|
||||
public function __construct($sTable, $sTableAlias, $aFields, $bToDelete = true, $aValues = array(), $oSelectedIdField = null)
|
||||
{
|
||||
@@ -70,6 +72,14 @@ class SQLQuery
|
||||
$this->m_oSelectedIdField = $oSelectedIdField;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a deep clone (as opposed to "clone" which does copy a reference to the underlying objects
|
||||
**/
|
||||
public function DeepClone()
|
||||
{
|
||||
return unserialize(serialize($this));
|
||||
}
|
||||
|
||||
public function GetTableAlias()
|
||||
{
|
||||
return $this->m_sTableAlias;
|
||||
@@ -292,8 +302,12 @@ class SQLQuery
|
||||
}
|
||||
|
||||
// Interface, build the SQL query
|
||||
public function RenderSelect($aOrderBy = array(), $aArgs = array(), $iLimitCount = 0, $iLimitStart = 0, $bGetCount = false)
|
||||
public function RenderSelect($aOrderBy = array(), $aArgs = array(), $iLimitCount = 0, $iLimitStart = 0, $bGetCount = false, $bBeautifulQuery = false)
|
||||
{
|
||||
$this->m_bBeautifulQuery = $bBeautifulQuery;
|
||||
$sLineSep = $this->m_bBeautifulQuery ? "\n" : '';
|
||||
$sIndent = $this->m_bBeautifulQuery ? " " : null;
|
||||
|
||||
// The goal will be to complete the lists as we build the Joins
|
||||
$aFrom = array();
|
||||
$aFields = array();
|
||||
@@ -304,18 +318,23 @@ class SQLQuery
|
||||
$aSelectedIdFields = array();
|
||||
$this->privRender($aFrom, $aFields, $aGroupBy, $oCondition, $aDelTables, $aSetValues, $aSelectedIdFields);
|
||||
|
||||
$sFrom = self::ClauseFrom($aFrom);
|
||||
$sFrom = self::ClauseFrom($aFrom, $sIndent);
|
||||
$sWhere = self::ClauseWhere($oCondition, $aArgs);
|
||||
if ($bGetCount)
|
||||
{
|
||||
if (count($aSelectedIdFields) > 0)
|
||||
{
|
||||
$sIDFields = implode(', ', $aSelectedIdFields);
|
||||
$sSQL = "SELECT COUNT(DISTINCT $sIDFields) AS COUNT FROM $sFrom WHERE $sWhere";
|
||||
$aCountFields = array();
|
||||
foreach ($aSelectedIdFields as $sFieldExpr)
|
||||
{
|
||||
$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";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sSQL = "SELECT COUNT(*) AS COUNT FROM $sFrom WHERE $sWhere";
|
||||
$sSQL = "SELECT$sLineSep COUNT(*) AS COUNT$sLineSep FROM $sFrom$sLineSep WHERE $sWhere";
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -334,14 +353,18 @@ class SQLQuery
|
||||
{
|
||||
$sLimit = '';
|
||||
}
|
||||
$sSQL = "SELECT DISTINCT $sSelect FROM $sFrom WHERE $sWhere $sOrderBy $sLimit";
|
||||
$sSQL = "SELECT$sLineSep DISTINCT $sSelect$sLineSep FROM $sFrom$sLineSep WHERE $sWhere$sLineSep $sOrderBy$sLineSep $sLimit";
|
||||
}
|
||||
return $sSQL;
|
||||
}
|
||||
|
||||
// Interface, build the SQL query
|
||||
public function RenderGroupBy($aArgs = array())
|
||||
public function RenderGroupBy($aArgs = array(), $bBeautifulQuery = false)
|
||||
{
|
||||
$this->m_bBeautifulQuery = $bBeautifulQuery;
|
||||
$sLineSep = $this->m_bBeautifulQuery ? "\n" : '';
|
||||
$sIndent = $this->m_bBeautifulQuery ? " " : null;
|
||||
|
||||
// The goal will be to complete the lists as we build the Joins
|
||||
$aFrom = array();
|
||||
$aFields = array();
|
||||
@@ -353,10 +376,10 @@ class SQLQuery
|
||||
$this->privRender($aFrom, $aFields, $aGroupBy, $oCondition, $aDelTables, $aSetValues, $aSelectedIdFields);
|
||||
|
||||
$sSelect = self::ClauseSelect($aFields);
|
||||
$sFrom = self::ClauseFrom($aFrom);
|
||||
$sFrom = self::ClauseFrom($aFrom, $sIndent);
|
||||
$sWhere = self::ClauseWhere($oCondition, $aArgs);
|
||||
$sGroupBy = self::ClauseGroupBy($aGroupBy);
|
||||
$sSQL = "SELECT $sSelect, COUNT(*) AS _itop_count_ FROM $sFrom WHERE $sWhere GROUP BY $sGroupBy";
|
||||
$sSQL = "SELECT $sSelect,$sLineSep COUNT(*) AS _itop_count_$sLineSep FROM $sFrom$sLineSep WHERE $sWhere$sLineSep GROUP BY $sGroupBy";
|
||||
return $sSQL;
|
||||
}
|
||||
|
||||
@@ -388,27 +411,30 @@ class SQLQuery
|
||||
return $sDelTables;
|
||||
}
|
||||
|
||||
private static function ClauseFrom($aFrom)
|
||||
private static function ClauseFrom($aFrom, $sIndent = null, $iIndentLevel = 0)
|
||||
{
|
||||
$sLineBreakLong = $sIndent ? "\n".str_repeat($sIndent, $iIndentLevel + 1) : '';
|
||||
$sLineBreak = $sIndent ? "\n".str_repeat($sIndent, $iIndentLevel) : '';
|
||||
|
||||
$sFrom = "";
|
||||
foreach ($aFrom as $sTableAlias => $aJoinInfo)
|
||||
{
|
||||
switch ($aJoinInfo["jointype"])
|
||||
{
|
||||
case "first":
|
||||
$sFrom .= "`".$aJoinInfo["tablename"]."` AS `$sTableAlias`";
|
||||
$sFrom .= " ".self::ClauseFrom($aJoinInfo["subfrom"]);
|
||||
$sFrom .= $sLineBreakLong."`".$aJoinInfo["tablename"]."` AS `$sTableAlias`";
|
||||
$sFrom .= self::ClauseFrom($aJoinInfo["subfrom"], $sIndent, $iIndentLevel + 1);
|
||||
break;
|
||||
case "inner":
|
||||
case "inner_tree":
|
||||
$sFrom .= " INNER JOIN (`".$aJoinInfo["tablename"]."` AS `$sTableAlias`";
|
||||
$sFrom .= " ".self::ClauseFrom($aJoinInfo["subfrom"]);
|
||||
$sFrom .= ") ON ".$aJoinInfo["joincondition"];
|
||||
$sFrom .= $sLineBreak."INNER JOIN ($sLineBreakLong`".$aJoinInfo["tablename"]."` AS `$sTableAlias`";
|
||||
$sFrom .= " ".self::ClauseFrom($aJoinInfo["subfrom"], $sIndent, $iIndentLevel + 1);
|
||||
$sFrom .= $sLineBreak.") ON ".$aJoinInfo["joincondition"];
|
||||
break;
|
||||
case "left":
|
||||
$sFrom .= " LEFT JOIN (`".$aJoinInfo["tablename"]."` AS `$sTableAlias`";
|
||||
$sFrom .= " ".self::ClauseFrom($aJoinInfo["subfrom"]);
|
||||
$sFrom .= ") ON ".$aJoinInfo["joincondition"];
|
||||
$sFrom .= $sLineBreak."LEFT JOIN ($sLineBreakLong`".$aJoinInfo["tablename"]."` AS `$sTableAlias`";
|
||||
$sFrom .= " ".self::ClauseFrom($aJoinInfo["subfrom"], $sIndent, $iIndentLevel + 1);
|
||||
$sFrom .= $sLineBreak.") ON ".$aJoinInfo["joincondition"];
|
||||
break;
|
||||
default:
|
||||
throw new CoreException("Unknown jointype: '".$aJoinInfo["jointype"]."'");
|
||||
@@ -462,8 +488,6 @@ class SQLQuery
|
||||
|
||||
private function privRenderSingleTable(&$aFrom, &$aFields, &$aGroupBy, &$aDelTables, &$aSetValues, &$aSelectedIdFields, $sCallerAlias = '', $aJoinData)
|
||||
{
|
||||
$aActualTableFields = CMDBSource::GetTableFieldsList($this->m_sTable);
|
||||
|
||||
$aTranslationTable[$this->m_sTable]['*'] = $this->m_sTableAlias;
|
||||
|
||||
// Handle the various kinds of join (or first table in the list)
|
||||
@@ -555,7 +579,6 @@ class SQLQuery
|
||||
{
|
||||
$aDelTables[] = "`{$this->m_sTableAlias}`";
|
||||
}
|
||||
//echo "<p>in privRenderSingleTable this->m_aValues<pre>".print_r($this->m_aValues, true)."</pre></p>\n";
|
||||
foreach($this->m_aValues as $sFieldName=>$value)
|
||||
{
|
||||
$aSetValues["`{$this->m_sTableAlias}`.`$sFieldName`"] = $value; // quoted further!
|
||||
@@ -580,6 +603,98 @@ class SQLQuery
|
||||
return $this->m_sTableAlias;
|
||||
}
|
||||
|
||||
}
|
||||
public function OptimizeJoins($aUsedTables, $bTopCall = true)
|
||||
{
|
||||
if ($bTopCall)
|
||||
{
|
||||
// Top call: complete the list of tables absolutely required to perform the right query
|
||||
$this->CollectUsedTables($aUsedTables);
|
||||
}
|
||||
|
||||
?>
|
||||
$aToDiscard = array();
|
||||
foreach ($this->m_aJoinSelects as $i => $aJoinInfo)
|
||||
{
|
||||
$oSQLQuery = $aJoinInfo["select"];
|
||||
$sTableAlias = $oSQLQuery->GetTableAlias();
|
||||
if ($oSQLQuery->OptimizeJoins($aUsedTables, false) && !array_key_exists($sTableAlias, $aUsedTables))
|
||||
{
|
||||
$aToDiscard[] = $i;
|
||||
}
|
||||
}
|
||||
foreach ($aToDiscard as $i)
|
||||
{
|
||||
unset($this->m_aJoinSelects[$i]);
|
||||
}
|
||||
|
||||
return (count($this->m_aJoinSelects) == 0);
|
||||
}
|
||||
|
||||
protected function CollectUsedTables(&$aTables)
|
||||
{
|
||||
$this->m_oConditionExpr->CollectUsedParents($aTables);
|
||||
foreach($this->m_aFields as $sFieldAlias => $oField)
|
||||
{
|
||||
$oField->CollectUsedParents($aTables);
|
||||
}
|
||||
if ($this->m_aGroupBy)
|
||||
{
|
||||
foreach($this->m_aGroupBy as $sAlias => $oExpression)
|
||||
{
|
||||
$oExpression->CollectUsedParents($aTables);
|
||||
}
|
||||
}
|
||||
if (!is_null($this->m_oSelectedIdField))
|
||||
{
|
||||
$this->m_oSelectedIdField->CollectUsedParents($aTables);
|
||||
}
|
||||
|
||||
foreach ($this->m_aJoinSelects as $i => $aJoinInfo)
|
||||
{
|
||||
$oSQLQuery = $aJoinInfo["select"];
|
||||
if ($oSQLQuery->HasRequiredTables($aTables))
|
||||
{
|
||||
// There is something required in the branch, then this node is a MUST
|
||||
if (isset($aJoinInfo['righttablealias']))
|
||||
{
|
||||
$aTables[$aJoinInfo['righttablealias']] = true;
|
||||
}
|
||||
if (isset($aJoinInfo["on_expression"]))
|
||||
{
|
||||
$sJoinCond = $aJoinInfo["on_expression"]->CollectUsedParents($aTables);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $aTables;
|
||||
}
|
||||
|
||||
// Is required in the JOIN, and therefore we must ensure that the join expression will be valid
|
||||
protected function HasRequiredTables(&$aTables)
|
||||
{
|
||||
$bResult = false;
|
||||
if (array_key_exists($this->m_sTableAlias, $aTables))
|
||||
{
|
||||
$bResult = true;
|
||||
}
|
||||
foreach ($this->m_aJoinSelects as $i => $aJoinInfo)
|
||||
{
|
||||
$oSQLQuery = $aJoinInfo["select"];
|
||||
if ($oSQLQuery->HasRequiredTables($aTables))
|
||||
{
|
||||
// There is something required in the branch, then this node is a MUST
|
||||
if (isset($aJoinInfo['righttablealias']))
|
||||
{
|
||||
$aTables[$aJoinInfo['righttablealias']] = true;
|
||||
}
|
||||
if (isset($aJoinInfo["on_expression"]))
|
||||
{
|
||||
$sJoinCond = $aJoinInfo["on_expression"]->CollectUsedParents($aTables);
|
||||
}
|
||||
$bResult = true;
|
||||
}
|
||||
}
|
||||
// None of the tables is in the list of required tables
|
||||
return $bResult;
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Object lifecycle management: stimulus
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Simple helper class to interpret and transform a template string
|
||||
@@ -21,10 +24,8 @@
|
||||
* $oString = new TemplateString("Blah $this->friendlyname$ is in location $this->location_id->name$ ('$this->location_id->org_id->name$)");
|
||||
* echo $oString->Render(array('this' => $oContact));
|
||||
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Persistent class Trigger and derived
|
||||
* User defined triggers, that may be used in conjunction with user defined actions
|
||||
*
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
@@ -63,7 +64,7 @@ abstract class Trigger extends cmdbAbstractObject
|
||||
|
||||
public function DoActivate($aContextArgs)
|
||||
{
|
||||
// Find the related
|
||||
// Find the related actions
|
||||
$oLinkedActions = $this->Get('action_list');
|
||||
while ($oLink = $oLinkedActions->Fetch())
|
||||
{
|
||||
@@ -75,6 +76,19 @@ abstract class Trigger extends cmdbAbstractObject
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given object is in the scope of this trigger
|
||||
* and can potentially be the subject of notifications
|
||||
* @param DBObject $oObject The object to check
|
||||
* @return bool
|
||||
*/
|
||||
public function IsInScope(DBObject $oObject)
|
||||
{
|
||||
// By default the answer is no
|
||||
// Overload this function in your own derived class for a different behavior
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
abstract class TriggerOnObject extends Trigger
|
||||
@@ -95,15 +109,81 @@ abstract class TriggerOnObject extends Trigger
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeClass("target_class", array("class_category"=>"bizmodel", "more_values"=>null, "sql"=>"target_class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeClass("target_class", array("class_category"=>"bizmodel", "more_values"=>"User,UserExternal,UserInternal,UserLDAP,UserLocal", "sql"=>"target_class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeOQL("filter", array("allowed_values"=>null, "sql"=>"filter", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('description', 'target_class', 'action_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('details', array('description', 'target_class', 'filter', 'action_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class', 'description')); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
|
||||
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
|
||||
}
|
||||
|
||||
public function DoCheckToWrite()
|
||||
{
|
||||
parent::DoCheckToWrite();
|
||||
|
||||
$sFilter = trim($this->Get('filter'));
|
||||
if (strlen($sFilter) > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
$oSearch = DBObjectSearch::FromOQL($sFilter);
|
||||
|
||||
if (!MetaModel::IsParentClass($this->Get('target_class'), $oSearch->GetClass()))
|
||||
{
|
||||
$this->m_aCheckIssues[] = Dict::Format('TriggerOnObject:WrongFilterClass', $this->Get('target_class'));
|
||||
}
|
||||
}
|
||||
catch(OqlException $e)
|
||||
{
|
||||
$this->m_aCheckIssues[] = Dict::Format('TriggerOnObject:WrongFilterQuery', $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given object is in the scope of this trigger
|
||||
* and can potentially be the subject of notifications
|
||||
* @param DBObject $oObject The object to check
|
||||
* @return bool
|
||||
*/
|
||||
public function IsInScope(DBObject $oObject)
|
||||
{
|
||||
$sRootClass = $this->Get('target_class');
|
||||
return ($oObject instanceof $sRootClass);
|
||||
}
|
||||
|
||||
public function DoActivate($aContextArgs)
|
||||
{
|
||||
$bGo = true;
|
||||
if (isset($aContextArgs['this->id']))
|
||||
{
|
||||
$bGo = $this->IsTargetObject($aContextArgs['this->id']);
|
||||
}
|
||||
if ($bGo)
|
||||
{
|
||||
parent::DoActivate($aContextArgs);
|
||||
}
|
||||
}
|
||||
|
||||
public function IsTargetObject($iObjectId)
|
||||
{
|
||||
$sFilter = trim($this->Get('filter'));
|
||||
if (strlen($sFilter) > 0)
|
||||
{
|
||||
$oSearch = DBObjectSearch::FromOQL($sFilter);
|
||||
$oSearch->AddCondition('id', $iObjectId, '=');
|
||||
$oSet = new DBObjectSet($oSearch);
|
||||
$bRet = ($oSet->Count() > 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
$bRet = true;
|
||||
}
|
||||
return $bRet;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* To trigger notifications when a ticket is updated from the portal
|
||||
@@ -128,7 +208,7 @@ class TriggerOnPortalUpdate extends TriggerOnObject
|
||||
MetaModel::Init_InheritAttributes();
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('description', 'target_class', 'action_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('details', array('description', 'target_class', 'filter', 'action_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class', 'description')); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
}
|
||||
@@ -155,7 +235,7 @@ abstract class TriggerOnStateChange extends TriggerOnObject
|
||||
MetaModel::Init_AddAttribute(new AttributeString("state", array("allowed_values"=>null, "sql"=>"state", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('description', 'target_class', 'state', 'action_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('details', array('description', 'target_class', 'filter', 'state', 'action_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class', 'state')); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
MetaModel::Init_SetZListItems('standard_search', array('description', 'target_class', 'state')); // Criteria of the std search form
|
||||
@@ -183,7 +263,7 @@ class TriggerOnStateEnter extends TriggerOnStateChange
|
||||
MetaModel::Init_InheritAttributes();
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('description', 'target_class', 'state', 'action_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('details', array('description', 'target_class', 'filter', 'state', 'action_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('target_class', 'state')); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
MetaModel::Init_SetZListItems('standard_search', array('description', 'target_class', 'state')); // Criteria of the std search form
|
||||
@@ -211,7 +291,7 @@ class TriggerOnStateLeave extends TriggerOnStateChange
|
||||
MetaModel::Init_InheritAttributes();
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('description', 'target_class', 'state', 'action_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('details', array('description', 'target_class', 'filter', 'state', 'action_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('target_class', 'state')); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
MetaModel::Init_SetZListItems('standard_search', array('description', 'target_class', 'state')); // Criteria of the std search form
|
||||
@@ -239,7 +319,7 @@ class TriggerOnObjectCreate extends TriggerOnObject
|
||||
MetaModel::Init_InheritAttributes();
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('description', 'target_class', 'action_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('details', array('description', 'target_class', 'filter', 'action_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class')); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
MetaModel::Init_SetZListItems('standard_search', array('description', 'target_class')); // Criteria of the std search form
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user