mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-13 23:44:11 +01:00
Compare commits
1855 Commits
support/1.
...
support/2.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
89e98a4ad7 | ||
|
|
649a9833e9 | ||
|
|
b3c2396da8 | ||
|
|
5717ae1889 | ||
|
|
823b33b0f5 | ||
|
|
9561ae6292 | ||
|
|
e25a89dac1 | ||
|
|
cc7f3d46a6 | ||
|
|
95d6b640b1 | ||
|
|
4ee0c62701 | ||
|
|
15f521d470 | ||
|
|
86e6c8295f | ||
|
|
9f1338ee2a | ||
|
|
96c8ee5e4d | ||
|
|
8e863d4890 | ||
|
|
cd769d5e49 | ||
|
|
04677fc2c7 | ||
|
|
ed6a464d8b | ||
|
|
5794f7d1ca | ||
|
|
7d42aa48cd | ||
|
|
614dd21aa9 | ||
|
|
706e6c56ff | ||
|
|
f048c6cb1f | ||
|
|
1804903ee4 | ||
|
|
17a8a896dc | ||
|
|
ed85260bb3 | ||
|
|
3c7fac7504 | ||
|
|
93feb700e8 | ||
|
|
48456082a9 | ||
|
|
fbc0456496 | ||
|
|
0fc3ee12b1 | ||
|
|
ce5f8c93cd | ||
|
|
b38eb97323 | ||
|
|
e6eb32cd21 | ||
|
|
c2429f2d05 | ||
|
|
4e4f2b4db9 | ||
|
|
2d7f963316 | ||
|
|
546d181ea9 | ||
|
|
143cefe4e3 | ||
|
|
ece152173f | ||
|
|
b08de31b3c | ||
|
|
0f967a41df | ||
|
|
4c3bf70cc4 | ||
|
|
35dd3f9610 | ||
|
|
a7f7424e54 | ||
|
|
83e2974b10 | ||
|
|
715ba066d3 | ||
|
|
9502003ff4 | ||
|
|
57c827bb1a | ||
|
|
690ac9be75 | ||
|
|
4c3c31c44d | ||
|
|
3c9ace5b53 | ||
|
|
bd5268dc42 | ||
|
|
133b6d4d29 | ||
|
|
fba3990c61 | ||
|
|
53e997cfba | ||
|
|
e738ba35b7 | ||
|
|
0773455ebc | ||
|
|
cafc6a8baf | ||
|
|
48f222df0b | ||
|
|
88726a0634 | ||
|
|
0ac522fc4c | ||
|
|
aa97703b64 | ||
|
|
30af416394 | ||
|
|
1f2ad9ecdb | ||
|
|
d1f1889c42 | ||
|
|
ca7ee0f138 | ||
|
|
909729e9f1 | ||
|
|
a222ba998d | ||
|
|
aedddf9dcc | ||
|
|
74de0d33ab | ||
|
|
8d8510a412 | ||
|
|
4dd83a0eb6 | ||
|
|
671ee353e8 | ||
|
|
d714235d67 | ||
|
|
c4b039c9c6 | ||
|
|
29e751278e | ||
|
|
87ed5d4a05 | ||
|
|
c8a20d01da | ||
|
|
47add0eeea | ||
|
|
2a9f69d70e | ||
|
|
e90e570469 | ||
|
|
b0e56e5897 | ||
|
|
c96842d82c | ||
|
|
bc79aecd73 | ||
|
|
99755ad7e8 | ||
|
|
4867461f69 | ||
|
|
3d21eecbba | ||
|
|
b822cff269 | ||
|
|
3aa0b77751 | ||
|
|
f4b10d3e81 | ||
|
|
ca90f9b32a | ||
|
|
9ca051d9d0 | ||
|
|
291f05683c | ||
|
|
4889ed5ac2 | ||
|
|
3fa354d00d | ||
|
|
f62934829d | ||
|
|
dee7f7b7aa | ||
|
|
1d0bfa7c92 | ||
|
|
8922c435b4 | ||
|
|
ec1ec854fb | ||
|
|
dfc248b836 | ||
|
|
481d05d082 | ||
|
|
4e4d3cf3da | ||
|
|
5fc68557dc | ||
|
|
7482a52fd9 | ||
|
|
50ca6cdd0f | ||
|
|
e83e1262a5 | ||
|
|
51bd403638 | ||
|
|
c786e8308a | ||
|
|
a7d3a5a488 | ||
|
|
2c70c60d2b | ||
|
|
96f02d1557 | ||
|
|
de6ddffe65 | ||
|
|
2c59fb894f | ||
|
|
77cf399c72 | ||
|
|
19a2180c2b | ||
|
|
18b73de512 | ||
|
|
bb741c39f4 | ||
|
|
c73ef6ae72 | ||
|
|
5abd9c6dad | ||
|
|
a6db04bafd | ||
|
|
04f46f4798 | ||
|
|
5ecf6b9e9d | ||
|
|
0135573956 | ||
|
|
a59915e5ee | ||
|
|
ebb3767ee4 | ||
|
|
047166f002 | ||
|
|
f45c783396 | ||
|
|
72f516685e | ||
|
|
2b4400c55d | ||
|
|
1df87b6331 | ||
|
|
cd7490472e | ||
|
|
b65131e4f5 | ||
|
|
fac8604cc6 | ||
|
|
e25da2a7c4 | ||
|
|
6d9e7f690f | ||
|
|
9c62952743 | ||
|
|
03c4964072 | ||
|
|
711949414d | ||
|
|
055a87306e | ||
|
|
37ebb51a2b | ||
|
|
1f8d4d379f | ||
|
|
1300811007 | ||
|
|
fbdd0dfd57 | ||
|
|
bc79663a3e | ||
|
|
570e4f8589 | ||
|
|
472802e11b | ||
|
|
f794d0222e | ||
|
|
b42a43d47b | ||
|
|
110aace270 | ||
|
|
68dd0513c6 | ||
|
|
08e757a08a | ||
|
|
996c590793 | ||
|
|
c93cc3a5bf | ||
|
|
bdccf6ea72 | ||
|
|
ed60346ae6 | ||
|
|
4953ea7701 | ||
|
|
82a8a0bba7 | ||
|
|
55c818b6b7 | ||
|
|
372c0835f7 | ||
|
|
52a028301f | ||
|
|
61b88d2689 | ||
|
|
fa856c32cd | ||
|
|
6d693bbfc7 | ||
|
|
7117b751b3 | ||
|
|
c30a88c9ef | ||
|
|
c1085fc398 | ||
|
|
42ac871f8e | ||
|
|
b1a404d909 | ||
|
|
ba82031a59 | ||
|
|
c8568af43b | ||
|
|
7ed60f711c | ||
|
|
e831b1a486 | ||
|
|
b5db86472d | ||
|
|
ed9ba815ab | ||
|
|
7b47e8c480 | ||
|
|
d2cd758ecc | ||
|
|
d2ea4023c2 | ||
|
|
f0ee5112b9 | ||
|
|
ada70b97d4 | ||
|
|
a546b39301 | ||
|
|
8f2cd66bf6 | ||
|
|
868d0d1b19 | ||
|
|
7d619f278e | ||
|
|
fd9008a163 | ||
|
|
357ae4abb1 | ||
|
|
2108a74f87 | ||
|
|
165d01e2a2 | ||
|
|
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 | ||
|
|
60cf926fdb | ||
|
|
5b93504709 | ||
|
|
6c1866ce89 | ||
|
|
eab09c5e17 | ||
|
|
c69eb2e5d7 | ||
|
|
55cc5ad2b7 | ||
|
|
18933c33b3 | ||
|
|
5c7604f950 | ||
|
|
6b92cab445 | ||
|
|
9af1de0bb4 | ||
|
|
ddd98ebab2 | ||
|
|
f5de7987bb | ||
|
|
12c06262ea | ||
|
|
1b55986e39 | ||
|
|
ef59badf5b | ||
|
|
cb57335c0b | ||
|
|
5fc4775903 | ||
|
|
088fcf9449 | ||
|
|
5ed95f3a1c | ||
|
|
a2e33634f5 | ||
|
|
2d641a0de3 | ||
|
|
da75724296 | ||
|
|
50f0358556 | ||
|
|
0e4c79b2e7 | ||
|
|
a008107d38 | ||
|
|
dc4bfc6b30 | ||
|
|
2da369431c | ||
|
|
5e40d7b009 | ||
|
|
8d56d8eb79 | ||
|
|
ab1143e0e1 | ||
|
|
d747b1fe26 | ||
|
|
20c5e1b6ad | ||
|
|
aa9c7c7091 | ||
|
|
2f9922f7df | ||
|
|
45bfee7ef0 | ||
|
|
2eddb57cbd | ||
|
|
4979a653ee | ||
|
|
82ba3962b0 | ||
|
|
04424f9762 | ||
|
|
dac73c1669 | ||
|
|
77129d010e | ||
|
|
d19116a88e | ||
|
|
d38b79de76 | ||
|
|
54a3a1a89a | ||
|
|
d18ff8339a | ||
|
|
e463bd8b48 | ||
|
|
93791255e6 | ||
|
|
bb404f8a95 | ||
|
|
308f40c11b | ||
|
|
645b2f5ad8 | ||
|
|
71208d362a | ||
|
|
39d2534bda | ||
|
|
6052e5c848 | ||
|
|
10aaa3f9bf | ||
|
|
b372a38c5d | ||
|
|
5eb97ae133 | ||
|
|
4cfeae9ca0 | ||
|
|
949a4639e2 | ||
|
|
e47251f2f4 | ||
|
|
7026eb3b76 | ||
|
|
e48a842881 | ||
|
|
819b2663ca | ||
|
|
95785ee22d | ||
|
|
c37135af55 | ||
|
|
f05f5865e9 | ||
|
|
2d9193e60e | ||
|
|
8fe559e5d1 | ||
|
|
d799585958 | ||
|
|
4eb018ac50 | ||
|
|
f0d87adc87 | ||
|
|
bc557f6e42 | ||
|
|
c8393a7d3a | ||
|
|
c44420c985 | ||
|
|
9064b81461 | ||
|
|
175e2351d6 | ||
|
|
9eb1fbee1d | ||
|
|
5726b2cd5c | ||
|
|
083c3d8613 | ||
|
|
cf63e128b1 | ||
|
|
c01cdbb569 | ||
|
|
38cca0c144 | ||
|
|
3b46af6a4f | ||
|
|
b001476a6f | ||
|
|
f0cd1cddb0 | ||
|
|
a707e9fde1 | ||
|
|
c612732037 | ||
|
|
2b5c7a7f59 | ||
|
|
aa8ea8f2fe | ||
|
|
16651d53e1 | ||
|
|
b0ec706f79 | ||
|
|
54f81153f3 | ||
|
|
e726420a94 | ||
|
|
148df28345 | ||
|
|
e126137a91 | ||
|
|
f9924cd635 | ||
|
|
55e0898587 | ||
|
|
dce0bce4ce | ||
|
|
82cf7776ca | ||
|
|
5430f52145 | ||
|
|
5125000994 | ||
|
|
367d97ffb2 | ||
|
|
4a3321dd0a | ||
|
|
fe55d35930 | ||
|
|
fad06f8f91 | ||
|
|
1840e24a3c | ||
|
|
b6c3b3c7ab | ||
|
|
1757554f37 | ||
|
|
9bfd7ae435 | ||
|
|
7c9e2df888 | ||
|
|
5a16c08bc9 | ||
|
|
0ffe040546 | ||
|
|
dc90de5a2b | ||
|
|
51ba8df159 | ||
|
|
1e97829d7d | ||
|
|
cacad13702 | ||
|
|
1a8b802276 | ||
|
|
08deefb5a4 | ||
|
|
e5bb006ba6 | ||
|
|
08f3e36cc8 | ||
|
|
a69c49e77a | ||
|
|
039f5042d9 | ||
|
|
4562edb4ea | ||
|
|
463ade4bf4 | ||
|
|
4f653d63b5 | ||
|
|
34578ff831 | ||
|
|
d40f7aa7ba | ||
|
|
d73e1f5fd3 | ||
|
|
47f13d6b70 | ||
|
|
18031b0c34 | ||
|
|
d953506950 | ||
|
|
1a040a3a3a | ||
|
|
e3dd7d4371 | ||
|
|
814ff60c07 | ||
|
|
5cfc82437f | ||
|
|
ad1b2901be | ||
|
|
e64e008aad | ||
|
|
9e10de70c9 | ||
|
|
cc0590cd3d | ||
|
|
716ba04f8e | ||
|
|
04f9e823db | ||
|
|
15a7517e4e | ||
|
|
2f0d058bfc | ||
|
|
9a1f0241ff | ||
|
|
f0cfe78043 | ||
|
|
d30cf0655d | ||
|
|
2e26c056f5 | ||
|
|
8d4bb6bcdf | ||
|
|
c5a4a02971 | ||
|
|
8c0fbe1913 | ||
|
|
5959be5929 | ||
|
|
d0bf023f42 | ||
|
|
38df2c71a2 | ||
|
|
8a8b8b87e3 | ||
|
|
8a85abf6b4 | ||
|
|
eb9c5d8fe2 | ||
|
|
7cdc90354c | ||
|
|
5ecf5815b8 | ||
|
|
74726ff742 | ||
|
|
79259814f3 | ||
|
|
264d10ffd7 | ||
|
|
5dcbdbad95 | ||
|
|
00873c1cb9 | ||
|
|
4b34fd4ecd | ||
|
|
59e754d8db | ||
|
|
4115ddbe13 | ||
|
|
479bef14e6 | ||
|
|
e4c113e412 | ||
|
|
2c00c115d6 | ||
|
|
a40e902808 | ||
|
|
341a97d2ac | ||
|
|
a970be510b | ||
|
|
e56aac9ef8 | ||
|
|
d6aa4118b0 | ||
|
|
bb7c81a914 | ||
|
|
0f0a881238 | ||
|
|
f4d2ffcf34 | ||
|
|
11ac749c24 | ||
|
|
d681e58671 | ||
|
|
d71a663208 | ||
|
|
90202ec232 | ||
|
|
f6559d43fc | ||
|
|
5bf31905ac | ||
|
|
7de5315a01 | ||
|
|
536ea2cc21 | ||
|
|
11a107b982 | ||
|
|
5d797afbd8 | ||
|
|
e540df4de9 | ||
|
|
b31b2a5939 | ||
|
|
a53be218f1 | ||
|
|
ae22563177 | ||
|
|
c87368ff42 | ||
|
|
b2bf8df779 | ||
|
|
debd0cf5bd | ||
|
|
28a7930a13 | ||
|
|
ef453537d3 | ||
|
|
f44a701d12 | ||
|
|
08350f7c65 | ||
|
|
25ff0e064c | ||
|
|
050a4813be | ||
|
|
1c7d6348c6 | ||
|
|
b31c54c979 | ||
|
|
7133788518 | ||
|
|
b5576df143 | ||
|
|
eedb28e97b | ||
|
|
25ddd1c2c8 | ||
|
|
a6e5b0b087 | ||
|
|
907d96b18d | ||
|
|
9b276405b9 | ||
|
|
fe8a5080cb | ||
|
|
8e3b2d7da6 | ||
|
|
c47b2b57ec | ||
|
|
1e605c43b0 | ||
|
|
3121cf602a | ||
|
|
c68fbf6cd9 | ||
|
|
508744262c | ||
|
|
214a107792 | ||
|
|
c7db32f845 | ||
|
|
719870663a | ||
|
|
7487a46888 | ||
|
|
a4034bbaca | ||
|
|
33a8a594f8 | ||
|
|
c009599ff8 | ||
|
|
014d25bcda | ||
|
|
1b686a3164 | ||
|
|
39be3b449e | ||
|
|
fb8d93319d | ||
|
|
d27ab4128c | ||
|
|
c4ffbf1798 | ||
|
|
44c2beff51 | ||
|
|
9c633bf0b8 | ||
|
|
793e0be473 | ||
|
|
3e3195d6cf | ||
|
|
dcecbee7d0 | ||
|
|
dfc02ccb77 | ||
|
|
554ececf51 | ||
|
|
f9d5b88c06 | ||
|
|
0dfbc28577 | ||
|
|
e0e7a2d71e | ||
|
|
cc4f9800c7 | ||
|
|
f9cbd7d81a | ||
|
|
f1ae18afcf | ||
|
|
dc3dc9e904 | ||
|
|
2f4cbcac25 | ||
|
|
65e55e0927 | ||
|
|
d41e77823f | ||
|
|
2d51bf9bc1 | ||
|
|
78cfb3229d | ||
|
|
048fafca08 | ||
|
|
d0b201e73f | ||
|
|
60d63839f6 | ||
|
|
b6ac94a8f1 | ||
|
|
46e3eac149 | ||
|
|
6826e132a8 | ||
|
|
11c85d7710 | ||
|
|
d0025358da | ||
|
|
a7b7264903 | ||
|
|
da2abef030 | ||
|
|
3a841269ec | ||
|
|
1241592fee | ||
|
|
885d5ecf9f | ||
|
|
3205d48f84 | ||
|
|
c354fecad4 | ||
|
|
009a91ab59 | ||
|
|
7ef5c8ddd7 | ||
|
|
a2affcc224 | ||
|
|
529b5d53fc | ||
|
|
144b5ae39f | ||
|
|
3679e7f8d5 | ||
|
|
645802ed32 | ||
|
|
38461b7be6 | ||
|
|
c1ee1d4e8a | ||
|
|
66e1890b27 | ||
|
|
a4fd923759 | ||
|
|
2533e2eca3 | ||
|
|
c96952a369 | ||
|
|
94b6730697 | ||
|
|
1cc2884541 | ||
|
|
b37414df59 | ||
|
|
eb5475cf3f | ||
|
|
ef50592a8d | ||
|
|
18a28df55f | ||
|
|
be4ac95549 | ||
|
|
619ce5f130 | ||
|
|
18b234c200 | ||
|
|
35fe13fb52 | ||
|
|
794c111ee1 | ||
|
|
6570b6a250 | ||
|
|
78ec612ba0 | ||
|
|
de915644a2 | ||
|
|
9c4ef578f6 | ||
|
|
73e715efb4 | ||
|
|
2c49d1f96f | ||
|
|
97647c5a5a | ||
|
|
114adc3b0b | ||
|
|
166102da5f | ||
|
|
57bb66fe41 | ||
|
|
4a16a76e37 | ||
|
|
52ef62258b | ||
|
|
475f11e4bc | ||
|
|
1de3672173 | ||
|
|
0d6244b9eb | ||
|
|
453da6be38 | ||
|
|
22acd83abb | ||
|
|
b172c53aad | ||
|
|
ff6ccacf9f | ||
|
|
b3dadcba77 | ||
|
|
92c1a4af63 | ||
|
|
60fb1f1126 | ||
|
|
fe7f65893e | ||
|
|
d4816ddc51 | ||
|
|
20077a4c3a | ||
|
|
392344dcd8 | ||
|
|
137f712a96 | ||
|
|
429cd73e99 | ||
|
|
3a5f73fe65 | ||
|
|
2645194f51 | ||
|
|
b97066dad7 | ||
|
|
b7e5e7b1da | ||
|
|
a8f53294a9 | ||
|
|
17f3318d63 | ||
|
|
cc1dc4a938 | ||
|
|
20b7944118 | ||
|
|
f0b3ec77da | ||
|
|
3c46ac9011 | ||
|
|
ea1193b90f | ||
|
|
8b493177e6 | ||
|
|
e9f707e480 | ||
|
|
104959f9ff | ||
|
|
e90e35dbc6 | ||
|
|
68ecf03f19 | ||
|
|
afc6efe91c | ||
|
|
9fce2da012 | ||
|
|
5767d14c3e | ||
|
|
a401212e31 | ||
|
|
5510b3964d | ||
|
|
5e01e0659e | ||
|
|
1da33effd8 | ||
|
|
f253b10ddd | ||
|
|
39de3e9af7 | ||
|
|
851db98745 | ||
|
|
123bac0e56 | ||
|
|
e888b11fff | ||
|
|
8dc13026a0 | ||
|
|
2931a0fd42 | ||
|
|
1392983a92 | ||
|
|
b6b9ef35e9 | ||
|
|
989f649405 | ||
|
|
01bae34a90 | ||
|
|
c7c69660bc | ||
|
|
da4f475751 | ||
|
|
a362494354 | ||
|
|
64e5d57ac3 | ||
|
|
cb5f09cbf2 | ||
|
|
780fb6dc27 | ||
|
|
87bf09995d | ||
|
|
63f4ec3f82 | ||
|
|
308ec94f8c | ||
|
|
6a9ea25b27 | ||
|
|
f29d673ffb | ||
|
|
cf65b58981 | ||
|
|
8f8f575c65 | ||
|
|
79509e5510 | ||
|
|
e147d54404 | ||
|
|
5976862a3c | ||
|
|
b2d7c51e6d | ||
|
|
248d4d3e10 | ||
|
|
32dd31b69d | ||
|
|
263594bc4a | ||
|
|
e07912af34 | ||
|
|
ea2843f04e | ||
|
|
4869d5e92b | ||
|
|
d8fc264adf | ||
|
|
72ac150faa | ||
|
|
9ac4f84e22 | ||
|
|
d74f562dba | ||
|
|
fe060e30e5 | ||
|
|
3e2d906f0d | ||
|
|
321e7dc309 | ||
|
|
123f5c4cf5 | ||
|
|
0563b05b03 | ||
|
|
701d49f70d | ||
|
|
90f3aa0c9d | ||
|
|
ea10d202a6 | ||
|
|
ec19ef982e | ||
|
|
487aa95ae2 | ||
|
|
9197c6a17c | ||
|
|
8231420c44 | ||
|
|
bb3d08b8a3 | ||
|
|
8656611dc2 | ||
|
|
6cb052043a | ||
|
|
02b34515f3 | ||
|
|
d081d1f4df | ||
|
|
c16dce00ae | ||
|
|
99c4128ee6 | ||
|
|
f17f4e1f78 | ||
|
|
a94ccb9091 | ||
|
|
32b312c871 | ||
|
|
c1fce0d68a | ||
|
|
b0f21fd656 | ||
|
|
e30bfb53c6 | ||
|
|
7a83237f11 | ||
|
|
22f79b7d99 | ||
|
|
7658f5dba5 | ||
|
|
0c2390cce6 | ||
|
|
bac970b5c7 | ||
|
|
9f186f3b60 | ||
|
|
e1dcdddebf | ||
|
|
ee6c2fcffe | ||
|
|
18c9852db3 | ||
|
|
a5547d4d26 | ||
|
|
fb234f3b15 | ||
|
|
25d29da058 | ||
|
|
169bf4948b | ||
|
|
17ef280b27 | ||
|
|
017f767a6e | ||
|
|
340ff612cd | ||
|
|
3746dad8f0 | ||
|
|
699f18394b | ||
|
|
6963d630d5 | ||
|
|
5c55291391 | ||
|
|
48fc735d1f | ||
|
|
dfe7997f53 | ||
|
|
53a2f9e527 | ||
|
|
14b86abdbe | ||
|
|
e00c32b867 | ||
|
|
7ff6e52ede | ||
|
|
f8a582b375 | ||
|
|
1eda4dde56 | ||
|
|
b0373f576c | ||
|
|
ffb3750806 | ||
|
|
649f7c609b | ||
|
|
ff201e8b8a | ||
|
|
f979dd40e0 | ||
|
|
32be1aafe0 | ||
|
|
982c55738f | ||
|
|
bdebb33822 | ||
|
|
48cb402874 | ||
|
|
c15e4ffb10 | ||
|
|
cbafb5f1d1 | ||
|
|
b1d0d039a3 | ||
|
|
d252d767b6 | ||
|
|
73968a5631 | ||
|
|
0999f00912 | ||
|
|
6fb3195d6e | ||
|
|
6ea3b62b55 | ||
|
|
ba3080594c | ||
|
|
a3a94cccbb | ||
|
|
e8c8e4a0d7 | ||
|
|
5b3350f6d5 | ||
|
|
b454b24e0f | ||
|
|
099afb5451 | ||
|
|
ec226ce76d | ||
|
|
5080bd58d5 | ||
|
|
5ec37fd26a | ||
|
|
8ae92cb50e | ||
|
|
f82a4ada23 | ||
|
|
e5ce12ed0a | ||
|
|
428f1a26cc | ||
|
|
9976999a88 | ||
|
|
da2b8ab4c0 | ||
|
|
0de75db474 | ||
|
|
b014596172 | ||
|
|
b46ed4764d | ||
|
|
2b1aecda53 | ||
|
|
19318973ca | ||
|
|
cac76a7e08 | ||
|
|
abd645ca98 | ||
|
|
7301cb9369 | ||
|
|
559de3d38f | ||
|
|
552ffd79b1 | ||
|
|
aae8ca7b8b | ||
|
|
13c636cab6 | ||
|
|
cc40fc5d91 | ||
|
|
bddfdb4a28 | ||
|
|
6bc639c6ce | ||
|
|
9b8ae03413 | ||
|
|
17f00198d3 | ||
|
|
5041ec2e0c | ||
|
|
ee4f7e5b6a | ||
|
|
cbb40b85dc | ||
|
|
9aca772209 | ||
|
|
c4db9cd84e | ||
|
|
205e80f8a5 | ||
|
|
06e9bd0c25 | ||
|
|
1c812f8b34 | ||
|
|
f3c5759721 | ||
|
|
69368a39cb | ||
|
|
ab5849b3cd | ||
|
|
1ab796bd40 | ||
|
|
c1221054b4 | ||
|
|
3bd681bc9d | ||
|
|
b33989ec62 | ||
|
|
b3121eebae | ||
|
|
7d8971139b | ||
|
|
04f20860dc | ||
|
|
8f21b9dabb | ||
|
|
eb89610465 | ||
|
|
13f4e90ee6 | ||
|
|
29f1dd5a69 | ||
|
|
a7e937ec58 | ||
|
|
bc2a8a8c4a | ||
|
|
c9b0a2b561 | ||
|
|
5fcea9bf69 | ||
|
|
3b42c86f46 | ||
|
|
038fe30fbe | ||
|
|
aa95e51e00 | ||
|
|
6a78c7dcc2 | ||
|
|
17aa1d1fc0 | ||
|
|
24244f1bac | ||
|
|
f7be35848c | ||
|
|
1aa7ff14fe | ||
|
|
c33a508376 | ||
|
|
37ec8b12f3 | ||
|
|
dde3efc0c7 | ||
|
|
54f3dec7a9 | ||
|
|
6be497ee7d | ||
|
|
5d9d44dc5e | ||
|
|
d547394ba2 | ||
|
|
ace0a3e278 | ||
|
|
dd9cb27971 | ||
|
|
a3dc8ab3d7 | ||
|
|
496bf386be | ||
|
|
c337616fea | ||
|
|
186ba76092 | ||
|
|
20a06c9212 | ||
|
|
e515dfb434 | ||
|
|
2ee85dd407 | ||
|
|
6df6e4a9cc | ||
|
|
c74c955973 | ||
|
|
a3a4fd0a75 | ||
|
|
c7f3ac0361 | ||
|
|
21002574ce | ||
|
|
64cae5b58b | ||
|
|
cc1105ff60 | ||
|
|
71655f0632 | ||
|
|
6e4e5be35d | ||
|
|
872a424a78 | ||
|
|
612c355e03 | ||
|
|
ffa9b21364 | ||
|
|
180311fd0a | ||
|
|
16b7714139 | ||
|
|
c6865cf63d | ||
|
|
905d9b5931 | ||
|
|
0850370275 | ||
|
|
38591f444e | ||
|
|
ae36aa1f74 | ||
|
|
264af56591 | ||
|
|
0a2c9d0e37 | ||
|
|
9d9d53c967 | ||
|
|
54315f41e4 | ||
|
|
a04e7ea375 | ||
|
|
795296e6e3 | ||
|
|
98ac85c098 | ||
|
|
e53c07393c | ||
|
|
48d66447b7 | ||
|
|
54b4f67ed6 | ||
|
|
9836797da9 | ||
|
|
931a260435 | ||
|
|
2a6d36b9cd | ||
|
|
9420c14c13 | ||
|
|
272807ba56 | ||
|
|
d05ea1863c | ||
|
|
6c8e021727 | ||
|
|
266135b1b9 | ||
|
|
02fc610a18 | ||
|
|
e2aa3cc69f | ||
|
|
de516c0ce4 | ||
|
|
92ac1fe2ca | ||
|
|
cc7844ea10 | ||
|
|
22bdc02b1b | ||
|
|
e8586515e3 | ||
|
|
071a30d928 | ||
|
|
488f1c47bf | ||
|
|
c15976f148 | ||
|
|
6f8e2e60ce | ||
|
|
b7b8da0848 | ||
|
|
2f1803b3b6 | ||
|
|
58e61c9d6b | ||
|
|
ddd98b9335 | ||
|
|
278b84ee38 | ||
|
|
c0764bce74 | ||
|
|
7cf0e2f747 | ||
|
|
c7e8627679 | ||
|
|
1c7d5b8fd2 | ||
|
|
18217e4adb | ||
|
|
1357e7e225 | ||
|
|
a941c2d14e | ||
|
|
cb55a75bcf | ||
|
|
1e20253229 | ||
|
|
53a48dc8e0 | ||
|
|
2ad9d69396 | ||
|
|
74baca3a7b | ||
|
|
2bd8700c90 | ||
|
|
51bd6cdf97 | ||
|
|
b02021a4ff | ||
|
|
96f3350029 | ||
|
|
e2e4c53b0d | ||
|
|
9c6605f7e7 | ||
|
|
f481652666 | ||
|
|
212389f43e | ||
|
|
f68680ada1 | ||
|
|
6859326646 | ||
|
|
a129c9814f | ||
|
|
05ee0caec2 | ||
|
|
6fff9d6a20 | ||
|
|
d2955557bf | ||
|
|
aa00cd9ae7 | ||
|
|
aa9d9ed578 | ||
|
|
9bd6fa61c5 | ||
|
|
f16997fb2d | ||
|
|
f4c1cf0818 | ||
|
|
01dd63f623 | ||
|
|
3393808c7a | ||
|
|
772c892b15 | ||
|
|
435d943f47 | ||
|
|
eab1060f8e | ||
|
|
cb8774370f | ||
|
|
23b1d15b64 | ||
|
|
61727aca02 | ||
|
|
8cfebdf723 | ||
|
|
506702c50b | ||
|
|
b9de1df6a8 | ||
|
|
a7175007be | ||
|
|
5d8acbb41a | ||
|
|
3ccbeac996 | ||
|
|
1643ba8e9c | ||
|
|
768dbd8946 | ||
|
|
c2d02a2394 | ||
|
|
b26f2738f6 | ||
|
|
781ec7e33a | ||
|
|
d09db3a920 | ||
|
|
998a8692cb | ||
|
|
b83ac447e8 | ||
|
|
6688f1811e | ||
|
|
ec26750b58 | ||
|
|
0f0abc0501 | ||
|
|
9ad6f2c994 | ||
|
|
abab47558b | ||
|
|
f3147be8f6 | ||
|
|
af185b79c8 | ||
|
|
3fc686e403 | ||
|
|
f5ea073c2b | ||
|
|
aa6d7578d1 | ||
|
|
05338caa23 | ||
|
|
a6ac78d7c1 | ||
|
|
9bcf78bfd6 | ||
|
|
9c97df5186 | ||
|
|
c2a8fd4e09 | ||
|
|
c8b6ec08e7 | ||
|
|
4293230416 | ||
|
|
bfddec242b | ||
|
|
ee3652acad | ||
|
|
5baa213e6a | ||
|
|
ca8ea8dd02 | ||
|
|
308d2626a9 | ||
|
|
8cab8dd7b7 | ||
|
|
0a5e37c592 | ||
|
|
72326435ce | ||
|
|
ca9f17d6e1 | ||
|
|
ff89c4d424 | ||
|
|
fa3b7ce545 | ||
|
|
c85feb7cea | ||
|
|
d50f812694 | ||
|
|
c7e27a836a | ||
|
|
c8a1380bef | ||
|
|
7732c9bc6a | ||
|
|
82c57972c6 | ||
|
|
0cc0c820a5 | ||
|
|
9150a569a7 | ||
|
|
3c76b90a48 | ||
|
|
5f2a5d9cfa | ||
|
|
4e40702809 | ||
|
|
a2b8813628 | ||
|
|
751ab5a4b4 | ||
|
|
b6bcade4c0 | ||
|
|
d6be6ca92c | ||
|
|
9024e0b2db | ||
|
|
ecca1aa070 | ||
|
|
3fde682653 | ||
|
|
aee8a98d84 | ||
|
|
3ab670e8c2 | ||
|
|
1fc7ce3b81 | ||
|
|
e8c44951a1 | ||
|
|
c11ca679d4 | ||
|
|
46781c349f | ||
|
|
189b802452 | ||
|
|
6b7687c80b | ||
|
|
906abd5fe6 | ||
|
|
0c539aada9 | ||
|
|
a3611f7f63 | ||
|
|
d62d6e14c8 | ||
|
|
4cd4d91225 | ||
|
|
5257287421 | ||
|
|
7e60a9fce7 | ||
|
|
b80cc36fb5 | ||
|
|
0e75824247 | ||
|
|
7470c8e72e | ||
|
|
a04f0ee816 | ||
|
|
d37244e6e2 | ||
|
|
82c4f43c4f | ||
|
|
1c2c244273 | ||
|
|
a991a84a8e | ||
|
|
532912984d | ||
|
|
265ef1e1ce | ||
|
|
70ad369ad6 | ||
|
|
723685b4cb | ||
|
|
061711afc8 | ||
|
|
8b6a8f02a4 | ||
|
|
893ec0f097 | ||
|
|
7c9d4c76a9 | ||
|
|
52fb63c0ee | ||
|
|
8a547bf104 | ||
|
|
0019ab216a | ||
|
|
8d52cd3a58 | ||
|
|
c7fbe56423 | ||
|
|
5ff5ea71de | ||
|
|
6297526da6 | ||
|
|
56ac89f6a6 | ||
|
|
aa46ab1a67 | ||
|
|
645b02b2d1 | ||
|
|
b2c5f183ec | ||
|
|
c6b60731d8 | ||
|
|
fd2f40f070 | ||
|
|
9fef41404f | ||
|
|
017aec3ae8 | ||
|
|
2ae4aca687 | ||
|
|
692fd91801 | ||
|
|
12ec9c3ba1 | ||
|
|
ac5e40f4ed | ||
|
|
f44afb8ee6 | ||
|
|
9e01277e49 | ||
|
|
73dc5eb922 | ||
|
|
a5091d9af0 | ||
|
|
a573d3cfa0 | ||
|
|
c4b7497770 | ||
|
|
1964658584 | ||
|
|
077b7be2a4 | ||
|
|
22c6df1a7a | ||
|
|
54d5497f0a | ||
|
|
9d2a8d0a67 | ||
|
|
20024729ec | ||
|
|
43315f50ea | ||
|
|
46edbbbd2b | ||
|
|
dc3713f0ea | ||
|
|
01a4a04364 | ||
|
|
7384191fb6 | ||
|
|
b8c930016f | ||
|
|
5d37201391 | ||
|
|
2c8963784c | ||
|
|
c42fa13b92 | ||
|
|
c02d62044c | ||
|
|
7bcb4ed3b2 | ||
|
|
834e9c48e1 | ||
|
|
ddfc84db6a | ||
|
|
b4a7d0b86c | ||
|
|
02cafd0e0b | ||
|
|
f5891a531e | ||
|
|
4681b03646 | ||
|
|
733953ac99 | ||
|
|
4837984ac3 | ||
|
|
4efd93defe | ||
|
|
35f7d143b8 | ||
|
|
d48fd1a12e | ||
|
|
a0900cd732 | ||
|
|
64b4922499 | ||
|
|
4574126428 | ||
|
|
9704dd8e35 | ||
|
|
f8d794bc93 | ||
|
|
5c7c7b98ee | ||
|
|
3826c1f886 | ||
|
|
58844b04ef | ||
|
|
178f7f3426 |
@@ -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
|
||||
*/
|
||||
|
||||
|
||||
@@ -274,7 +274,7 @@ class UserRightsMatrix extends UserRightsAddOnAPI
|
||||
return true;
|
||||
}
|
||||
|
||||
public function GetSelectFilter($oUser, $sClass)
|
||||
public function GetSelectFilter($oUser, $sClass, $aSettings = array())
|
||||
{
|
||||
$oNullFilter = new DBObjectSearch($sClass);
|
||||
return $oNullFilter;
|
||||
|
||||
@@ -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
|
||||
@@ -47,7 +47,7 @@ class UserRightsNull extends UserRightsAddOnAPI
|
||||
return true;
|
||||
}
|
||||
|
||||
public function GetSelectFilter($oUser, $sClass)
|
||||
public function GetSelectFilter($oUser, $sClass, $aSettings = array())
|
||||
{
|
||||
$oNullFilter = new DBObjectSearch($sClass);
|
||||
return $oNullFilter;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
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);
|
||||
@@ -97,7 +97,7 @@ class URP_Profiles extends UserRightsBaseClass
|
||||
|
||||
function DoShowGrantSumary($oPage)
|
||||
{
|
||||
if ($this->GetName() == "Administrator")
|
||||
if ($this->GetRawName() == "Administrator")
|
||||
{
|
||||
// Looks dirty, but ok that's THE ONE
|
||||
$oPage->p(Dict::S('UI:UserManagement:AdminProfile+'));
|
||||
@@ -734,7 +734,7 @@ exit;
|
||||
return true;
|
||||
}
|
||||
|
||||
public function GetSelectFilter($oUser, $sClass)
|
||||
public function GetSelectFilter($oUser, $sClass, $aSettings = array())
|
||||
{
|
||||
$aConditions = array();
|
||||
foreach ($this->m_aDimensions as $iDimension => $oDimension)
|
||||
|
||||
@@ -1,41 +1,40 @@
|
||||
<?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/>
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -45,127 +44,215 @@ class ajax_page extends WebPage
|
||||
{
|
||||
parent::__construct($s_title);
|
||||
$this->m_sReadyScript = "";
|
||||
$this->add_header("Content-type: text/html; charset=utf-8");
|
||||
//$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_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
|
||||
*/
|
||||
public function output()
|
||||
{
|
||||
if (!empty($this->sContentType))
|
||||
{
|
||||
$this->add_header('Content-type: '.$this->sContentType);
|
||||
}
|
||||
if (!empty($this->sContentDisposition))
|
||||
{
|
||||
$this->add_header('Content-Disposition: '.$this->sContentDisposition.'; filename="'.$this->sContentFileName.'"');
|
||||
}
|
||||
foreach($this->a_headers as $s_header)
|
||||
{
|
||||
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]');
|
||||
|
||||
// This selector will be reused when selecting actual tab widget A elements.
|
||||
var tab_a_selector = 'ul.ui-tabs-nav a';
|
||||
|
||||
// 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' });
|
||||
|
||||
// Define our own click handler for the tabs, overriding the default.
|
||||
tabs.find( tab_a_selector ).click(function()
|
||||
// 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)
|
||||
{
|
||||
var state = {};
|
||||
|
||||
// Get the id of this tab widget.
|
||||
var id = $(this).closest( 'div[id^=tabbedContent]' ).attr( 'id' );
|
||||
$('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.
|
||||
var tab_a_selector = 'ul.ui-tabs-nav a';
|
||||
|
||||
// Get the index of this tab.
|
||||
var idx = $(this).parent().prevAll().length;
|
||||
|
||||
// Set the state!
|
||||
state[ id ] = idx;
|
||||
$.bbq.pushState( state );
|
||||
});
|
||||
// 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' });
|
||||
|
||||
// Define our own click handler for the tabs, overriding the default.
|
||||
tabs.find( tab_a_selector ).click(function()
|
||||
{
|
||||
var state = {};
|
||||
|
||||
// Get the id of this tab widget.
|
||||
var id = $(this).closest( 'div[id^=tabbedContent]' ).attr( 'id' );
|
||||
|
||||
// Get the index of this tab.
|
||||
var idx = $(this).parent().prevAll().length;
|
||||
|
||||
// Set the state!
|
||||
state[ id ] = idx;
|
||||
$.bbq.pushState( state );
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
tabs.tabs();
|
||||
}
|
||||
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}{$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_{$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++;
|
||||
}
|
||||
|
||||
$s_captured_output = ob_get_contents();
|
||||
ob_end_clean();
|
||||
echo $this->s_content;
|
||||
$this->add_ready_script(
|
||||
<<<EOF
|
||||
$(".date-pick").datepicker({
|
||||
showOn: 'button',
|
||||
buttonImage: '../images/calendar.png',
|
||||
buttonImageOnly: true,
|
||||
dateFormat: 'yy-mm-dd',
|
||||
constrainInput: false,
|
||||
changeMonth: true,
|
||||
changeYear: true
|
||||
});
|
||||
$(".datetime-pick").datepicker({
|
||||
showOn: 'button',
|
||||
buttonImage: '../images/calendar.png',
|
||||
buttonImageOnly: true,
|
||||
dateFormat: 'yy-mm-dd 00:00:00',
|
||||
constrainInput: false,
|
||||
changeMonth: true,
|
||||
changeYear: true
|
||||
});
|
||||
EOF
|
||||
);
|
||||
}
|
||||
$s_captured_output = $this->ob_get_clean_safe();
|
||||
if (($this->sContentType == 'text/html') && ($this->sContentDisposition == 'inline'))
|
||||
{
|
||||
// inline content != attachment && html => filter all scripts for malicious XSS scripts
|
||||
echo self::FilterXSS($this->s_content);
|
||||
}
|
||||
else
|
||||
{
|
||||
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)
|
||||
{
|
||||
@@ -176,7 +263,7 @@ EOF
|
||||
if (!empty($this->s_deferred_content))
|
||||
{
|
||||
echo "<script type=\"text/javascript\">\n";
|
||||
echo "\$('body').append('".$this->s_deferred_content."');\n";
|
||||
echo "\$('body').append('".addslashes(str_replace("\n", '', $this->s_deferred_content))."');\n";
|
||||
echo "\n</script>\n";
|
||||
}
|
||||
if (!empty($this->m_sReadyScript))
|
||||
@@ -185,9 +272,15 @@ EOF
|
||||
echo $this->m_sReadyScript; // Ready Scripts are output as simple scripts
|
||||
echo "\n</script>\n";
|
||||
}
|
||||
|
||||
if (trim($s_captured_output) != "")
|
||||
{
|
||||
echo $s_captured_output;
|
||||
echo self::FilterXSS($s_captured_output);
|
||||
}
|
||||
|
||||
if (class_exists('MetaModel'))
|
||||
{
|
||||
MetaModel::RecordQueryTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,15 +296,75 @@ 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
|
||||
{
|
||||
parent::add($sHtml);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Records the current state of the 'html' part of the page output
|
||||
* @return mixed The current state of the 'html' output
|
||||
*/
|
||||
public function 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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the part of the html output that occurred since the call to start_capture
|
||||
* and removes this part from the current html output
|
||||
* @param $offset mixed The value returned by start_capture
|
||||
* @return string The part of the html output that was added since the call to start_capture
|
||||
*/
|
||||
public function end_capture($offset)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add any text or HTML fragment (identified by an ID) at the end of the body of the page
|
||||
* This is useful to add hidden content, DIVs or FORMs that should not
|
||||
* be embedded into each other.
|
||||
*/
|
||||
public function add_at_the_end($s_html, $sId = '')
|
||||
{
|
||||
if ($sId != '')
|
||||
{
|
||||
$this->add_script("$('#{$sId}').remove();"); // Remove any previous instance of the same Id
|
||||
}
|
||||
$this->s_deferred_content .= $s_html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a script to be executed when the DOM is ready (typical JQuery use)
|
||||
@@ -220,10 +373,7 @@ EOF
|
||||
*/
|
||||
public function add_ready_script($sScript)
|
||||
{
|
||||
// Does nothing in ajax rendered content.. for now...
|
||||
// Maybe we should add this as a simple <script> tag at the end of the output
|
||||
// considering that at this time everything in the page is "ready"...
|
||||
$this->m_sReadyScript .= $sScript;
|
||||
$this->m_sReadyScript .= $sScript."\n";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -236,6 +386,9 @@ EOF
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static function FilterXSS($sHTML)
|
||||
{
|
||||
return str_ireplace(array('<script', '</script>'), array('<!-- <removed-script', '</removed-script> -->'), $sHTML);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
@@ -1,33 +1,37 @@
|
||||
<?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');
|
||||
require_once(APPROOT.'/application/cmdbabstract.class.inc.php');
|
||||
require_once(APPROOT.'/application/displayblock.class.inc.php');
|
||||
require_once(APPROOT.'/application/sqlblock.class.inc.php');
|
||||
require_once(APPROOT.'/application/audit.category.class.inc.php');
|
||||
require_once(APPROOT.'/application/audit.rule.class.inc.php');
|
||||
require_once(APPROOT.'/application/query.class.inc.php');
|
||||
require_once(APPROOT.'/setup/moduleinstallation.class.inc.php');
|
||||
//require_once(APPROOT.'/application/menunode.class.inc.php');
|
||||
require_once(APPROOT.'/application/utils.inc.php');
|
||||
|
||||
|
||||
@@ -1,29 +1,67 @@
|
||||
<?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");
|
||||
|
||||
/**
|
||||
* Interface for directing end-users to the relevant application
|
||||
*/
|
||||
interface iDBObjectURLMaker
|
||||
{
|
||||
public static function MakeObjectURL($sClass, $iId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Direct end-users to the standard iTop application: UI.php
|
||||
*/
|
||||
class iTopStandardURLMaker implements iDBObjectURLMaker
|
||||
{
|
||||
public static function MakeObjectURL($sClass, $iId)
|
||||
{
|
||||
$sPage = DBObject::ComputeStandardUIPage($sClass);
|
||||
$sAbsoluteUrl = utils::GetAbsoluteUrlAppRoot();
|
||||
$sUrl = "{$sAbsoluteUrl}pages/$sPage?operation=details&class=$sClass&id=$iId";
|
||||
return $sUrl;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Direct end-users to the standard Portal application
|
||||
*/
|
||||
class PortalURLMaker implements iDBObjectURLMaker
|
||||
{
|
||||
public static function MakeObjectURL($sClass, $iId)
|
||||
{
|
||||
$sAbsoluteUrl = utils::GetAbsoluteUrlAppRoot();
|
||||
$sUrl = "{$sAbsoluteUrl}portal/index.php?operation=details&class=$sClass&id=$iId";
|
||||
return $sUrl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper class to store and manipulate the parameters that make the application's context
|
||||
*
|
||||
@@ -40,12 +78,16 @@ class ApplicationContext
|
||||
protected $aValues;
|
||||
protected static $aDefaultValues; // Cache shared among all instances
|
||||
|
||||
public function __construct()
|
||||
public function __construct($bReadContext = true)
|
||||
{
|
||||
$this->aNames = array(
|
||||
'org_id', 'menu'
|
||||
);
|
||||
$this->ReadContext();
|
||||
if ($bReadContext)
|
||||
{
|
||||
$this->ReadContext();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -54,10 +96,10 @@ class ApplicationContext
|
||||
*/
|
||||
protected function ReadContext()
|
||||
{
|
||||
if (empty(self::$aDefaultValues))
|
||||
if (!isset(self::$aDefaultValues))
|
||||
{
|
||||
self::$aDefaultValues = array();
|
||||
$aContext = utils::ReadParam('c', array());
|
||||
$aContext = utils::ReadParam('c', array(), false, 'context_param');
|
||||
foreach($this->aNames as $sName)
|
||||
{
|
||||
$sValue = isset($aContext[$sName]) ? $aContext[$sName] : '';
|
||||
@@ -74,6 +116,7 @@ class ApplicationContext
|
||||
if (MetaModel::IsValidClass('Organization'))
|
||||
{
|
||||
$oSearchFilter = new DBObjectSearch('Organization');
|
||||
$oSearchFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', true);
|
||||
$oSet = new CMDBObjectSet($oSearchFilter);
|
||||
$iCount = $oSet->Count();
|
||||
if ($iCount == 1)
|
||||
@@ -126,7 +169,7 @@ class ApplicationContext
|
||||
$sContext = "";
|
||||
foreach($this->aValues as $sName => $sValue)
|
||||
{
|
||||
$sContext .= "<input type=\"hidden\" name=\"c[$sName]\" value=\"$sValue\" />\n";
|
||||
$sContext .= "<input type=\"hidden\" name=\"c[$sName]\" value=\"".htmlentities($sValue, ENT_QUOTES, 'UTF-8')."\" />\n";
|
||||
}
|
||||
return $sContext;
|
||||
}
|
||||
@@ -166,5 +209,154 @@ class ApplicationContext
|
||||
unset($this->aValues[$sParamName]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the given object with the default values provided by the context
|
||||
*/
|
||||
public function InitObjectFromContext(DBObject &$oObj)
|
||||
{
|
||||
$sClass = get_class($oObj);
|
||||
foreach($this->GetNames() as $key)
|
||||
{
|
||||
$aCallSpec = array($sClass, 'MapContextParam');
|
||||
if (is_callable($aCallSpec))
|
||||
{
|
||||
$sAttCode = call_user_func($aCallSpec, $key); // Returns null when there is no mapping for this parameter
|
||||
|
||||
if (MetaModel::IsValidAttCode($sClass, $sAttCode))
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
if ($oAttDef->IsWritable())
|
||||
{
|
||||
$value = $this->GetCurrentValue($key, null);
|
||||
if (!is_null($value))
|
||||
{
|
||||
$oObj->Set($sAttCode, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static $m_sUrlMakerClass = null;
|
||||
|
||||
/**
|
||||
* Set the current application url provider
|
||||
* @param sClass string Class implementing iDBObjectURLMaker
|
||||
* @return void
|
||||
*/
|
||||
public static function SetUrlMakerClass($sClass = 'iTopStandardURLMaker')
|
||||
{
|
||||
$sPrevious = self::GetUrlMakerClass();
|
||||
|
||||
self::$m_sUrlMakerClass = $sClass;
|
||||
$_SESSION['UrlMakerClass'] = $sClass;
|
||||
|
||||
return $sPrevious;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current application url provider
|
||||
* @return string the name of the class
|
||||
*/
|
||||
public static function GetUrlMakerClass()
|
||||
{
|
||||
if (is_null(self::$m_sUrlMakerClass))
|
||||
{
|
||||
if (isset($_SESSION['UrlMakerClass']))
|
||||
{
|
||||
self::$m_sUrlMakerClass = $_SESSION['UrlMakerClass'];
|
||||
}
|
||||
else
|
||||
{
|
||||
self::$m_sUrlMakerClass = 'iTopStandardURLMaker';
|
||||
}
|
||||
}
|
||||
return self::$m_sUrlMakerClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current application url provider
|
||||
* @return string the name of the class
|
||||
*/
|
||||
public static function MakeObjectUrl($sObjClass, $sObjKey, $sUrlMakerClass = null, $bWithNavigationContext = true)
|
||||
{
|
||||
$oAppContext = new ApplicationContext();
|
||||
|
||||
if (is_null($sUrlMakerClass))
|
||||
{
|
||||
$sUrlMakerClass = self::GetUrlMakerClass();
|
||||
}
|
||||
$sUrl = call_user_func(array($sUrlMakerClass, 'MakeObjectUrl'), $sObjClass, $sObjKey);
|
||||
if (strlen($sUrl) > 0)
|
||||
{
|
||||
if ($bWithNavigationContext)
|
||||
{
|
||||
return $sUrl."&".$oAppContext->GetForLink();
|
||||
}
|
||||
else
|
||||
{
|
||||
return $sUrl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
protected static $m_aPluginProperties = null;
|
||||
|
||||
/**
|
||||
* Load plugin properties for the current session
|
||||
* @return void
|
||||
*/
|
||||
protected static function LoadPluginProperties()
|
||||
{
|
||||
if (isset($_SESSION['PluginProperties']))
|
||||
{
|
||||
self::$m_aPluginProperties = $_SESSION['PluginProperties'];
|
||||
}
|
||||
else
|
||||
{
|
||||
self::$m_aPluginProperties = array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set plugin properties
|
||||
* @param sPluginClass string Class implementing any plugin interface
|
||||
* @param sProperty string Name of the property
|
||||
* @param value scalar Value (numeric or string)
|
||||
* @return void
|
||||
*/
|
||||
public static function SetPluginProperty($sPluginClass, $sProperty, $value)
|
||||
{
|
||||
if (is_null(self::$m_aPluginProperties)) self::LoadPluginProperties();
|
||||
|
||||
self::$m_aPluginProperties[$sPluginClass][$sProperty] = $value;
|
||||
$_SESSION['PluginProperties'][$sPluginClass][$sProperty] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get plugin properties
|
||||
* @param sPluginClass string Class implementing any plugin interface
|
||||
* @return array of sProperty=>value pairs
|
||||
*/
|
||||
public static function GetPluginProperties($sPluginClass)
|
||||
{
|
||||
if (is_null(self::$m_aPluginProperties)) self::LoadPluginProperties();
|
||||
|
||||
if (array_key_exists($sPluginClass, self::$m_aPluginProperties))
|
||||
{
|
||||
return self::$m_aPluginProperties[$sPluginClass];
|
||||
}
|
||||
else
|
||||
{
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
|
||||
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,32 +1,33 @@
|
||||
<?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");
|
||||
|
||||
class CLIPage
|
||||
class CLIPage implements Page
|
||||
{
|
||||
function __construct($s_title)
|
||||
{
|
||||
@@ -34,6 +35,14 @@ class CLIPage
|
||||
|
||||
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-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/>
|
||||
|
||||
|
||||
/**
|
||||
* 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,18 +32,33 @@ 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()
|
||||
{
|
||||
$this->add_header("Content-Length: ".strlen(trim($this->s_content)));
|
||||
$this->add_header("Content-Length: ".strlen(trim($this->s_content)));
|
||||
|
||||
// Get the unexpected output but do nothing with it
|
||||
$sTrash = $this->ob_get_clean_safe();
|
||||
|
||||
foreach($this->a_headers as $s_header)
|
||||
{
|
||||
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)
|
||||
@@ -93,4 +109,3 @@ class CSVPage extends WebPage
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
872
application/dashboard.class.inc.php
Normal file
872
application/dashboard.class.inc.php
Normal file
@@ -0,0 +1,872 @@
|
||||
<?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/>
|
||||
|
||||
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->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
|
||||
set_error_handler(array('Dashboard', 'ErrorHandler'));
|
||||
$oDoc = new DOMDocument();
|
||||
$oDoc->loadXML($sXml);
|
||||
restore_error_handler();
|
||||
$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(MetaModel::GetConfig()->Get('min_reload_interval'), (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
|
||||
*/
|
||||
public static function ErrorHandler($errno, $errstr, $errfile, $errline)
|
||||
{
|
||||
if ($errno == E_WARNING && (substr_count($errstr,"DOMDocument::loadXML()")>0))
|
||||
{
|
||||
throw new DOMException($errstr);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function ToXml()
|
||||
{
|
||||
$oDoc = new DOMDocument();
|
||||
$oDoc->formatOutput = true; // indent (must be loaded with option LIBXML_NOBLANKS)
|
||||
$oDoc->preserveWhiteSpace = true; // otherwise the formatOutput option would have no effect
|
||||
|
||||
$oMainNode = $oDoc->createElement('dashboard');
|
||||
$oMainNode->setAttribute('xmlns:xsi', "http://www.w3.org/2001/XMLSchema-instance");
|
||||
$oDoc->appendChild($oMainNode);
|
||||
|
||||
$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(MetaModel::GetConfig()->Get('min_reload_interval'), (int) $aParams['auto_reload_sec']);
|
||||
|
||||
foreach($aParams['cells'] as $aCell)
|
||||
{
|
||||
$aCellDashlets = array();
|
||||
foreach($aCell as $aDashletParams)
|
||||
{
|
||||
$sDashletClass = $aDashletParams['dashlet_class'];
|
||||
$sId = $aDashletParams['dashlet_id'];
|
||||
$oNewDashlet = new $sDashletClass($this->oMetaModel, $sId);
|
||||
|
||||
$oForm = $oNewDashlet->GetForm();
|
||||
$oForm->SetParamsContainer($sId);
|
||||
$oForm->SetPrefix('');
|
||||
$aValues = $oForm->ReadParams();
|
||||
$oNewDashlet->FromParams($aValues);
|
||||
$aCellDashlets[] = $oNewDashlet;
|
||||
}
|
||||
$this->aCells[] = $aCellDashlets;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function Save()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function GetLayout()
|
||||
{
|
||||
return $this->sLayoutClass;
|
||||
}
|
||||
|
||||
public function SetLayout($sLayoutClass)
|
||||
{
|
||||
$this->sLayoutClass = $sLayoutClass;
|
||||
}
|
||||
|
||||
public function GetTitle()
|
||||
{
|
||||
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(MetaModel::GetConfig()->Get('min_reload_interval'), (int)$iAutoReloadSec);
|
||||
}
|
||||
|
||||
public function AddDashlet($oDashlet)
|
||||
{
|
||||
$sId = $this->GetNewDashletId();
|
||||
$oDashlet->SetId($sId);
|
||||
$this->aCells[] = array($oDashlet);
|
||||
}
|
||||
|
||||
public function Render($oPage, $bEditMode = false, $aExtraParams = array())
|
||||
{
|
||||
$oPage->add('<h1>'.htmlentities(Dict::S($this->sTitle), ENT_QUOTES, 'UTF-8', false).'</h1>');
|
||||
$oLayout = new $this->sLayoutClass;
|
||||
$oLayout->Render($oPage, $this->aCells, $bEditMode, $aExtraParams);
|
||||
if (!$bEditMode)
|
||||
{
|
||||
$oPage->add_linked_script('../js/dashlet.js');
|
||||
$oPage->add_linked_script('../js/dashboard.js');
|
||||
}
|
||||
}
|
||||
|
||||
public function RenderProperties($oPage)
|
||||
{
|
||||
// menu to pick a layout and edit other properties of the dashboard
|
||||
$oPage->add('<div class="ui-widget-content ui-corner-all"><div class="ui-widget-header ui-corner-all" style="text-align:center; padding: 2px;">'.Dict::S('UI:DashboardEdit:Properties').'</div>');
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot();
|
||||
|
||||
$oPage->add('<div style="text-align:center">'.Dict::S('UI:DashboardEdit:Layout').'</div>');
|
||||
$oPage->add('<div id="select_layout" style="text-align:center">');
|
||||
foreach( get_declared_classes() as $sLayoutClass)
|
||||
{
|
||||
if (is_subclass_of($sLayoutClass, 'DashboardLayout'))
|
||||
{
|
||||
$oReflection = new ReflectionClass($sLayoutClass);
|
||||
if (!$oReflection->isAbstract())
|
||||
{
|
||||
$aCallSpec = array($sLayoutClass, 'GetInfo');
|
||||
$aInfo = call_user_func($aCallSpec);
|
||||
$sChecked = ($this->sLayoutClass == $sLayoutClass) ? 'checked' : '';
|
||||
$oPage->add('<input type="radio" name="layout_class" '.$sChecked.' value="'.$sLayoutClass.'" id="layout_'.$sLayoutClass.'"><label for="layout_'.$sLayoutClass.'"><img src="'.$sUrl.$aInfo['icon'].'" /></label>'); // title="" on either the img or the label does nothing !
|
||||
}
|
||||
}
|
||||
}
|
||||
$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 DesignerIntegerField('auto_reload_sec', Dict::S('UI:DashboardEdit:AutoReloadSec'), $this->iAutoReloadSec);
|
||||
$oField->SetBoundaries(MetaModel::GetConfig()->Get('min_reload_interval'), null); // no upper limit
|
||||
$oForm->AddField($oField);
|
||||
|
||||
|
||||
$this->SetFormParams($oForm);
|
||||
$oForm->RenderAsPropertySheet($oPage, false, '.itop-dashboard');
|
||||
|
||||
$oPage->add('</div>');
|
||||
|
||||
$sRateTitle = addslashes(Dict::Format('UI:DashboardEdit:AutoReloadSec+', MetaModel::GetConfig()->Get('min_reload_interval')));
|
||||
$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'));
|
||||
} );
|
||||
|
||||
$('#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
|
||||
);
|
||||
}
|
||||
|
||||
public function RenderDashletsSelection($oPage)
|
||||
{
|
||||
// Toolbox/palette to drag and drop dashlets
|
||||
$oPage->add('<div class="ui-widget-content ui-corner-all"><div class="ui-widget-header ui-corner-all" style="text-align:center; padding: 2px;">'.Dict::S('UI:DashboardEdit:Dashlets').'</div>');
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot();
|
||||
|
||||
$oPage->add('<div id="select_dashlet" style="text-align:center">');
|
||||
foreach( get_declared_classes() as $sDashletClass)
|
||||
{
|
||||
if (is_subclass_of($sDashletClass, 'Dashlet'))
|
||||
{
|
||||
$oReflection = new ReflectionClass($sDashletClass);
|
||||
if (!$oReflection->isAbstract())
|
||||
{
|
||||
$aCallSpec = array($sDashletClass, 'IsVisible');
|
||||
$bVisible = call_user_func($aCallSpec);
|
||||
if ($bVisible)
|
||||
{
|
||||
$aCallSpec = array($sDashletClass, 'GetInfo');
|
||||
$aInfo = call_user_func($aCallSpec);
|
||||
$oPage->add('<span dashlet_class="'.$sDashletClass.'" class="dashlet_icon ui-widget-content ui-corner-all" id="dashlet_'.$sDashletClass.'" title="'.$aInfo['label'].'" style="width:34px; height:34px; display:inline-block; margin:2px;"><img src="'.$sUrl.$aInfo['icon'].'" /></span>');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$oPage->add('</div>');
|
||||
|
||||
$oPage->add('</div>');
|
||||
$oPage->add_ready_script("$('.dashlet_icon').draggable({helper: 'clone', appendTo: 'body', zIndex: 10000, revert:'invalid'});");
|
||||
}
|
||||
|
||||
public function RenderDashletsProperties($oPage)
|
||||
{
|
||||
// Toolbox/palette to edit the properties of each dashlet
|
||||
$oPage->add('<div class="ui-widget-content ui-corner-all"><div class="ui-widget-header ui-corner-all" style="text-align:center; padding: 2px;">'.Dict::S('UI:DashboardEdit:DashletProperties').'</div>');
|
||||
|
||||
$oPage->add('<div id="dashlet_properties" style="text-align:center">');
|
||||
foreach($this->aCells as $aCell)
|
||||
{
|
||||
foreach($aCell as $oDashlet)
|
||||
{
|
||||
$sId = $oDashlet->GetID();
|
||||
$sClass = get_class($oDashlet);
|
||||
if ($oDashlet->IsVisible())
|
||||
{
|
||||
$oPage->add('<div class="dashlet_properties" id="dashlet_properties_'.$sId.'" style="display:none">');
|
||||
$oForm = $oDashlet->GetForm();
|
||||
$this->SetFormParams($oForm);
|
||||
$oForm->RenderAsPropertySheet($oPage, false, '.itop-dashboard');
|
||||
$oPage->add('</div>');
|
||||
}
|
||||
}
|
||||
}
|
||||
$oPage->add('</div>');
|
||||
|
||||
$oPage->add('</div>');
|
||||
}
|
||||
|
||||
protected function GetNewDashletId()
|
||||
{
|
||||
$iNewId = 0;
|
||||
foreach($this->aCells as $aDashlets)
|
||||
{
|
||||
foreach($aDashlets as $oDashlet)
|
||||
{
|
||||
$iNewId = max($iNewId, (int)$oDashlet->GetID());
|
||||
}
|
||||
}
|
||||
return $iNewId + 1;
|
||||
}
|
||||
|
||||
abstract protected function SetFormParams($oForm);
|
||||
}
|
||||
|
||||
class RuntimeDashboard extends Dashboard
|
||||
{
|
||||
protected $bCustomized;
|
||||
|
||||
public function __construct($sId)
|
||||
{
|
||||
parent::__construct($sId);
|
||||
$this->bCustomized = false;
|
||||
$this->oMetaModel = new ModelReflectionRuntime();
|
||||
}
|
||||
|
||||
public function SetCustomFlag($bCustomized)
|
||||
{
|
||||
$this->bCustomized = $bCustomized;
|
||||
}
|
||||
|
||||
protected function SetFormParams($oForm)
|
||||
{
|
||||
$oForm->SetSubmitParams(utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php', array('operation' => 'update_dashlet_property'));
|
||||
}
|
||||
|
||||
public function Save()
|
||||
{
|
||||
$sXml = $this->ToXml();
|
||||
$oUDSearch = new DBObjectSearch('UserDashboard');
|
||||
$oUDSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
|
||||
$oUDSearch->AddCondition('menu_code', $this->sId, '=');
|
||||
$oUDSet = new DBObjectSet($oUDSearch);
|
||||
if ($oUDSet->Count() > 0)
|
||||
{
|
||||
// Assuming there is at most one couple {user, menu}!
|
||||
$oUserDashboard = $oUDSet->Fetch();
|
||||
$oUserDashboard->Set('contents', $sXml);
|
||||
|
||||
$oUserDashboard->DBUpdate();
|
||||
}
|
||||
else
|
||||
{
|
||||
// No such customized dasboard for the current user, let's create a new record
|
||||
$oUserDashboard = new UserDashboard();
|
||||
$oUserDashboard->Set('user_id', UserRights::GetUserId());
|
||||
$oUserDashboard->Set('menu_code', $this->sId);
|
||||
$oUserDashboard->Set('contents', $sXml);
|
||||
|
||||
$oUserDashboard->DBInsert();
|
||||
}
|
||||
}
|
||||
|
||||
public function Revert()
|
||||
{
|
||||
$oUDSearch = new DBObjectSearch('UserDashboard');
|
||||
$oUDSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
|
||||
$oUDSearch->AddCondition('menu_code', $this->sId, '=');
|
||||
$oUDSet = new DBObjectSet($oUDSearch);
|
||||
if ($oUDSet->Count() > 0)
|
||||
{
|
||||
// Assuming there is at most one couple {user, menu}!
|
||||
$oUserDashboard = $oUDSet->Fetch();
|
||||
$oUserDashboard->DBDelete();
|
||||
}
|
||||
}
|
||||
|
||||
public function RenderEditionTools($oPage)
|
||||
{
|
||||
$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)
|
||||
{
|
||||
$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(
|
||||
<<<EOF
|
||||
$('#logOffBtn').parent().before('$sEditMenu');
|
||||
$('#DashboardMenu>ul').popupmenu();
|
||||
|
||||
EOF
|
||||
);
|
||||
$oPage->add_script(
|
||||
<<<EOF
|
||||
function EditDashboard(sId)
|
||||
{
|
||||
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', {operation: 'dashboard_editor', id: sId},
|
||||
function(data)
|
||||
{
|
||||
$('body').append(data);
|
||||
}
|
||||
);
|
||||
return false;
|
||||
}
|
||||
function RevertDashboard(sId)
|
||||
{
|
||||
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', {operation: 'revert_dashboard', dashboard_id: sId},
|
||||
function(data)
|
||||
{
|
||||
$('body').append(data);
|
||||
}
|
||||
);
|
||||
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">');
|
||||
$oPage->add('<div class="ui-layout-center">');
|
||||
$this->Render($oPage, true);
|
||||
$oPage->add('</div>');
|
||||
$oPage->add('<div class="ui-layout-east">');
|
||||
$this->RenderProperties($oPage);
|
||||
$this->RenderDashletsSelection($oPage);
|
||||
$this->RenderDashletsProperties($oPage);
|
||||
$oPage->add('</div>');
|
||||
$oPage->add('<div id="event_bus"/>'); // For exchanging messages between the panes, same as in the designer
|
||||
$oPage->add('</div>');
|
||||
|
||||
$sDialogTitle = Dict::S('UI:DashboardEdit:Title');
|
||||
$sOkButtonLabel = Dict::S('UI:Button:Save');
|
||||
$sCancelButtonLabel = Dict::S('UI:Button:Cancel');
|
||||
|
||||
$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';
|
||||
|
||||
$sExitConfirmationMessage = addslashes(Dict::S('UI:NavigateAwayConfirmationMessage'));
|
||||
$sCancelConfirmationMessage = addslashes(Dict::S('UI:CancelConfirmationMessage'));
|
||||
$sAutoApplyConfirmationMessage = addslashes(Dict::S('UI:AutoApplyConfirmationMessage'));
|
||||
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
window.bLeavingOnUserAction = false;
|
||||
|
||||
$('#dashboard_editor').dialog({
|
||||
height: $('body').height() - 50,
|
||||
width: $('body').width() - 50,
|
||||
modal: true,
|
||||
title: '$sDialogTitle',
|
||||
buttons: [
|
||||
{ text: "$sOkButtonLabel", click: function() {
|
||||
var oDashboard = $('.itop-dashboard').data('itopRuntimedashboard');
|
||||
if (oDashboard.is_dirty())
|
||||
{
|
||||
if (!confirm('$sAutoApplyConfirmationMessage'))
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
oDashboard.apply_changes();
|
||||
}
|
||||
}
|
||||
window.bLeavingOnUserAction = true;
|
||||
oDashboard.save();
|
||||
} },
|
||||
{ text: "$sCancelButtonLabel", click: function() {
|
||||
var oDashboard = $('.itop-dashboard').data('itopRuntimedashboard');
|
||||
if (oDashboard.is_modified())
|
||||
{
|
||||
if (!confirm('$sCancelConfirmationMessage'))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
window.bLeavingOnUserAction = true;
|
||||
$(this).dialog( "close" );
|
||||
$(this).remove();
|
||||
} },
|
||||
],
|
||||
close: function() { $(this).remove(); }
|
||||
});
|
||||
|
||||
$('#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'}
|
||||
});
|
||||
|
||||
dashboard_prop_size = GetUserPreference('dashboard_prop_size', 350);
|
||||
$('#dashboard_editor').layout({
|
||||
east: {
|
||||
minSize: 200,
|
||||
size: dashboard_prop_size,
|
||||
togglerLength_open: 0,
|
||||
togglerLength_closed: 0,
|
||||
onresize_end: function(name, elt, state, options, layout)
|
||||
{
|
||||
if (state.isSliding == false)
|
||||
{
|
||||
SetUserPreference('dashboard_prop_size', state.size, true);
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
window.onbeforeunload = function() {
|
||||
if (!window.bLeavingOnUserAction)
|
||||
{
|
||||
var oDashboard = $('.itop-dashboard').data('itopRuntimedashboard');
|
||||
if (oDashboard)
|
||||
{
|
||||
if (oDashboard.is_dirty())
|
||||
{
|
||||
return '$sExitConfirmationMessage';
|
||||
}
|
||||
if (oDashboard.is_modified())
|
||||
{
|
||||
return '$sExitConfirmationMessage';
|
||||
}
|
||||
}
|
||||
}
|
||||
// return nothing ! safer for IE
|
||||
};
|
||||
EOF
|
||||
);
|
||||
$oPage->add_ready_script("");
|
||||
}
|
||||
|
||||
public static function GetDashletCreationForm($sOQL = null)
|
||||
{
|
||||
$oForm = new DesignerForm();
|
||||
|
||||
// Get the list of all 'dashboard' menus in which we can insert a dashlet
|
||||
$aAllMenus = ApplicationMenu::ReflectionMenuNodes();
|
||||
$aAllowedDashboards = array();
|
||||
foreach($aAllMenus as $idx => $aMenu)
|
||||
{
|
||||
$oMenu = $aMenu['node'];
|
||||
$sParentId = $aMenu['parent'];
|
||||
if ($oMenu instanceof DashboardMenuNode)
|
||||
{
|
||||
$sMenuLabel = $oMenu->GetTitle();
|
||||
$sParentLabel = Dict::S('Menu:'.$sParentId);
|
||||
if ($sParentLabel != $sMenuLabel)
|
||||
{
|
||||
$aAllowedDashboards[$oMenu->GetMenuId()] = $sParentLabel.' - '.$sMenuLabel;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aAllowedDashboards[$oMenu->GetMenuId()] = $sMenuLabel;
|
||||
}
|
||||
}
|
||||
}
|
||||
asort($aAllowedDashboards);
|
||||
|
||||
$aKeys = array_keys($aAllowedDashboards); // Select the first one by default
|
||||
$sDefaultDashboard = $aKeys[0];
|
||||
$oField = new DesignerComboField('menu_id', Dict::S('UI:DashletCreation:Dashboard'), $sDefaultDashboard);
|
||||
$oField->SetAllowedValues($aAllowedDashboards);
|
||||
$oField->SetMandatory(true);
|
||||
$oForm->AddField($oField);
|
||||
|
||||
// Get the list of possible dashlets that support a creation from
|
||||
// an OQL
|
||||
$aDashlets = array();
|
||||
foreach(get_declared_classes() as $sDashletClass)
|
||||
{
|
||||
if (is_subclass_of($sDashletClass, 'Dashlet'))
|
||||
{
|
||||
$oReflection = new ReflectionClass($sDashletClass);
|
||||
if (!$oReflection->isAbstract())
|
||||
{
|
||||
$aCallSpec = array($sDashletClass, 'CanCreateFromOQL');
|
||||
$bShorcutMode = call_user_func($aCallSpec);
|
||||
if ($bShorcutMode)
|
||||
{
|
||||
$aCallSpec = array($sDashletClass, 'GetInfo');
|
||||
$aInfo = call_user_func($aCallSpec);
|
||||
$aDashlets[$sDashletClass] = array('label' => $aInfo['label'], 'class' => $sDashletClass, 'icon' => $aInfo['icon']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$oSelectorField = new DesignerFormSelectorField('dashlet_class', Dict::S('UI:DashletCreation:DashletType'), '');
|
||||
$oForm->AddField($oSelectorField);
|
||||
foreach($aDashlets as $sDashletClass => $aDashletInfo)
|
||||
{
|
||||
$oSubForm = new DesignerForm();
|
||||
$oMetaModel = new ModelReflectionRuntime();
|
||||
$oDashlet = new $sDashletClass($oMetaModel, 0);
|
||||
$oDashlet->GetPropertiesFieldsFromOQL($oSubForm, $sOQL);
|
||||
|
||||
$oSelectorField->AddSubForm($oSubForm, $aDashletInfo['label'], $aDashletInfo['class']);
|
||||
}
|
||||
$oField = new DesignerBooleanField('open_editor', Dict::S('UI:DashletCreation:EditNow'), true);
|
||||
$oForm->AddField($oField);
|
||||
|
||||
return $oForm;
|
||||
}
|
||||
|
||||
public static function GetDashletCreationDlgFromOQL($oPage, $sOQL)
|
||||
{
|
||||
$oPage->add('<div id="dashlet_creation_dlg">');
|
||||
|
||||
$oForm = self::GetDashletCreationForm($sOQL);
|
||||
|
||||
$oForm->Render($oPage);
|
||||
$oPage->add('</div>');
|
||||
|
||||
$sDialogTitle = Dict::S('UI:DashletCreation:Title');
|
||||
$sOkButtonLabel = Dict::S('UI:Button:Ok');
|
||||
$sCancelButtonLabel = Dict::S('UI:Button:Cancel');
|
||||
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
$('#dashlet_creation_dlg').dialog({
|
||||
width: 400,
|
||||
modal: true,
|
||||
title: '$sDialogTitle',
|
||||
buttons: [
|
||||
{ text: "$sOkButtonLabel", click: function() {
|
||||
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 = 'add_dashlet';
|
||||
var me = $(this);
|
||||
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', oParams, function(data) {
|
||||
me.dialog( "close" );
|
||||
me.remove();
|
||||
$('body').append(data);
|
||||
});
|
||||
} },
|
||||
{ text: "$sCancelButtonLabel", click: function() {
|
||||
$(this).dialog( "close" ); $(this).remove();
|
||||
} },
|
||||
],
|
||||
close: function() { $(this).remove(); }
|
||||
});
|
||||
EOF
|
||||
);
|
||||
}
|
||||
}
|
||||
212
application/dashboardlayout.class.inc.php
Normal file
212
application/dashboardlayout.class.inc.php
Normal file
@@ -0,0 +1,212 @@
|
||||
<?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()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
abstract public function Render($oPage, $aDashlets, $bEditMode = false);
|
||||
|
||||
static public function GetInfo()
|
||||
{
|
||||
return array(
|
||||
'label' => '',
|
||||
'icon' => '',
|
||||
'description' => '',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class DashboardLayoutMultiCol extends DashboardLayout
|
||||
{
|
||||
protected $iNbCols;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->iNbCols = 1;
|
||||
}
|
||||
|
||||
protected function TrimCell($aDashlets)
|
||||
{
|
||||
$aKeys = array_reverse(array_keys($aDashlets));
|
||||
$idx = 0;
|
||||
$bNoVisibleFound = true;
|
||||
while($idx < count($aKeys) && $bNoVisibleFound)
|
||||
{
|
||||
$oDashlet = $aDashlets[$aKeys[$idx]];
|
||||
if ($oDashlet->IsVisible())
|
||||
{
|
||||
$bNoVisibleFound = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
unset($aDashlets[$aKeys[$idx]]);
|
||||
}
|
||||
$idx++;
|
||||
}
|
||||
return $aDashlets;
|
||||
}
|
||||
|
||||
protected function TrimCellsArray($aCells)
|
||||
{
|
||||
foreach($aCells as $key => $aDashlets)
|
||||
{
|
||||
$aCells[$key] = $this->TrimCell($aDashlets);
|
||||
}
|
||||
$aKeys = array_reverse(array_keys($aCells));
|
||||
$idx = 0;
|
||||
$bNoVisibleFound = true;
|
||||
while($idx < count($aKeys) && $bNoVisibleFound)
|
||||
{
|
||||
$aDashlets = $aCells[$aKeys[$idx]];
|
||||
if (count($aDashlets) > 0)
|
||||
{
|
||||
$bNoVisibleFound = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
unset($aCells[$aKeys[$idx]]);
|
||||
}
|
||||
$idx++;
|
||||
}
|
||||
return $aCells;
|
||||
|
||||
}
|
||||
|
||||
public function Render($oPage, $aCells, $bEditMode = false, $aExtraParams = array())
|
||||
{
|
||||
// Trim the list of cells to remove the invisible/empty ones at the end of the array
|
||||
$aCells = $this->TrimCellsArray($aCells);
|
||||
|
||||
$oPage->add('<table style="width:100%"><tbody>');
|
||||
$iCellIdx = 0;
|
||||
$fColSize = 100 / $this->iNbCols;
|
||||
$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++)
|
||||
{
|
||||
$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];
|
||||
if (count($aDashlets) > 0)
|
||||
{
|
||||
foreach($aDashlets as $oDashlet)
|
||||
{
|
||||
if ($oDashlet->IsVisible())
|
||||
{
|
||||
$oDashlet->DoRender($oPage, $bEditMode, true /* bEnclosingDiv */, $aExtraParams);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$oPage->add(' ');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$oPage->add(' ');
|
||||
}
|
||||
$oPage->add('</td>');
|
||||
$iCellIdx++;
|
||||
}
|
||||
$oPage->add('</tr>');
|
||||
}
|
||||
if ($bEditMode) // Add one row for extensibility
|
||||
{
|
||||
$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++)
|
||||
{
|
||||
$oPage->add("<td $sStyle>");
|
||||
$oPage->add(' ');
|
||||
$oPage->add('</td>');
|
||||
}
|
||||
$oPage->add('</tr>');
|
||||
}
|
||||
$oPage->add('</tbody></table>');
|
||||
}
|
||||
}
|
||||
|
||||
class DashboardLayoutOneCol extends DashboardLayoutMultiCol
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->iNbCols = 1;
|
||||
}
|
||||
static public function GetInfo()
|
||||
{
|
||||
return array(
|
||||
'label' => 'One Column',
|
||||
'icon' => 'images/layout_1col.png',
|
||||
'description' => '',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DashboardLayoutTwoCols extends DashboardLayoutMultiCol
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->iNbCols = 2;
|
||||
}
|
||||
static public function GetInfo()
|
||||
{
|
||||
return array(
|
||||
'label' => 'Two Columns',
|
||||
'icon' => 'images/layout_2col.png',
|
||||
'description' => '',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DashboardLayoutThreeCols extends DashboardLayoutMultiCol
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->iNbCols = 3;
|
||||
}
|
||||
static public function GetInfo()
|
||||
{
|
||||
return array(
|
||||
'label' => 'Two Columns',
|
||||
'icon' => 'images/layout_3col.png',
|
||||
'description' => '',
|
||||
);
|
||||
}
|
||||
}
|
||||
1626
application/dashlet.class.inc.php
Normal file
1626
application/dashlet.class.inc.php
Normal file
File diff suppressed because it is too large
Load Diff
908
application/datatable.class.inc.php
Normal file
908
application/datatable.class.inc.php
Normal file
@@ -0,0 +1,908 @@
|
||||
<?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/>
|
||||
/**
|
||||
* Data Table to display a set of objects in a tabular manner in HTML
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
class DataTable
|
||||
{
|
||||
protected $iListId; // Unique ID inside the web page
|
||||
protected $sTableId; // identifier for saving the settings (combined with the class aliases)
|
||||
protected $oSet; // The set of objects to display
|
||||
protected $aClassAliases; // The aliases (alias => class) inside the set
|
||||
protected $iNbObjects; // Total number of objects inthe set
|
||||
protected $bUseCustomSettings; // Whether or not the current display uses custom settings
|
||||
protected $oDefaultSettings; // the default settings for displaying such a list
|
||||
|
||||
/**
|
||||
* @param $iListId mixed Unique ID for this div/table in the page
|
||||
* @param $oSet DBObjectSet The set of data to display
|
||||
* @param $aClassAliases Hash The list of classes/aliases to be displayed in this set $sAlias => $sClassName
|
||||
* @param $sTableId mixed A string (or null) identifying this table in order to persist its settings
|
||||
*/
|
||||
public function __construct($iListId, $oSet, $aClassAliases, $sTableId = null)
|
||||
{
|
||||
$this->iListId = utils::GetSafeId($iListId); // Make a "safe" ID for jQuery
|
||||
$this->oSet = $oSet;
|
||||
$this->aClassAliases = $aClassAliases;
|
||||
$this->sTableId = $sTableId;
|
||||
$this->iNbObjects = $oSet->Count();
|
||||
$this->bUseCustomSettings = false;
|
||||
$this->oDefaultSettings = null;
|
||||
}
|
||||
|
||||
public function Display(WebPage $oPage, DataTableSettings $oSettings, $bActionsMenu, $sSelectMode, $bViewLink, $aExtraParams)
|
||||
{
|
||||
$this->oDefaultSettings = $oSettings;
|
||||
|
||||
// Identified tables can have their own specific settings
|
||||
$oCustomSettings = DataTableSettings::GetTableSettings($this->aClassAliases, $this->sTableId);
|
||||
|
||||
if ($oCustomSettings != null)
|
||||
{
|
||||
// Custom settings overload the default ones
|
||||
$this->bUseCustomSettings = true;
|
||||
if ($this->oDefaultSettings->iDefaultPageSize == 0)
|
||||
{
|
||||
$oCustomSettings->iDefaultPageSize = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$oCustomSettings = $oSettings;
|
||||
}
|
||||
|
||||
if ($oCustomSettings->iDefaultPageSize > 0)
|
||||
{
|
||||
$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
|
||||
$bToolkitMenu = false;
|
||||
}
|
||||
|
||||
return $this->GetAsHTML($oPage, $oCustomSettings->iDefaultPageSize, $oCustomSettings->iDefaultPageSize, 0, $oCustomSettings->aColumns, $bActionsMenu, $bToolkitMenu, $sSelectMode, $bViewLink, $aExtraParams);
|
||||
}
|
||||
|
||||
public function GetAsHTML(WebPage $oPage, $iPageSize, $iDefaultPageSize, $iPageIndex, $aColumns, $bActionsMenu, $bToolkitMenu, $sSelectMode, $bViewLink, $aExtraParams)
|
||||
{
|
||||
$sObjectsCount = $this->GetObjectCount($oPage, $sSelectMode);
|
||||
$sPager = $this->GetPager($oPage, $iPageSize, $iDefaultPageSize, $iPageIndex);
|
||||
$sActionsMenu = '';
|
||||
$sToolkitMenu = '';
|
||||
if ($bActionsMenu)
|
||||
{
|
||||
$sActionsMenu = $this->GetActionsMenu($oPage, $aExtraParams);
|
||||
}
|
||||
if ($bToolkitMenu)
|
||||
{
|
||||
$sToolkitMenu = $this->GetToolkitMenu($oPage, $aExtraParams);
|
||||
}
|
||||
$sDataTable = $this->GetHTMLTable($oPage, $aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams);
|
||||
$sConfigDlg = $this->GetTableConfigDlg($oPage, $aColumns, $bViewLink, $iDefaultPageSize);
|
||||
|
||||
$sHtml = "<table id=\"datatable_{$this->iListId}\" class=\"datatable\">";
|
||||
$sHtml .= "<tr><td>";
|
||||
$sHtml .= "<table style=\"width:100%;\">";
|
||||
$sHtml .= "<tr><td class=\"pagination_container\">$sObjectsCount</td><td class=\"menucontainer\">$sToolkitMenu $sActionsMenu</td></tr>";
|
||||
$sHtml .= "<tr>$sPager</tr>";
|
||||
$sHtml .= "</table>";
|
||||
$sHtml .= "</td></tr>";
|
||||
$sHtml .= "<tr><td class=\"datacontents\">$sDataTable</td></tr>";
|
||||
$sHtml .= "</table>\n";
|
||||
$oPage->add_at_the_end($sConfigDlg);
|
||||
|
||||
$aOptions = array(
|
||||
'sPersistentId' => '',
|
||||
'sFilter' => $this->oSet->GetFilter()->serialize(),
|
||||
'oColumns' => $aColumns,
|
||||
'sSelectMode' => $sSelectMode,
|
||||
'sViewLink' => ($bViewLink ? 'true' : 'false'),
|
||||
'iNbObjects' => $this->iNbObjects,
|
||||
'iDefaultPageSize' => $iDefaultPageSize,
|
||||
'iPageSize' => $iPageSize,
|
||||
'iPageIndex' => $iPageIndex,
|
||||
'oClassAliases' => $this->aClassAliases,
|
||||
'sTableId' => $this->sTableId,
|
||||
'oExtraParams' => $aExtraParams,
|
||||
'sRenderUrl' => utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php',
|
||||
'oRenderParameters' => array('str' => ''), // Forces JSON to encode this as a object...
|
||||
'oDefaultSettings' => array('str' => ''), // Forces JSON to encode this as a object...
|
||||
'oLabels' => array('moveup' => Dict::S('UI:Button:MoveUp'), 'movedown' => Dict::S('UI:Button:MoveDown')),
|
||||
);
|
||||
if($this->oDefaultSettings != null)
|
||||
{
|
||||
$aOptions['oDefaultSettings'] = $this->GetAsHash($this->oDefaultSettings);
|
||||
}
|
||||
$sJSOptions = json_encode($aOptions);
|
||||
$oPage->add_ready_script("$('#datatable_{$this->iListId}').datatable($sJSOptions);");
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* When refreshing the body of a paginated table, get the rows of the table (inside the TBODY)
|
||||
* return string The HTML rows to insert inside the <tbody> node
|
||||
*/
|
||||
public function GetAsHTMLTableRows(WebPage $oPage, $iPageSize, $aColumns, $sSelectMode, $bViewLink, $aExtraParams)
|
||||
{
|
||||
$aAttribs = $this->GetHTMLTableConfig($aColumns, $sSelectMode, $bViewLink);
|
||||
$aValues = $this->GetHTMLTableValues($aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams);
|
||||
|
||||
$sHtml = '';
|
||||
foreach($aValues as $aRow)
|
||||
{
|
||||
$sHtml .= $oPage->GetTableRow($aRow, $aAttribs);
|
||||
}
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
protected function GetObjectCount(WebPage $oPage, $sSelectMode)
|
||||
{
|
||||
if (($sSelectMode == 'single') || ($sSelectMode == 'multiple'))
|
||||
{
|
||||
$sHtml = '<div class="pagination_objcount">'.Dict::Format('UI:Pagination:HeaderSelection', '<span id="total">'.$this->iNbObjects.'</span>', '<span class="selectedCount">0</span>').'</div>';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sHtml = '<div class="pagination_objcount">'.Dict::Format('UI:Pagination:HeaderNoSelection', '<span id="total">'.$this->iNbObjects.'</span>').'</div>';
|
||||
}
|
||||
return $sHtml;
|
||||
}
|
||||
protected function GetPager(WebPage $oPage, $iPageSize, $iDefaultPageSize, $iPageIndex)
|
||||
{
|
||||
$sHtml = '';
|
||||
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
|
||||
{
|
||||
$sPagerStyle = '';
|
||||
}
|
||||
|
||||
$sCombo = '<select class="pagesize">';
|
||||
for($iPage = 1; $iPage < 5; $iPage++)
|
||||
{
|
||||
$iNbItems = $iPage * $iDefaultPageSize;
|
||||
$sSelected = ($iNbItems == $iPageSize) ? 'selected="selected"' : '';
|
||||
$sCombo .= "<option $sSelected value=\"$iNbItems\">$iNbItems</option>";
|
||||
}
|
||||
$sSelected = ($iPageSize < 1) ? 'selected="selected"' : '';
|
||||
$sCombo .= "<option $sSelected value=\"-1\">".Dict::S('UI:Pagination:All')."</option>";
|
||||
$sCombo .= '</select>';
|
||||
|
||||
$sPages = Dict::S('UI:Pagination:PagesLabel');
|
||||
$sPageSizeCombo = Dict::Format('UI:Pagination:PageSize', $sCombo);
|
||||
|
||||
$iNbPages = ($iPageSize < 1) ? 1 : ceil($this->iNbObjects / $iPageSize);
|
||||
if ($iNbPages == 1)
|
||||
{
|
||||
// No need to display the pager
|
||||
$sPagerStyle = 'style="display:none"';
|
||||
}
|
||||
$aPagesToDisplay = array();
|
||||
for($idx = 0; $idx <= min(4, $iNbPages-1); $idx++)
|
||||
{
|
||||
if ($idx == 0)
|
||||
{
|
||||
$aPagesToDisplay[$idx] = '<span page="0" class="curr_page">1</span>';
|
||||
}
|
||||
else
|
||||
{
|
||||
$aPagesToDisplay[$idx] = "<span id=\"gotopage_$idx\" class=\"gotopage\" page=\"$idx\">".(1+$idx)."</span>";
|
||||
}
|
||||
}
|
||||
$iLastPageIdx = $iNbPages - 1;
|
||||
if (!isset($aPagesToDisplay[$iLastPageIdx]))
|
||||
{
|
||||
unset($aPagesToDisplay[$idx - 1]); // remove the last page added to make room for the very last page
|
||||
$aPagesToDisplay[$iLastPageIdx] = "<span id=\"gotopage_$iLastPageIdx\" class=\"gotopage\" page=\"$iLastPageIdx\">... $iNbPages</span>";
|
||||
}
|
||||
$sPagesLinks = implode('', $aPagesToDisplay);
|
||||
$sPagesList = '['.implode(',', array_keys($aPagesToDisplay)).']';
|
||||
|
||||
$sSelectionMode = ($iNbPages == 1) ? '' : 'positive';
|
||||
$sHtml =
|
||||
<<<EOF
|
||||
<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>
|
||||
<td><img src="../images/prev.png" class="prev"/></td>
|
||||
<td><span id="index">$sPagesLinks</span></td>
|
||||
<td><img src="../images/next.png" class="next"/></td>
|
||||
<td><img src="../images/last.png" class="last"/></td>
|
||||
<td>$sPageSizeCombo</td>
|
||||
<td><span id="loading"> </span><input type="hidden" name="selectionMode" value="$sSelectionMode"></input>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
EOF;
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
protected function GetActionsMenu(WebPage $oPage, $aExtraParams)
|
||||
{
|
||||
$oMenuBlock = new MenuBlock($this->oSet->GetFilter(), 'list');
|
||||
|
||||
$sHtml = $oMenuBlock->GetRenderContent($oPage, $aExtraParams, $this->iListId);
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
protected function GetToolkitMenu(WebPage $oPage, $aExtraParams)
|
||||
{
|
||||
$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, $this->sTableId, $this->iListId);
|
||||
$this->oSet->Rewind();
|
||||
$sHtml .= $oPage->RenderPopupMenuItems($aActions);
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
protected function GetTableConfigDlg(WebPage $oPage, $aColumns, $bViewLink, $iDefaultPageSize)
|
||||
{
|
||||
$sHtml = "<div id=\"datatable_dlg_{$this->iListId}\" style=\"display: none;\">";
|
||||
$sHtml .= "<form onsubmit=\"return false\">";
|
||||
$sChecked = ($this->bUseCustomSettings) ? '' : 'checked';
|
||||
$sHtml .= "<p><input id=\"dtbl_dlg_settings_{$this->iListId}\" type=\"radio\" name=\"settings\" $sChecked value=\"defaults\"><label for=\"dtbl_dlg_settings_{$this->iListId}\"> ".Dict::S('UI:UseDefaultSettings').'</label></p>';
|
||||
$sHtml .= "<fieldset>";
|
||||
$sChecked = ($this->bUseCustomSettings) ? 'checked': '';
|
||||
$sHtml .= "<legend class=\"transparent\"><input id=\"dtbl_dlg_specific_{$this->iListId}\" type=\"radio\" class=\"specific_settings\" name=\"settings\" $sChecked value=\"specific\"><label for=\"dtbl_dlg_specific_{$this->iListId}\"> ".Dict::S('UI:UseSpecificSettings')."</label></legend>";
|
||||
$sHtml .= Dict::S('UI:ColumnsAndSortOrder').'<br/><ul class="sortable_field_list" id="sfl_'.$this->iListId.'"></ul>';
|
||||
|
||||
$sHtml .= '<p>'.Dict::Format('UI:Display_X_ItemsPerPage', '<input type="text" size="4" name="page_size" value="'.$iDefaultPageSize.'">').'</p>';
|
||||
$sHtml .= "</fieldset>";
|
||||
$sHtml .= "<fieldset>";
|
||||
$sSaveChecked = ($this->sTableId != null) ? 'checked' : '';
|
||||
$sCustomDisabled = ($this->sTableId == null) ? 'disabled="disabled" stay-disabled="true" ' : '';
|
||||
$sCustomChecked = ($this->sTableId != null) ? 'checked' : '';
|
||||
$sGenericChecked = ($this->sTableId == null) ? 'checked' : '';
|
||||
$sHtml .= "<legend class=\"transparent\"><input id=\"dtbl_dlg_save_{$this->iListId}\" type=\"checkbox\" $sSaveChecked name=\"save_settings\"><label for=\"dtbl_dlg_save_{$this->iListId}\"> ".Dict::S('UI:UseSavetheSettings')."</label></legend>";
|
||||
$sHtml .= "<p><input id=\"dtbl_dlg_this_list_{$this->iListId}\" type=\"radio\" name=\"scope\" $sCustomChecked $sCustomDisabled value=\"this_list\"><label for=\"dtbl_dlg_this_list_{$this->iListId}\"> ".Dict::S('UI:OnlyForThisList').'</label> ';
|
||||
$sHtml .= "<input id=\"dtbl_dlg_all_{$this->iListId}\" type=\"radio\" name=\"scope\" $sGenericChecked value=\"defaults\"><label for=\"dtbl_dlg_all_{$this->iListId}\"> ".Dict::S('UI:ForAllLists').'</label></p>';
|
||||
$sHtml .= "</fieldset>";
|
||||
$sHtml .= '<table style="width:100%"><tr><td style="text-align:center;">';
|
||||
$sHtml .= '<button type="button" onclick="$(\'#datatable_'.$this->iListId.'\').datatable(\'onDlgCancel\'); $(\'#datatable_dlg_'.$this->iListId.'\').dialog(\'close\')">'.Dict::S('UI:Button:Cancel').'</button>';
|
||||
$sHtml .= '</td><td style="text-align:center;">';
|
||||
$sHtml .= '<button type="submit" onclick="$(\'#datatable_'.$this->iListId.'\').datatable(\'onDlgOk\');$(\'#datatable_dlg_'.$this->iListId.'\').dialog(\'close\');">'.Dict::S('UI:Button:Ok').'</button>';
|
||||
$sHtml .= '</td></tr></table>';
|
||||
$sHtml .= "</form>";
|
||||
$sHtml .= "</div>";
|
||||
|
||||
$sDlgTitle = addslashes(Dict::S('UI:ListConfigurationTitle'));
|
||||
$oPage->add_ready_script("$('#datatable_dlg_{$this->iListId}').dialog({autoOpen: false, title: '$sDlgTitle', width: 500, close: function() { $('#datatable_{$this->iListId}').datatable('onDlgCancel'); } });");
|
||||
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
public function GetAsHash($oSetting)
|
||||
{
|
||||
$aSettings = array('iDefaultPageSize' => $oSetting->iDefaultPageSize, 'oColumns' => $oSetting->aColumns);
|
||||
return $aSettings;
|
||||
}
|
||||
|
||||
protected function GetHTMLTableConfig($aColumns, $sSelectMode, $bViewLink)
|
||||
{
|
||||
$aAttribs = array();
|
||||
if ($sSelectMode == 'multiple')
|
||||
{
|
||||
$aAttribs['form::select'] = array('label' => "<input type=\"checkbox\" onClick=\"CheckAll('.selectList{$this->iListId}:not(:disabled)', this.checked);\" class=\"checkAll\"></input>", 'description' => Dict::S('UI:SelectAllToggle+'));
|
||||
}
|
||||
else if ($sSelectMode == 'single')
|
||||
{
|
||||
$aAttribs['form::select'] = array('label' => "", 'description' => '');
|
||||
}
|
||||
|
||||
foreach($this->aClassAliases as $sAlias => $sClassName)
|
||||
{
|
||||
foreach($aColumns[$sAlias] as $sAttCode => $aData)
|
||||
{
|
||||
if ($aData['checked'])
|
||||
{
|
||||
if ($sAttCode == '_key_')
|
||||
{
|
||||
$aAttribs['key_'.$sAlias] = array('label' => MetaModel::GetName($sClassName), 'description' => '');
|
||||
}
|
||||
else
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClassName, $sAttCode);
|
||||
$aAttribs[$sAttCode.'_'.$sAlias] = array('label' => MetaModel::GetLabel($sClassName, $sAttCode), 'description' => $oAttDef->GetOrderByHint());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $aAttribs;
|
||||
}
|
||||
|
||||
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;
|
||||
while (($aObjects = $this->oSet->FetchAssoc()) && ($iMaxObjects != 0))
|
||||
{
|
||||
$bFirstObject = true;
|
||||
$aRow = array();
|
||||
foreach($this->aClassAliases as $sAlias => $sClassName)
|
||||
{
|
||||
if (is_object($aObjects[$sAlias]))
|
||||
{
|
||||
$sHilightClass = $aObjects[$sAlias]->GetHilightClass();
|
||||
if ($sHilightClass != '')
|
||||
{
|
||||
$aRow['@class'] = $sHilightClass;
|
||||
}
|
||||
if ((($sSelectMode == 'single') || ($sSelectMode == 'multiple')) && $bFirstObject)
|
||||
{
|
||||
if (array_key_exists('selection_enabled', $aExtraParams) && isset($aExtraParams['selection_enabled'][$aObjects[$sAlias]->GetKey()]))
|
||||
{
|
||||
$sDisabled = ($aExtraParams['selection_enabled'][$aObjects[$sAlias]->GetKey()]) ? '' : ' disabled="disabled"';
|
||||
}
|
||||
else
|
||||
{
|
||||
$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] = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$bFirstObject = false;
|
||||
}
|
||||
$aValues[] = $aRow;
|
||||
$iMaxObjects--;
|
||||
}
|
||||
return $aValues;
|
||||
}
|
||||
|
||||
public function GetHTMLTable(WebPage $oPage, $aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams)
|
||||
{
|
||||
$iNbPages = ($iPageSize < 1) ? 1 : ceil($this->iNbObjects / $iPageSize);
|
||||
if ($iPageSize < 1)
|
||||
{
|
||||
$iPageSize = -1; // convention: no pagination
|
||||
}
|
||||
$aAttribs = $this->GetHTMLTableConfig($aColumns, $sSelectMode, $bViewLink);
|
||||
|
||||
$aValues = $this->GetHTMLTableValues($aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams);
|
||||
|
||||
$sHtml = '<table class="listContainer">';
|
||||
|
||||
foreach($this->oSet->GetFilter()->GetInternalParams() as $sName => $sValue)
|
||||
{
|
||||
$aExtraParams['query_params'][$sName] = $sValue;
|
||||
}
|
||||
|
||||
$sHtml .= "<tr><td>";
|
||||
$sHtml .= $oPage->GetTable($aAttribs, $aValues);
|
||||
$sHtml .= '</td></tr>';
|
||||
$sHtml .= '</table>';
|
||||
$iCount = $this->iNbObjects;
|
||||
|
||||
$aArgs = $this->oSet->GetArgs();
|
||||
$sExtraParams = addslashes(str_replace('"', "'", json_encode(array_merge($aExtraParams, $aArgs)))); // JSON encode, change the style of the quotes and escape them
|
||||
$sSelectModeJS = '';
|
||||
$sHeaders = '';
|
||||
if (($sSelectMode == 'single') || ($sSelectMode == 'multiple'))
|
||||
{
|
||||
$sSelectModeJS = $sSelectMode;
|
||||
$sHeaders = 'headers: { 0: {sorter: false}},';
|
||||
}
|
||||
$sDisplayKey = ($bViewLink) ? 'true' : 'false';
|
||||
// Protect against duplicate elements in the Zlist
|
||||
$aUniqueOrderedList = array();
|
||||
foreach($this->aClassAliases as $sAlias => $sClassName)
|
||||
{
|
||||
foreach($aColumns[$sAlias] as $sAttCode => $aData)
|
||||
{
|
||||
if ($aData['checked'])
|
||||
{
|
||||
$aUniqueOrderedList[$sAttCode] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
$aUniqueOrderedList = array_keys($aUniqueOrderedList);
|
||||
$sJSColumns = json_encode($aColumns);
|
||||
$sJSClassAliases = json_encode($this->aClassAliases);
|
||||
$sCssCount = isset($aExtraParams['cssCount']) ? ", cssCount: '{$aExtraParams['cssCount']}'" : '';
|
||||
$this->oSet->ApplyParameters();
|
||||
// Display the actual sort order of the table
|
||||
$aRealSortOrder = $this->oSet->GetRealSortOrder();
|
||||
$aDefaultSort = array();
|
||||
$iColOffset = 0;
|
||||
if (($sSelectMode == 'single') || ($sSelectMode == 'multiple'))
|
||||
{
|
||||
$iColOffset += 1;
|
||||
}
|
||||
if ($bViewLink)
|
||||
{
|
||||
// $iColOffset += 1;
|
||||
}
|
||||
foreach($aRealSortOrder as $sColCode => $bAscending)
|
||||
{
|
||||
$iPos = array_search($sColCode, $aUniqueOrderedList);
|
||||
if ($iPos !== false)
|
||||
{
|
||||
$aDefaultSort[] = "[".($iColOffset+$iPos).",".($bAscending ? '0' : '1')."]";
|
||||
}
|
||||
else if (($iPos = array_search(preg_replace('/_friendlyname$/', '', $sColCode), $aUniqueOrderedList)) !== false)
|
||||
{
|
||||
// if sorted on the friendly name of an external key, then consider it sorted on the column that shows the links
|
||||
$aDefaultSort[] = "[".($iColOffset+$iPos).",".($bAscending ? '0' : '1')."]";
|
||||
}
|
||||
else if($sColCode == 'friendlyname' && $bViewLink)
|
||||
{
|
||||
$aDefaultSort[] = "[".($iColOffset).",".($bAscending ? '0' : '1')."]";
|
||||
}
|
||||
}
|
||||
$sFakeSortList = '';
|
||||
if (count($aDefaultSort) > 0)
|
||||
{
|
||||
$sFakeSortList = '['.implode(',', $aDefaultSort).']';
|
||||
}
|
||||
$sOQL = addslashes($this->oSet->GetFilter()->serialize());
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
var oTable = $('#{$this->iListId} table.listResults');
|
||||
oTable.tableHover();
|
||||
oTable.tablesorter( { $sHeaders widgets: ['myZebra', 'truncatedList']} ).tablesorterPager({container: $('#pager{$this->iListId}'), totalRows:$iCount, size: $iPageSize, filter: '$sOQL', extra_params: '$sExtraParams', select_mode: '$sSelectModeJS', displayKey: $sDisplayKey, columns: $sJSColumns, class_aliases: $sJSClassAliases $sCssCount});
|
||||
EOF
|
||||
);
|
||||
if ($sFakeSortList != '')
|
||||
{
|
||||
$oPage->add_ready_script("oTable.trigger(\"fakesorton\", [$sFakeSortList]);");
|
||||
}
|
||||
//if ($iNbPages == 1)
|
||||
if (false)
|
||||
{
|
||||
if (isset($aExtraParams['cssCount']))
|
||||
{
|
||||
$sCssCount = $aExtraParams['cssCount'];
|
||||
if ($sSelectMode == 'single')
|
||||
{
|
||||
$sSelectSelector = ":radio[name^=selectObj]";
|
||||
}
|
||||
else if ($sSelectMode == 'multiple')
|
||||
{
|
||||
$sSelectSelector = ":checkbox[name^=selectObj]";
|
||||
}
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
$('#{$this->iListId} table.listResults $sSelectSelector').change(function() {
|
||||
var c = $('{$sCssCount}');
|
||||
var v = $('#{$this->iListId} table.listResults $sSelectSelector:checked').length;
|
||||
c.val(v);
|
||||
$('#{$this->iListId} .selectedCount').text(v);
|
||||
c.trigger('change');
|
||||
});
|
||||
EOF
|
||||
);
|
||||
}
|
||||
}
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
public function UpdatePager(WebPage $oPage, $iDefaultPageSize, $iStart)
|
||||
{
|
||||
$iPageSize = ($iDefaultPageSize < 1) ? 1 : $iDefaultPageSize;
|
||||
$iPageIndex = 1 + floor($iStart / $iPageSize);
|
||||
$sHtml = $this->GetPager($oPage, $iPageSize, $iDefaultPageSize, $iPageIndex);
|
||||
$oPage->add_ready_script("$('#pager{$this->iListId}').html('".str_replace("\n", ' ', addslashes($sHtml))."');");
|
||||
if ($iDefaultPageSize < 1)
|
||||
{
|
||||
$oPage->add_ready_script("$('#pager{$this->iListId}').parent().hide()");
|
||||
}
|
||||
else
|
||||
{
|
||||
$oPage->add_ready_script("$('#pager{$this->iListId}').parent().show()");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DataTableSettings implements Serializable
|
||||
{
|
||||
public $aClassAliases;
|
||||
public $sTableId;
|
||||
public $iDefaultPageSize;
|
||||
public $aColumns;
|
||||
|
||||
|
||||
public function __construct($aClassAliases, $sTableId = null)
|
||||
{
|
||||
$this->aClassAliases = $aClassAliases;
|
||||
$this->sTableId = $sTableId;
|
||||
$this->iDefaultPageSize = 10;
|
||||
$this->aColumns = array();
|
||||
}
|
||||
|
||||
protected function Init($iDefaultPageSize, $aSortOrder, $aColumns)
|
||||
{
|
||||
$this->iDefaultPageSize = $iDefaultPageSize;
|
||||
$this->aColumns = $aColumns;
|
||||
$this->FixVisibleColumns();
|
||||
}
|
||||
|
||||
public function serialize()
|
||||
{
|
||||
// Save only the 'visible' columns
|
||||
$aColumns = array();
|
||||
foreach($this->aClassAliases as $sAlias => $sClass)
|
||||
{
|
||||
$aColumns[$sAlias] = array();
|
||||
foreach($this->aColumns[$sAlias] as $sAttCode => $aData)
|
||||
{
|
||||
unset($aData['label']); // Don't save the display name
|
||||
unset($aData['alias']); // Don't save the alias (redundant)
|
||||
unset($aData['code']); // Don't save the code (redundant)
|
||||
if ($aData['checked'])
|
||||
{
|
||||
$aColumns[$sAlias][$sAttCode] = $aData;
|
||||
}
|
||||
}
|
||||
}
|
||||
return serialize(
|
||||
array(
|
||||
'iDefaultPageSize' => $this->iDefaultPageSize,
|
||||
'aColumns' => $aColumns,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function unserialize($sData)
|
||||
{
|
||||
$aData = unserialize($sData);
|
||||
$this->iDefaultPageSize = $aData['iDefaultPageSize'];
|
||||
$this->aColumns = $aData['aColumns'];
|
||||
foreach($this->aClassAliases as $sAlias => $sClass)
|
||||
{
|
||||
foreach($this->aColumns[$sAlias] as $sAttCode => $aData)
|
||||
{
|
||||
$aFieldData = false;
|
||||
if ($sAttCode == '_key_')
|
||||
{
|
||||
$aFieldData = $this->GetFieldData($sAlias, $sAttCode, null, true /* bChecked */, $aData['sort']);
|
||||
}
|
||||
else if (MetaModel::isValidAttCode($sClass, $sAttCode))
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
$aFieldData = $this->GetFieldData($sAlias, $sAttCode, $oAttDef, true /* bChecked */, $aData['sort']);
|
||||
}
|
||||
|
||||
if ($aFieldData)
|
||||
{
|
||||
$this->aColumns[$sAlias][$sAttCode] = $aFieldData;
|
||||
}
|
||||
else
|
||||
{
|
||||
unset($this->aColumns[$sAlias][$sAttCode]);
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->FixVisibleColumns();
|
||||
}
|
||||
|
||||
static public function GetDataModelSettings($aClassAliases, $bViewLink, $aDefaultLists)
|
||||
{
|
||||
$oSettings = new DataTableSettings($aClassAliases);
|
||||
// Retrieve the class specific settings for each class/alias based on the 'list' ZList
|
||||
//TODO let the caller pass some other default settings (another Zlist, extre fields...)
|
||||
$aColumns = array();
|
||||
foreach($aClassAliases as $sAlias => $sClass)
|
||||
{
|
||||
if ($aDefaultLists == null)
|
||||
{
|
||||
$aList = cmdbAbstract::FlattenZList(MetaModel::GetZListItems($sClass, 'list'));
|
||||
}
|
||||
else
|
||||
{
|
||||
$aList = $aDefaultLists[$sAlias];
|
||||
}
|
||||
|
||||
$aSortOrder = MetaModel::GetOrderByDefault($sClass);
|
||||
if ($bViewLink)
|
||||
{
|
||||
$sSort = 'none';
|
||||
if(array_key_exists('friendlyname', $aSortOrder))
|
||||
{
|
||||
$sSort = $aSortOrder['friendlyname'] ? 'asc' : 'desc';
|
||||
}
|
||||
$aColumns[$sAlias]['_key_'] = $oSettings->GetFieldData($sAlias, '_key_', null, true /* bChecked */, $sSort);
|
||||
}
|
||||
foreach($aList as $sAttCode)
|
||||
{
|
||||
$sSort = 'none';
|
||||
if(array_key_exists($sAttCode, $aSortOrder))
|
||||
{
|
||||
$sSort = $aSortOrder[$sAttCode] ? 'asc' : 'desc';
|
||||
}
|
||||
$oAttDef = Metamodel::GetAttributeDef($sClass, $sAttCode);
|
||||
$aFieldData = $oSettings->GetFieldData($sAlias, $sAttCode, $oAttDef, true /* bChecked */, $sSort);
|
||||
if ($aFieldData) $aColumns[$sAlias][$sAttCode] = $aFieldData;
|
||||
}
|
||||
}
|
||||
$iDefaultPageSize = appUserPreferences::GetPref('default_page_size', MetaModel::GetConfig()->GetMinDisplayLimit());
|
||||
$oSettings->Init($iDefaultPageSize, $aSortOrder, $aColumns);
|
||||
return $oSettings;
|
||||
}
|
||||
|
||||
protected function FixVisibleColumns()
|
||||
{
|
||||
foreach($this->aClassAliases as $sAlias => $sClass)
|
||||
{
|
||||
foreach($this->aColumns[$sAlias] as $sAttCode => $aData)
|
||||
{
|
||||
// Remove non-existent columns
|
||||
// TODO: check if the existing ones are still valid (in case their type changed)
|
||||
if (($sAttCode != '_key_') && (!MetaModel::IsValidAttCode($sClass, $sAttCode)))
|
||||
{
|
||||
unset($this->aColumns[$sAlias][$sAttCode]);
|
||||
}
|
||||
}
|
||||
$aList = MetaModel::ListAttributeDefs($sClass);
|
||||
|
||||
// Add the other (non visible ones), sorted in alphabetical order
|
||||
$aTempData = array();
|
||||
foreach($aList as $sAttCode => $oAttDef)
|
||||
{
|
||||
if ( (!array_key_exists($sAttCode, $this->aColumns[$sAlias])) && (!$oAttDef instanceof AttributeLinkSet))
|
||||
{
|
||||
$aFieldData = $this->GetFieldData($sAlias, $sAttCode, $oAttDef, false /* bChecked */, 'none');
|
||||
if ($aFieldData) $aTempData[$aFieldData['label']] = $aFieldData;
|
||||
}
|
||||
}
|
||||
ksort($aTempData);
|
||||
foreach($aTempData as $sLabel => $aFieldData)
|
||||
{
|
||||
$this->aColumns[$sAlias][$aFieldData['code']] = $aFieldData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static public function GetTableSettings($aClassAliases, $sTableId = null, $bOnlyOnTable = false)
|
||||
{
|
||||
$pref = null;
|
||||
$oSettings = new DataTableSettings($aClassAliases, $sTableId);
|
||||
|
||||
if ($sTableId != null)
|
||||
{
|
||||
// An identified table, let's fetch its own settings (if any)
|
||||
$pref = appUserPreferences::GetPref($oSettings->GetPrefsKey($sTableId), null);
|
||||
}
|
||||
|
||||
if ($pref == 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
|
||||
return null;
|
||||
}
|
||||
}
|
||||
$oSettings->unserialize($pref);
|
||||
|
||||
return $oSettings;
|
||||
}
|
||||
|
||||
public function GetSortOrder()
|
||||
{
|
||||
$aSortOrder = array();
|
||||
foreach($this->aColumns as $sAlias => $aColumns)
|
||||
{
|
||||
foreach($aColumns as $aColumn)
|
||||
{
|
||||
if ($aColumn['sort'] != 'none')
|
||||
{
|
||||
$sCode = ($aColumn['code'] == '_key_') ? 'friendlyname' : $aColumn['code'];
|
||||
$aSortOrder[$sCode] = ($aColumn['sort']=='asc'); // true for ascending, false for descending
|
||||
}
|
||||
}
|
||||
break; // TODO: For now the Set object supports only sorting on the first class of the set
|
||||
}
|
||||
return $aSortOrder;
|
||||
}
|
||||
|
||||
public function Save($sTargetTableId = null)
|
||||
{
|
||||
$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($sSaveId), $sSettings);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function SaveAsDefault()
|
||||
{
|
||||
$sSettings = $this->serialize();
|
||||
appUserPreferences::SetPref($this->GetPrefsKey(null), $sSettings);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clear the preferences for this particular table
|
||||
* @param $bResetAll boolean If true,the settings for all tables of the same class(es)/alias(es) are reset
|
||||
*/
|
||||
public function ResetToDefault($bResetAll)
|
||||
{
|
||||
if (($this->sTableId == null) && (!$bResetAll)) return false; // Cannot reset, the table is not identified, use force $bResetAll instead
|
||||
if ($bResetAll)
|
||||
{
|
||||
// Turn the key into a suitable PCRE pattern
|
||||
$sKey = $this->GetPrefsKey(null);
|
||||
$sPattern = str_replace(array('|'), array('\\|'), $sKey); // escape the | character
|
||||
$sPattern = '#^'.str_replace(array('*'), array('.*'), $sPattern).'$#'; // Don't use slash as the delimiter since it's used in our key to delimit aliases
|
||||
appUserPreferences::UnsetPref($sPattern, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
appUserPreferences::UnsetPref($this->GetPrefsKey($this->sTableId), false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function GetPrefsKey($sTableId = null)
|
||||
{
|
||||
if ($sTableId == null) $sTableId = '*';
|
||||
$aKeys = array();
|
||||
foreach($this->aClassAliases as $sAlias => $sClass)
|
||||
{
|
||||
$aKeys[] = $sAlias.'-'.$sClass;
|
||||
}
|
||||
return implode('/', $aKeys).'|'.$sTableId;
|
||||
}
|
||||
|
||||
protected function GetFieldData($sAlias, $sAttCode, $oAttDef, $bChecked, $sSort)
|
||||
{
|
||||
$ret = false;
|
||||
if ($sAttCode == '_key_')
|
||||
{
|
||||
$sLabel = Dict::Format('UI:ExtKey_AsLink', MetaModel::GetName($this->aClassAliases[$sAlias]));
|
||||
$ret = array(
|
||||
'label' => $sLabel,
|
||||
'checked' => true,
|
||||
'disabled' => true,
|
||||
'alias' => $sAlias,
|
||||
'code' => $sAttCode,
|
||||
'sort' => $sSort,
|
||||
);
|
||||
}
|
||||
else if (!$oAttDef->IsLinkSet())
|
||||
{
|
||||
$sLabel = $oAttDef->GetLabel();
|
||||
if ($oAttDef->IsExternalKey())
|
||||
{
|
||||
$sLabel = Dict::Format('UI:ExtKey_AsLink', $oAttDef->GetLabel());
|
||||
}
|
||||
else if ($oAttDef->IsExternalField())
|
||||
{
|
||||
$oExtAttDef = $oAttDef->GetExtAttDef();
|
||||
$sLabel = Dict::Format('UI:ExtField_AsRemoteField', $oAttDef->GetLabel(), $oExtAttDef->GetLabel());
|
||||
}
|
||||
elseif ($oAttDef instanceof AttributeFriendlyName)
|
||||
{
|
||||
$sLabel = Dict::Format('UI:ExtKey_AsFriendlyName', $oAttDef->GetLabel());
|
||||
}
|
||||
$ret = array(
|
||||
'label' => $sLabel,
|
||||
'checked' => $bChecked,
|
||||
'disabled' => false,
|
||||
'alias' => $sAlias,
|
||||
'code' => $sAttCode,
|
||||
'sort' => $sSort,
|
||||
);
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
536
application/excelexporter.class.inc.php
Normal file
536
application/excelexporter.class.inc.php
Normal file
@@ -0,0 +1,536 @@
|
||||
<?php
|
||||
require_once('xlsxwriter.class.php');
|
||||
|
||||
class ExcelExporter
|
||||
{
|
||||
protected $sToken;
|
||||
protected $aStatistics;
|
||||
protected $sState;
|
||||
protected $fStartTime;
|
||||
protected $oSearch;
|
||||
protected $aObjectsIDs;
|
||||
protected $aTableHeaders;
|
||||
protected $aAuthorizedClasses;
|
||||
protected $iChunkSize = 1000;
|
||||
protected $iPosition;
|
||||
protected $sOutputFilePath;
|
||||
protected $bAdvancedMode;
|
||||
|
||||
public function __construct($sToken = null)
|
||||
{
|
||||
$this->aStatistics = array(
|
||||
'objects_count' => 0,
|
||||
'total_duration' => 0,
|
||||
'data_retrieval_duration' => 0,
|
||||
'excel_build_duration' => 0,
|
||||
'excel_write_duration' => 0,
|
||||
'peak_memory_usage' => 0,
|
||||
);
|
||||
$this->fStartTime = microtime(true);
|
||||
$this->oSearch = null;
|
||||
|
||||
$this->sState = 'new';
|
||||
$this->aObjectsIDs = array();
|
||||
$this->iPosition = 0;
|
||||
$this->aAuthorizedClasses = null;
|
||||
$this->aTableHeaders = null;
|
||||
$this->sOutputFilePath = null;
|
||||
$this->bAdvancedMode = false;
|
||||
$this->CheckDataDir();
|
||||
if ($sToken == null)
|
||||
{
|
||||
$this->sToken = $this->GetNewToken();
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->sToken = $sToken;
|
||||
$this->ReloadState();
|
||||
}
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
if (($this->sState != 'done') && ($this->sState != 'error') && ($this->sToken != null))
|
||||
{
|
||||
// Operation in progress, save the state
|
||||
$this->SaveState();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Operation completed, cleanup the temp files
|
||||
@unlink($this->GetStateFile());
|
||||
@unlink($this->GetDataFile());
|
||||
}
|
||||
self::CleanupOldFiles();
|
||||
}
|
||||
|
||||
public function SetChunkSize($iChunkSize)
|
||||
{
|
||||
$this->iChunkSize = $iChunkSize;
|
||||
}
|
||||
|
||||
public function SetOutputFilePath($sDestFilePath)
|
||||
{
|
||||
$this->sOutputFilePath = $sDestFilePath;
|
||||
}
|
||||
|
||||
public function SetAdvancedMode($bAdvanced)
|
||||
{
|
||||
$this->bAdvancedMode = $bAdvanced;
|
||||
}
|
||||
|
||||
public function SaveState()
|
||||
{
|
||||
$aState = array(
|
||||
'state' => $this->sState,
|
||||
'statistics' => $this->aStatistics,
|
||||
'filter' => $this->oSearch->serialize(),
|
||||
'position' => $this->iPosition,
|
||||
'chunk_size' => $this->iChunkSize,
|
||||
'object_ids' => $this->aObjectsIDs,
|
||||
'output_file_path' => $this->sOutputFilePath,
|
||||
'advanced_mode' => $this->bAdvancedMode,
|
||||
);
|
||||
|
||||
file_put_contents($this->GetStateFile(), json_encode($aState));
|
||||
|
||||
return $this->sToken;
|
||||
}
|
||||
|
||||
public function ReloadState()
|
||||
{
|
||||
if ($this->sToken == null)
|
||||
{
|
||||
throw new Exception('ExcelExporter not initialized with a token, cannot reload state');
|
||||
}
|
||||
|
||||
if (!file_exists($this->GetStateFile()))
|
||||
{
|
||||
throw new Exception("ExcelExporter: missing status file '".$this->GetStateFile()."', cannot reload state.");
|
||||
}
|
||||
$sJson = file_get_contents($this->GetStateFile());
|
||||
$aState = json_decode($sJson, true);
|
||||
if ($aState === null)
|
||||
{
|
||||
throw new Exception("ExcelExporter:corrupted status file '".$this->GetStateFile()."', not a JSON, cannot reload state.");
|
||||
}
|
||||
|
||||
$this->sState = $aState['state'];
|
||||
$this->aStatistics = $aState['statistics'];
|
||||
$this->oSearch = DBObjectSearch::unserialize($aState['filter']);
|
||||
$this->iPosition = $aState['position'];
|
||||
$this->iChunkSize = $aState['chunk_size'];
|
||||
$this->aObjectsIDs = $aState['object_ids'];
|
||||
$this->sOutputFilePath = $aState['output_file_path'];
|
||||
$this->bAdvancedMode = $aState['advanced_mode'];
|
||||
}
|
||||
|
||||
public function SetObjectList($oSearch)
|
||||
{
|
||||
$this->oSearch = $oSearch;
|
||||
}
|
||||
|
||||
public function Run()
|
||||
{
|
||||
$sCode = 'error';
|
||||
$iPercentage = 100;
|
||||
$sMessage = Dict::Format('ExcelExporter:ErrorUnexpected_State', $this->sState);
|
||||
$fTime = microtime(true);
|
||||
|
||||
try
|
||||
{
|
||||
switch($this->sState)
|
||||
{
|
||||
case 'new':
|
||||
$oIDSet = new DBObjectSet($this->oSearch);
|
||||
$oIDSet->OptimizeColumnLoad(array('id'));
|
||||
$this->aObjectsIDs = array();
|
||||
while($oObj = $oIDSet->Fetch())
|
||||
{
|
||||
$this->aObjectsIDs[] = $oObj->GetKey();
|
||||
}
|
||||
$sCode = 'retrieving-data';
|
||||
$iPercentage = 5;
|
||||
$sMessage = Dict::S('ExcelExporter:RetrievingData');
|
||||
$this->iPosition = 0;
|
||||
$this->aStatistics['objects_count'] = count($this->aObjectsIDs);
|
||||
$this->aStatistics['data_retrieval_duration'] += microtime(true) - $fTime;
|
||||
|
||||
// The first line of the file is the "headers" specifying the label and the type of each column
|
||||
$this->GetFieldsList($oIDSet, $this->bAdvancedMode);
|
||||
$sRow = json_encode($this->aTableHeaders);
|
||||
$hFile = @fopen($this->GetDataFile(), 'ab');
|
||||
if ($hFile === false)
|
||||
{
|
||||
throw new Exception('ExcelExporter: Failed to open temporary data file: "'.$this->GetDataFile().'" for writing.');
|
||||
}
|
||||
fwrite($hFile, $sRow."\n");
|
||||
fclose($hFile);
|
||||
|
||||
// Next state
|
||||
$this->sState = 'retrieving-data';
|
||||
break;
|
||||
|
||||
case 'retrieving-data':
|
||||
$oCurrentSearch = clone $this->oSearch;
|
||||
$aIDs = array_slice($this->aObjectsIDs, $this->iPosition, $this->iChunkSize);
|
||||
|
||||
$oCurrentSearch->AddCondition('id', $aIDs, 'IN');
|
||||
$hFile = @fopen($this->GetDataFile(), 'ab');
|
||||
if ($hFile === false)
|
||||
{
|
||||
throw new Exception('ExcelExporter: Failed to open temporary data file: "'.$this->GetDataFile().'" for writing.');
|
||||
}
|
||||
$oSet = new DBObjectSet($oCurrentSearch);
|
||||
$this->GetFieldsList($oSet, $this->bAdvancedMode);
|
||||
while($aObjects = $oSet->FetchAssoc())
|
||||
{
|
||||
$aRow = array();
|
||||
foreach($this->aAuthorizedClasses as $sAlias => $sClassName)
|
||||
{
|
||||
$oObj = $aObjects[$sAlias];
|
||||
if ($this->bAdvancedMode)
|
||||
{
|
||||
$aRow[] = $oObj->GetKey();
|
||||
}
|
||||
foreach($this->aFieldsList[$sAlias] as $sAttCodeEx => $oAttDef)
|
||||
{
|
||||
$value = $oObj->Get($sAttCodeEx);
|
||||
if ($value instanceOf ormCaseLog)
|
||||
{
|
||||
// Extract the case log as text and remove the "===" which make Excel think that the cell contains a formula the next time you edit it!
|
||||
$sExcelVal = trim(preg_replace('/========== ([^=]+) ============/', '********** $1 ************', $value->GetText()));
|
||||
}
|
||||
else
|
||||
{
|
||||
$sExcelVal = $oAttDef->GetEditValue($value, $oObj);
|
||||
}
|
||||
$aRow[] = $sExcelVal;
|
||||
}
|
||||
}
|
||||
$sRow = json_encode($aRow);
|
||||
fwrite($hFile, $sRow."\n");
|
||||
}
|
||||
fclose($hFile);
|
||||
|
||||
if (($this->iPosition + $this->iChunkSize) > count($this->aObjectsIDs))
|
||||
{
|
||||
// Next state
|
||||
$this->sState = 'building-excel';
|
||||
$sCode = 'building-excel';
|
||||
$iPercentage = 80;
|
||||
$sMessage = Dict::S('ExcelExporter:BuildingExcelFile');
|
||||
}
|
||||
else
|
||||
{
|
||||
$sCode = 'retrieving-data';
|
||||
$this->iPosition += $this->iChunkSize;
|
||||
$iPercentage = 5 + round(75 * ($this->iPosition / count($this->aObjectsIDs)));
|
||||
$sMessage = Dict::S('ExcelExporter:RetrievingData');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'building-excel':
|
||||
$hFile = @fopen($this->GetDataFile(), 'rb');
|
||||
if ($hFile === false)
|
||||
{
|
||||
throw new Exception('ExcelExporter: Failed to open temporary data file: "'.$this->GetDataFile().'" for reading.');
|
||||
}
|
||||
$sHeaders = fgets($hFile);
|
||||
$aHeaders = json_decode($sHeaders, true);
|
||||
|
||||
$aData = array();
|
||||
while($sLine = fgets($hFile))
|
||||
{
|
||||
$aRow = json_decode($sLine);
|
||||
$aData[] = $aRow;
|
||||
}
|
||||
fclose($hFile);
|
||||
@unlink($this->GetDataFile());
|
||||
|
||||
$fStartExcel = microtime(true);
|
||||
$writer = new XLSXWriter();
|
||||
$writer->setAuthor(UserRights::GetUserFriendlyName());
|
||||
$writer->writeSheet($aData,'Sheet1', $aHeaders);
|
||||
$fExcelTime = microtime(true) - $fStartExcel;
|
||||
$this->aStatistics['excel_build_duration'] = $fExcelTime;
|
||||
|
||||
$fTime = microtime(true);
|
||||
$writer->writeToFile($this->GetExcelFilePath());
|
||||
$fExcelSaveTime = microtime(true) - $fTime;
|
||||
$this->aStatistics['excel_write_duration'] = $fExcelSaveTime;
|
||||
|
||||
// Next state
|
||||
$this->sState = 'done';
|
||||
$sCode = 'done';
|
||||
$iPercentage = 100;
|
||||
$sMessage = Dict::S('ExcelExporter:Done');
|
||||
break;
|
||||
|
||||
case 'done':
|
||||
$this->sState = 'done';
|
||||
$sCode = 'done';
|
||||
$iPercentage = 100;
|
||||
$sMessage = Dict::S('ExcelExporter:Done');
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
$sCode = 'error';
|
||||
$sMessage = $e->getMessage();
|
||||
}
|
||||
|
||||
$this->aStatistics['total_duration'] += microtime(true) - $fTime;
|
||||
$peak_memory = memory_get_peak_usage(true);
|
||||
if ($peak_memory > $this->aStatistics['peak_memory_usage'])
|
||||
{
|
||||
$this->aStatistics['peak_memory_usage'] = $peak_memory;
|
||||
}
|
||||
|
||||
return array(
|
||||
'code' => $sCode,
|
||||
'message' => $sMessage,
|
||||
'percentage' => $iPercentage,
|
||||
);
|
||||
}
|
||||
|
||||
public function GetExcelFilePath()
|
||||
{
|
||||
if ($this->sOutputFilePath == null)
|
||||
{
|
||||
return APPROOT.'data/bulk_export/'.$this->sToken.'.xlsx';
|
||||
}
|
||||
else
|
||||
{
|
||||
return $this->sOutputFilePath;
|
||||
}
|
||||
}
|
||||
|
||||
public static function GetExcelFileFromToken($sToken)
|
||||
{
|
||||
return @file_get_contents(APPROOT.'data/bulk_export/'.$sToken.'.xlsx');
|
||||
}
|
||||
|
||||
public static function CleanupFromToken($sToken)
|
||||
{
|
||||
@unlink(APPROOT.'data/bulk_export/'.$sToken.'.status');
|
||||
@unlink(APPROOT.'data/bulk_export/'.$sToken.'.data');
|
||||
@unlink(APPROOT.'data/bulk_export/'.$sToken.'.xlsx');
|
||||
}
|
||||
|
||||
public function Cleanup()
|
||||
{
|
||||
self::CleanupFromToken($this->sToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all files in the data/bulk_export directory which are older than 1 day
|
||||
* unless a different delay is configured.
|
||||
*/
|
||||
public static function CleanupOldFiles()
|
||||
{
|
||||
$aFiles = glob(APPROOT.'data/bulk_export/*.*');
|
||||
$iDelay = MetaModel::GetConfig()->Get('xlsx_exporter_cleanup_old_files_delay');
|
||||
|
||||
if($iDelay > 0)
|
||||
{
|
||||
foreach($aFiles as $sFile)
|
||||
{
|
||||
$iModificationTime = filemtime($sFile);
|
||||
|
||||
if($iModificationTime < (time() - $iDelay))
|
||||
{
|
||||
// Temporary files older than one day are deleted
|
||||
//echo "Supposed to delete: '".$sFile." (Unix Modification Time: $iModificationTime)'\n";
|
||||
@unlink($sFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function DisplayStatistics(Page $oPage)
|
||||
{
|
||||
$aStats = array(
|
||||
'Number of objects exported' => $this->aStatistics['objects_count'],
|
||||
'Total export duration' => sprintf('%.3f s', $this->aStatistics['total_duration']),
|
||||
'Data retrieval duration' => sprintf('%.3f s', $this->aStatistics['data_retrieval_duration']),
|
||||
'Excel build duration' => sprintf('%.3f s', $this->aStatistics['excel_build_duration']),
|
||||
'Excel write duration' => sprintf('%.3f s', $this->aStatistics['excel_write_duration']),
|
||||
'Peak memory usage' => self::HumanDisplay($this->aStatistics['peak_memory_usage']),
|
||||
);
|
||||
|
||||
if ($oPage instanceof CLIPage)
|
||||
{
|
||||
$oPage->add($this->GetStatistics('text'));
|
||||
}
|
||||
else
|
||||
{
|
||||
$oPage->add($this->GetStatistics('html'));
|
||||
}
|
||||
}
|
||||
|
||||
public function GetStatistics($sFormat = 'html')
|
||||
{
|
||||
$sStats = '';
|
||||
$aStats = array(
|
||||
'Number of objects exported' => $this->aStatistics['objects_count'],
|
||||
'Total export duration' => sprintf('%.3f s', $this->aStatistics['total_duration']),
|
||||
'Data retrieval duration' => sprintf('%.3f s', $this->aStatistics['data_retrieval_duration']),
|
||||
'Excel build duration' => sprintf('%.3f s', $this->aStatistics['excel_build_duration']),
|
||||
'Excel write duration' => sprintf('%.3f s', $this->aStatistics['excel_write_duration']),
|
||||
'Peak memory usage' => self::HumanDisplay($this->aStatistics['peak_memory_usage']),
|
||||
);
|
||||
|
||||
if ($sFormat == 'text')
|
||||
{
|
||||
foreach($aStats as $sLabel => $sValue)
|
||||
{
|
||||
$sStats .= "+------------------------------+----------+\n";
|
||||
$sStats .= sprintf("|%-30s|%10s|\n", $sLabel, $sValue);
|
||||
}
|
||||
$sStats .= "+------------------------------+----------+";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sStats .= '<table><tbody>';
|
||||
foreach($aStats as $sLabel => $sValue)
|
||||
{
|
||||
$sStats .= "<tr><td>$sLabel</td><td>$sValue</td></tr>";
|
||||
}
|
||||
$sStats .= '</tbody></table>';
|
||||
|
||||
}
|
||||
return $sStats;
|
||||
}
|
||||
|
||||
public static function HumanDisplay($iSize)
|
||||
{
|
||||
$aUnits = array('B','KB','MB','GB','TB','PB');
|
||||
return @round($iSize/pow(1024,($i=floor(log($iSize,1024)))),2).' '.$aUnits[$i];
|
||||
}
|
||||
|
||||
protected function CheckDataDir()
|
||||
{
|
||||
if(!is_dir(APPROOT."data/bulk_export"))
|
||||
{
|
||||
@mkdir(APPROOT."data/bulk_export", 0777, true /* recursive */);
|
||||
clearstatcache();
|
||||
}
|
||||
if (!is_writable(APPROOT."data/bulk_export"))
|
||||
{
|
||||
throw new Exception('Data directory "'.APPROOT.'data/bulk_export" could not be written.');
|
||||
}
|
||||
}
|
||||
|
||||
protected function GetStateFile($sToken = null)
|
||||
{
|
||||
if ($sToken == null)
|
||||
{
|
||||
$sToken = $this->sToken;
|
||||
}
|
||||
return APPROOT."data/bulk_export/$sToken.status";
|
||||
}
|
||||
|
||||
protected function GetDataFile()
|
||||
{
|
||||
return APPROOT.'data/bulk_export/'.$this->sToken.'.data';
|
||||
}
|
||||
|
||||
protected function GetNewToken()
|
||||
{
|
||||
$iNum = rand();
|
||||
do
|
||||
{
|
||||
$iNum++;
|
||||
$sToken = sprintf("%08x", $iNum);
|
||||
$sFileName = $this->GetStateFile($sToken);
|
||||
$hFile = @fopen($sFileName, 'x');
|
||||
}
|
||||
while($hFile === false);
|
||||
|
||||
fclose($hFile);
|
||||
return $sToken;
|
||||
}
|
||||
|
||||
protected function GetFieldsList($oSet, $bFieldsAdvanced = false, $bLocalize = true, $aFields = null)
|
||||
{
|
||||
$this->aFieldsList = array();
|
||||
|
||||
$oAppContext = new ApplicationContext();
|
||||
$aClasses = $oSet->GetFilter()->GetSelectedClasses();
|
||||
$this->aAuthorizedClasses = array();
|
||||
foreach($aClasses as $sAlias => $sClassName)
|
||||
{
|
||||
if (UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $oSet) && (UR_ALLOWED_YES || UR_ALLOWED_DEPENDS))
|
||||
{
|
||||
$this->aAuthorizedClasses[$sAlias] = $sClassName;
|
||||
}
|
||||
}
|
||||
$aAttribs = array();
|
||||
$this->aTableHeaders = array();
|
||||
foreach($this->aAuthorizedClasses as $sAlias => $sClassName)
|
||||
{
|
||||
$aList[$sAlias] = array();
|
||||
|
||||
foreach(MetaModel::ListAttributeDefs($sClassName) as $sAttCode => $oAttDef)
|
||||
{
|
||||
if (is_null($aFields) || (count($aFields) == 0))
|
||||
{
|
||||
// Standard list of attributes (no link sets)
|
||||
if ($oAttDef->IsScalar() && ($oAttDef->IsWritable() || $oAttDef->IsExternalField()))
|
||||
{
|
||||
$sAttCodeEx = $oAttDef->IsExternalField() ? $oAttDef->GetKeyAttCode().'->'.$oAttDef->GetExtAttCode() : $sAttCode;
|
||||
|
||||
if ($oAttDef->IsExternalKey(EXTKEY_ABSOLUTE))
|
||||
{
|
||||
if ($bFieldsAdvanced)
|
||||
{
|
||||
$aList[$sAlias][$sAttCodeEx] = $oAttDef;
|
||||
|
||||
if ($oAttDef->IsExternalKey(EXTKEY_RELATIVE))
|
||||
{
|
||||
$sRemoteClass = $oAttDef->GetTargetClass();
|
||||
foreach(MetaModel::GetReconcKeys($sRemoteClass) as $sRemoteAttCode)
|
||||
{
|
||||
$this->aFieldsList[$sAlias][$sAttCode.'->'.$sRemoteAttCode] = MetaModel::GetAttributeDef($sRemoteClass, $sRemoteAttCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Any other attribute
|
||||
$this->aFieldsList[$sAlias][$sAttCodeEx] = $oAttDef;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// User defined list of attributes
|
||||
if (in_array($sAttCode, $aFields) || in_array($sAlias.'.'.$sAttCode, $aFields))
|
||||
{
|
||||
$this->aFieldsList[$sAlias][$sAttCode] = $oAttDef;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($bFieldsAdvanced)
|
||||
{
|
||||
$this->aTableHeaders['id'] = '0';
|
||||
}
|
||||
foreach($this->aFieldsList[$sAlias] as $sAttCodeEx => $oAttDef)
|
||||
{
|
||||
$sLabel = $bLocalize ? MetaModel::GetLabel($sClassName, $sAttCodeEx, isset($aParams['showMandatoryFields'])) : $sAttCodeEx;
|
||||
if($oAttDef instanceof AttributeDateTime)
|
||||
{
|
||||
$this->aTableHeaders[$sLabel] = 'datetime';
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->aTableHeaders[$sLabel] = 'string';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1681
application/forms.class.inc.php
Normal file
1681
application/forms.class.inc.php
Normal file
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/>
|
||||
|
||||
|
||||
/**
|
||||
* 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');
|
||||
|
||||
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/>
|
||||
|
||||
|
||||
/**
|
||||
* 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');
|
||||
@@ -48,7 +49,7 @@ class iTopWizardWebPage extends iTopWebPage
|
||||
$sStyle = ($iIndex == $this->m_iCurrentStep) ? 'wizActiveStep' : 'wizStep';
|
||||
$aSteps[] = "<div class=\"$sStyle\"><span>$sStepTitle</span></div>";
|
||||
}
|
||||
$sWizardHeader = "<div class=\"wizHeader\"><h1>{$this->s_title}</h1>\n".implode("<div class=\"wizSeparator\"><img align=\"bottom\" src=\"../images/wizArrow.gif\"></div>", $aSteps)."<br style=\"clear:both;\"/></div>\n";
|
||||
$sWizardHeader = "<div class=\"wizHeader\"><h1>".htmlentities($this->s_title, ENT_QUOTES, 'UTF-8')."</h1>\n".implode("<div class=\"wizSeparator\"><img align=\"bottom\" src=\"../images/wizArrow.gif\"></div>", $aSteps)."<br style=\"clear:both;\"/></div>\n";
|
||||
$this->s_content = "$sWizardHeader<div class=\"wizContainer\">".$this->s_content."</div>";
|
||||
parent::output();
|
||||
}
|
||||
|
||||
@@ -1,101 +1,119 @@
|
||||
<?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");
|
||||
/**
|
||||
* Web page used for displaying the login form
|
||||
*/
|
||||
|
||||
class LoginWebPage extends NiceWebPage
|
||||
{
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #1C94C4;
|
||||
font-size: 16pt;
|
||||
}
|
||||
.v-spacer {
|
||||
padding-top: 1em;
|
||||
}
|
||||
EOF
|
||||
);
|
||||
public static function NewLoginWebPage()
|
||||
{
|
||||
return new self::$sHandlerClass;
|
||||
}
|
||||
|
||||
protected static $m_sLoginFailedMessage = '';
|
||||
|
||||
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)
|
||||
{
|
||||
case 'cas':
|
||||
utils::InitCASClient();
|
||||
// force CAS authentication
|
||||
phpCAS::forceAuthentication(); // Will redirect the user and exit since the user is not yet authenticated
|
||||
break;
|
||||
|
||||
case 'basic':
|
||||
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;
|
||||
@@ -103,40 +121,272 @@ EOF
|
||||
case 'external':
|
||||
case 'form':
|
||||
default: // In case the settings get messed up...
|
||||
$sAuthUser = utils::ReadParam('auth_user', '');
|
||||
$sAuthPwd = utils::ReadParam('suggest_pwd', '');
|
||||
$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)
|
||||
{
|
||||
$this->add("<p class=\"hilite\">".Dict::S('UI:Login:IncorrectLoginPassword')."</p>\n");
|
||||
if (self::$m_sLoginFailedMessage != '')
|
||||
{
|
||||
$this->add("<p class=\"hilite\">".self::$m_sLoginFailedMessage."</p>\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->add("<p class=\"hilite\">".Dict::S('UI:Login:IncorrectLoginPassword')."</p>\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->add("<p>".Dict::S('UI:Login:IdentifyYourself')."</p>\n");
|
||||
}
|
||||
$this->add("<form method=\"post\">\n");
|
||||
$this->add("<table width=\"100%\">\n");
|
||||
$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=\"$sAuthUser\" /></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=\"$sAuthPwd\" /></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("<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\"><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");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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");
|
||||
}
|
||||
else
|
||||
{
|
||||
$oEncryptedToken = $oUser->Get('reset_pwd_token');
|
||||
|
||||
if (!$oEncryptedToken->CheckPassword($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");
|
||||
}
|
||||
else
|
||||
{
|
||||
$oEncryptedPassword = $oUser->Get('reset_pwd_token');
|
||||
if (!$oEncryptedPassword->CheckPassword($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', '');
|
||||
$sAuthPwd = utils::ReadParam('suggest_pwd', '');
|
||||
$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()
|
||||
@@ -155,7 +405,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)
|
||||
@@ -163,11 +413,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");
|
||||
@@ -185,15 +435,10 @@ EOF
|
||||
$sPreviousLoginMode = '';
|
||||
}
|
||||
// Unset all of the session variables.
|
||||
$_SESSION = array();
|
||||
unset($_SESSION['auth_user']);
|
||||
unset($_SESSION['login_mode']);
|
||||
// If it's desired to kill the session, also delete the session cookie.
|
||||
// Note: This will destroy the session, and not just the session data!
|
||||
if (isset($_COOKIE[session_name()]))
|
||||
{
|
||||
setcookie(session_name(), '', time()-3600, '/');
|
||||
}
|
||||
// Finally, destroy the session.
|
||||
session_destroy();
|
||||
}
|
||||
|
||||
static function SecureConnectionRequired()
|
||||
@@ -201,25 +446,36 @@ 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... redirect to a secured one
|
||||
$sUrl = Utils::GetAbsoluteUrl(true /* query string */, true /* force HTTPS */);
|
||||
header("Location: $sUrl");
|
||||
exit;
|
||||
// Non secured URL... request for a secure connection
|
||||
throw new Exception('Secure connection required!');
|
||||
}
|
||||
|
||||
$aAllowedLoginTypes = MetaModel::GetConfig()->GetAllowedLoginTypes();
|
||||
@@ -229,7 +485,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
|
||||
{
|
||||
@@ -241,11 +497,23 @@ EOF
|
||||
$sLoginType = $aAllowedLoginTypes[$index];
|
||||
switch($sLoginType)
|
||||
{
|
||||
case 'cas':
|
||||
utils::InitCASClient();
|
||||
// check CAS authentication
|
||||
if (phpCAS::isAuthenticated())
|
||||
{
|
||||
$sAuthUser = phpCAS::getUser();
|
||||
$sAuthPwd = '';
|
||||
$sLoginMode = 'cas';
|
||||
$sAuthentication = 'external';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'form':
|
||||
// iTop standard mode: form based authentication
|
||||
$sAuthUser = utils::ReadPostedParam('auth_user', '');
|
||||
$sAuthPwd = utils::ReadPostedParam('auth_pwd', '');
|
||||
if ($sAuthUser != '')
|
||||
$sAuthUser = utils::ReadPostedParam('auth_user', '', false, 'raw_data');
|
||||
$sAuthPwd = utils::ReadPostedParam('auth_pwd', null, false, 'raw_data');
|
||||
if (($sAuthUser != '') && ($sAuthPwd !== null))
|
||||
{
|
||||
$sLoginMode = 'form';
|
||||
}
|
||||
@@ -262,7 +530,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;
|
||||
@@ -270,12 +553,10 @@ EOF
|
||||
case 'external':
|
||||
// Web server supplied authentication
|
||||
$bExternalAuth = false;
|
||||
$sExtAuthVar = MetaModel::GetConfig()->GetExternalAuthenticationVariable(); // In which variable is the info passed ?
|
||||
$sEval = '$bExternalAuth = isset('.$sExtAuthVar.');';
|
||||
eval($sEval);
|
||||
if ($bExternalAuth)
|
||||
{
|
||||
eval('$sAuthUser = '.$sExtAuthVar.';'); // Retrieve the value
|
||||
$sExtAuthVar = MetaModel::GetConfig()->GetExternalAuthenticationVariable(); // In which variable is the info passed ?
|
||||
eval('$sAuthUser = isset('.$sExtAuthVar.') ? '.$sExtAuthVar.' : false;'); // Retrieve the value
|
||||
if ($sAuthUser && (strlen($sAuthUser) > 0))
|
||||
{
|
||||
$sAuthPwd = ''; // No password in this case the web server already authentified the user...
|
||||
$sLoginMode = 'external';
|
||||
$sAuthentication = 'external';
|
||||
@@ -284,10 +565,10 @@ EOF
|
||||
|
||||
case 'url':
|
||||
// Credentials passed directly in the url
|
||||
$sAuthUser = utils::ReadParam('auth_user', '');
|
||||
if ($sAuthUser != '')
|
||||
$sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data');
|
||||
$sAuthPwd = utils::ReadParam('auth_pwd', null, false, 'raw_data');
|
||||
if (($sAuthUser != '') && ($sAuthPwd !== null))
|
||||
{
|
||||
$sAuthPwd = utils::ReadParam('auth_pwd', '');
|
||||
$sLoginMode = 'url';
|
||||
}
|
||||
break;
|
||||
@@ -307,20 +588,56 @@ 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
|
||||
{
|
||||
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, $sAuthentication))
|
||||
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, $sLoginMode, $sAuthentication))
|
||||
{
|
||||
//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
|
||||
{
|
||||
@@ -341,21 +658,47 @@ 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', '');
|
||||
session_name(MetaModel::GetConfig()->Get('session_name'));
|
||||
session_start();
|
||||
|
||||
if ($operation == 'logoff')
|
||||
{
|
||||
@@ -376,16 +719,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;
|
||||
@@ -394,35 +765,47 @@ EOF
|
||||
{
|
||||
$sAuthUser = $_SESSION['auth_user'];
|
||||
UserRights::Login($sAuthUser); // Set the user's language
|
||||
$sOldPwd = utils::ReadPostedParam('old_pwd');
|
||||
$sNewPwd = utils::ReadPostedParam('new_pwd');
|
||||
$sOldPwd = utils::ReadPostedParam('old_pwd', '', false, 'raw_data');
|
||||
$sNewPwd = utils::ReadPostedParam('new_pwd', '', false, 'raw_data');
|
||||
if (UserRights::CanChangePassword() && ((!UserRights::CheckCredentials($sAuthUser, $sOldPwd)) || (!UserRights::ChangePassword($sOldPwd, $sNewPwd))))
|
||||
{
|
||||
$oPage = 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 SetupWebPage(Dict::S('UI:PageTitle:FatalError'));
|
||||
$oP->add("<h1>".Dict::S('UI:Login:Error:AccessAdmin')."</h1>\n");
|
||||
$oP->p("<a href=\"../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: ../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);
|
||||
}
|
||||
return $sMessage;
|
||||
}
|
||||
|
||||
if ($iOnExit == self::EXIT_RETURN)
|
||||
{
|
||||
return $iRet;
|
||||
}
|
||||
else
|
||||
{
|
||||
return $sMessage;
|
||||
}
|
||||
}
|
||||
} // End of class
|
||||
?>
|
||||
|
||||
@@ -1,30 +1,32 @@
|
||||
<?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');
|
||||
require_once(APPROOT.'/application/template.class.inc.php');
|
||||
require_once(APPROOT."/application/user.dashboard.class.inc.php");
|
||||
|
||||
|
||||
/**
|
||||
@@ -59,42 +61,115 @@ require_once(APPROOT.'/application/template.class.inc.php');
|
||||
|
||||
class ApplicationMenu
|
||||
{
|
||||
static $bAdditionalMenusLoaded = false;
|
||||
static $aRootMenus = array();
|
||||
static $aMenusIndex = array();
|
||||
static $sFavoriteSiloQuery = 'SELECT Organization';
|
||||
|
||||
static public function LoadAdditionalMenus()
|
||||
{
|
||||
if (!self::$bAdditionalMenusLoaded)
|
||||
{
|
||||
// Build menus from module handlers
|
||||
//
|
||||
foreach(get_declared_classes() as $sPHPClass)
|
||||
{
|
||||
if (is_subclass_of($sPHPClass, 'ModuleHandlerAPI'))
|
||||
{
|
||||
$aCallSpec = array($sPHPClass, 'OnMenuCreation');
|
||||
call_user_func($aCallSpec);
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the query used to limit the list of displayed organizations in the drop-down menu
|
||||
* @param $sOQL string The OQL query returning a list of Organization objects
|
||||
* @return none
|
||||
*/
|
||||
static public function SetFavoriteSiloQuery($sOQL)
|
||||
{
|
||||
self::$sFavoriteSiloQuery = $sOQL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query used to limit the list of displayed organizations in the drop-down menu
|
||||
* @return string The OQL query returning a list of Organization objects
|
||||
*/
|
||||
static public function GetFavoriteSiloQuery()
|
||||
{
|
||||
return self::$sFavoriteSiloQuery;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Main function to add a menu entry into the application, can be called during the definition
|
||||
* of the data model objects
|
||||
*/
|
||||
static public function InsertMenu(MenuNode $oMenuNode, $iParentIndex = -1, $fRank)
|
||||
static public function InsertMenu(MenuNode $oMenuNode, $iParentIndex, $fRank)
|
||||
{
|
||||
$index = self::GetMenuIndexById($oMenuNode->GetMenuId());
|
||||
if ($index == -1)
|
||||
{
|
||||
// The menu does not already exist, insert it
|
||||
$index = count(self::$aMenusIndex);
|
||||
self::$aMenusIndex[$index] = array( 'node' => $oMenuNode, 'children' => array());
|
||||
|
||||
if ($iParentIndex == -1)
|
||||
{
|
||||
$sParentId = '';
|
||||
self::$aRootMenus[] = array ('rank' => $fRank, 'index' => $index);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sParentId = self::$aMenusIndex[$iParentIndex]['node']->GetMenuId();
|
||||
self::$aMenusIndex[$iParentIndex]['children'][] = array ('rank' => $fRank, 'index' => $index);
|
||||
}
|
||||
|
||||
// Note: At the time when 'parent', 'rank' and 'source_file' have been added for the reflection API,
|
||||
// they were not used to display the menus (redundant or unused)
|
||||
//
|
||||
$aBacktrace = debug_backtrace();
|
||||
$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
|
||||
{
|
||||
// the menu already exists, let's combine the conditions that make it visible
|
||||
self::$aMenusIndex[$index]['node']->AddCondition($oMenuNode);
|
||||
}
|
||||
return $index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reflection API - Get menu entries
|
||||
*/
|
||||
static public function ReflectionMenuNodes()
|
||||
{
|
||||
self::LoadAdditionalMenus();
|
||||
return self::$aMenusIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
usort(self::$aRootMenus, array('ApplicationMenu', 'CompareOnRank'));
|
||||
$iAccordion = 0;
|
||||
$iActiveMenu = ApplicationMenu::GetActiveNodeId();
|
||||
$iActiveMenu = self::GetMenuIndexById(self::GetActiveNodeId());
|
||||
foreach(self::$aRootMenus as $aMenu)
|
||||
{
|
||||
$oMenuNode = self::GetMenuNode($aMenu['index']);
|
||||
@@ -109,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>');
|
||||
@@ -187,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)
|
||||
@@ -213,22 +289,23 @@ class ApplicationMenu
|
||||
|
||||
/**
|
||||
* Retrieves the currently active menu (if any, otherwise the first menu is the default)
|
||||
* @return MenuNode or null if there is no menu at all !
|
||||
* @return string The Id of the currently active menu
|
||||
*/
|
||||
static public function GetActiveNodeId()
|
||||
{
|
||||
$oAppContext = new ApplicationContext();
|
||||
$iMenuIndex = $oAppContext->GetCurrentValue('menu', -1);
|
||||
|
||||
if ($iMenuIndex == -1)
|
||||
$sMenuId = $oAppContext->GetCurrentValue('menu', null);
|
||||
if ($sMenuId === null)
|
||||
{
|
||||
// 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']);
|
||||
$iMenuIndex = $oMenuNode->GetIndex();
|
||||
$aChildren = self::$aMenusIndex[$oFirstGroup->GetIndex()]['children'];
|
||||
usort($aChildren, array('ApplicationMenu', 'CompareOnRank'));
|
||||
$oMenuNode = self::GetMenuNode($aChildren[0]['index']);
|
||||
$sMenuId = $oMenuNode->GetMenuId();
|
||||
}
|
||||
return $iMenuIndex;
|
||||
return $sMenuId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,25 +340,30 @@ abstract class MenuNode
|
||||
protected $sMenuId;
|
||||
protected $index;
|
||||
|
||||
/**
|
||||
* Properties reflecting how the node has been declared
|
||||
*/
|
||||
protected $aReflectionProperties;
|
||||
|
||||
/**
|
||||
* Class of objects to check if the menu is enabled, null if none
|
||||
*/
|
||||
protected $m_sEnableClass;
|
||||
protected $m_aEnableClasses;
|
||||
|
||||
/**
|
||||
* User Rights Action code to check if the menu is enabled, null if none
|
||||
*/
|
||||
protected $m_iEnableAction;
|
||||
protected $m_aEnableActions;
|
||||
|
||||
/**
|
||||
* User Rights allowed results (actually a bitmask) to check if the menu is enabled, null if none
|
||||
*/
|
||||
protected $m_iEnableActionResults;
|
||||
protected $m_aEnableActionResults;
|
||||
|
||||
/**
|
||||
* Stimulus to check: if the user can 'apply' this stimulus, then she/he can see this menu
|
||||
*/
|
||||
protected $m_sEnableStimulus;
|
||||
protected $m_aEnableStimuli;
|
||||
|
||||
/**
|
||||
* Create a menu item, sets the condition to have it displayed and inserts it into the application's main menu
|
||||
@@ -297,12 +379,25 @@ abstract class MenuNode
|
||||
public function __construct($sMenuId, $iParentIndex = -1, $fRank = 0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
|
||||
{
|
||||
$this->sMenuId = $sMenuId;
|
||||
$this->m_sEnableClass = $sEnableClass;
|
||||
$this->m_iEnableAction = $iActionCode;
|
||||
$this->m_iEnableActionResults = $iAllowedResults;
|
||||
$this->m_sEnableStimulus = $sEnableStimulus;
|
||||
$this->aReflectionProperties = array();
|
||||
if (strlen($sEnableClass) > 0)
|
||||
{
|
||||
$this->aReflectionProperties['enable_class'] = $sEnableClass;
|
||||
$this->aReflectionProperties['enable_action'] = $iActionCode;
|
||||
$this->aReflectionProperties['enable_permission'] = $iAllowedResults;
|
||||
$this->aReflectionProperties['enable_stimulus'] = $sEnableStimulus;
|
||||
}
|
||||
$this->m_aEnableClasses = array($sEnableClass);
|
||||
$this->m_aEnableActions = array($iActionCode);
|
||||
$this->m_aEnableActionResults = array($iAllowedResults);
|
||||
$this->m_aEnableStimuli = array($sEnableStimulus);
|
||||
$this->index = ApplicationMenu::InsertMenu($this, $iParentIndex, $fRank);
|
||||
}
|
||||
|
||||
public function ReflectionProperties()
|
||||
{
|
||||
return $this->aReflectionProperties;
|
||||
}
|
||||
|
||||
public function GetMenuId()
|
||||
{
|
||||
@@ -311,12 +406,12 @@ abstract class MenuNode
|
||||
|
||||
public function GetTitle()
|
||||
{
|
||||
return Dict::S("Menu:$this->sMenuId");
|
||||
return Dict::S("Menu:$this->sMenuId", str_replace('_', ' ', $this->sMenuId));
|
||||
}
|
||||
|
||||
public function GetLabel()
|
||||
{
|
||||
return Dict::S("Menu:$this->sMenuId+");
|
||||
return Dict::S("Menu:$this->sMenuId+", "");
|
||||
}
|
||||
|
||||
public function GetIndex()
|
||||
@@ -324,44 +419,70 @@ 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->GetIndex();
|
||||
return $this->AddParams('../pages/UI.php', $aExtraParams);
|
||||
$aExtraParams['c[menu]'] = $this->GetMenuId();
|
||||
return $this->AddParams(utils::GetAbsoluteUrlAppRoot().'pages/UI.php', $aExtraParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a limiting display condition for the same menu node. The conditions will be combined with a AND
|
||||
* @param $oMenuNode MenuNode Another definition of the same menu node, with potentially different access restriction
|
||||
* @return void
|
||||
*/
|
||||
public function AddCondition(MenuNode $oMenuNode)
|
||||
{
|
||||
foreach($oMenuNode->m_aEnableClasses as $index => $sClass )
|
||||
{
|
||||
$this->m_aEnableClasses[] = $sClass;
|
||||
$this->m_aEnableActions[] = $oMenuNode->m_aEnableActions[$index];
|
||||
$this->m_aEnableActionResults[] = $oMenuNode->m_aEnableActionResults[$index];
|
||||
$this->m_aEnableStimuli[] = $oMenuNode->m_aEnableStimuli[$index];
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Tells whether the menu is enabled (i.e. displayed) for the current user
|
||||
* @return bool True if enabled, false otherwise
|
||||
*/
|
||||
public function IsEnabled()
|
||||
{
|
||||
if ($this->m_sEnableClass != null)
|
||||
foreach($this->m_aEnableClasses as $index => $sClass)
|
||||
{
|
||||
if (MetaModel::IsValidClass($this->m_sEnableClass))
|
||||
if ($sClass != null)
|
||||
{
|
||||
if ($this->m_sEnableStimulus != null)
|
||||
if (MetaModel::IsValidClass($sClass))
|
||||
{
|
||||
if (!UserRights::IsStimulusAllowed($this->m_sEnableClass, $this->m_sEnableStimulus))
|
||||
if ($this->m_aEnableStimuli[$index] != null)
|
||||
{
|
||||
return false;
|
||||
if (!UserRights::IsStimulusAllowed($sClass, $this->m_aEnableStimuli[$index]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if ($this->m_aEnableActions[$index] != null)
|
||||
{
|
||||
$iResult = UserRights::IsActionAllowed($sClass, $this->m_aEnableActions[$index]);
|
||||
if (!($iResult & $this->m_aEnableActionResults[$index]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($this->m_iEnableAction != null)
|
||||
else
|
||||
{
|
||||
$iResult = UserRights::IsActionAllowed($this->m_sEnableClass, $this->m_iEnableAction);
|
||||
if (($iResult & $this->m_iEnableActionResults))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -437,6 +558,7 @@ class TemplateMenuNode extends MenuNode
|
||||
{
|
||||
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
|
||||
$this->sTemplateFile = $sTemplateFile;
|
||||
$this->aReflectionProperties['template_file'] = $sTemplateFile;
|
||||
}
|
||||
|
||||
public function GetHyperlink($aExtraParams)
|
||||
@@ -450,6 +572,7 @@ class TemplateMenuNode extends MenuNode
|
||||
$sTemplate = @file_get_contents($this->sTemplateFile);
|
||||
if ($sTemplate !== false)
|
||||
{
|
||||
$aExtraParams['table_id'] = 'Menu_'.$this->GetMenuId();
|
||||
$oTemplate = new DisplayTemplate($sTemplate);
|
||||
$oTemplate->Render($oPage, $aExtraParams);
|
||||
}
|
||||
@@ -495,6 +618,8 @@ class OQLMenuNode extends MenuNode
|
||||
$this->sOQL = $sOQL;
|
||||
$this->bSearch = $bSearch;
|
||||
$this->m_aParams = array();
|
||||
$this->aReflectionProperties['oql'] = $sOQL;
|
||||
$this->aReflectionProperties['do_search'] = $bSearch;
|
||||
// Enhancement: we could set as the "enable" condition that the user has enough rights to "read" the objects
|
||||
// of the class specified by the OQL...
|
||||
}
|
||||
@@ -506,47 +631,47 @@ class OQLMenuNode extends MenuNode
|
||||
public function SetParameters($aParams)
|
||||
{
|
||||
$this->m_aParams = $aParams;
|
||||
foreach($aParams as $sKey => $value)
|
||||
{
|
||||
$this->aReflectionProperties[$sKey] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
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 = '';
|
||||
}
|
||||
// The standard template used for all such pages: a (closed) search form at the top and a list of results at the bottom
|
||||
$sTemplate = '';
|
||||
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)
|
||||
{
|
||||
$sTemplate .= <<<EOF
|
||||
<itopblock BlockClass="DisplayBlock" type="search" asynchronous="false" encoding="text/oql">$this->sOQL</itopblock>
|
||||
EOF;
|
||||
$aParams = array_merge(array('open' => $bSearchOpen, 'table_id' => $sUsageId), $aExtraParams);
|
||||
$oBlock = new DisplayBlock($oSearch, 'search', false /* Asynchronous */, $aParams);
|
||||
$oBlock->Display($oPage, 0);
|
||||
}
|
||||
$sParams = '';
|
||||
if (!empty($this->m_aParams))
|
||||
{
|
||||
$sParams = 'parameters="';
|
||||
foreach($this->m_aParams as $sName => $sValue)
|
||||
{
|
||||
$sParams .= $sName.':'.$sValue.';';
|
||||
}
|
||||
$sParams .= '"';
|
||||
}
|
||||
$sTemplate .= <<<EOF
|
||||
<p class="page-header">$sIcon<itopstring>$this->sPageTitle</itopstring></p>
|
||||
<itopblock BlockClass="DisplayBlock" type="list" asynchronous="false" encoding="text/oql" $sParams>$this->sOQL</itopblock>
|
||||
EOF;
|
||||
$oTemplate = new DisplayTemplate($sTemplate);
|
||||
$oTemplate->Render($oPage, $aExtraParams);
|
||||
|
||||
$oPage->add("<p class=\"page-header\">$sIcon ".Dict::S($sTitle)."</p>");
|
||||
|
||||
$aParams = array_merge(array('table_id' => $sUsageId), $aExtraParams);
|
||||
$oBlock = new DisplayBlock($oSearch, 'list', false /* Asynchronous */, $aParams);
|
||||
$oBlock->Display($oPage, $sUsageId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class defines a menu item that displays a search form for the given class of objects
|
||||
*/
|
||||
@@ -572,16 +697,15 @@ class SearchMenuNode extends MenuNode
|
||||
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
|
||||
$this->sPageTitle = "Menu:$sMenuId+";
|
||||
$this->sClass = $sClass;
|
||||
$this->aReflectionProperties['class'] = $sClass;
|
||||
}
|
||||
|
||||
public function RenderContent(WebPage $oPage, $aExtraParams = array())
|
||||
{
|
||||
// The standard template used for all such pages: an open search form at the top
|
||||
$sTemplate = <<<EOF
|
||||
<itopblock BlockClass="DisplayBlock" type="search" asynchronous="false" encoding="text/oql" parameters="open:true">SELECT $this->sClass</itopblock>
|
||||
EOF;
|
||||
$oTemplate = new DisplayTemplate($sTemplate);
|
||||
$oTemplate->Render($oPage, $aExtraParams);
|
||||
$oSearch = new DBObjectSearch($this->sClass);
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -611,11 +735,12 @@ class WebPageMenuNode extends MenuNode
|
||||
{
|
||||
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
|
||||
$this->sHyperlink = $sHyperlink;
|
||||
$this->aReflectionProperties['url'] = $sHyperlink;
|
||||
}
|
||||
|
||||
public function GetHyperlink($aExtraParams)
|
||||
{
|
||||
$aExtraParams['c[menu]'] = $this->GetIndex();
|
||||
$aExtraParams['c[menu]'] = $this->GetMenuId();
|
||||
return $this->AddParams( $this->sHyperlink, $aExtraParams);
|
||||
}
|
||||
|
||||
@@ -648,12 +773,13 @@ class NewObjectMenuNode extends MenuNode
|
||||
{
|
||||
parent::__construct($sMenuId, $iParentIndex, $fRank);
|
||||
$this->sClass = $sClass;
|
||||
$this->aReflectionProperties['class'] = $sClass;
|
||||
}
|
||||
|
||||
public function GetHyperlink($aExtraParams)
|
||||
{
|
||||
$sHyperlink = '../pages/UI.php?operation=new&class='.$this->sClass;
|
||||
$aExtraParams['c[menu]'] = $this->GetIndex();
|
||||
$sHyperlink = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=new&class='.$this->sClass;
|
||||
$aExtraParams['c[menu]'] = $this->GetMenuId();
|
||||
return $this->AddParams($sHyperlink, $aExtraParams);
|
||||
}
|
||||
|
||||
@@ -684,4 +810,241 @@ class NewObjectMenuNode extends MenuNode
|
||||
assert(false); // Shall never be called, the external web page will handle the display by itself
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
require_once(APPROOT.'application/dashboard.class.inc.php');
|
||||
/**
|
||||
* This class defines a menu item which content is based on XML dashboard.
|
||||
*/
|
||||
class DashboardMenuNode extends MenuNode
|
||||
{
|
||||
protected $sDashboardFile;
|
||||
|
||||
/**
|
||||
* 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 string $sTemplateFile Path (or URL) to the file that will be used as a template for displaying the page's content
|
||||
* @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, $sDashboardFile, $iParentIndex, $fRank = 0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
|
||||
{
|
||||
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
|
||||
$this->sDashboardFile = $sDashboardFile;
|
||||
$this->aReflectionProperties['definition_file'] = $sDashboardFile;
|
||||
}
|
||||
|
||||
public function GetHyperlink($aExtraParams)
|
||||
{
|
||||
if ($this->sDashboardFile == '') return '';
|
||||
return parent::GetHyperlink($aExtraParams);
|
||||
}
|
||||
|
||||
public function GetDashboard()
|
||||
{
|
||||
$sDashboardDefinition = @file_get_contents($this->sDashboardFile);
|
||||
if ($sDashboardDefinition !== false)
|
||||
{
|
||||
$bCustomized = false;
|
||||
|
||||
// Search for an eventual user defined dashboard, overloading the existing one
|
||||
$oUDSearch = new DBObjectSearch('UserDashboard');
|
||||
$oUDSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
|
||||
$oUDSearch->AddCondition('menu_code', $this->sMenuId, '=');
|
||||
$oUDSet = new DBObjectSet($oUDSearch);
|
||||
if ($oUDSet->Count() > 0)
|
||||
{
|
||||
// Assuming there is at most one couple {user, menu}!
|
||||
$oUserDashboard = $oUDSet->Fetch();
|
||||
$sDashboardDefinition = $oUserDashboard->Get('contents');
|
||||
$bCustomized = true;
|
||||
|
||||
}
|
||||
$oDashboard = new RuntimeDashboard($this->sMenuId);
|
||||
$oDashboard->FromXml($sDashboardDefinition);
|
||||
$oDashboard->SetCustomFlag($bCustomized);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oDashboard = null;
|
||||
}
|
||||
return $oDashboard;
|
||||
}
|
||||
|
||||
public function RenderContent(WebPage $oPage, $aExtraParams = array())
|
||||
{
|
||||
$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)
|
||||
{
|
||||
$sId = addslashes($this->sMenuId);
|
||||
$oPage->add_ready_script("EditDashboard('$sId');");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$oPage->p("Error: failed to load dashboard file: '{$this->sDashboardFile}'");
|
||||
}
|
||||
}
|
||||
|
||||
public function RenderEditor(WebPage $oPage)
|
||||
{
|
||||
$oDashboard = $this->GetDashboard();
|
||||
if ($oDashboard != null)
|
||||
{
|
||||
$oDashboard->RenderEditor($oPage);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oPage->p("Error: failed to load dashboard file: '{$this->sDashboardFile}'");
|
||||
}
|
||||
}
|
||||
|
||||
public function AddDashlet($oDashlet)
|
||||
{
|
||||
$oDashboard = $this->GetDashboard();
|
||||
if ($oDashboard != null)
|
||||
{
|
||||
$oDashboard->AddDashlet($oDashlet);
|
||||
$oDashboard->Save();
|
||||
}
|
||||
else
|
||||
{
|
||||
$oPage->p("Error: failed to load dashboard file: '{$this->sDashboardFile}'");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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");
|
||||
@@ -30,30 +31,148 @@ require_once(APPROOT."/application/webpage.class.inc.php");
|
||||
class NiceWebPage extends WebPage
|
||||
{
|
||||
var $m_aReadyScripts;
|
||||
var $m_sRootUrl;
|
||||
|
||||
public function __construct($s_title)
|
||||
{
|
||||
parent::__construct($s_title);
|
||||
$this->m_aReadyScripts = array();
|
||||
$this->add_linked_script("../js/jquery-1.4.2.min.js");
|
||||
//$this->add_linked_script("../js/jquery.history_remote.pack.js");
|
||||
$this->add_linked_stylesheet('../css/ui-lightness/jquery-ui-1.8.2.custom.css');
|
||||
$this->add_linked_script('../js/jquery-ui-1.8.2.custom.min.js');
|
||||
//$this->add_linked_script("../js/ui.resizable.js");
|
||||
// $this->add_linked_script("../js/ui.tabs.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");
|
||||
// $this->add_linked_script("../js/jqModal.js");
|
||||
$this->add_linked_stylesheet("../css/light-grey.css");
|
||||
// $this->add_linked_stylesheet("../js/themes/light/light.tabs.css");
|
||||
//$this->add_linked_stylesheet("../css/jquery.tabs-ie.css", "lte IE 7");
|
||||
// $this->add_linked_stylesheet("../css/jqModal.css");
|
||||
$this->add_ready_script(' window.setTimeout(hovertipInit, 1);');
|
||||
}
|
||||
// table sorting
|
||||
$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
|
||||
$.tablesorter.addWidget({
|
||||
// give the widget a id
|
||||
id: "truncatedList",
|
||||
// format is called when the on init and when a sorting has finished
|
||||
format: function(table)
|
||||
{
|
||||
// Check if there is a "truncated" line
|
||||
this.truncatedList = false;
|
||||
if ($("tr td.truncated",table).length > 0)
|
||||
{
|
||||
this.truncatedList = true;
|
||||
}
|
||||
if (this.truncatedList)
|
||||
{
|
||||
$("tr td",table).removeClass('truncated');
|
||||
$("tr:last td",table).addClass('truncated');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$.tablesorter.addWidget({
|
||||
// give the widget a id
|
||||
id: "myZebra",
|
||||
// format is called when the on init and when a sorting has finished
|
||||
format: function(table)
|
||||
{
|
||||
// Replace the 'red even' lines by 'red_even' since most browser do not support 2 classes selector in CSS, etc..
|
||||
$("tbody tr:even",table).addClass('even');
|
||||
$("tbody tr.red:even",table).removeClass('red').removeClass('even').addClass('red_even');
|
||||
$("tbody tr.orange:even",table).removeClass('orange').removeClass('even').addClass('orange_even');
|
||||
$("tbody tr.green:even",table).removeClass('green').removeClass('even').addClass('green_even');
|
||||
// In case we sort again the table, we need to remove the added 'even' classes on odd rows
|
||||
$("tbody tr:odd",table).removeClass('even');
|
||||
$("tbody tr.red_even:odd",table).removeClass('even').removeClass('red_even').addClass('red');
|
||||
$("tbody tr.orange_even:odd",table).removeClass('even').removeClass('orange_even').addClass('orange');
|
||||
$("tbody tr.green_even:odd",table).removeClass('even').removeClass('green_even').addClass('green');
|
||||
}
|
||||
});
|
||||
$("table.listResults").tableHover(); // hover tables
|
||||
EOF
|
||||
);
|
||||
$this->add_linked_stylesheet("../css/light-grey.css");
|
||||
|
||||
$this->m_sRootUrl = $this->GetAbsoluteUrlAppRoot();
|
||||
$sAbsURLAppRoot = addslashes($this->m_sRootUrl);
|
||||
$sAbsURLModulesRoot = addslashes($this->GetAbsoluteUrlModulesRoot());
|
||||
$sEnvironment = addslashes(utils::GetCurrentEnvironment());
|
||||
|
||||
$sAppContext = addslashes($this->GetApplicationContext());
|
||||
|
||||
$this->add_script(
|
||||
<<<EOF
|
||||
function GetAbsoluteUrlAppRoot()
|
||||
{
|
||||
return '$sAbsURLAppRoot';
|
||||
}
|
||||
|
||||
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';
|
||||
if (sContext.length > 0)
|
||||
{
|
||||
if (sURL.indexOf('?') == -1)
|
||||
{
|
||||
return sURL+'?'+sContext;
|
||||
}
|
||||
return sURL+'&'+sContext;
|
||||
}
|
||||
return sURL;
|
||||
}
|
||||
EOF
|
||||
);
|
||||
}
|
||||
|
||||
public function SetRootUrl($sRootUrl)
|
||||
{
|
||||
$this->m_sRootUrl = $sRootUrl;
|
||||
}
|
||||
|
||||
public function small_p($sText)
|
||||
{
|
||||
$this->add("<p style=\"font-size:smaller\">$sText</p>\n");
|
||||
}
|
||||
}
|
||||
|
||||
public function GetAbsoluteUrlAppRoot()
|
||||
{
|
||||
return utils::GetAbsoluteUrlAppRoot();
|
||||
}
|
||||
|
||||
public function GetAbsoluteUrlModulesRoot()
|
||||
{
|
||||
return utils::GetAbsoluteUrlModulesRoot();
|
||||
}
|
||||
|
||||
function GetApplicationContext()
|
||||
{
|
||||
$oAppContext = new ApplicationContext();
|
||||
return $oAppContext->GetForLink();
|
||||
}
|
||||
|
||||
// By Rom, used by CSVImport and Advanced search
|
||||
public function MakeClassesSelect($sName, $sDefaultValue, $iWidthPx, $iActionCode = null)
|
||||
@@ -100,6 +219,7 @@ class NiceWebPage extends WebPage
|
||||
*/
|
||||
public function output()
|
||||
{
|
||||
//$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,31 +1,43 @@
|
||||
<?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");
|
||||
require_once(APPROOT."/application/applicationcontext.class.inc.php");
|
||||
require_once(APPROOT."/application/user.preferences.class.inc.php");
|
||||
|
||||
define('BUTTON_CANCEL', 1);
|
||||
define('BUTTON_BACK', 2);
|
||||
define('BUTTON_NEXT', 4);
|
||||
define('BUTTON_FINISH', 8);
|
||||
|
||||
define('PARAM_ARROW_SEP', '_x_');
|
||||
|
||||
class TransactionException extends Exception
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Web page with some associated CSS and scripts (jquery) for a fancier display
|
||||
* of the Portal web page
|
||||
@@ -35,16 +47,24 @@ class PortalWebPage extends NiceWebPage
|
||||
/**
|
||||
* Portal menu
|
||||
*/
|
||||
protected $m_sWelcomeMsg;
|
||||
protected $m_aMenuButtons;
|
||||
|
||||
public function __construct($sTitle, $sAlternateStyleSheet = '')
|
||||
{
|
||||
$this->m_sWelcomeMsg = '';
|
||||
$this->m_aMenuButtons = array();
|
||||
parent::__construct($sTitle);
|
||||
$this->add_header("Content-type: text/html; charset=utf-8");
|
||||
$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");
|
||||
@@ -62,10 +82,16 @@ class PortalWebPage extends NiceWebPage
|
||||
$this->add_linked_script("../js/jquery.popupmenu.js");
|
||||
$this->add_linked_script("../js/date.js");
|
||||
$this->add_linked_script("../js/jquery.tablesorter.min.js");
|
||||
$this->add_linked_script("../js/jquery.tablesorter.pager.js");
|
||||
$this->add_linked_script("../js/jquery.blockUI.js");
|
||||
$this->add_linked_script("../js/utils.js");
|
||||
$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_linked_script("../js/ckeditor/ckeditor.js");
|
||||
$this->add_linked_script("../js/ckeditor/adapters/jquery.js");
|
||||
$this->add_ready_script(
|
||||
<<<EOF
|
||||
try
|
||||
@@ -106,8 +132,6 @@ try
|
||||
}
|
||||
});
|
||||
|
||||
$("table.listResults").tableHover(); // hover tables
|
||||
$(".listResults").tablesorter( { widgets: ['myZebra', 'truncatedList']} ); // sortable and zebra tables
|
||||
$(".date-pick").datepicker({
|
||||
showOn: 'button',
|
||||
buttonImage: '../images/calendar.png',
|
||||
@@ -117,7 +141,18 @@ try
|
||||
changeMonth: true,
|
||||
changeYear: true
|
||||
});
|
||||
$('.resizable').resizable(); // Make resizable everything that claims to be resizable !
|
||||
|
||||
$(".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(); });
|
||||
}
|
||||
catch(err)
|
||||
@@ -130,9 +165,18 @@ EOF
|
||||
|
||||
$this->add_script(
|
||||
<<<EOF
|
||||
function CheckSelection(sMessage)
|
||||
function CheckSelection(sMessage, sInputId)
|
||||
{
|
||||
var bResult = ($('input:checked').length > 0);
|
||||
var bResult;
|
||||
if (sInputId.length > 0)
|
||||
{
|
||||
bResult = ($('input[name='+sInputId+']:checked').length > 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// First select found...
|
||||
bResult = ($('input:checked').length > 0);
|
||||
}
|
||||
if (!bResult)
|
||||
{
|
||||
alert(sMessage);
|
||||
@@ -140,19 +184,99 @@ EOF
|
||||
return bResult;
|
||||
}
|
||||
|
||||
function GoBack()
|
||||
|
||||
function GetAbsoluteUrlAppRoot()
|
||||
{
|
||||
var form = $('#request_form');
|
||||
var step = $('input[name=step]');
|
||||
return '$sAbsURLAppRoot';
|
||||
}
|
||||
|
||||
function GetAbsoluteUrlModulesRoot()
|
||||
{
|
||||
return '$sAbsURLModulesRoot';
|
||||
}
|
||||
|
||||
function AddAppContext(sURL)
|
||||
{
|
||||
var sContext = '$sAppContext';
|
||||
if (sContext.length > 0)
|
||||
{
|
||||
if (sURL.indexOf('?') == -1)
|
||||
{
|
||||
return sURL+'?'+sContext;
|
||||
}
|
||||
return sURL+'&'+sContext;
|
||||
}
|
||||
return sURL;
|
||||
}
|
||||
|
||||
function GoBack(sFormId)
|
||||
{
|
||||
var form = $('#'+sFormId);
|
||||
var step_back = $('input[name=step_back]');
|
||||
|
||||
form.unbind('submit'); // De-activate validation
|
||||
step.val(step.val() -2); // To go Back one step: next step is x, current step is x-1, previous step is x-2
|
||||
|
||||
step_back.val(1);
|
||||
form.submit(); // Go
|
||||
}
|
||||
|
||||
function GoHome()
|
||||
{
|
||||
var form = $('FORM');
|
||||
form.unbind('submit'); // De-activate validation
|
||||
window.location.href = '?operation=';
|
||||
return false;
|
||||
}
|
||||
|
||||
function SetWizardNextStep(sStep)
|
||||
{
|
||||
var next_step = $('input[id=next_step]');
|
||||
next_step.val(sStep);
|
||||
}
|
||||
|
||||
// For disabling the CKEditor at init time when the corresponding textarea is disabled !
|
||||
CKEDITOR.plugins.add( 'disabler',
|
||||
{
|
||||
init : function( editor )
|
||||
{
|
||||
editor.on( 'instanceReady', function(e)
|
||||
{
|
||||
e.removeListener();
|
||||
$('#'+ editor.name).trigger('update');
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
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 = '')
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a welcome message (optional)
|
||||
*/
|
||||
public function SetWelcomeMessage($sMsg)
|
||||
{
|
||||
$this->m_sWelcomeMsg = $sMsg;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a button to the portal's main menu
|
||||
@@ -161,17 +285,648 @@ EOF
|
||||
{
|
||||
$this->m_aMenuButtons[] = array('id' => $sId, 'label' => $sLabel, 'hyperlink' => $sHyperlink);
|
||||
}
|
||||
|
||||
var $m_bEnableDisconnectButton = true;
|
||||
public function EnableDisconnectButton($bEnable)
|
||||
{
|
||||
$this->m_bEnableDisconnectButton = $bEnable;
|
||||
}
|
||||
|
||||
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 = '';
|
||||
$this->AddMenuButton('logoff', 'Portal:Disconnect', '../pages/logoff.php'); // This menu is always present and is the last one
|
||||
if ($this->m_bEnableDisconnectButton)
|
||||
{
|
||||
$this->AddMenuButton('logoff', 'Portal:Disconnect', utils::GetAbsoluteUrlAppRoot().'pages/logoff.php'); // This menu is always present and is the last one
|
||||
}
|
||||
foreach($this->m_aMenuButtons as $aMenuItem)
|
||||
{
|
||||
$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="banner"><div id="logo"></div>'.$sMenu.'</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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a list of objects, without any hyperlink (except for the object's details)
|
||||
* @param DBObjectSet $oSet The set of objects to display
|
||||
* @param Array $aZList The ZList (list of field codes) to use for the tabular display
|
||||
* @param String $sEmptyListMessage Message displayed whenever the list is empty
|
||||
* @return string The HTML text representing the list
|
||||
*/
|
||||
public function DisplaySet($oSet, $aZList, $sEmptyListMessage = '')
|
||||
{
|
||||
if ($oSet->Count() > 0)
|
||||
{
|
||||
$sClass = $oSet->GetClass();
|
||||
if (is_subclass_of($sClass, 'cmdbAbstractObject'))
|
||||
{
|
||||
// Home-made and very limited display of an object set
|
||||
|
||||
$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
|
||||
{
|
||||
// Home-made and very limited display of an object set
|
||||
$aAttribs = array();
|
||||
$aValues = array();
|
||||
$aAttribs['key'] = array('label' => MetaModel::GetName($sClass), 'description' => '');
|
||||
foreach($aZList as $sAttCode)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
$aAttribs[$sAttCode] = array('label' => $oAttDef->GetLabel(), 'description' => $oAttDef->GetDescription());
|
||||
}
|
||||
while($oObj = $oSet->Fetch())
|
||||
{
|
||||
$aRow = array();
|
||||
|
||||
$aRow['key'] = '<a href="./index.php?operation=details&class='.get_class($oObj).'&id='.$oObj->GetKey().'">'.$oObj->GetName().'</a>';
|
||||
$sHilightClass = $oObj->GetHilightClass();
|
||||
if ($sHilightClass != '')
|
||||
{
|
||||
$aRow['@class'] = $sHilightClass;
|
||||
}
|
||||
foreach($aZList as $sAttCode)
|
||||
{
|
||||
$aRow[$sAttCode] = $oObj->GetAsHTML($sAttCode);
|
||||
}
|
||||
$aValues[$oObj->GetKey()] = $aRow;
|
||||
}
|
||||
$this->table($aAttribs, $aValues);
|
||||
}
|
||||
}
|
||||
elseif (strlen($sEmptyListMessage) > 0)
|
||||
{
|
||||
$this->add($sEmptyListMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the attributes of an object (no title, no form)
|
||||
* @param Object $oObj Any kind of object
|
||||
* @param aAttList The list of attributes to display
|
||||
* @return void
|
||||
*/
|
||||
public function DisplayObjectDetails($oObj, $aAttList)
|
||||
{
|
||||
$sClass = get_class($oObj);
|
||||
$aDetails = array();
|
||||
foreach($aAttList as $sAttCode)
|
||||
{
|
||||
$iFlags = $oObj->GetAttributeFlags($sAttCode);
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
|
||||
if ( (!$oAttDef->IsLinkSet()) && (($iFlags & OPT_ATT_HIDDEN) == 0) )
|
||||
{
|
||||
// Don't display linked set and non-visible attributes (in this state)
|
||||
$sDisplayValue = $oObj->GetAsHTML($sAttCode);
|
||||
$aDetails[] = array('label' => '<span title="'.MetaModel::GetDescription($sClass, $sAttCode).'">'.MetaModel::GetLabel($sClass, $sAttCode).'</span>', 'value' => $sDisplayValue);
|
||||
}
|
||||
}
|
||||
$this->details($aDetails);
|
||||
}
|
||||
|
||||
/**
|
||||
* DisplayObjectLinkset
|
||||
* @param Object $oObj Any kind of object
|
||||
* @param $sLinkSetAttCode The attribute code of the link set attribute to display
|
||||
* @param $sRemoteAttCode The external key on the linked class, pointing to the remote objects
|
||||
* @param $aZList The list of attribute of the remote object
|
||||
* @param $sEmptyListMessage The message to display if the list is empty
|
||||
* @return void
|
||||
*/
|
||||
public function DisplayObjectLinkset($oObj, $sLinkSetAttCode, $sRemoteAttCode, $aZList, $sEmptyListMessage = '', $oSearchRestriction = null)
|
||||
{
|
||||
if (empty($sEmptyListMessage))
|
||||
{
|
||||
$sEmptyListMessage = Dict::S('UI:Search:NoObjectFound');
|
||||
}
|
||||
|
||||
$oLinkSet = $oObj->Get($sLinkSetAttCode);
|
||||
if ($oLinkSet->Count() > 0)
|
||||
{
|
||||
$sClass = $oLinkSet->GetClass();
|
||||
$oExtKeyToRemote = MetaModel::GetAttributeDef($sClass, $sRemoteAttCode);
|
||||
$sRemoteClass = $oExtKeyToRemote->GetTargetClass();
|
||||
|
||||
if (is_null($oSearchRestriction))
|
||||
{
|
||||
$oObjSearch = new DBObjectSearch($sRemoteClass);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oObjSearch = $oSearchRestriction;
|
||||
}
|
||||
$oObjSearch->AddCondition_ReferencedBy($oLinkSet->GetFilter(), $sRemoteAttCode);
|
||||
|
||||
$aExtraParams = array('menu' => false, 'toolkit_menu' => false, 'zlist' => false, 'extra_fields' => implode(',', $aZList));
|
||||
$oBlock = new DisplayBlock($oObjSearch, 'list', false);
|
||||
$oBlock->Display($this, 1, $aExtraParams);
|
||||
}
|
||||
elseif (strlen($sEmptyListMessage) > 0)
|
||||
{
|
||||
$this->add($sEmptyListMessage);
|
||||
}
|
||||
}
|
||||
|
||||
protected function DisplaySearchField($sClass, $sAttSpec, $aExtraParams, $sPrefix, $sFieldName = null, $aFilterParams = array())
|
||||
{
|
||||
if (is_null($sFieldName))
|
||||
{
|
||||
$sFieldName = str_replace('->', PARAM_ARROW_SEP, $sAttSpec);
|
||||
}
|
||||
|
||||
$iPos = strpos($sAttSpec, '->');
|
||||
if ($iPos !== false)
|
||||
{
|
||||
$sAttCode = substr($sAttSpec, 0, $iPos);
|
||||
$sSubSpec = substr($sAttSpec, $iPos + 2);
|
||||
|
||||
if (!MetaModel::IsValidAttCode($sClass, $sAttCode))
|
||||
{
|
||||
throw new Exception("Invalid attribute code '$sClass/$sAttCode' in search specification '$sAttSpec'");
|
||||
}
|
||||
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
if ($oAttDef->IsLinkSet())
|
||||
{
|
||||
$sTargetClass = $oAttDef->GetLinkedClass();
|
||||
}
|
||||
elseif ($oAttDef->IsExternalKey(EXTKEY_ABSOLUTE))
|
||||
{
|
||||
$sTargetClass = $oAttDef->GetTargetClass(EXTKEY_ABSOLUTE);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Attribute specification '$sAttSpec', '$sAttCode' should be either a link set or an external key");
|
||||
}
|
||||
$this->DisplaySearchField($sTargetClass, $sSubSpec, $aExtraParams, $sPrefix, $sFieldName, $aFilterParams);
|
||||
}
|
||||
else
|
||||
{
|
||||
// $sAttSpec is an attribute code
|
||||
//
|
||||
$this->add('<span style="white-space: nowrap;padding:5px;display:inline-block;">');
|
||||
$sFilterValue = '';
|
||||
$sFilterValue = utils::ReadParam($sPrefix.$sFieldName, '', false, 'raw_data');
|
||||
$sFilterOpCode = null; // Use the default 'loose' OpCode
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttSpec);
|
||||
if ($oAttDef->IsExternalKey())
|
||||
{
|
||||
$sTargetClass = $oAttDef->GetTargetClass();
|
||||
$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();
|
||||
$this->add("<label>".MetaModel::GetFilterLabel($sClass, $sAttSpec).":</label> ");
|
||||
//$oWidget = UIExtKeyWidget::DIsplayFromAttCode($sAttSpec, $sClass, $oAttDef->GetLabel(), $oAllowedValues, $sFilterValue, $sPrefix.$sFieldName, false, '', $sPrefix, '');
|
||||
//$this->add($oWidget->Display($this, $aExtraParams, true /* bSearchMode */));
|
||||
$aExtKeyParams = $aExtraParams;
|
||||
$aExtKeyParams['iFieldSize'] = $oAttDef->GetMaxSize();
|
||||
$aExtKeyParams['iMinChars'] = $oAttDef->GetMinAutoCompleteChars();
|
||||
// DisplayFromAttCode($this, $sAttCode, $sClass, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName = '', $sFormPrefix = '', $aArgs, $bSearchMode = false)
|
||||
$sHtml = UIExtKeyWidget::DisplayFromAttCode($this, $sAttSpec, $sClass, $oAttDef->GetLabel(), $oAllowedValues, $sFilterValue, $sPrefix.$sFieldName, false, $sPrefix.$sFieldName, $sPrefix, $aExtKeyParams, true);
|
||||
$this->add($sHtml);
|
||||
}
|
||||
else
|
||||
{
|
||||
$aAllowedValues = MetaModel::GetAllowedValues_flt($sClass, $sAttSpec, $aExtraParams);
|
||||
if (is_null($aAllowedValues))
|
||||
{
|
||||
// Any value is possible, display an input box
|
||||
$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
|
||||
{
|
||||
//Enum field or external key, display a combo
|
||||
$sValue = "<select name=\"$sPrefix$sFieldName\">\n";
|
||||
$sValue .= "<option value=\"\">".Dict::S('UI:SearchValue:Any')."</option>\n";
|
||||
foreach($aAllowedValues as $key => $value)
|
||||
{
|
||||
if ($sFilterValue == $key)
|
||||
{
|
||||
$sSelected = ' selected';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sSelected = '';
|
||||
}
|
||||
$sValue .= "<option value=\"$key\"$sSelected>$value</option>\n";
|
||||
}
|
||||
$sValue .= "</select>\n";
|
||||
$this->add("<label>".MetaModel::GetFilterLabel($sClass, $sAttSpec).":</label> $sValue\n");
|
||||
}
|
||||
}
|
||||
unset($aExtraParams[$sFieldName]);
|
||||
$this->add('</span> ');
|
||||
|
||||
$sTip = $oAttDef->GetHelpOnSmartSearch();
|
||||
if (strlen($sTip) > 0)
|
||||
{
|
||||
$sTip = addslashes($sTip);
|
||||
$sTip = str_replace(array("\n", "\r"), " ", $sTip);
|
||||
// :input does represent in form visible input (INPUT, SELECT, TEXTAREA)
|
||||
$this->add_ready_script("$(':input[name={$sPrefix}$sFieldName]').qtip( { content: '$sTip', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(
|
||||
<<<EOF
|
||||
$("#dh_$sPrefix").click( function() {
|
||||
$("#ds_$sPrefix").slideToggle('normal', function() { $("#ds_$sPrefix").parent().resize(); } );
|
||||
$("#dh_$sPrefix").toggleClass('open');
|
||||
});
|
||||
EOF
|
||||
);
|
||||
$this->add("<form id=\"search_$sClass\" action=\"\" method=\"post\">\n"); // Don't use $_SERVER['SCRIPT_NAME'] since the form may be called asynchronously (from ajax.php)
|
||||
// $this->add("<h2>".Dict::Format('UI:SearchFor_Class_Objects', 'xxxxxx')."</h2>\n");
|
||||
$this->add("<p>\n");
|
||||
foreach($aAttList as $sAttSpec)
|
||||
{
|
||||
//$oAppContext->Reset($sAttSpec); // Make sure the same parameter will not be passed twice
|
||||
$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)
|
||||
{
|
||||
// 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");
|
||||
$this->add("</div>\n");
|
||||
$this->add("<div class=\"HRDrawer\"></div>\n");
|
||||
$this->add("<div id=\"dh_$sPrefix\" class=\"DrawerHandle\">".Dict::S('UI:SearchToggle')."</div>\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Read parameters from the page
|
||||
* Parameters that were absent from the page's parameters are not set in the resulting hash array
|
||||
* @input string $sMethod Either get or post
|
||||
* @return Hash Array of name => value corresponding to the parameters that were passed to the page
|
||||
*/
|
||||
public function ReadAllParams($sParamList, $sPrefix = 'attr_')
|
||||
{
|
||||
$aParams = explode(',', $sParamList);
|
||||
$aValues = array();
|
||||
foreach($aParams as $sName)
|
||||
{
|
||||
$sName = trim($sName);
|
||||
$value = utils::ReadParam($sPrefix.$sName, null, false, 'raw_data');
|
||||
if (!is_null($value))
|
||||
{
|
||||
$aValues[$sName] = $value;
|
||||
}
|
||||
}
|
||||
return $aValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs a list of parameters as hidden fields
|
||||
* Example: attr_dummy[-123][id] = "blah"
|
||||
* @param Hash $aParameters Array name => value for the parameters
|
||||
* @param Array $aExclude The list of parameters that must not be handled this way (probably already in the visible part of the form)
|
||||
* @return void
|
||||
*/
|
||||
protected function DumpHiddenParamsInternal($sName, $value)
|
||||
{
|
||||
if (is_array($value))
|
||||
{
|
||||
foreach($value as $sKey => $item)
|
||||
{
|
||||
$this->DumpHiddenParamsInternal($sName.'['.$sKey.']', $item);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->Add("<input type=\"hidden\" name=\"$sName\" value=\"$value\">");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs a list of parameters as hidden field into the current page
|
||||
* (must be called when inside a form)
|
||||
* @param Hash $aParameters Array name => value for the parameters
|
||||
* @param Array $aExclude The list of parameters that must not be handled this way (probably already in the visible part of the form)
|
||||
* @return void
|
||||
*/
|
||||
public function DumpHiddenParams($aParameters, $aExclude = null, $sPrefix = 'attr_')
|
||||
{
|
||||
foreach($aParameters as $sAttCode => $value)
|
||||
{
|
||||
if (is_null($aExclude) || !in_array($sAttCode, $aExclude))
|
||||
{
|
||||
$this->DumpHiddenParamsInternal($sPrefix.$sAttCode, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function PostedParamsToFilter($sClass, $aAttList, $sPrefix)
|
||||
{
|
||||
$oFilter = new DBObjectSearch($sClass);
|
||||
$iCountParams = 0;
|
||||
foreach($aAttList as $sAttSpec)
|
||||
{
|
||||
$sFieldName = str_replace('->', PARAM_ARROW_SEP, $sAttSpec);
|
||||
$value = utils::ReadPostedParam($sPrefix.$sFieldName, null, 'raw_data');
|
||||
if (!is_null($value) && (is_array($value) ? count($value)>0 : strlen($value)>0))
|
||||
{
|
||||
$oFilter->AddConditionAdvanced($sAttSpec, $value);
|
||||
$iCountParams++;
|
||||
}
|
||||
}
|
||||
if ($iCountParams == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return $oFilter;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the object form POSTED arguments, and writes it into the DB (applies a stimuli if requested)
|
||||
* @param DBObject $oObj The object to update
|
||||
* $param array $aAttList If set, this will limit the list of updated attributes
|
||||
* @return void
|
||||
*/
|
||||
public function DoUpdateObjectFromPostedForm(DBObject $oObj, $aAttList = null)
|
||||
{
|
||||
$sTransactionId = utils::ReadPostedParam('transaction_id', '');
|
||||
if (!utils::IsTransactionValid($sTransactionId))
|
||||
{
|
||||
throw new TransactionException();
|
||||
}
|
||||
|
||||
$sClass = get_class($oObj);
|
||||
|
||||
$sStimulus = trim(utils::ReadPostedParam('apply_stimulus', ''));
|
||||
$sTargetState = '';
|
||||
if (!empty($sStimulus))
|
||||
{
|
||||
// Compute the target state
|
||||
|
||||
$aTransitions = $oObj->EnumTransitions();
|
||||
if (!isset($aTransitions[$sStimulus]))
|
||||
{
|
||||
throw new ApplicationException(Dict::Format('UI:Error:Invalid_Stimulus_On_Object_In_State', $sStimulus, $oObj->GetName(), $oObj->GetStateLabel()));
|
||||
}
|
||||
$sTargetState = $aTransitions[$sStimulus]['target_state'];
|
||||
}
|
||||
|
||||
$oObj->UpdateObjectFromPostedForm('' /* form prefix */, $aAttList, $sTargetState);
|
||||
|
||||
// Optional: apply a stimulus
|
||||
//
|
||||
if (!empty($sStimulus))
|
||||
{
|
||||
if (!$oObj->ApplyStimulus($sStimulus))
|
||||
{
|
||||
throw new Exception("Cannot apply stimulus '$sStimulus' to {$oObj->GetName()}");
|
||||
}
|
||||
}
|
||||
|
||||
if ($oObj->IsModified())
|
||||
{
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the object of the specified Class/ID.
|
||||
* @param WebPage $oP The current page
|
||||
* @return DBObject The found object, or throws an exception in case of failure
|
||||
*/
|
||||
public function FindObjectFromArgs($aAllowedClasses = null)
|
||||
{
|
||||
$sClass = utils::ReadParam('class', '', true, 'class');
|
||||
$iId = utils::ReadParam('id', 0, true, 'integer');
|
||||
|
||||
if (empty($sClass))
|
||||
{
|
||||
throw new Exception("Missing argument 'class'");
|
||||
}
|
||||
if (!MetaModel::IsValidClass($sClass))
|
||||
{
|
||||
throw new Exception("Wrong value for argument 'class': $sClass");
|
||||
}
|
||||
if ($iId == 0)
|
||||
{
|
||||
throw new Exception("Missing argument 'id'");
|
||||
}
|
||||
|
||||
if(!is_null($aAllowedClasses))
|
||||
{
|
||||
$bAllowed = false;
|
||||
foreach($aAllowedClasses as $sParentClass)
|
||||
{
|
||||
if (MetaModel::IsParentClass($sParentClass, $sClass))
|
||||
{
|
||||
$bAllowed = true;
|
||||
}
|
||||
}
|
||||
if (!$bAllowed)
|
||||
{
|
||||
throw new Exception("Class '$sClass not allowed in this implementation'");
|
||||
}
|
||||
}
|
||||
|
||||
$oObj = MetaModel::GetObject($sClass, $iId, false);
|
||||
if (!is_object($oObj))
|
||||
{
|
||||
throw new Exception("Could not find the object $sClass/$iId");
|
||||
}
|
||||
return $oObj;
|
||||
}
|
||||
|
||||
var $m_sWizardId = null;
|
||||
|
||||
public function WizardFormStart($sId = '', $sNextStep = null, $bAttachment = false, $sMethod = 'post')
|
||||
{
|
||||
$this->m_sWizardId = $sId;
|
||||
|
||||
// multipart... needed for file upload
|
||||
$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)
|
||||
{
|
||||
// Back into the past history
|
||||
array_pop($aPreviousSteps);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Moving forward
|
||||
array_push($aPreviousSteps, utils::ReadParam('next_step'));
|
||||
}
|
||||
|
||||
$sStepHistory = implode(',', $aPreviousSteps);
|
||||
$this->add("<input type=\"hidden\" id=\"step_history\" name=\"step_history\" value=\"".htmlentities($sStepHistory, ENT_QUOTES, 'UTF-8')."\">");
|
||||
|
||||
if (!is_null($sNextStep))
|
||||
{
|
||||
$this->add("<input type=\"hidden\" id=\"next_step\" name=\"next_step\" value=\"$sNextStep\">");
|
||||
}
|
||||
$this->add("<input type=\"hidden\" id=\"step_back\" name=\"step_back\" value=\"0\">");
|
||||
|
||||
$sTransactionId = utils::GetNewTransactionId();
|
||||
$this->SetTransactionId($sTransactionId);
|
||||
$this->add("<input type=\"hidden\" id=\"transaction_id\" name=\"transaction_id\" value=\"$sTransactionId\">\n");
|
||||
$this->add_ready_script("$(window).unload(function() { OnUnload('$sTransactionId') } );\n");
|
||||
}
|
||||
|
||||
public function WizardFormButtons($iButtonFlags)
|
||||
{
|
||||
$aButtons = array();
|
||||
if ($iButtonFlags & BUTTON_CANCEL)
|
||||
{
|
||||
$aButtons[] = "<input id=\"btn_cancel\" type=\"button\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"GoHome();\">";
|
||||
}
|
||||
if ($iButtonFlags & BUTTON_BACK)
|
||||
{
|
||||
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)
|
||||
{
|
||||
$aButtons[] = "<input id=\"btn_next\" type=\"submit\" value=\"".Dict::S('UI:Button:Next')."\">";
|
||||
}
|
||||
if ($iButtonFlags & BUTTON_FINISH)
|
||||
{
|
||||
$aButtons[] = "<input id=\"btn_finish\" type=\"submit\" value=\"".Dict::S('UI:Button:Finish')."\">";
|
||||
}
|
||||
|
||||
$this->add('<div id="buttons">');
|
||||
$this->add(implode('', $aButtons));
|
||||
$this->add('</div>');
|
||||
}
|
||||
|
||||
public function WizardFormEnd()
|
||||
{
|
||||
$this->add("</form>\n");
|
||||
}
|
||||
|
||||
public function GetWizardStep()
|
||||
{
|
||||
if (utils::ReadParam('step_back', 0) == 1)
|
||||
{
|
||||
// Take the value into the history - one level above
|
||||
$aPreviousSteps = $this->GetWizardStepHistory();
|
||||
array_pop($aPreviousSteps);
|
||||
return end($aPreviousSteps);
|
||||
}
|
||||
else
|
||||
{
|
||||
return utils::ReadParam('next_step');
|
||||
}
|
||||
}
|
||||
|
||||
protected function GetWizardStepHistory()
|
||||
{
|
||||
$sRawHistory = trim(utils::ReadParam('step_history', '', false, 'raw_data'));
|
||||
if (strlen($sRawHistory) == 0)
|
||||
{
|
||||
return array();
|
||||
}
|
||||
else
|
||||
{
|
||||
return explode(',', $sRawHistory);
|
||||
}
|
||||
}
|
||||
|
||||
public function WizardCheckSelectionOnSubmit($sMessageIfNoSelection, $sInputName = '')
|
||||
{
|
||||
$this->add_ready_script(
|
||||
<<<EOF
|
||||
$('#{$this->m_sWizardId}').submit(function() {
|
||||
return CheckSelection('$sMessageIfNoSelection', '$sInputName');
|
||||
});
|
||||
EOF
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
130
application/query.class.inc.php
Normal file
130
application/query.class.inc.php
Normal file
@@ -0,0 +1,130 @@
|
||||
<?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/>
|
||||
|
||||
|
||||
/**
|
||||
* Persistent class Event and derived
|
||||
* Application internal events
|
||||
* There is also a file log
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
abstract class Query extends cmdbAbstractObject
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb,view_in_gui,application",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_query",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "realclass",
|
||||
"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())));
|
||||
MetaModel::Init_AddAttribute(new AttributeText("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeText("fields", array("allowed_values"=>null, "sql"=>"fields", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('name', 'description', 'fields')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('description')); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
MetaModel::Init_SetZListItems('standard_search', array('name', 'description', 'fields')); // Criteria of the std search form
|
||||
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
|
||||
}
|
||||
}
|
||||
|
||||
class QueryOQL extends Query
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb,view_in_gui,application",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_query_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())));
|
||||
|
||||
// Display lists
|
||||
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', '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())
|
||||
{
|
||||
$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');
|
||||
$sMessage = null;
|
||||
try
|
||||
{
|
||||
$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);
|
||||
}
|
||||
}
|
||||
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 DesignerIntegerField('auto_reload_sec', Dict::S('Class:ShortcutOQL/Attribute:auto_reload_sec'), MetaModel::GetConfig()->GetStandardReloadInterval());
|
||||
$oField->SetBoundaries(MetaModel::GetConfig()->Get('min_reload_interval'), null); // no upper limit
|
||||
$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::Format('Class:ShortcutOQL/Attribute:auto_reload_sec/tip', MetaModel::GetConfig()->Get('min_reload_interval')));
|
||||
|
||||
$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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
531
application/sqlblock.class.inc.php
Normal file
531
application/sqlblock.class.inc.php
Normal file
@@ -0,0 +1,531 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
/**
|
||||
* SqlBlock - display tables or charts, given an SQL query - use cautiously!
|
||||
*
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
require_once(APPROOT.'/application/webpage.class.inc.php');
|
||||
require_once(APPROOT.'/application/utils.inc.php');
|
||||
|
||||
require_once(APPROOT.'/pages/php-ofc-library/open-flash-chart.php');
|
||||
|
||||
/**
|
||||
* Helper class to design optimized dashboards, based on an SQL query
|
||||
*
|
||||
*/
|
||||
class SqlBlock
|
||||
{
|
||||
protected $m_sQuery;
|
||||
protected $m_aColumns;
|
||||
protected $m_sTitle;
|
||||
protected $m_sType;
|
||||
protected $m_aParams;
|
||||
|
||||
public function __construct($sQuery, $aColumns, $sTitle, $sType, $aParams = array())
|
||||
{
|
||||
$this->m_sQuery = $sQuery;
|
||||
$this->m_aColumns = $aColumns;
|
||||
$this->m_sTitle = $sTitle;
|
||||
$this->m_sType = $sType;
|
||||
$this->m_aParams = $aParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a SqlBlock object from an XML template
|
||||
/*
|
||||
*
|
||||
* <sqlblock>
|
||||
* <sql>SELECT date_format(start_date, '%d') AS Date, count(*) AS Count FROM ticket WHERE DATE_SUB(NOW(), INTERVAL 15 DAY) < start_date AND finalclass = 'UserIssue' GROUP BY date_format(start_date, '%d') AND $CONDITION(param1, ticket.org_id)$</sql>
|
||||
* <type>table</type>
|
||||
* <title>UserRequest:Overview-Title</title>
|
||||
* <parameter>
|
||||
* <name>param1</name>
|
||||
* <type>context</type>
|
||||
* <mapping>org_id</mapping>
|
||||
* </parameter>
|
||||
* <column>
|
||||
* <name>Date</name>
|
||||
* <label>UserRequest:Overview-Date</label>
|
||||
* <drilldown></drilldown>
|
||||
* </column>
|
||||
* <column>
|
||||
* <name>Count</name>
|
||||
* <label>UserRequest:Overview-Count</label>
|
||||
* <drilldown>SELECT UserIssue WHERE date_format(start_date, '%d') = :Date</drilldown>
|
||||
* </column>
|
||||
* </sqlblock>
|
||||
*
|
||||
* Tags
|
||||
* - sql: a (My)SQL query. Do not forget to use html entities (e.g. < for <)
|
||||
* - type: table (default), bars or pie. If bars or pie is selected only the two first columns are taken into account.
|
||||
* - title: optional title, typed in clear or given as a dictionnary entry
|
||||
* - parameter: specifies how to map the context parameters (namely org_id) to a given named parameter in the query.
|
||||
* The expression $CONDITION(<param_name>, <sql_column_name>) will be automatically replaced by:
|
||||
* either the string "1" if there is no restriction on the organisation in iTop
|
||||
* or the string "(<sql_column_name>=<value_of_org_id>)" if there is a limitation to one organizations in iTop
|
||||
* or the string "(<sql_column_name> IN (<values_of_org_id>))" if there is a limitation to a given set of organizations in iTop
|
||||
* - column: specification of a column (not displayed if omitted)
|
||||
* - column / name: name of the column in the SQL query (use aliases)
|
||||
* - column / label: label, typed in clear or given as a dictionnary entry
|
||||
* - column / drilldown: NOT IMPLEMENTED YET - OQL with parameters corresponding to column names (in the query)
|
||||
*
|
||||
* @param $sTemplate string The XML template
|
||||
* @return DisplayBlock The DisplayBlock object, or null if the template is invalid
|
||||
*/
|
||||
public static function FromTemplate($sTemplate)
|
||||
{
|
||||
$oXml = simplexml_load_string('<root>'.$sTemplate.'</root>', 'SimpleXMLElement', LIBXML_NOCDATA);
|
||||
if (false)
|
||||
{
|
||||
// Debug
|
||||
echo "<pre>\n";
|
||||
print_r($oXml);
|
||||
echo "</pre>\n";
|
||||
}
|
||||
|
||||
if (isset($oXml->title))
|
||||
{
|
||||
$sTitle = (string)$oXml->title;
|
||||
}
|
||||
if (isset($oXml->type))
|
||||
{
|
||||
$sType = (string)$oXml->type;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sType = 'table';
|
||||
}
|
||||
if (!isset($oXml->sql))
|
||||
{
|
||||
throw new Exception('Missing tag "sql" in sqlblock');
|
||||
}
|
||||
$sQuery = (string)$oXml->sql;
|
||||
|
||||
$aColumns = array();
|
||||
if (isset($oXml->column))
|
||||
{
|
||||
foreach ($oXml->column AS $oColumnData)
|
||||
{
|
||||
if (!isset($oColumnData->name))
|
||||
{
|
||||
throw new Exception("Missing tag 'name' in sqlblock/column");
|
||||
}
|
||||
$sName = (string) $oColumnData->name;
|
||||
if (strlen($sName) == 0)
|
||||
{
|
||||
throw new Exception("Empty tag 'name' in sqlblock/column");
|
||||
}
|
||||
|
||||
$aColumns[$sName] = array();
|
||||
if (isset($oColumnData->label))
|
||||
{
|
||||
$sLabel = (string)$oColumnData->label;
|
||||
if (strlen($sLabel) > 0)
|
||||
{
|
||||
$aColumns[$sName]['label'] = Dict::S($sLabel);
|
||||
}
|
||||
}
|
||||
if (isset($oColumnData->drilldown))
|
||||
{
|
||||
$sDrillDown = (string)$oColumnData->drilldown;
|
||||
if (strlen($sDrillDown) > 0)
|
||||
{
|
||||
$aColumns[$sName]['drilldown'] = $sDrillDown;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$aParams = array();
|
||||
if (isset($oXml->parameter))
|
||||
{
|
||||
foreach ($oXml->parameter AS $oParamData)
|
||||
{
|
||||
if (!isset($oParamData->name))
|
||||
{
|
||||
throw new Exception("Missing tag 'name' for parameter in sqlblock/column");
|
||||
}
|
||||
$sName = (string) $oParamData->name;
|
||||
if (strlen($sName) == 0)
|
||||
{
|
||||
throw new Exception("Empty tag 'name' for parameter in sqlblock/column");
|
||||
}
|
||||
if (!isset($oParamData->mapping))
|
||||
{
|
||||
throw new Exception("Missing tag 'mapping' for parameter in sqlblock/column");
|
||||
}
|
||||
$sMapping = (string) $oParamData->mapping;
|
||||
if (strlen($sMapping) == 0)
|
||||
{
|
||||
throw new Exception("Empty tag 'mapping' for parameter in sqlblock/column");
|
||||
}
|
||||
|
||||
if (isset($oParamData->type))
|
||||
{
|
||||
$sParamType = $oParamData->type;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sParamType = 'context';
|
||||
}
|
||||
$aParams[$sName] = array('mapping' => $sMapping, 'type' => $sParamType);
|
||||
}
|
||||
}
|
||||
|
||||
return new SqlBlock($sQuery, $aColumns, $sTitle, $sType, $aParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the defined parameters into the SQL query
|
||||
* @return string the SQL query to execute
|
||||
*/
|
||||
public function BuildQuery()
|
||||
{
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sQuery = $this->m_sQuery;
|
||||
$sQuery = str_replace('$DB_PREFIX$', MetaModel::GetConfig()->GetDBSubname(), $sQuery); // put the tables DB prefix (if any)
|
||||
foreach($this->m_aParams as $sName => $aParam)
|
||||
{
|
||||
if ($aParam['type'] == 'context')
|
||||
{
|
||||
$sSearchPattern = '/\$CONDITION\('.$sName.',([^\)]+)\)\$/';
|
||||
$value = $oAppContext->GetCurrentValue($aParam['mapping']);
|
||||
if (empty($value))
|
||||
{
|
||||
$sSQLExpr = '(1)';
|
||||
}
|
||||
else
|
||||
{
|
||||
// Special case for managing the hierarchy of organizations
|
||||
if (($aParam['mapping'] == 'org_id') && ( MetaModel::IsValidClass('Organization')))
|
||||
{
|
||||
$sHierarchicalKeyCode = MetaModel::IsHierarchicalClass('Organization');
|
||||
if ($sHierarchicalKeyCode != false)
|
||||
{
|
||||
// organizations are in hierarchy... gather all the orgs below the given one...
|
||||
$sOQL = "SELECT Organization AS node JOIN Organization AS root ON node.$sHierarchicalKeyCode BELOW root.id WHERE root.id = :value";
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), array(), array('value' => $value));
|
||||
$aOrgIds = array();
|
||||
while($oOrg = $oSet->Fetch())
|
||||
{
|
||||
$aOrgIds[]= $oOrg->GetKey();
|
||||
}
|
||||
$sSQLExpr = '($1 IN('.implode(',', $aOrgIds).'))';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sSQLExpr = '($1 = '.CMDBSource::Quote($value).')';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$sSQLExpr = '($1 = '.CMDBSource::Quote($value).')';
|
||||
}
|
||||
}
|
||||
$sQuery = preg_replace($sSearchPattern, $sSQLExpr, $sQuery);
|
||||
}
|
||||
}
|
||||
return $sQuery;
|
||||
}
|
||||
|
||||
public function RenderContent(WebPage $oPage, $aExtraParams = array())
|
||||
{
|
||||
if (empty($aExtraParams['currentId']))
|
||||
{
|
||||
$sId = 'sqlblock_'.$oPage->GetUniqueId(); // Works only if the page is not an Ajax one !
|
||||
}
|
||||
else
|
||||
{
|
||||
$sId = $aExtraParams['currentId'];
|
||||
}
|
||||
// $oPage->add($this->GetRenderContent($oPage, $aExtraParams, $sId));
|
||||
|
||||
$sQuery = $this->BuildQuery();
|
||||
$res = CMDBSource::Query($sQuery);
|
||||
$aQueryCols = CMDBSource::GetColumns($res);
|
||||
|
||||
// Prepare column definitions (check + give default values)
|
||||
//
|
||||
foreach($this->m_aColumns as $sName => $aColumnData)
|
||||
{
|
||||
if (!in_array($sName, $aQueryCols))
|
||||
{
|
||||
throw new Exception("Unknown column name '$sName' in sqlblock column");
|
||||
}
|
||||
if (!isset($aColumnData['label']))
|
||||
{
|
||||
$this->m_aColumns[$sName]['label'] = $sName;
|
||||
}
|
||||
if (isset($aColumnData['drilldown']) && !empty($aColumnData['drilldown']))
|
||||
{
|
||||
// Check if the OQL is valid
|
||||
try
|
||||
{
|
||||
$this->m_aColumns[$sName]['filter'] = DBObjectSearch::FromOQL($aColumnData['drilldown']);
|
||||
}
|
||||
catch(OQLException $e)
|
||||
{
|
||||
unset($aColumnData['drilldown']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (strlen($this->m_sTitle) > 0)
|
||||
{
|
||||
$oPage->add("<h2>".Dict::S($this->m_sTitle)."</h2>\n");
|
||||
}
|
||||
|
||||
switch ($this->m_sType)
|
||||
{
|
||||
case 'bars':
|
||||
case 'pie':
|
||||
$aColNames = array_keys($this->m_aColumns);
|
||||
$sXColName = $aColNames[0];
|
||||
$sYColName = $aColNames[1];
|
||||
$aData = array();
|
||||
$aRows = array();
|
||||
while($aRow = CMDBSource::FetchArray($res))
|
||||
{
|
||||
$aData[$aRow[$sXColName]] = $aRow[$sYColName];
|
||||
$aRows[$aRow[$sXColName]] = $aRow;
|
||||
}
|
||||
$this->RenderChart($oPage, $sId, $aData, $this->m_aColumns[$sYColName]['drilldown'], $aRows);
|
||||
break;
|
||||
|
||||
default:
|
||||
case 'table':
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sContext = $oAppContext->GetForLink();
|
||||
if (!empty($sContext))
|
||||
{
|
||||
$sContext = '&'.$sContext;
|
||||
}
|
||||
$aDisplayConfig = array();
|
||||
foreach($this->m_aColumns as $sName => $aColumnData)
|
||||
{
|
||||
$aDisplayConfig[$sName] = array('label' => $aColumnData['label'], 'description' => '');
|
||||
}
|
||||
|
||||
$aDisplayData = array();
|
||||
while($aRow = CMDBSource::FetchArray($res))
|
||||
{
|
||||
$aSQLColNames = array_keys($aRow);
|
||||
$aDisplayRow = array();
|
||||
foreach($this->m_aColumns as $sName => $aColumnData)
|
||||
{
|
||||
if (isset($aColumnData['filter']))
|
||||
{
|
||||
$sFilter = $aColumnData['drilldown'];
|
||||
$sClass = $aColumnData['filter']->GetClass();
|
||||
$sFilter = str_replace('SELECT '.$sClass, '', $sFilter);
|
||||
foreach($aSQLColNames as $sColName)
|
||||
{
|
||||
$sFilter = str_replace(':'.$sColName, "'".addslashes( $aRow[$sColName] )."'", $sFilter);
|
||||
}
|
||||
$sURL = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search_oql&search_form=0&oql_class='.$sClass.'&oql_clause='.urlencode($sFilter).'&format=html'.$sContext;
|
||||
$aDisplayRow[$sName] = '<a href="'.$sURL.'">'.$aRow[$sName]."</a>";
|
||||
}
|
||||
else
|
||||
{
|
||||
$aDisplayRow[$sName] = $aRow[$sName];
|
||||
}
|
||||
}
|
||||
$aDisplayData[] = $aDisplayRow;
|
||||
}
|
||||
$oPage->table($aDisplayConfig, $aDisplayData);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function GetRenderContent(WebPage $oPage, $aExtraParams = array(), $sId)
|
||||
{
|
||||
$sHtml = '';
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
protected function RenderChart($oPage, $sId, $aValues, $sDrillDown = '', $aRows = array())
|
||||
{
|
||||
// 1- Compute Open Flash Chart data
|
||||
//
|
||||
$aValueKeys = array();
|
||||
$index = 0;
|
||||
if ((count($aValues) > 0) && ($sDrillDown != ''))
|
||||
{
|
||||
$oFilter = DBObjectSearch::FromOQL($sDrillDown);
|
||||
$sClass = $oFilter->GetClass();
|
||||
$sOQLClause = str_replace('SELECT '.$sClass, '', $sDrillDown);
|
||||
$aSQLColNames = array_keys(current($aRows)); // Read the list of columns from the current (i.e. first) element of the array
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sURL = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search_oql&search_form=0&oql_class='.$sClass.'&format=html&'.$oAppContext->GetForLink().'&oql_clause=';
|
||||
}
|
||||
$aURLs = array();
|
||||
foreach($aValues as $key => $value)
|
||||
{
|
||||
// Make sure that values are integers (so that max() will work....)
|
||||
// and build an array of STRING with the keys (numeric keys are transformed into string by PHP :-(
|
||||
$aValues[$key] = (int)$value;
|
||||
$aValueKeys[] = (string)$key;
|
||||
|
||||
// Build the custom query for the 'drill down' on each element
|
||||
if ($sDrillDown != '')
|
||||
{
|
||||
$sFilter = $sOQLClause;
|
||||
foreach($aSQLColNames as $sColName)
|
||||
{
|
||||
$sFilter = str_replace(':'.$sColName, "'".addslashes( $aRows[$key][$sColName] )."'", $sFilter);
|
||||
$aURLs[$index] = $sURL.urlencode($sFilter);
|
||||
}
|
||||
}
|
||||
$index++;
|
||||
}
|
||||
|
||||
$oChart = new open_flash_chart();
|
||||
|
||||
if ($this->m_sType == 'bars')
|
||||
{
|
||||
$oChartElement = new bar_glass();
|
||||
|
||||
if (count($aValues) > 0)
|
||||
{
|
||||
$maxValue = max($aValues);
|
||||
}
|
||||
else
|
||||
{
|
||||
$maxValue = 1;
|
||||
}
|
||||
$oYAxis = new y_axis();
|
||||
$aMagicValues = array(1,2,5,10);
|
||||
$iMultiplier = 1;
|
||||
$index = 0;
|
||||
$iTop = $aMagicValues[$index % count($aMagicValues)]*$iMultiplier;
|
||||
while($maxValue > $iTop)
|
||||
{
|
||||
$index++;
|
||||
$iTop = $aMagicValues[$index % count($aMagicValues)]*$iMultiplier;
|
||||
if (($index % count($aMagicValues)) == 0)
|
||||
{
|
||||
$iMultiplier = $iMultiplier * 10;
|
||||
}
|
||||
}
|
||||
//echo "oYAxis->set_range(0, $iTop, $iMultiplier);\n";
|
||||
$oYAxis->set_range(0, $iTop, $iMultiplier);
|
||||
$oChart->set_y_axis( $oYAxis );
|
||||
$aBarValues = array();
|
||||
foreach($aValues as $iValue)
|
||||
{
|
||||
$oBarValue = new bar_value($iValue);
|
||||
$oBarValue->on_click("ofc_drilldown_{$sId}");
|
||||
$aBarValues[] = $oBarValue;
|
||||
}
|
||||
$oChartElement->set_values($aBarValues);
|
||||
//$oChartElement->set_values(array_values($aValues));
|
||||
$oXAxis = new x_axis();
|
||||
$oXLabels = new x_axis_labels();
|
||||
// set them vertical
|
||||
$oXLabels->set_vertical();
|
||||
// set the label text
|
||||
$oXLabels->set_labels($aValueKeys);
|
||||
// Add the X Axis Labels to the X Axis
|
||||
$oXAxis->set_labels( $oXLabels );
|
||||
$oChart->set_x_axis( $oXAxis );
|
||||
}
|
||||
else
|
||||
{
|
||||
$oChartElement = new pie();
|
||||
$oChartElement->set_start_angle( 35 );
|
||||
$oChartElement->set_animate( true );
|
||||
$oChartElement->set_tooltip( '#label# - #val# (#percent#)' );
|
||||
$oChartElement->set_colours( array('#FF8A00', '#909980', '#2C2B33', '#CCC08D', '#596664') );
|
||||
|
||||
$aData = array();
|
||||
foreach($aValues as $sValue => $iValue)
|
||||
{
|
||||
$oPieValue = new pie_value($iValue, $sValue); //@@ BUG: not passed via ajax !!!
|
||||
$oPieValue->on_click("ofc_drilldown_{$sId}");
|
||||
$aData[] = $oPieValue;
|
||||
}
|
||||
|
||||
$oChartElement->set_values( $aData );
|
||||
$oChart->x_axis = null;
|
||||
}
|
||||
|
||||
// Title given in HTML
|
||||
//$oTitle = new title($this->m_sTitle);
|
||||
//$oChart->set_title($oTitle);
|
||||
$oChart->set_bg_colour('#FFFFFF');
|
||||
$oChart->add_element( $oChartElement );
|
||||
|
||||
$sData = $oChart->toPrettyString();
|
||||
$sData = json_encode($sData);
|
||||
|
||||
// 2- Declare the Javascript function that will render the chart data\
|
||||
//
|
||||
$oPage->add_script(
|
||||
<<< EOF
|
||||
function ofc_get_data_{$sId}()
|
||||
{
|
||||
return $sData;
|
||||
}
|
||||
EOF
|
||||
);
|
||||
|
||||
if (count($aURLs) > 0)
|
||||
{
|
||||
$sURLList = '';
|
||||
foreach($aURLs as $index => $sURL)
|
||||
{
|
||||
$sURLList .= "\taURLs[$index] = '".addslashes($sURL)."';\n";
|
||||
}
|
||||
|
||||
$oPage->add_script(
|
||||
<<< EOF
|
||||
function ofc_drilldown_{$sId}(index)
|
||||
{
|
||||
var aURLs = new Array();
|
||||
{$sURLList}
|
||||
var sURL = aURLs[index];
|
||||
|
||||
window.location.href = sURL; // Navigate !
|
||||
}
|
||||
EOF
|
||||
);
|
||||
}
|
||||
|
||||
// 3- Insert the Open Flash chart
|
||||
//
|
||||
$oPage->add("<div id=\"$sId\"><div>\n");
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
swfobject.embedSWF( "../images/open-flash-chart.swf",
|
||||
"{$sId}",
|
||||
"100%", "300","9.0.0",
|
||||
"expressInstall.swf",
|
||||
{"get-data":"ofc_get_data_{$sId}", "id":"{$sId}"},
|
||||
{'wmode': 'transparent'}
|
||||
);
|
||||
EOF
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -1,31 +1,50 @@
|
||||
<?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');
|
||||
require_once(APPROOT.'/application/utils.inc.php');
|
||||
|
||||
MetaModel::Startup(ITOP_CONFIG_FILE);
|
||||
session_name('itop-'.md5(APPROOT));
|
||||
session_start();
|
||||
$sSwitchEnv = utils::ReadParam('switch_env', null);
|
||||
if (($sSwitchEnv != null) && (file_exists(APPCONF.$sSwitchEnv.'/'.ITOP_CONFIG_FILE)))
|
||||
{
|
||||
$_SESSION['itop_env'] = $sSwitchEnv;
|
||||
$sEnv = $sSwitchEnv;
|
||||
// TODO: reset the credentials as well ??
|
||||
}
|
||||
else if (isset($_SESSION['itop_env']))
|
||||
{
|
||||
$sEnv = $_SESSION['itop_env'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$sEnv = ITOP_DEFAULT_ENV;
|
||||
$_SESSION['itop_env'] = ITOP_DEFAULT_ENV;
|
||||
}
|
||||
$sConfigFile = APPCONF.$sEnv.'/'.ITOP_CONFIG_FILE;
|
||||
MetaModel::Startup($sConfigFile);
|
||||
|
||||
?>
|
||||
|
||||
@@ -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');
|
||||
@@ -35,7 +36,15 @@ class DisplayTemplate
|
||||
|
||||
public function __construct($sTemplate)
|
||||
{
|
||||
$this->m_aTags = array('itopblock', 'itopcheck', 'itoptabs', 'itoptab', 'itoptoggle', 'itopstring');
|
||||
$this->m_aTags = array (
|
||||
'itopblock',
|
||||
'itopcheck',
|
||||
'itoptabs',
|
||||
'itoptab',
|
||||
'itoptoggle',
|
||||
'itopstring',
|
||||
'sqlblock'
|
||||
);
|
||||
$this->m_sTemplate = $sTemplate;
|
||||
}
|
||||
|
||||
@@ -203,6 +212,11 @@ class DisplayTemplate
|
||||
$oPage->add(Dict::S($sContent));
|
||||
break;
|
||||
|
||||
case 'sqlblock':
|
||||
$oBlock = SqlBlock::FromTemplate($sContent);
|
||||
$oBlock->RenderContent($oPage);
|
||||
break;
|
||||
|
||||
case 'itopblock': // No longer used, handled by DisplayBlock::FromTemplate see above
|
||||
$oPage->add("<!-- Application Error: should be handled by DisplayBlock::FromTemplate -->");
|
||||
break;
|
||||
@@ -227,7 +241,6 @@ class DisplayTemplate
|
||||
<itopblock blockclass="HistoryBlock" type="toggle" encoding="text/oql">SELECT CMDBChangeOp WHERE objkey = $id$ AND objclass = \'$class$\'</itopblock>
|
||||
</div>
|
||||
<img src="../../images/connect_to_network.png" style="margin-top:-10px; margin-right:10px; float:right">
|
||||
<itopblock blockclass="DisplayBlock" asynchronous="false" type="bare_details" encoding="text/oql">SELECT NetworkDevice AS d WHERE d.id = $id$</itopblock>
|
||||
<itoptabs>
|
||||
<itoptab name="Interfaces">
|
||||
<itopblock blockclass="DisplayBlock" type="list" encoding="text/oql">SELECT Interface AS i WHERE i.device_id = $id$</itopblock>
|
||||
@@ -248,6 +261,163 @@ class DisplayTemplate
|
||||
}
|
||||
}
|
||||
|
||||
//DisplayTemplate::UnitTest();
|
||||
/**
|
||||
* Special type of template for displaying the details of an object
|
||||
* On top of the defaut 'blocks' managed by the parent class, the following placeholders
|
||||
* are available in such a template:
|
||||
* $attribute_code$ An attribute of the object (in edit mode this is the input for the attribute)
|
||||
* $attribute_code->label()$ The label of an attribute
|
||||
* $PlugIn:plugInClass->properties()$ The ouput of OnDisplayProperties of the specified plugInClass
|
||||
*/
|
||||
class ObjectDetailsTemplate extends DisplayTemplate
|
||||
{
|
||||
public function __construct($sTemplate, $oObj, $sFormPrefix = '')
|
||||
{
|
||||
parent::__construct($sTemplate);
|
||||
$this->m_oObj = $oObj;
|
||||
$this->m_sPrefix = $sFormPrefix;
|
||||
}
|
||||
|
||||
public function Render(WebPage $oPage, $aParams = array(), $bEditMode = false)
|
||||
{
|
||||
$sStateAttCode = MetaModel :: GetStateAttributeCode(get_class($this->m_oObj));
|
||||
$aTemplateFields = array();
|
||||
preg_match_all('/\\$this->([a-z0-9_]+)\\$/', $this->m_sTemplate, $aMatches);
|
||||
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);
|
||||
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();
|
||||
|
||||
$sClass = get_class($this->m_oObj);
|
||||
// Renders the fields used in the template
|
||||
foreach(MetaModel::ListAttributeDefs(get_class($this->m_oObj)) as $sAttCode => $oAttDef)
|
||||
{
|
||||
$aParams['this->label('.$sAttCode.')'] = $oAttDef->GetLabel();
|
||||
$aParams['this->comments('.$sAttCode.')'] = isset($aFieldsComments[$sAttCode]) ? $aFieldsComments[$sAttCode] : '';
|
||||
$iInputId = '2_'.$sAttCode; // TODO: generate a real/unique prefix...
|
||||
if (in_array($sAttCode, $aTemplateFields))
|
||||
{
|
||||
if ($this->m_oObj->IsNew())
|
||||
{
|
||||
$iFlags = $this->m_oObj->GetInitialStateAttributeFlags($sAttCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
$iFlags = $this->m_oObj->GetAttributeFlags($sAttCode);
|
||||
}
|
||||
if (($iFlags & OPT_ATT_MANDATORY) && $this->m_oObj->IsNew())
|
||||
{
|
||||
$iFlags = $iFlags & ~OPT_ATT_READONLY; // Mandatory fields cannot be read-only when creating an object
|
||||
}
|
||||
|
||||
if ((!$oAttDef->IsWritable()) || ($sStateAttCode == $sAttCode))
|
||||
{
|
||||
$iFlags = $iFlags | OPT_ATT_READONLY;
|
||||
}
|
||||
|
||||
if ($iFlags & OPT_ATT_HIDDEN)
|
||||
{
|
||||
$aParams['this->label('.$sAttCode.')'] = '';
|
||||
$aParams['this->field('.$sAttCode.')'] = '';
|
||||
$aParams['this->comments('.$sAttCode.')'] = '';
|
||||
$aParams['this->'.$sAttCode] = '';
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($bEditMode && ($iFlags & (OPT_ATT_READONLY|OPT_ATT_SLAVE)))
|
||||
{
|
||||
// Check if the attribute is not read-only because of a synchro...
|
||||
$aReasons = array();
|
||||
$sSynchroIcon = '';
|
||||
if ($iFlags & OPT_ATT_SLAVE)
|
||||
{
|
||||
$iSynchroFlags = $this->m_oObj->GetSynchroReplicaFlags($sAttCode, $aReasons);
|
||||
$sSynchroIcon = " <img id=\"synchro_$sInputId\" src=\"../images/transp-lock.png\" style=\"vertical-align:middle\"/>";
|
||||
$sTip = '';
|
||||
foreach($aReasons as $aRow)
|
||||
{
|
||||
$sTip .= "<p>Synchronized with {$aRow['name']} - {$aRow['description']}</p>";
|
||||
}
|
||||
$oPage->add_ready_script("$('#synchro_$iInputId').qtip( { content: '$sTip', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );");
|
||||
}
|
||||
|
||||
// Attribute is read-only
|
||||
$sHTMLValue = "<span id=\"field_{$iInputId}\">".$this->m_oObj->GetAsHTML($sAttCode);
|
||||
$sHTMLValue .= '<input type="hidden" id="'.$iInputId.'" name="attr_'.$sAttCode.'" value="'.htmlentities($this->m_oObj->Get($sAttCode), ENT_QUOTES, 'UTF-8').'"/></span>';
|
||||
$aFieldsMap[$sAttCode] = $iInputId;
|
||||
$aParams['this->comments('.$sAttCode.')'] = $sSynchroIcon;
|
||||
}
|
||||
|
||||
if ($bEditMode && !($iFlags & OPT_ATT_READONLY)) //TODO: check the data synchro status...
|
||||
{
|
||||
$aParams['this->field('.$sAttCode.')'] = "<span id=\"field_{$iInputId}\">".$this->m_oObj->GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef,
|
||||
$this->m_oObj->Get($sAttCode),
|
||||
$this->m_oObj->GetEditValue($sAttCode),
|
||||
$iInputId, // InputID
|
||||
'',
|
||||
$iFlags,
|
||||
array('this' => $this->m_oObj) // aArgs
|
||||
).'</span>';
|
||||
$aFieldsMap[$sAttCode] = $iInputId;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aParams['this->field('.$sAttCode.')'] = $this->m_oObj->GetAsHTML($sAttCode);
|
||||
}
|
||||
$aParams['this->'.$sAttCode] = "<table class=\"field\"><tr><td class=\"label\">".$aParams['this->label('.$sAttCode.')'].":</td><td>".$aParams['this->field('.$sAttCode.')']."</td><td>".$aParams['this->comments('.$sAttCode.')']."</td></tr></table>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Renders the PlugIns used in the template
|
||||
preg_match_all('/\\$PlugIn:([A-Za-z0-9_]+)->properties\\(\\)\\$/', $this->m_sTemplate, $aMatches);
|
||||
$aPlugInProperties = $aMatches[1];
|
||||
foreach($aPlugInProperties as $sPlugInClass)
|
||||
{
|
||||
$oInstance = MetaModel::GetPlugins('iApplicationUIExtension', $sPlugInClass);
|
||||
if ($oInstance != null) // Safety check...
|
||||
{
|
||||
$offset = $oPage->start_capture();
|
||||
$oInstance->OnDisplayProperties($this->m_oObj, $oPage, $bEditMode);
|
||||
$sContent = $oPage->end_capture($offset);
|
||||
$aParams["PlugIn:{$sPlugInClass}->properties()"]= $sContent;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aParams["PlugIn:{$sPlugInClass}->properties()"]= "Missing PlugIn: $sPlugInClass";
|
||||
}
|
||||
}
|
||||
|
||||
$offset = $oPage->start_capture();
|
||||
parent::Render($oPage, $aParams);
|
||||
$sContent = $oPage->end_capture($offset);
|
||||
// Remove empty table rows in case some attributes are hidden...
|
||||
$sContent = preg_replace('/<tr[^>]*>\s*(<td[^>]*>\s*<\\/td>)+\s*<\\/tr>/im', '', $sContent);
|
||||
$oPage->add($sContent);
|
||||
return $aFieldsMap;
|
||||
}
|
||||
}
|
||||
|
||||
//DisplayTemplate::UnitTest();
|
||||
?>
|
||||
|
||||
@@ -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,15 +1,6 @@
|
||||
<div style="width:100%;background: #fff url(../images/welcome.jpg) top left no-repeat;">
|
||||
<style>
|
||||
.dashboard {
|
||||
vertical-align:top;
|
||||
width:50%;
|
||||
border:0px solid #000;
|
||||
background-color:#F9F9F1;
|
||||
padding:10px;
|
||||
text-align:left;
|
||||
font-size:10pt;
|
||||
}
|
||||
.dashboard2 {
|
||||
.welcome_popup_cell {
|
||||
vertical-align:top;
|
||||
width:50%;
|
||||
border:0px solid #000;
|
||||
@@ -17,15 +8,7 @@ border:0px solid #000;
|
||||
padding:5px;
|
||||
text-align:left;
|
||||
}
|
||||
|
||||
td.dashboard li {
|
||||
margin-top: 5px;
|
||||
display: list-item;
|
||||
}
|
||||
td div.display_block {
|
||||
padding:0;
|
||||
}
|
||||
tr td.dashboard2, tr td.dashboard2 ul {
|
||||
tr td.welcome_popup_cell, tr td.welcome_popup_cell ul {
|
||||
font-size:10pt;
|
||||
}
|
||||
</style>
|
||||
@@ -35,10 +18,10 @@ font-size:10pt;
|
||||
<p></p>
|
||||
<table border="0" style="padding:10px;border-spacing: 10px;width:100%">
|
||||
<tr>
|
||||
<td class="dashboard2">
|
||||
<td class="welcome_popup_cell">
|
||||
<itopstring>UI:WelcomeMenu:LeftBlock</itopstring>
|
||||
</td>
|
||||
<td class="dashboard2">
|
||||
<td class="welcome_popup_cell">
|
||||
<itopstring>UI:WelcomeMenu:RightBlock</itopstring>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -1,28 +1,109 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 Combodo SARL
|
||||
// Copyright (C) 2010-2015 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
|
||||
* until the user's session expires. This class is actually a wrapper to the underlying implementation
|
||||
* which choice is configured via the parameter 'transaction_storage'
|
||||
*
|
||||
* @package iTop
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
class privUITransaction
|
||||
{
|
||||
/**
|
||||
* Create a new transaction id, store it in the session and return its id
|
||||
* @param void
|
||||
* @return int The identifier of the new transaction
|
||||
*/
|
||||
public static function GetNewTransactionId()
|
||||
{
|
||||
$bTransactionsEnabled = MetaModel::GetConfig()->Get('transactions_enabled');
|
||||
if (!$bTransactionsEnabled)
|
||||
{
|
||||
return 'notransactions'; // Any value will do
|
||||
}
|
||||
$sClass = 'privUITransaction'.MetaModel::GetConfig()->Get('transaction_storage');
|
||||
if (!class_exists($sClass, false))
|
||||
{
|
||||
IssueLog::Error("Incorrect value '".MetaModel::GetConfig()->Get('transaction_storage')."' for 'transaction_storage', the class '$sClass' does not exists. Using privUITransactionSession instead for storing sessions.");
|
||||
$sClass = 'privUITransactionSession';
|
||||
}
|
||||
|
||||
return (string)$sClass::GetNewTransactionId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a transaction is valid or not and (optionally) remove the valid transaction from
|
||||
* the session so that another call to IsTransactionValid for the same transaction id
|
||||
* will return false
|
||||
* @param int $id Identifier of the transaction, as returned by GetNewTransactionId
|
||||
* @param bool $bRemoveTransaction True if the transaction must be removed
|
||||
* @return bool True if the transaction is valid, false otherwise
|
||||
*/
|
||||
public static function IsTransactionValid($id, $bRemoveTransaction = true)
|
||||
{
|
||||
$bTransactionsEnabled = MetaModel::GetConfig()->Get('transactions_enabled');
|
||||
if (!$bTransactionsEnabled)
|
||||
{
|
||||
return true; // All values are valid
|
||||
}
|
||||
$sClass = 'privUITransaction'.MetaModel::GetConfig()->Get('transaction_storage');
|
||||
if (!class_exists($sClass, false))
|
||||
{
|
||||
$sClass = 'privUITransactionSession';
|
||||
}
|
||||
|
||||
return $sClass::IsTransactionValid($id, $bRemoveTransaction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the transaction specified by its id
|
||||
* @param int $id The Identifier (as returned by GetNewTranscationId) of the transaction to be removed.
|
||||
* @return void
|
||||
*/
|
||||
public static function RemoveTransaction($id)
|
||||
{
|
||||
$bTransactionsEnabled = MetaModel::GetConfig()->Get('transactions_enabled');
|
||||
if (!$bTransactionsEnabled)
|
||||
{
|
||||
return; // Nothing to do
|
||||
}
|
||||
$sClass = 'privUITransaction'.MetaModel::GetConfig()->Get('transaction_storage');
|
||||
if (!class_exists($sClass, false))
|
||||
{
|
||||
$sClass = 'privUITransactionSession';
|
||||
}
|
||||
|
||||
$sClass::RemoveTransaction($id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The original (and by default) mechanism for storing transaction information
|
||||
* as an array in the $_SESSION variable
|
||||
*
|
||||
*/
|
||||
class privUITransactionSession
|
||||
{
|
||||
/**
|
||||
* Create a new transaction id, store it in the session and return its id
|
||||
@@ -38,11 +119,11 @@ class privUITransaction
|
||||
// Strictly speaking, the two lines below should be grouped together
|
||||
// by a critical section
|
||||
// sem_acquire($rSemIdentified);
|
||||
$id = 1 + count($_SESSION['transactions']);
|
||||
$id = str_replace(array('.', ' '), '', microtime()); //1 + count($_SESSION['transactions']);
|
||||
$_SESSION['transactions'][$id] = true;
|
||||
// sem_release($rSemIdentified);
|
||||
|
||||
return sprintf("%d", $id);
|
||||
return (string)$id;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,4 +175,178 @@ class privUITransaction
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
/**
|
||||
* An alternate implementation for storing the transactions as temporary files
|
||||
* Useful when using an in-memory storage for the session which do not
|
||||
* guarantee mutual exclusion for writing
|
||||
*/
|
||||
class privUITransactionFile
|
||||
{
|
||||
/**
|
||||
* Create a new transaction id, store it in the session and return its id
|
||||
* @param void
|
||||
* @return int The identifier of the new transaction
|
||||
*/
|
||||
public static function GetNewTransactionId()
|
||||
{
|
||||
if (!is_dir(APPROOT.'data/transactions'))
|
||||
{
|
||||
if (!is_writable(APPROOT.'data'))
|
||||
{
|
||||
throw new Exception('The directory "'.APPROOT.'data" must be writable to the application.');
|
||||
}
|
||||
if (!@mkdir(APPROOT.'data/transactions'))
|
||||
{
|
||||
throw new Exception('Failed to create the directory "'.APPROOT.'data/transactions". Ajust the rights on the parent directory or let an administrator create the transactions directory and give the web sever enough rights to write into it.');
|
||||
}
|
||||
}
|
||||
if (!is_writable(APPROOT.'data/transactions'))
|
||||
{
|
||||
throw new Exception('The directory "'.APPROOT.'data/transactions" must be writable to the application.');
|
||||
}
|
||||
self::CleanupOldTransactions();
|
||||
$id = basename(tempnam(APPROOT.'data/transactions', self::GetUserPrefix()));
|
||||
self::Info('GetNewTransactionId: Created transaction: '.$id);
|
||||
|
||||
return (string)$id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a transaction is valid or not and (optionally) remove the valid transaction from
|
||||
* the session so that another call to IsTransactionValid for the same transaction id
|
||||
* will return false
|
||||
* @param int $id Identifier of the transaction, as returned by GetNewTransactionId
|
||||
* @param bool $bRemoveTransaction True if the transaction must be removed
|
||||
* @return bool True if the transaction is valid, false otherwise
|
||||
*/
|
||||
public static function IsTransactionValid($id, $bRemoveTransaction = true)
|
||||
{
|
||||
$sFilepath = APPROOT.'data/transactions/'.$id;
|
||||
clearstatcache(true, $sFilepath);
|
||||
$bResult = file_exists($sFilepath);
|
||||
if ($bResult)
|
||||
{
|
||||
if ($bRemoveTransaction)
|
||||
{
|
||||
$bResult = @unlink($sFilepath);
|
||||
if (!$bResult)
|
||||
{
|
||||
self::Error('IsTransactionValid: FAILED to remove transaction '.$id);
|
||||
}
|
||||
else
|
||||
{
|
||||
self::Info('IsTransactionValid: OK. Removed transaction: '.$id);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self::Info("IsTransactionValid: Transaction '$id' not found. Pending transactions for this user:\n".implode("\n", self::GetPendingTransactions()));
|
||||
}
|
||||
return $bResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the transaction specified by its id
|
||||
* @param int $id The Identifier (as returned by GetNewTransactionId) of the transaction to be removed.
|
||||
* @return void
|
||||
*/
|
||||
public static function RemoveTransaction($id)
|
||||
{
|
||||
$bSuccess = true;
|
||||
$sFilepath = APPROOT.'data/transactions/'.$id;
|
||||
clearstatcache(true, $sFilepath);
|
||||
if(!file_exists($sFilepath))
|
||||
{
|
||||
$bSuccess = false;
|
||||
self::Error("RemoveTransaction: Transaction '$id' not found. Pending transactions for this user:\n".implode("\n", self::GetPendingTransactions()));
|
||||
}
|
||||
$bSuccess = @unlink($sFilepath);
|
||||
if (!$bSuccess)
|
||||
{
|
||||
self::Error('RemoveTransaction: FAILED to remove transaction '.$id);
|
||||
}
|
||||
else
|
||||
{
|
||||
self::Info('RemoveTransaction: OK '.$id);
|
||||
}
|
||||
return $bSuccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup old transactions which have been pending since more than 24 hours
|
||||
* Use filemtime instead of filectime since filectime may be affected by operations on the directory (like changing the access rights)
|
||||
*/
|
||||
protected static function CleanupOldTransactions()
|
||||
{
|
||||
$iLimit = time() - 24*3600;
|
||||
clearstatcache();
|
||||
$aTransactions = glob(APPROOT.'data/transactions/*-*');
|
||||
foreach($aTransactions as $sFileName)
|
||||
{
|
||||
if (filemtime($sFileName) < $iLimit)
|
||||
{
|
||||
@unlink($sFileName);
|
||||
self::Info('CleanupOldTransactions: Deleted transaction: '.$sFileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For debugging purposes: gets the pending transactions of the current user
|
||||
* as an array, with the date of the creation of the transaction file
|
||||
*/
|
||||
protected static function GetPendingTransactions()
|
||||
{
|
||||
clearstatcache();
|
||||
$aResult = array();
|
||||
$aTransactions = glob(APPROOT.'data/transactions/'.self::GetUserPrefix().'*');
|
||||
foreach($aTransactions as $sFileName)
|
||||
{
|
||||
$aResult[] = date('Y-m-d H:i:s', filemtime($sFileName)).' - '.basename($sFileName);
|
||||
}
|
||||
sort($aResult);
|
||||
return $aResult;
|
||||
}
|
||||
|
||||
protected static function GetUserPrefix()
|
||||
{
|
||||
$sPrefix = substr(UserRights::GetUser(), 0, 10);
|
||||
$sPrefix = preg_replace('/[^a-zA-Z0-9-_]/', '_', $sPrefix);
|
||||
return $sPrefix.'-';
|
||||
}
|
||||
|
||||
protected static function Info($sText)
|
||||
{
|
||||
self::Write('Info | '.$sText);
|
||||
}
|
||||
|
||||
protected static function Warning($sText)
|
||||
{
|
||||
self::Write('Warning | '.$sText);
|
||||
}
|
||||
|
||||
protected static function Error($sText)
|
||||
{
|
||||
self::Write('Error | '.$sText);
|
||||
}
|
||||
|
||||
protected static function Write($sText)
|
||||
{
|
||||
$bLogEnabled = MetaModel::GetConfig()->Get('log_transactions');
|
||||
if ($bLogEnabled)
|
||||
{
|
||||
$hLogFile = @fopen(APPROOT.'log/transactions.log', 'a');
|
||||
if ($hLogFile !== false)
|
||||
{
|
||||
flock($hLogFile, LOCK_EX);
|
||||
$sDate = date('Y-m-d H:i:s');
|
||||
fwrite($hLogFile, "$sDate | $sText\n");
|
||||
fflush($hLogFile);
|
||||
flock($hLogFile, LOCK_UN);
|
||||
fclose($hLogFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
@@ -63,150 +63,272 @@ require_once(APPROOT.'/application/displayblock.class.inc.php');
|
||||
|
||||
class UIExtKeyWidget
|
||||
{
|
||||
protected static $iWidgetIndex = 0;
|
||||
protected $sAttCode;
|
||||
protected $sNameSuffix;
|
||||
protected $iId;
|
||||
protected $sTitle;
|
||||
protected $sTargetClass;
|
||||
protected $sAttCode;
|
||||
protected $bSearchMode;
|
||||
|
||||
public function __construct($sAttCode, $sClass, $sTitle, $aAllowedValues, $value, $iInputId, $bMandatory, $sNameSuffix = '', $sFieldPrefix = '', $sFormPrefix = '')
|
||||
//public function __construct($sAttCode, $sClass, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sNameSuffix = '', $sFieldPrefix = '', $sFormPrefix = '')
|
||||
static public function DisplayFromAttCode($oPage, $sAttCode, $sClass, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName = '', $sFormPrefix = '', $aArgs, $bSearchMode = false)
|
||||
{
|
||||
self::$iWidgetIndex++;
|
||||
$this->sAttCode = $sAttCode;
|
||||
$this->sClass = $sClass;
|
||||
$this->oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
$this->sNameSuffix = $sNameSuffix;
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
$sTargetClass = $oAttDef->GetTargetClass();
|
||||
$iMaxComboLength = $oAttDef->GetMaximumComboLength();
|
||||
$bAllowTargetCreation = $oAttDef->AllowTargetCreation();
|
||||
if (!$bSearchMode)
|
||||
{
|
||||
$sDisplayStyle = $oAttDef->GetDisplayStyle();
|
||||
}
|
||||
else
|
||||
{
|
||||
$sDisplayStyle = 'select'; // In search mode, always use a drop-down list
|
||||
}
|
||||
$oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, $sAttCode, $bSearchMode);
|
||||
return $oWidget->Display($oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName, $sFormPrefix, $aArgs, null, $sDisplayStyle);
|
||||
}
|
||||
|
||||
public function __construct($sTargetClass, $iInputId, $sAttCode = '', $bSearchMode = false)
|
||||
{
|
||||
$this->sTargetClass = $sTargetClass;
|
||||
$this->iId = $iInputId;
|
||||
$this->aAllowedValues = $aAllowedValues;
|
||||
$this->value = $value;
|
||||
$this->sFieldPrefix = $sFieldPrefix;
|
||||
$this->sTargetClass = $this->oAttDef->GetTargetClass();
|
||||
$this->sTitle = $sTitle;
|
||||
$this->sFormPrefix = $sFormPrefix;
|
||||
$this->bMandatory = $bMandatory;
|
||||
$this->sAttCode = $sAttCode;
|
||||
$this->bSearchMode = $bSearchMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, $aArgs = array(), $bSearchMode = false)
|
||||
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))
|
||||
{
|
||||
$this->bSearchMode = $bSearchMode;
|
||||
}
|
||||
$sTitle = addslashes($sTitle);
|
||||
$oPage->add_linked_script('../js/extkeywidget.js');
|
||||
$oPage->add_linked_script('../js/forms-json-utils.js');
|
||||
|
||||
$bCreate = (!$bSearchMode) && (!MetaModel::IsAbstract($this->sTargetClass)) && (UserRights::IsActionAllowed($this->sTargetClass, UR_ACTION_BULK_MODIFY) && $this->oAttDef->AllowTargetCreation());
|
||||
$bCreate = (!$this->bSearchMode) && (!MetaModel::IsAbstract($this->sTargetClass)) && (UserRights::IsActionAllowed($this->sTargetClass, UR_ACTION_BULK_MODIFY) && $bAllowTargetCreation);
|
||||
$bExtensions = true;
|
||||
$sMessage = Dict::S('UI:Message:EmptyList:UseSearchForm');
|
||||
$sAttrFieldPrefix = ($bSearchMode) ? '' : 'attr_';
|
||||
$sAttrFieldPrefix = ($this->bSearchMode) ? '' : 'attr_';
|
||||
|
||||
$sHTMLValue = "<span style=\"white-space:nowrap\">"; // no wrap
|
||||
if($bSearchMode)
|
||||
$sFilter = addslashes($oAllowedValues->GetFilter()->ToOQL());
|
||||
if($this->bSearchMode)
|
||||
{
|
||||
$sWizHelper = 'null';
|
||||
$sWizHelperJSON = "''";
|
||||
$sJSSearchMode = 'true';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sWizHelper = 'oWizardHelper'.$this->sFormPrefix;
|
||||
}
|
||||
if (count($this->aAllowedValues) < $this->oAttDef->GetMaximumComboLength())
|
||||
{
|
||||
// Few choices, use a normal 'select'
|
||||
$sSelectMode = 'true';
|
||||
|
||||
$sHelpText = $this->oAttDef->GetHelpOnEdition();
|
||||
|
||||
// In case there are no valid values, the select will be empty, thus blocking the user from validating the form
|
||||
$sHTMLValue = "<select title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$this->sFieldPrefix}{$this->sAttCode}{$this->sNameSuffix}\" id=\"$this->iId\">\n";
|
||||
if ($bSearchMode)
|
||||
if (isset($aArgs['wizHelper']))
|
||||
{
|
||||
$sHTMLValue .= "<option value=\"\">".Dict::S('UI:SearchValue:Any')."</option>\n";
|
||||
$sWizHelper = $aArgs['wizHelper'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$sHTMLValue .= "<option value=\"\">".Dict::S('UI:SelectOne')."</option>\n";
|
||||
$sWizHelper = 'oWizardHelper'.$sFormPrefix;
|
||||
}
|
||||
foreach($this->aAllowedValues as $key => $display_value)
|
||||
$sWizHelperJSON = $sWizHelper.'.UpdateWizardToJSON()';
|
||||
$sJSSearchMode = 'false';
|
||||
}
|
||||
if (is_null($oAllowedValues))
|
||||
{
|
||||
throw new Exception('Implementation: null value for allowed values definition');
|
||||
}
|
||||
elseif ($oAllowedValues->Count() < $iMaxComboLength)
|
||||
{
|
||||
// Discrete list of values, use a SELECT or RADIO buttons depending on the config
|
||||
switch($sDisplayStyle)
|
||||
{
|
||||
if ((count($this->aAllowedValues) == 1) && ($this->bMandatory == 'true') )
|
||||
case 'radio':
|
||||
case 'radio_horizontal':
|
||||
case 'radio_vertical':
|
||||
$sValidationField = "<span id=\"v_{$this->iId}\"></span>";
|
||||
$sHTMLValue = '';
|
||||
$bVertical = ($sDisplayStyle != 'radio_horizontal');
|
||||
$bExtensions = false;
|
||||
$oAllowedValues->Rewind();
|
||||
$aAllowedValues = array();
|
||||
while($oObj = $oAllowedValues->Fetch())
|
||||
{
|
||||
// When there is only once choice, select it by default
|
||||
$sSelected = ' selected';
|
||||
$aAllowedValues[$oObj->GetKey()] = $oObj->GetName();
|
||||
}
|
||||
$sHTMLValue = $oPage->GetRadioButtons($aAllowedValues, $value, $this->iId, "{$sAttrFieldPrefix}{$sFieldName}", $bMandatory, $bVertical, $sValidationField);
|
||||
$aEventsList[] ='change';
|
||||
break;
|
||||
|
||||
case 'select':
|
||||
default:
|
||||
$sSelectMode = 'true';
|
||||
|
||||
$sHelpText = ''; //$this->oAttDef->GetHelpOnEdition();
|
||||
|
||||
if ($this->bSearchMode)
|
||||
{
|
||||
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
|
||||
{
|
||||
$sSelected = ($this->value == $key) ? ' selected' : '';
|
||||
$sHTMLValue = "<select title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" id=\"$this->iId\">\n";
|
||||
$sHTMLValue .= "<option value=\"\">".Dict::S('UI:SelectOne')."</option>\n";
|
||||
}
|
||||
$sHTMLValue .= "<option value=\"$key\"$sSelected>$display_value</option>\n";
|
||||
}
|
||||
$sHTMLValue .= "</select>\n";
|
||||
$oPage->add_ready_script(
|
||||
$oAllowedValues->Rewind();
|
||||
while($oObj = $oAllowedValues->Fetch())
|
||||
{
|
||||
$key = $oObj->GetKey();
|
||||
$display_value = $oObj->GetName();
|
||||
|
||||
if (($oAllowedValues->Count() == 1) && ($bMandatory == 'true') )
|
||||
{
|
||||
// When there is only once choice, select it by default
|
||||
$sSelected = ' selected';
|
||||
}
|
||||
else
|
||||
{
|
||||
$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->sClass}', '{$this->sAttCode}', '{$this->sNameSuffix}', $sSelectMode, $sWizHelper);
|
||||
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', true, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode);
|
||||
oACWidget_{$this->iId}.emptyHtml = "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>$sMessage</p></div>";
|
||||
$('#$this->iId').bind('update', function() { oACWidget_{$this->iId}.Update(); } );
|
||||
$('#$this->iId').bind('change', function() { $(this).trigger('extkeychange') } );
|
||||
|
||||
EOF
|
||||
);
|
||||
);
|
||||
} // Switch
|
||||
}
|
||||
else
|
||||
{
|
||||
// Too many choices, use an autocomplete
|
||||
$sSelectMode = 'false';
|
||||
|
||||
if ($this->oAttDef->IsNull($this->value)) // Null values are displayed as ''
|
||||
// Check that the given value is allowed
|
||||
$oSearch = $oAllowedValues->GetFilter();
|
||||
$oSearch->AddCondition('id', $value);
|
||||
$oSet = new DBObjectSet($oSearch);
|
||||
if ($oSet->Count() == 0)
|
||||
{
|
||||
$sDisplayValue = '';
|
||||
$value = null;
|
||||
}
|
||||
|
||||
if (is_null($value) || ($value == 0)) // Null values are displayed as ''
|
||||
{
|
||||
$sDisplayValue = isset($aArgs['sDefaultValue']) ? $aArgs['sDefaultValue'] : '';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sDisplayValue = $this->GetObjectName($this->value);
|
||||
$sDisplayValue = $this->GetObjectName($value);
|
||||
}
|
||||
$sFormPrefix = $this->sFormPrefix;
|
||||
$iMinChars = $this->oAttDef->GetMinAutoCompleteChars();
|
||||
$iFieldSize = $this->oAttDef->GetMaxSize();
|
||||
$iMinChars = isset($aArgs['iMinChars']) ? $aArgs['iMinChars'] : 3; //@@@ $this->oAttDef->GetMinAutoCompleteChars();
|
||||
$iFieldSize = isset($aArgs['iFieldSize']) ? $aArgs['iFieldSize'] : 30; //@@@ $this->oAttDef->GetMaxSize();
|
||||
|
||||
// the input for the auto-complete
|
||||
$sHTMLValue = "<input count=\"".count($this->aAllowedValues)."\" type=\"text\" id=\"label_$this->iId\" size=\"30\" maxlength=\"$iFieldSize\" value=\"$sDisplayValue\"/> ";
|
||||
$sHTMLValue .= "<a class=\"no-arrow\" href=\"javascript:oACWidget_{$this->iId}.Search();\"><img id=\"mini_search_{$this->iId}\" style=\"border:0;vertical-align:middle;\" src=\"../images/mini_search.gif\" /></a> ";
|
||||
$sHTMLValue = "<input count=\"".$oAllowedValues->Count()."\" type=\"text\" id=\"label_$this->iId\" size=\"$iFieldSize\" value=\"$sDisplayValue\"/> ";
|
||||
$sHTMLValue .= "<img id=\"mini_search_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_search.gif\" onClick=\"oACWidget_{$this->iId}.Search();\"/> ";
|
||||
|
||||
// another hidden input to store & pass the object's Id
|
||||
$sHTMLValue .= "<input type=\"hidden\" id=\"$this->iId\" name=\"{$sAttrFieldPrefix}{$this->sFieldPrefix}{$this->sAttCode}{$this->sNameSuffix}\" value=\"$this->value\" />\n";
|
||||
|
||||
$sHTMLValue .= "<input type=\"hidden\" id=\"$this->iId\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" value=\"".htmlentities($value, ENT_QUOTES, 'UTF-8')."\" />\n";
|
||||
|
||||
$JSSearchMode = $this->bSearchMode ? 'true' : 'false';
|
||||
// Scripts to start the autocomplete and bind some events to it
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sClass}', '{$this->sAttCode}', '{$this->sNameSuffix}', $sSelectMode, $sWizHelper);
|
||||
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', false, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode);
|
||||
oACWidget_{$this->iId}.emptyHtml = "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>$sMessage</p></div>";
|
||||
$('#label_$this->iId').autocomplete('./ajax.render.php', { scroll:true, minChars:{$iMinChars}, formatItem:formatItem, autoFill:false, matchContains:true, keyHolder:'#{$this->iId}', extraParams:{operation:'autocomplete', sclass:'{$this->sClass}',attCode:'{$this->sAttCode}'}});
|
||||
$('#label_$this->iId').blur(function() { $(this).search(); } );
|
||||
$('#label_$this->iId').autocomplete(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', { scroll:true, minChars:{$iMinChars}, autoFill:false, matchContains:true, mustMatch: true, keyHolder:'#{$this->iId}', extraParams:{operation:'ac_extkey', sTargetClass:'{$this->sTargetClass}',sFilter:'$sFilter',bSearchMode:$JSSearchMode, json: function() { return $sWizHelperJSON; } }});
|
||||
$('#label_$this->iId').keyup(function() { if ($(this).val() == '') { $('#$this->iId').val(''); } } ); // Useful for search forms: empty value in the "label", means no value, immediatly !
|
||||
$('#label_$this->iId').result( function(event, data, formatted) { OnAutoComplete('{$this->iId}', event, data, formatted); } );
|
||||
$('#$this->iId').bind('update', function() { oACWidget_{$this->iId}.Update(); } );
|
||||
if ($('#ac_dlg_{$this->iId}').length == 0)
|
||||
{
|
||||
$('body').append('<div id="ac_dlg_{$this->iId}"></div>');
|
||||
}
|
||||
EOF
|
||||
);
|
||||
//$oPage->add_at_the_end($this->GetSearchDialog($oPage)); // To prevent adding forms inside the main form
|
||||
$oPage->add_at_the_end('<div id="ac_dlg_'.$this->iId.'"></div>'); // The place where to download the search dialog is outside of the main form (to prevent nested forms)
|
||||
|
||||
}
|
||||
if ($bCreate)
|
||||
if ($bExtensions && MetaModel::IsHierarchicalClass($this->sTargetClass) !== false)
|
||||
{
|
||||
$sHTMLValue .= "<a class=\"no-arrow\" href=\"javascript:oACWidget_{$this->iId}.CreateObject();\"><img id=\"mini_add_{$this->iId}\" style=\"border:0;vertical-align:middle;\" src=\"../images/mini_add.gif\" /></a> ";
|
||||
$oPage->add_at_the_end('<div id="ajax_'.$this->iId.'"></div>');
|
||||
$sHTMLValue .= "<img id=\"mini_tree_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_tree.gif\" onClick=\"oACWidget_{$this->iId}.HKDisplay();\"/> ";
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
if ($('#ac_tree_{$this->iId}').length == 0)
|
||||
{
|
||||
$('body').append('<div id="ac_tree_{$this->iId}"></div>');
|
||||
}
|
||||
EOF
|
||||
);
|
||||
}
|
||||
if ($bCreate && $bExtensions)
|
||||
{
|
||||
$sHTMLValue .= "<img id=\"mini_add_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_add.gif\" onClick=\"oACWidget_{$this->iId}.CreateObject();\"/> ";
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
if ($('#ajax_{$this->iId}').length == 0)
|
||||
{
|
||||
$('body').append('<div id="ajax_{$this->iId}"></div>');
|
||||
}
|
||||
EOF
|
||||
);
|
||||
}
|
||||
if ($sDisplayStyle == 'select')
|
||||
{
|
||||
$sHTMLValue .= "<span id=\"v_{$this->iId}\"></span>";
|
||||
}
|
||||
$sHTMLValue .= "<span id=\"v_{$this->iId}\"></span>";
|
||||
$sHTMLValue .= "</span>"; // end of no wrap
|
||||
return $sHTMLValue;
|
||||
}
|
||||
|
||||
public function GetSearchDialog(WebPage $oPage)
|
||||
public function GetSearchDialog(WebPage $oPage, $sTitle, $oCurrObject = null)
|
||||
{
|
||||
$sHTML = '<div class="wizContainer" style="vertical-align:top;"><div id="dc_'.$this->iId.'">';
|
||||
|
||||
$oFilter = new DBObjectSearch($this->sTargetClass);
|
||||
$oSet = new CMDBObjectSet($oFilter);
|
||||
$oBlock = new DisplayBlock($oFilter, 'search', false);
|
||||
if ( ($oCurrObject != null) && ($this->sAttCode != ''))
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($oCurrObject), $this->sAttCode);
|
||||
$aArgs = array('this' => $oCurrObject);
|
||||
$aParams = array('query_params' => $aArgs);
|
||||
$oSet = $oAttDef->GetAllowedValuesAsObjectSet($aArgs);
|
||||
$oFilter = $oSet->GetFilter();
|
||||
}
|
||||
else
|
||||
{
|
||||
$aParams = array();
|
||||
$oFilter = new DBObjectSearch($this->sTargetClass);
|
||||
}
|
||||
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
|
||||
$oBlock = new DisplayBlock($oFilter, 'search', false, $aParams);
|
||||
$sHTML .= $oBlock->GetDisplay($oPage, $this->iId, array('open' => true, 'currentId' => $this->iId));
|
||||
$sHTML .= "<form id=\"fr_{$this->iId}\" OnSubmit=\"return oACWidget_{$this->iId}.DoOk();\">\n";
|
||||
$sHTML .= "<div id=\"dr_{$this->iId}\" style=\"vertical-align:top;background: #fff;height:100%;overflow:auto;padding:0;border:0;\">\n";
|
||||
@@ -214,10 +336,11 @@ EOF
|
||||
$sHTML .= "</div>\n";
|
||||
$sHTML .= "<input type=\"button\" id=\"btn_cancel_{$this->iId}\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"$('#ac_dlg_{$this->iId}').dialog('close');\"> ";
|
||||
$sHTML .= "<input type=\"button\" id=\"btn_ok_{$this->iId}\" value=\"".Dict::S('UI:Button:Ok')."\" onClick=\"oACWidget_{$this->iId}.DoOk();\">";
|
||||
$sHTML .= "<input type=\"hidden\" id=\"count_{$this->iId}\" value=\"0\">";
|
||||
$sHTML .= "</form>\n";
|
||||
$sHTML .= '</div></div>';
|
||||
|
||||
$sDialogTitle = addslashes($this->sTitle);
|
||||
$sDialogTitle = addslashes($sTitle);
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
$('#ac_dlg_{$this->iId}').dialog({ width: $(window).width()*0.8, height: $(window).height()*0.8, autoOpen: false, modal: true, title: '$sDialogTitle', resizeStop: oACWidget_{$this->iId}.UpdateSizes, close: oACWidget_{$this->iId}.OnClose });
|
||||
@@ -234,22 +357,43 @@ EOF
|
||||
* @param string $sRemoteClass Name of the "remote" class to perform the search on, must be a derived class of m_sRemoteClass
|
||||
* @param Array $aAlreadyLinkedIds List of IDs of objects of "remote" class already linked, to be filtered out of the search
|
||||
*/
|
||||
public function SearchObjectsToSelect(WebPage $oP, $sTargetClass = '')
|
||||
public function SearchObjectsToSelect(WebPage $oP, $sFilter, $sRemoteClass = '', $oObj = null)
|
||||
{
|
||||
if ($sTargetClass != '')
|
||||
if (is_null($sFilter))
|
||||
{
|
||||
// assert(MetaModel::IsParentClass($this->m_sRemoteClass, $sRemoteClass));
|
||||
$oFilter = new DBObjectSearch($sTargetClass);
|
||||
throw new Exception('Implementation: null value for allowed values definition');
|
||||
}
|
||||
else
|
||||
|
||||
$oFilter = DBObjectSearch::FromOQL($sFilter);
|
||||
if (strlen($sRemoteClass) > 0)
|
||||
{
|
||||
// No remote class specified use the one defined in the linkedset
|
||||
$oFilter = new DBObjectSearch($this->sTargetClass);
|
||||
$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
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for objects to be selected
|
||||
* @param WebPage $oP The page used for the output (usually an AjaxWebPage)
|
||||
* @param string $sFilter The OQL expression used to define/limit limit the scope of possible values
|
||||
* @param DBObject $oObj The current object for the OQL context
|
||||
* @param string $sContains The text of the autocomplete to filter the results
|
||||
*/
|
||||
public function AutoComplete(WebPage $oP, $sFilter, $oObj = null, $sContains)
|
||||
{
|
||||
if (is_null($sFilter))
|
||||
{
|
||||
throw new Exception('Implementation: null value for allowed values definition');
|
||||
}
|
||||
$oValuesSet = new ValueSetObjects($sFilter, 'friendlyname'); // Bypass GetName() to avoid the encoding by htmlentities
|
||||
$oValuesSet->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
|
||||
$aValues = $oValuesSet->GetValues(array('this' => $oObj), $sContains);
|
||||
foreach($aValues as $sKey => $sFriendlyName)
|
||||
{
|
||||
$oP->add(trim($sFriendlyName)."\t".$sKey."\n");
|
||||
}
|
||||
$oFilter->AddCondition('id', array_keys($this->aAllowedValues), 'IN');
|
||||
$oSet = new CMDBObjectSet($oFilter);
|
||||
$oBlock = new DisplayBlock($oFilter, 'list', false);
|
||||
$oBlock->Display($oP, $this->iId, array('menu' => false, 'selection_mode' => true, 'selection_type' => 'single', 'display_limit' => false)); // Don't display the 'Actions' menu on the results
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -257,41 +401,114 @@ EOF
|
||||
*/
|
||||
public function GetObjectName($iObjId)
|
||||
{
|
||||
$oObj = MetaModel::GetObject($this->sTargetClass, $iObjId);
|
||||
return $oObj->GetName();
|
||||
$aModifierProps = array();
|
||||
$aModifierProps['UserRightsGetSelectFilter']['bSearchMode'] = $this->bSearchMode;
|
||||
|
||||
$oObj = MetaModel::GetObject($this->sTargetClass, $iObjId, false, false, $aModifierProps);
|
||||
if ($oObj)
|
||||
{
|
||||
return $oObj->GetName();
|
||||
}
|
||||
else
|
||||
{
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the form to create a new object of the 'target' class
|
||||
*/
|
||||
public function GetObjectCreationForm(WebPage $oPage)
|
||||
public function GetObjectCreationForm(WebPage $oPage, $oCurrObject)
|
||||
{
|
||||
$sDialogTitle = addslashes($this->sTitle);
|
||||
// Set all the default values in an object and clone this "default" object
|
||||
$oNewObj = MetaModel::NewObject($this->sTargetClass);
|
||||
|
||||
// 1st - set context values
|
||||
$oAppContext = new ApplicationContext();
|
||||
$oAppContext->InitObjectFromContext($oNewObj);
|
||||
|
||||
// 2nd set the default values from the constraint on the external key... if any
|
||||
if ( ($oCurrObject != null) && ($this->sAttCode != ''))
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($oCurrObject), $this->sAttCode);
|
||||
$aParams = array('this' => $oCurrObject);
|
||||
$oSet = $oAttDef->GetAllowedValuesAsObjectSet($aParams);
|
||||
$aConsts = $oSet->ListConstantFields();
|
||||
$sClassAlias = $oSet->GetFilter()->GetClassAlias();
|
||||
if (isset($aConsts[$sClassAlias]))
|
||||
{
|
||||
foreach($aConsts[$sClassAlias] as $sAttCode => $value)
|
||||
{
|
||||
$oNewObj->Set($sAttCode, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3rd - set values from the page argument 'default'
|
||||
$oNewObj->UpdateObjectFromArg('default');
|
||||
|
||||
$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, null, array(), array('formPrefix' => $this->iId, 'noRelations' => true));
|
||||
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);");
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the hierarchy of the 'target' class
|
||||
*/
|
||||
public function DisplayHierarchy(WebPage $oPage, $sFilter, $currValue, $oObj)
|
||||
{
|
||||
$sDialogTitle = addslashes(Dict::Format('UI:HierarchyOf_Class', MetaModel::GetName($this->sTargetClass)));
|
||||
$oPage->add('<div id="dlg_tree_'.$this->iId.'"><div class="wizContainer" style="vertical-align:top;"><div style="overflow:auto;background:#fff;margin-bottom:5px;" id="tree_'.$this->iId.'">');
|
||||
$oPage->add('<table style="width:100%"><tr><td>');
|
||||
if (is_null($sFilter))
|
||||
{
|
||||
throw new Exception('Implementation: null value for allowed values definition');
|
||||
}
|
||||
try
|
||||
{
|
||||
$oFilter = DBObjectSearch::FromOQL($sFilter);
|
||||
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
|
||||
$oSet = new DBObjectSet($oFilter, array(), array('this' => $oObj));
|
||||
}
|
||||
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 '.$this->m_sTargetClass;
|
||||
$oFilter = DBObjectSearch::FromOQL($sOQL);
|
||||
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
|
||||
$oSet = new DBObjectSet($oFilter);
|
||||
}
|
||||
|
||||
$sHKAttCode = MetaModel::IsHierarchicalClass($this->sTargetClass);
|
||||
$this->DumpTree($oPage, $oSet, $sHKAttCode, $currValue);
|
||||
|
||||
$oPage->add('</td></tr></table>');
|
||||
$oPage->add('</div>');
|
||||
$oPage->add("<input type=\"button\" id=\"btn_cancel_{$this->iId}\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"$('#dlg_tree_{$this->iId}').dialog('close');\"> ");
|
||||
$oPage->add("<input type=\"button\" id=\"btn_ok_{$this->iId}\" value=\"".Dict::S('UI:Button:Ok')."\" onClick=\"oACWidget_{$this->iId}.DoHKOk();\">");
|
||||
|
||||
$oPage->add('</div></div>');
|
||||
$oPage->add_ready_script("\$('#tree_$this->iId ul').treeview();\n");
|
||||
$oPage->add_ready_script("\$('#dlg_tree_$this->iId').dialog({ width: 'auto', height: 'auto', autoOpen: true, modal: true, title: '$sDialogTitle', resizeStop: oACWidget_{$this->iId}.OnHKResize, close: oACWidget_{$this->iId}.OnHKClose });\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the form to create a new object of the 'target' class
|
||||
*/
|
||||
public function DoCreateObject($oPage)
|
||||
{
|
||||
$oObj = MetaModel::NewObject($this->sTargetClass);
|
||||
$aErrors = $oObj->UpdateObject($this->sFormPrefix.$this->iId);
|
||||
$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
|
||||
@@ -299,5 +516,69 @@ EOF
|
||||
return array('name' => implode(' ', $aErrors), 'id' => 0);
|
||||
}
|
||||
}
|
||||
|
||||
function DumpTree($oP, $oSet, $sParentAttCode, $currValue)
|
||||
{
|
||||
$aTree = array();
|
||||
$aNodes = array();
|
||||
while($oObj = $oSet->Fetch())
|
||||
{
|
||||
$iParentId = $oObj->Get($sParentAttCode);
|
||||
if (!isset($aTree[$iParentId]))
|
||||
{
|
||||
$aTree[$iParentId] = array();
|
||||
}
|
||||
$aTree[$iParentId][$oObj->GetKey()] = $oObj->GetName();
|
||||
$aNodes[$oObj->GetKey()] = $oObj;
|
||||
}
|
||||
|
||||
$aParents = array_keys($aTree);
|
||||
$aRoots = array();
|
||||
foreach($aParents as $id)
|
||||
{
|
||||
if (!array_key_exists($id, $aNodes))
|
||||
{
|
||||
$aRoots[] = $id;
|
||||
}
|
||||
}
|
||||
foreach($aRoots as $iRootId)
|
||||
{
|
||||
$this->DumpNodes($oP, $iRootId, $aTree, $aNodes, $currValue);
|
||||
}
|
||||
}
|
||||
|
||||
function DumpNodes($oP, $iRootId, $aTree, $aNodes, $currValue)
|
||||
{
|
||||
$bSelect = true;
|
||||
$bMultiple = false;
|
||||
$sSelect = '';
|
||||
if (array_key_exists($iRootId, $aTree))
|
||||
{
|
||||
$aSortedRoots = $aTree[$iRootId];
|
||||
asort($aSortedRoots);
|
||||
$oP->add("<ul>\n");
|
||||
$fUniqueId = microtime(true);
|
||||
foreach($aSortedRoots as $id => $sName)
|
||||
{
|
||||
if ($bSelect)
|
||||
{
|
||||
$sChecked = ($aNodes[$id]->GetKey() == $currValue) ? 'checked' : '';
|
||||
if ($bMultiple)
|
||||
{
|
||||
$sSelect = '<input id="input_'.$fUniqueId.'_'.$aNodes[$id]->GetKey().'" type="checkbox" value="'.$aNodes[$id]->GetKey().'" name="selectObject[]" '.$sChecked.'> ';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sSelect = '<input id="input_'.$fUniqueId.'_'.$aNodes[$id]->GetKey().'" type="radio" value="'.$aNodes[$id]->GetKey().'" name="selectObject" '.$sChecked.'> ';
|
||||
}
|
||||
}
|
||||
$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");
|
||||
}
|
||||
$oP->add("</ul>\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
|
||||
@@ -79,7 +80,7 @@ class UIHTMLEditorWidget
|
||||
|
||||
// Could also be bound to 'instanceReady.ckeditor'
|
||||
$oPage->add_ready_script("$('#$iId').bind('validate', function(evt, sFormId) { return ValidateCKEditField('$iId', '', {$this->m_sMandatory}, sFormId, '') } );\n");
|
||||
$oPage->add_ready_script("$('#$iId').bind('update', function() { BlockField('cke_$iId', $('#$iId').attr('disabled')); } );\n");
|
||||
$oPage->add_ready_script("$('#$iId').bind('update', function() { BlockField('cke_$iId', $('#$iId').attr('disabled')); $(this).data('ckeditorInstance').setReadOnly($(this).prop('disabled')); } );\n");
|
||||
|
||||
return $sHtmlValue;
|
||||
}
|
||||
|
||||
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($sRemoteClass);
|
||||
}
|
||||
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');
|
||||
@@ -34,6 +35,7 @@ class UILinksWidget
|
||||
protected $m_iInputId;
|
||||
protected $m_aAttributes;
|
||||
protected $m_sExtKeyToRemote;
|
||||
protected $m_sExtKeyToMe;
|
||||
protected $m_sLinkedClass;
|
||||
protected $m_sRemoteClass;
|
||||
protected $m_bDuplicatesAllowed;
|
||||
@@ -50,6 +52,7 @@ class UILinksWidget
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sAttCode);
|
||||
$this->m_sLinkedClass = $oAttDef->GetLinkedClass();
|
||||
$this->m_sExtKeyToRemote = $oAttDef->GetExtKeyToRemote();
|
||||
$this->m_sExtKeyToMe = $oAttDef->GetExtKeyToMe();
|
||||
$oLinkingAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $this->m_sExtKeyToRemote);
|
||||
$this->m_sRemoteClass = $oLinkingAttDef->GetTargetClass();
|
||||
$sExtKeyToMe = $oAttDef->GetExtKeyToMe();
|
||||
@@ -60,8 +63,9 @@ class UILinksWidget
|
||||
$this->m_aTableConfig = array();
|
||||
$this->m_aTableConfig['form::checkbox'] = array( 'label' => "<input class=\"select_all\" type=\"checkbox\" value=\"1\" onClick=\"CheckAll('#linkedset_{$this->m_sAttCode}{$this->m_sNameSuffix} .selection', this.checked); oWidget".$this->m_iInputId.".OnSelectChange();\">", 'description' => Dict::S('UI:SelectAllToggle+'));
|
||||
|
||||
foreach(MetaModel::ListAttributeDefs($this->m_sLinkedClass) as $sAttCode=>$oAttDef)
|
||||
foreach(MetaModel::FlattenZList(MetaModel::GetZListItems($this->m_sLinkedClass, 'list')) as $sAttCode)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $sAttCode);
|
||||
if ($sStateAttCode == $sAttCode)
|
||||
{
|
||||
// State attribute is always hidden from the UI
|
||||
@@ -97,43 +101,110 @@ class UILinksWidget
|
||||
* @param Hash $aArgs Extra context arguments
|
||||
* @return string The HTML fragment of the one-row form
|
||||
*/
|
||||
protected function GetFormRow(WebPage $oP, DBObject $oLinkedObj, $linkObjOrId = null, $aArgs = array() )
|
||||
protected function GetFormRow(WebPage $oP, DBObject $oLinkedObj, $linkObjOrId = null, $aArgs = array(), $oCurrentObj )
|
||||
{
|
||||
$sPrefix = "$this->m_sAttCode{$this->m_sNameSuffix}";
|
||||
$aRow = array();
|
||||
if(is_object($linkObjOrId))
|
||||
$aFieldsMap = array();
|
||||
if(is_object($linkObjOrId) && (!$linkObjOrId->IsNew()))
|
||||
{
|
||||
$key = $linkObjOrId->GetKey();
|
||||
$iRemoteObjKey = $linkObjOrId->Get($this->m_sExtKeyToRemote);
|
||||
$sPrefix .= "[$key][";
|
||||
$sNameSuffix = "]"; // To make a tabular form
|
||||
$aArgs['prefix'] = $sPrefix;
|
||||
$aArgs['wizHelper'] = "oWizardHelper{$this->m_iInputId}{$key}";
|
||||
$aArgs['this'] = $linkObjOrId;
|
||||
$aRow['form::checkbox'] = "<input class=\"selection\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"$key\">";
|
||||
$aRow['form::checkbox'] .= "<input type=\"hidden\" name=\"attr_{$sPrefix}id{$sNameSuffix}\" value=\"$key\">";
|
||||
foreach($this->m_aEditableFields as $sFieldCode)
|
||||
{
|
||||
$sFieldId = $this->m_iInputId.'_'.$sFieldCode.'['.$linkObjOrId->GetKey().']';
|
||||
$sSafeId = str_replace(array('[',']','-'), '_', $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;
|
||||
}
|
||||
$sState = $linkObjOrId->GetState();
|
||||
}
|
||||
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][";
|
||||
$sNameSuffix = "]"; // To make a tabular form
|
||||
$aArgs['prefix'] = $sPrefix;
|
||||
$aArgs['wizHelper'] = "oWizardHelper{$this->m_iInputId}_".(-$linkObjOrId);
|
||||
$aArgs['this'] = $oNewLinkObj;
|
||||
$aRow['form::checkbox'] = "<input class=\"selection\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"$linkObjOrId\">";
|
||||
$aRow['form::checkbox'] .= "<input type=\"hidden\" name=\"attr_{$sPrefix}id{$sNameSuffix}\" value=\"\">";
|
||||
foreach($this->m_aEditableFields as $sFieldCode)
|
||||
{
|
||||
$sFieldId = $this->m_iInputId.'_'.$sFieldCode.'['.$linkObjOrId.']';
|
||||
$sSafeId = str_replace(array('[',']','-'), '_', $sFieldId);
|
||||
$sSafeId = utils::GetSafeId($sFieldId);
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $sFieldCode);
|
||||
$aRow[$sFieldCode] = cmdbAbstractObject::GetFormElementForField($oP, $this->m_sLinkedClass, $sFieldCode, $oAttDef, '' /* TO DO/ call GetDefaultValue($oObject->ToArgs()) */, '' /* DisplayValue */, $sSafeId /* id */, $sNameSuffix, 0, $aArgs);
|
||||
$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;
|
||||
}
|
||||
$sState = '';
|
||||
$oP->add_script(
|
||||
<<<EOF
|
||||
$(".date-pick").datepicker({
|
||||
showOn: 'button',
|
||||
buttonImage: '../images/calendar.png',
|
||||
buttonImageOnly: true,
|
||||
dateFormat: 'yy-mm-dd',
|
||||
constrainInput: false,
|
||||
changeMonth: true,
|
||||
changeYear: true
|
||||
});
|
||||
$(".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
|
||||
);
|
||||
}
|
||||
|
||||
$sExtKeyToMeId = utils::GetSafeId($sPrefix.$this->m_sExtKeyToMe);
|
||||
$aFieldsMap[$this->m_sExtKeyToMe] = $sExtKeyToMeId;
|
||||
$aRow['form::checkbox'] .= "<input type=\"hidden\" id=\"$sExtKeyToMeId\" value=\"".$oCurrentObj->GetKey()."\">";
|
||||
|
||||
$sExtKeyToRemoteId = utils::GetSafeId($sPrefix.$this->m_sExtKeyToRemote);
|
||||
$aFieldsMap[$this->m_sExtKeyToRemote] = $sExtKeyToRemoteId;
|
||||
$aRow['form::checkbox'] .= "<input type=\"hidden\" id=\"$sExtKeyToRemoteId\" value=\"$iRemoteObjKey\">";
|
||||
|
||||
$iFieldsCount = count($aFieldsMap);
|
||||
$sJsonFieldsMap = json_encode($aFieldsMap);
|
||||
|
||||
$oP->add_script(
|
||||
<<<EOF
|
||||
var {$aArgs['wizHelper']} = new WizardHelper('{$this->m_sLinkedClass}', '', '$sState');
|
||||
{$aArgs['wizHelper']}.SetFieldsMap($sJsonFieldsMap);
|
||||
{$aArgs['wizHelper']}.SetFieldsCount($iFieldsCount);
|
||||
EOF
|
||||
);
|
||||
$aRow['static::key'] = $oLinkedObj->GetHyperLink();
|
||||
foreach(MetaModel::GetZListItems($this->m_sRemoteClass, 'list') as $sFieldCode)
|
||||
{
|
||||
@@ -168,7 +239,7 @@ class UILinksWidget
|
||||
*/
|
||||
protected function DisplayFormTable(WebPage $oP, $aConfig, $aData)
|
||||
{
|
||||
$sHtml = '';
|
||||
$sHtml = "<input type=\"hidden\" name=\"attr_{$this->m_sAttCode}{$this->m_sNameSuffix}\" value=\"\">";
|
||||
$sHtml .= "<table class=\"listResults\">\n";
|
||||
// Header
|
||||
$sHtml .= "<thead>\n";
|
||||
@@ -182,17 +253,17 @@ class UILinksWidget
|
||||
|
||||
// Content
|
||||
$sHtml .= "</tbody>\n";
|
||||
if (count($aData) == 0)
|
||||
$sEmptyRowStyle = '';
|
||||
if (count($aData) != 0)
|
||||
{
|
||||
$sHtml .= "<tr id=\"{$this->m_sAttCode}{$this->m_sNameSuffix}_empty_row\"><td colspan=\"".count($aConfig)."\" style=\"text-align:center;\">".Dict::S('UI:Message:EmptyList:UseAdd')."<input type=\"hidden\" name=\"attr_{$this->m_sAttCode}{$this->m_sNameSuffix}\" value=\"\"></td></td>";
|
||||
$sEmptyRowStyle = 'style="display:none;"';
|
||||
}
|
||||
else
|
||||
|
||||
foreach($aData as $iRowId => $aRow)
|
||||
{
|
||||
foreach($aData as $iRowId => $aRow)
|
||||
{
|
||||
$sHtml .= $this->DisplayFormRow($oP, $aConfig, $aRow, $iRowId);
|
||||
}
|
||||
}
|
||||
$sHtml .= $this->DisplayFormRow($oP, $aConfig, $aRow, $iRowId);
|
||||
}
|
||||
$sHtml .= "<tr $sEmptyRowStyle id=\"{$this->m_sAttCode}{$this->m_sNameSuffix}_empty_row\"><td colspan=\"".count($aConfig)."\" style=\"text-align:center;\">".Dict::S('UI:Message:EmptyList:UseAdd')."</td></tr>";
|
||||
$sHtml .= "</tbody>\n";
|
||||
|
||||
// Footer
|
||||
@@ -207,35 +278,48 @@ class UILinksWidget
|
||||
* @param WebPage $oP The web page used for all the output
|
||||
* @param DBObjectSet The initial value of the linked set
|
||||
* @param Hash $aArgs Extra context arguments
|
||||
* @param string $sFormPrefix prefix of the fields in the current form
|
||||
* @param DBObject $oCurrentObj the current object to which the linkset is related
|
||||
* @return string The HTML fragment to be inserted into the page
|
||||
*/
|
||||
public function Display(WebPage $oPage, DBObjectSet $oValue, $aArgs = array())
|
||||
public function Display(WebPage $oPage, DBObjectSet $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj)
|
||||
{
|
||||
$sHtmlValue = '';
|
||||
$sTargetClass = self::GetTargetClass($this->m_sClass, $this->m_sAttCode);
|
||||
$sHtmlValue .= "<div id=\"linkedset_{$this->m_sAttCode}{$this->m_sNameSuffix}\">\n";
|
||||
$sHtmlValue .= "<input type=\"hidden\" id=\"{$sFormPrefix}{$this->m_iInputId}\">\n";
|
||||
$oValue->Rewind();
|
||||
$aForm = array();
|
||||
while($oCurrentLink = $oValue->Fetch())
|
||||
{
|
||||
$aRow = array();
|
||||
$key = $oCurrentLink->GetKey();
|
||||
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $oCurrentLink->Get($this->m_sExtKeyToRemote));
|
||||
if ($oCurrentLink->IsNew())
|
||||
{
|
||||
$key = -$oLinkedObj->GetKey();
|
||||
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs, $oCurrentObj);
|
||||
}
|
||||
else
|
||||
{
|
||||
$key = $oCurrentLink->GetKey();
|
||||
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs, $oCurrentObj);
|
||||
}
|
||||
|
||||
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs);
|
||||
}
|
||||
$sHtmlValue .= $this->DisplayFormTable($oPage, $this->m_aTableConfig, $aForm);
|
||||
$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);
|
||||
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();\" >";
|
||||
$sHtmlValue .= " <input id=\"{$this->m_sAttCode}{$this->m_sNameSuffix}_btnAdd\" type=\"button\" value=\"".Dict::Format('UI:AddLinkedObjectsOf_Class', MetaModel::GetName($this->m_sRemoteClass))."\" onClick=\"oWidget{$this->m_iInputId}.AddObjects();\"></span>\n";
|
||||
$sHtmlValue .= " <input id=\"{$this->m_sAttCode}{$this->m_sNameSuffix}_btnAdd\" type=\"button\" value=\"".Dict::Format('UI:AddLinkedObjectsOf_Class', MetaModel::GetName($this->m_sRemoteClass))."\" onClick=\"oWidget{$this->m_iInputId}.AddObjects();\"><span id=\"{$this->m_sAttCode}{$this->m_sNameSuffix}_indicatorAdd\"></span></span>\n";
|
||||
$sHtmlValue .= "<span style=\"clear:both;\"><p> </p></span>\n";
|
||||
$sHtmlValue .= "</div>\n";
|
||||
$oPage->add_at_the_end($this->GetObjectPickerDialog($oPage)); // To prevent adding forms inside the main form
|
||||
$oPage->add_at_the_end("<div id=\"dlg_{$this->m_sAttCode}{$this->m_sNameSuffix}\"></div>"); // To prevent adding forms inside the main form
|
||||
return $sHtmlValue;
|
||||
}
|
||||
|
||||
@@ -258,27 +342,26 @@ EOF
|
||||
return $sTargetClass;
|
||||
}
|
||||
|
||||
protected function GetObjectPickerDialog($oPage)
|
||||
public function GetObjectPickerDialog($oPage, $oCurrentObj)
|
||||
{
|
||||
$sHtml = "<div id=\"dlg_{$this->m_sAttCode}{$this->m_sNameSuffix}\">";
|
||||
$sHtml .= "<div class=\"wizContainer\" style=\"vertical-align:top;\">\n";
|
||||
$sHtml = "<div class=\"wizContainer\" style=\"vertical-align:top;\">\n";
|
||||
$oFilter = new DBObjectSearch($this->m_sRemoteClass);
|
||||
$oSet = new CMDBObjectSet($oFilter);
|
||||
$this->SetSearchDefaultFromContext($oCurrentObj, $oFilter);
|
||||
$oBlock = new DisplayBlock($oFilter, 'search', false);
|
||||
$sHtml .= $oBlock->GetDisplay($oPage, "SearchFormToAdd_{$this->m_sAttCode}{$this->m_sNameSuffix}", array('open' => true));
|
||||
$sHtml .= "<form id=\"ObjectsAddForm_{$this->m_sAttCode}{$this->m_sNameSuffix}\" OnSubmit=\"return oWidget{$this->m_iInputId}.DoAddObjects(this.id);\">\n";
|
||||
$sHtml .= "<div id=\"SearchResultsToAdd_{$this->m_sAttCode}{$this->m_sNameSuffix}\" 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=\"button\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"$('#dlg_{$this->m_sAttCode}{$this->m_sNameSuffix}').dialog('close');\"> <input type=\"submit\" value=\"".Dict::S('UI:Button:Add')."\">";
|
||||
$sHtml .= "<input type=\"hidden\" id=\"count_{$this->m_sAttCode}{$this->m_sNameSuffix}\" value=\"0\"/>";
|
||||
$sHtml .= "<input type=\"button\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"$('#dlg_{$this->m_sAttCode}{$this->m_sNameSuffix}').dialog('close');\"> <input id=\"btn_ok_{$this->m_sAttCode}{$this->m_sNameSuffix}\" disabled=\"disabled\" type=\"submit\" value=\"".Dict::S('UI:Button:Add')."\">";
|
||||
$sHtml .= "</div>\n";
|
||||
$sHtml .= "</form>\n";
|
||||
$sHtml .= "</div>\n";
|
||||
$oPage->add($sHtml);
|
||||
$oPage->add_ready_script("$('#dlg_{$this->m_sAttCode}{$this->m_sNameSuffix}').dialog({ width: $(window).width()*0.8, height: $(window).height()*0.8, autoOpen: false, modal: true, resizeStop: oWidget{$this->m_iInputId}.UpdateSizes });");
|
||||
$oPage->add_ready_script("$('#dlg_{$this->m_sAttCode}{$this->m_sNameSuffix}').dialog('option', {title:'".addslashes(Dict::Format('UI:AddObjectsOf_Class_LinkedWith_Class', MetaModel::GetName($this->m_sLinkedClass), MetaModel::GetName($this->m_sClass)))."'});");
|
||||
$oPage->add_ready_script("$('#SearchFormToAdd_{$this->m_sAttCode}{$this->m_sNameSuffix} form').bind('submit.uilinksWizard', oWidget{$this->m_iInputId}.SearchObjectsToAdd);");
|
||||
$oPage->add_ready_script("$('#SearchFormToAdd_{$this->m_sAttCode}{$this->m_sNameSuffix}').resize(oWidget{$this->m_iInputId}.UpdateSizes);");
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -332,18 +415,19 @@ EOF
|
||||
}
|
||||
$oSet = new CMDBObjectSet($oFilter);
|
||||
$oBlock = new DisplayBlock($oFilter, 'list', false);
|
||||
$oBlock->Display($oP, 'ResultsToAdd', array('menu' => false, 'selection_mode' => true, 'display_limit' => false)); // Don't display the 'Actions' menu on the results
|
||||
$oBlock->Display($oP, "ResultsToAdd_{$this->m_sAttCode}", array('menu' => false, 'cssCount'=> '#count_'.$this->m_sAttCode.$this->m_sNameSuffix , 'selection_mode' => true, 'table_id' => 'add_'.$this->m_sAttCode)); // Don't display the 'Actions' menu on the results
|
||||
}
|
||||
|
||||
public function DoAddObjects(WebPage $oP, $aLinkedObjectIds = array())
|
||||
public function DoAddObjects(WebPage $oP, $oFullSetFilter, $oCurrentObj)
|
||||
{
|
||||
$aTable = array();
|
||||
$aLinkedObjectIds = utils::ReadMultipleSelection($oFullSetFilter);
|
||||
|
||||
foreach($aLinkedObjectIds as $iObjectId)
|
||||
{
|
||||
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $iObjectId);
|
||||
if (is_object($oLinkedObj))
|
||||
{
|
||||
$aRow = $this->GetFormRow($oP, $oLinkedObj, -$iObjectId ); // Not yet created link get negative Ids
|
||||
$aRow = $this->GetFormRow($oP, $oLinkedObj, -$iObjectId, array(), $oCurrentObj ); // Not yet created link get negative Ids
|
||||
$oP->add($this->DisplayFormRow($oP, $this->m_aTableConfig, $aRow, -$iObjectId));
|
||||
}
|
||||
else
|
||||
@@ -352,5 +436,47 @@ EOF
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,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,13 +52,15 @@ class UIPasswordWidget
|
||||
{
|
||||
$sCode = $this->sAttCode.$this->sNameSuffix;
|
||||
$iWidgetIndex = self::$iWidgetIndex;
|
||||
$sPasswordValue = utils::ReadPostedParam("attr_$sCode", '*****');
|
||||
$sConfirmPasswordValue = utils::ReadPostedParam("attr_{$sCode}_confirmed", '*****');
|
||||
|
||||
$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.'" id="'.$this->iId.'" value="'.htmlentities($sPasswordValue, ENT_QUOTES, 'UTF-8').'"/> <span class="form_validation" id="v_'.$this->iId.'"></span><br/>';
|
||||
$sHtmlValue .= '<input type="password" maxlength="255" id="'.$this->iId.'_confirm" value="'.htmlentities($sConfirmPasswordValue, ENT_QUOTES, 'UTF-8').'" name="attr_'.$sCode.'_confirmed"/> '.Dict::S('UI:PasswordConfirm').' <input id="'.$this->iId.'_reset" type="button" value="'.Dict::S('UI:Button:ResetPassword').'" onClick="ResetPwd(\''.$this->iId.'\');">';
|
||||
$sHtmlValue .= '<input type="hidden" id="'.$this->iId.'_changed" name="attr_'.$sCode.'_changed" value="'.$sChangedValue.'"/>';
|
||||
$sHtmlValue = '<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/>';
|
||||
$sHtmlValue .= '<input type="password" maxlength="255" id="'.$this->iId.'_confirm" value="'.htmlentities($sConfirmPasswordValue, ENT_QUOTES, 'UTF-8').'" name="attr_'.$sCode.'[confirm]"/> '.Dict::S('UI:PasswordConfirm').' <input id="'.$this->iId.'_reset" type="button" value="'.Dict::S('UI:Button:ResetPassword').'" onClick="ResetPwd(\''.$this->iId.'\');">';
|
||||
$sHtmlValue .= '<input type="hidden" id="'.$this->iId.'_changed" name="attr_'.$sCode.'[changed]" value="'.$sChangedValue.'"/>';
|
||||
|
||||
$oPage->add_ready_script("$('#$this->iId').bind('keyup change', function(evt) { return PasswordFieldChanged('$this->iId') } );"); // Bind to a custom event: validate
|
||||
$oPage->add_ready_script("$('#$this->iId').bind('keyup change validate', function(evt, sFormId) { return ValidatePasswordField('$this->iId', sFormId) } );"); // Bind to a custom event: validate
|
||||
|
||||
@@ -1,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
|
||||
@@ -85,7 +86,7 @@ class UILinksWizard
|
||||
$sTargetClass = $oAttDef->GetTargetClass();
|
||||
$oTargetObj = MetaModel::GetObject($sTargetClass, $this->m_iObjectId);
|
||||
|
||||
$oP->set_title("iTop - ".MetaModel::GetName($this->m_sLinkedClass)." objects linked with ".MetaModel::GetName(get_class($oTargetObj)).": ".$oTargetObj->GetName());
|
||||
$oP->set_title("iTop - ".MetaModel::GetName($this->m_sLinkedClass)." objects linked with ".MetaModel::GetName(get_class($oTargetObj)).": ".$oTargetObj->GetRawName());
|
||||
$oP->add("<div class=\"wizContainer\">\n");
|
||||
$oP->add("<form method=\"post\">\n");
|
||||
$oP->add("<div class=\"page_header\">\n");
|
||||
@@ -97,15 +98,14 @@ class UILinksWizard
|
||||
$oP->add("<input type=\"hidden\" name=\"linking_attcode\" value=\"{$this->m_sLinkingAttCode}\">\n");
|
||||
$oP->add("<h1>".Dict::Format('UI:ManageObjectsOf_Class_LinkedWith_Class_Instance', MetaModel::GetName($this->m_sLinkedClass), MetaModel::GetName(get_class($oTargetObj)), "<span class=\"hilite\">".$oTargetObj->GetHyperlink()."</span>")."</h1>\n");
|
||||
$oP->add("</div>\n");
|
||||
$oP->add("<script type=\"text/javascript\">\n");
|
||||
$oP->add(
|
||||
$oP->add_script(
|
||||
<<<EOF
|
||||
function OnSelectChange()
|
||||
{
|
||||
var nbChecked = $('.selection:checked').length;
|
||||
if (nbChecked > 0)
|
||||
{
|
||||
$('#btnRemove').attr('disabled','');
|
||||
$('#btnRemove').removeAttr('disabled');
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -132,7 +132,7 @@ class UILinksWizard
|
||||
function AddObjects()
|
||||
{
|
||||
// TO DO: compute the list of objects already linked with the current Object
|
||||
$.post( '../pages/ajax.render.php', { 'operation': 'addObjects',
|
||||
$.post( GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', { 'operation': 'addObjects',
|
||||
'class': '{$this->m_sClass}',
|
||||
'linkageAttr': '{$this->m_sLinkageAttr}',
|
||||
'linkedClass': '{$this->m_sLinkedClass}',
|
||||
@@ -175,7 +175,7 @@ class UILinksWizard
|
||||
theMap['operation'] = 'searchObjectsToAdd';
|
||||
|
||||
// Run the query and display the results
|
||||
$.post( '../pages/ajax.render.php', theMap,
|
||||
$.post( GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', theMap,
|
||||
function(data)
|
||||
{
|
||||
$('#SearchResultsToAdd').html(data);
|
||||
@@ -223,7 +223,7 @@ class UILinksWizard
|
||||
theMap['operation'] = 'doAddObjects';
|
||||
|
||||
// Run the query and display the results
|
||||
$.post( '../pages/ajax.render.php', theMap,
|
||||
$.post( GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', theMap,
|
||||
function(data)
|
||||
{
|
||||
//console.log('Data: ' + data);
|
||||
@@ -257,7 +257,6 @@ class UILinksWizard
|
||||
}
|
||||
EOF
|
||||
);
|
||||
$oP->Add("</script>\n");
|
||||
$oP->add_ready_script("InitForm();");
|
||||
$oFilter = new DBObjectSearch($this->m_sClass);
|
||||
$oFilter->AddCondition($this->m_sLinkageAttr, $this->m_iObjectId, '=');
|
||||
@@ -395,7 +394,7 @@ EOF
|
||||
$oFilter = new DBObjectSearch($this->m_sLinkedClass);
|
||||
$oSet = new CMDBObjectSet($oFilter);
|
||||
$oBlock = new DisplayBlock($oFilter, 'list', false);
|
||||
$oBlock->Display($oP, 'ResultsToAdd', array('menu' => false, 'selection_mode' => true, 'display_limit' => false)); // Don't display the 'Actions' menu on the results
|
||||
$oBlock->Display($oP, 'ResultsToAdd', array('menu' => false, 'selection_mode' => true)); // Don't display the 'Actions' menu on the results
|
||||
}
|
||||
|
||||
public function DoAddObjects(WebPage $oP, $aLinkedObjectIds = array())
|
||||
|
||||
@@ -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
|
||||
@@ -53,7 +54,7 @@ class UIWizard
|
||||
{
|
||||
if ($iStepIndex == 1) // one big form that contains everything, to make sure that the uploaded files are posted too
|
||||
{
|
||||
$this->m_oPage->add("<form method=\"post\" enctype=\"multipart/form-data\" action=\"../pages/UI.php\">\n");
|
||||
$this->m_oPage->add("<form method=\"post\" enctype=\"multipart/form-data\" action=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php\">\n");
|
||||
}
|
||||
$this->m_oPage->add("<div class=\"wizContainer\" id=\"wizStep$iStepIndex\" style=\"display:none;\">\n");
|
||||
$this->m_oPage->add("<a name=\"step$iStepIndex\" />\n");
|
||||
@@ -112,8 +113,7 @@ class UIWizard
|
||||
<input type=\"button\" value=\"".Dict::S('UI:Button:Next')."\" onClick=\"GoToStep($iStepIndex, 1+$iStepIndex)\" />
|
||||
<input type=\"button\" value=\"".Dict::S('UI:Button:Finish')."\" $sDisabled onClick=\"GoToStep($iStepIndex, 1+$nbSteps)\" />
|
||||
</div>\n");
|
||||
$this->m_oPage->add("
|
||||
<script type=\"text/javascript\">
|
||||
$this->m_oPage->add_script("
|
||||
function OnEnterStep{$iStepIndex}()
|
||||
{
|
||||
oWizardHelper.ResetQuery();
|
||||
@@ -123,7 +123,7 @@ $sJSHandlerCode
|
||||
|
||||
oWizardHelper.AjaxQueryServer();
|
||||
}
|
||||
</script>\n");
|
||||
");
|
||||
$this->m_oPage->add("</div>\n\n");
|
||||
}
|
||||
|
||||
@@ -139,16 +139,15 @@ $sJSHandlerCode
|
||||
$this->m_oPage->add("<input type=\"hidden\" name=\"operation\" value=\"wizard_apply_new\" />\n");
|
||||
$this->m_oPage->add("<input type=\"hidden\" name=\"transaction_id\" value=\"".utils::GetNewTransactionId()."\" />\n");
|
||||
$this->m_oPage->add("<input type=\"hidden\" id=\"wizard_json_obj\" name=\"json_obj\" value=\"\" />\n");
|
||||
$this->m_oPage->add("<script type=\"text/javascript\">\n");
|
||||
$this->m_oPage->add("function OnEnterStep$iStepIndex() {\n");
|
||||
$sScript = "function OnEnterStep$iStepIndex() {\n";
|
||||
foreach($aFieldsMap as $iInputId => $sAttCode)
|
||||
{
|
||||
$this->m_oPage->add("\toWizardHelper.UpdateCurrentValue('$sAttCode');\n");
|
||||
$sScript .= "\toWizardHelper.UpdateCurrentValue('$sAttCode');\n";
|
||||
}
|
||||
$this->m_oPage->add("\toWizardHelper.Preview('object_preview');\n");
|
||||
$this->m_oPage->add("\t$('#wizard_json_obj').val(oWizardHelper.ToJSON());\n");
|
||||
$this->m_oPage->add("}\n");
|
||||
$this->m_oPage->add("</script>\n");
|
||||
$sScript .= "\toWizardHelper.Preview('object_preview');\n";
|
||||
$sScript .= "\t$('#wizard_json_obj').val(oWizardHelper.ToJSON());\n";
|
||||
$sScript .= "}\n";
|
||||
$this->m_oPage->add_script($sScript);
|
||||
$this->m_oPage->add("<div id=\"object_preview\">\n");
|
||||
$this->m_oPage->add("</div>\n");
|
||||
$this->m_oPage->add($oAppContext->GetForForm());
|
||||
|
||||
61
application/user.dashboard.class.inc.php
Normal file
61
application/user.dashboard.class.inc.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?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/>
|
||||
|
||||
/**
|
||||
* Store and retrieve user custom dashboards
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
require_once(APPROOT.'/core/dbobject.class.php');
|
||||
|
||||
/**
|
||||
* This class is used to store, in a persistent manner, a dashboard edited by a user
|
||||
*/
|
||||
class UserDashboard extends DBObject
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "gui",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "user_id",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_app_dashboards",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", array("targetclass"=>"User", "allowed_values"=>null, "sql"=>"user_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("menu_code", array("allowed_values"=>null, "sql"=>"menu_code", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeText("contents", array("allowed_values"=>null, "sql"=>"contents", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Overloading this function here to secure a fix done right before the release
|
||||
* The real fix should be to implement this verb in DBObject
|
||||
*/
|
||||
public function DBDeleteTracked(CMDBChange $oChange, $bSkipStrongSecurity = null, &$oDeletionPlan = null)
|
||||
{
|
||||
$this->DBDelete($oDeletionPlan);
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -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');
|
||||
@@ -77,6 +77,42 @@ class appUserPreferences extends DBObject
|
||||
self::Save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the value for a given preference (or list of preferences that matches a pattern), and updates the database
|
||||
* @param string $sPattern Code/Pattern of the properties/preferences to reset
|
||||
* @param boolean $bPattern Whether or not the supplied code is a PCRE pattern
|
||||
*/
|
||||
static function UnsetPref($sCodeOrPattern, $bPattern = false)
|
||||
{
|
||||
if (self::$oUserPrefs == null)
|
||||
{
|
||||
self::Load();
|
||||
}
|
||||
$aPrefs = self::$oUserPrefs->Get('preferences');
|
||||
if ($bPattern)
|
||||
{
|
||||
// the supplied code is a pattern, clear all preferences that match
|
||||
foreach($aPrefs as $sKey => $void)
|
||||
{
|
||||
if (preg_match($sCodeOrPattern, $sKey))
|
||||
{
|
||||
unset($aPrefs[$sKey]);
|
||||
}
|
||||
}
|
||||
self::$oUserPrefs->Set('preferences', $aPrefs);
|
||||
}
|
||||
else
|
||||
{
|
||||
unset($aPrefs[$sCodeOrPattern]);
|
||||
self::$oUserPrefs->Set('preferences', $aPrefs);
|
||||
}
|
||||
// Save only if needed
|
||||
if (self::$oUserPrefs->IsModified())
|
||||
{
|
||||
self::Save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this function to get all the preferences for the user, packed as a JSON object
|
||||
* @return string JSON representation of the preferences
|
||||
@@ -94,7 +130,7 @@ class appUserPreferences extends DBObject
|
||||
/**
|
||||
* Call this function if the user has changed (like when doing a logoff...)
|
||||
*/
|
||||
static public function Reset()
|
||||
static public function ResetPreferences()
|
||||
{
|
||||
self::$oUserPrefs = null;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,28 +1,45 @@
|
||||
<?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/>
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Generic interface common to CLI and Web pages
|
||||
*/
|
||||
Interface Page
|
||||
{
|
||||
public function output();
|
||||
public function add($sText);
|
||||
public function p($sText);
|
||||
public function pre($sText);
|
||||
public function add_comment($sText);
|
||||
public function table($aConfig, $aData, $aParams = array());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Simple helper class to ease the production of HTML pages
|
||||
*
|
||||
@@ -34,12 +51,13 @@
|
||||
* $oPage->p("Hello World !");
|
||||
* $oPage->output();
|
||||
*/
|
||||
class WebPage
|
||||
class WebPage implements Page
|
||||
{
|
||||
protected $s_title;
|
||||
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;
|
||||
@@ -47,13 +65,20 @@ class WebPage
|
||||
protected $a_base;
|
||||
protected $iNextId;
|
||||
protected $iTransactionId;
|
||||
|
||||
protected $sContentType;
|
||||
protected $sContentDisposition;
|
||||
protected $sContentFileName;
|
||||
protected $bTrashUnexpectedOutput;
|
||||
protected $s_sOutputFormat;
|
||||
protected $a_OutputOptions;
|
||||
|
||||
public function __construct($s_title)
|
||||
{
|
||||
$this->s_title = $s_title;
|
||||
$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();
|
||||
@@ -61,6 +86,12 @@ class WebPage
|
||||
$this->a_base = array( 'href' => '', 'target' => '');
|
||||
$this->iNextId = 0;
|
||||
$this->iTransactionId = 0;
|
||||
$this->sContentType = '';
|
||||
$this->sContentDisposition = '';
|
||||
$this->sContentFileName = '';
|
||||
$this->bTrashUnexpectedOutput = false;
|
||||
$this->s_OutputFormat = utils::ReadParam('output_format', 'html');
|
||||
$this->a_OutputOptions = array();
|
||||
ob_start(); // Start capturing the output
|
||||
}
|
||||
|
||||
@@ -90,11 +121,11 @@ class WebPage
|
||||
}
|
||||
|
||||
/**
|
||||
* Add any text or HTML fragment at the end of the body of the page
|
||||
* Add any text or HTML fragment (identified by an ID) at the end of the body of the page
|
||||
* This is useful to add hidden content, DIVs or FORMs that should not
|
||||
* be embedded into each other.
|
||||
*/
|
||||
public function add_at_the_end($s_html)
|
||||
public function add_at_the_end($s_html, $sId = '')
|
||||
{
|
||||
$this->s_deferred_content .= $s_html;
|
||||
}
|
||||
@@ -115,6 +146,13 @@ class WebPage
|
||||
$this->add('<pre>'.$s_html.'</pre>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a comment
|
||||
*/
|
||||
public function add_comment($sText)
|
||||
{
|
||||
$this->add('<!--'.$sText.'-->');
|
||||
}
|
||||
/**
|
||||
* Add a paragraph to the body of the page
|
||||
*/
|
||||
@@ -154,27 +192,33 @@ class WebPage
|
||||
$sHtml .= "<tbody>\n";
|
||||
foreach($aData as $aRow)
|
||||
{
|
||||
if (isset($aRow['@class'])) // Row specific class, for hilighting certain rows
|
||||
{
|
||||
$sHtml .= "<tr class=\"{$aRow['@class']}\">\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sHtml .= "<tr>\n";
|
||||
}
|
||||
foreach($aConfig as $sName=>$aAttribs)
|
||||
{
|
||||
$aMatches = array();
|
||||
$sClass = isset($aAttribs['class']) ? 'class="'.$aAttribs['class'].'"' : '';
|
||||
$sValue = ($aRow[$sName] === '') ? ' ' : $aRow[$sName];
|
||||
$sHtml .= "<td $sClass>$sValue</td>\n";
|
||||
}
|
||||
$sHtml .= "</tr>\n";
|
||||
$sHtml .= $this->GetTableRow($aRow, $aConfig);
|
||||
}
|
||||
$sHtml .= "</tbody>\n";
|
||||
$sHtml .= "</table>\n";
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
public function GetTableRow($aRow, $aConfig)
|
||||
{
|
||||
$sHtml = '';
|
||||
if (isset($aRow['@class'])) // Row specific class, for hilighting certain rows
|
||||
{
|
||||
$sHtml .= "<tr class=\"{$aRow['@class']}\">";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sHtml .= "<tr>";
|
||||
}
|
||||
foreach($aConfig as $sName=>$aAttribs)
|
||||
{
|
||||
$sClass = isset($aAttribs['class']) ? 'class="'.$aAttribs['class'].'"' : '';
|
||||
$sValue = ($aRow[$sName] === '') ? ' ' : $aRow[$sName];
|
||||
$sHtml .= "<td $sClass>$sValue</td>";
|
||||
}
|
||||
$sHtml .= "</tr>";
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add some Javascript to the header of the page
|
||||
@@ -191,6 +235,16 @@ class WebPage
|
||||
{
|
||||
// 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
|
||||
*/
|
||||
@@ -240,6 +294,28 @@ class WebPage
|
||||
|
||||
$this->add($this->GetDetails($aFields));
|
||||
}
|
||||
|
||||
/**
|
||||
* Records the current state of the 'html' part of the page output
|
||||
* @return mixed The current state of the 'html' output
|
||||
*/
|
||||
public function start_capture()
|
||||
{
|
||||
return strlen($this->s_content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the part of the html output that occurred since the call to start_capture
|
||||
* and removes this part from the current html output
|
||||
* @param $offset mixed The value returned by start_capture
|
||||
* @return string The part of the html output that was added since the call to start_capture
|
||||
*/
|
||||
public function end_capture($offset)
|
||||
{
|
||||
$sCaptured = substr($this->s_content, $offset);
|
||||
$this->s_content = substr($this->s_content, 0, $offset);
|
||||
return $sCaptured;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a special kind of TABLE useful for displaying the details of an object from a hash array of data
|
||||
@@ -269,7 +345,106 @@ class WebPage
|
||||
$sHtml .= "</table>\n";
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a set of radio buttons suitable for editing a field/attribute of an object (including its validation)
|
||||
* @param $aAllowedValues hash Array of value => display_value
|
||||
* @param $value mixed Current value for the field/attribute
|
||||
* @param $iId mixed Unique Id for the input control in the page
|
||||
* @param $sFieldName string The name of the field, attr_<$sFieldName> will hold the value for the field
|
||||
* @param $bMandatory bool Whether or not the field is mandatory
|
||||
* @param $bVertical bool Disposition of the radio buttons vertical or horizontal
|
||||
* @param $sValidationField string HTML fragment holding the validation field (exclamation icon...)
|
||||
* @return string The HTML fragment corresponding to the radio buttons
|
||||
*/
|
||||
public function GetRadioButtons($aAllowedValues, $value, $iId, $sFieldName, $bMandatory, $bVertical, $sValidationField)
|
||||
{
|
||||
$idx = 0;
|
||||
$sHTMLValue = '';
|
||||
foreach($aAllowedValues as $key => $display_value)
|
||||
{
|
||||
if ((count($aAllowedValues) == 1) && ($bMandatory == 'true') )
|
||||
{
|
||||
// When there is only once choice, select it by default
|
||||
$sSelected = ' checked';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sSelected = ($value == $key) ? ' checked' : '';
|
||||
}
|
||||
$sHTMLValue .= "<input type=\"radio\" id=\"{$iId}_{$key}\" name=\"radio_$sFieldName\" onChange=\"$('#{$iId}').val(this.value).trigger('change');\" value=\"$key\"$sSelected><label class=\"radio\" for=\"{$iId}_{$key}\"> $display_value</label> ";
|
||||
if ($bVertical)
|
||||
{
|
||||
if ($idx == 0)
|
||||
{
|
||||
// Validation icon at the end of the first line
|
||||
$sHTMLValue .= " {$sValidationField}\n";
|
||||
}
|
||||
$sHTMLValue .= "<br>\n";
|
||||
}
|
||||
$idx++;
|
||||
}
|
||||
$sHTMLValue .= "<input type=\"hidden\" id=\"$iId\" name=\"$sFieldName\" value=\"$value\"/>";
|
||||
if (!$bVertical)
|
||||
{
|
||||
// Validation icon at the end of the line
|
||||
$sHTMLValue .= " {$sValidationField}\n";
|
||||
}
|
||||
return $sHTMLValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Discard unexpected output data (such as PHP warnings)
|
||||
* This is a MUST when the Page output is DATA (download of a document, download CSV export, download ...)
|
||||
*/
|
||||
public function TrashUnexpectedOutput()
|
||||
{
|
||||
$this->bTrashUnexpectedOutput = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the output buffer and deal with its contents:
|
||||
* - trash unexpected output if the flag has been set
|
||||
* - report unexpected behaviors such as the output buffering being stopped
|
||||
*
|
||||
* Possible improvement: I've noticed that several output buffers are stacked,
|
||||
* if they are not empty, the output will be corrupted. The solution would
|
||||
* consist in unstacking all of them (and concatenate the contents).
|
||||
*/
|
||||
protected function ob_get_clean_safe()
|
||||
{
|
||||
$sOutput = ob_get_contents();
|
||||
if ($sOutput === false)
|
||||
{
|
||||
$sMsg = "Design/integration issue: No output buffer. Some piece of code has called ob_get_clean() or ob_end_clean() without calling ob_start()";
|
||||
if ($this->bTrashUnexpectedOutput)
|
||||
{
|
||||
IssueLog::Error($sMsg);
|
||||
$sOutput = '';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sOutput = $sMsg;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ob_end_clean(); // on some versions of PHP doing so when the output buffering is stopped can cause a notice
|
||||
if ($this->bTrashUnexpectedOutput)
|
||||
{
|
||||
if (trim($sOutput) != '')
|
||||
{
|
||||
if (Utils::GetConfig() && Utils::GetConfig()->Get('debug_report_spurious_chars'))
|
||||
{
|
||||
IssueLog::Error("Trashing unexpected output:'$s_captured_output'\n");
|
||||
}
|
||||
}
|
||||
$sOutput = '';
|
||||
}
|
||||
}
|
||||
return $sOutput;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs (via some echo) the complete HTML page by assembling all its elements
|
||||
*/
|
||||
@@ -279,25 +454,25 @@ class WebPage
|
||||
{
|
||||
header($s_header);
|
||||
}
|
||||
$s_captured_output = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
$s_captured_output = $this->ob_get_clean_safe();
|
||||
echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n";
|
||||
echo "<html>\n";
|
||||
echo "<head>\n";
|
||||
echo "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n";
|
||||
echo "<title>{$this->s_title}</title>\n";
|
||||
echo "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n";
|
||||
echo "<title>".htmlentities($this->s_title, ENT_QUOTES, 'UTF-8')."</title>\n";
|
||||
echo $this->get_base_tag();
|
||||
foreach($this->a_linked_scripts as $s_script)
|
||||
{
|
||||
// Make sure that the URL to the script contains the application's version number
|
||||
// so that the new script do NOT get reloaded from the cache when the application is upgraded
|
||||
if (strpos('?', $s_script) === false)
|
||||
if (strpos($s_script, '?') === false)
|
||||
{
|
||||
$s_script .= "?version=".ITOP_VERSION;
|
||||
$s_script .= "?itopversion=".ITOP_VERSION;
|
||||
}
|
||||
else
|
||||
{
|
||||
$s_script .= "&version=".ITOP_VERSION;
|
||||
$s_script .= "&itopversion=".ITOP_VERSION;
|
||||
}
|
||||
echo "<script type=\"text/javascript\" src=\"$s_script\"></script>\n";
|
||||
}
|
||||
@@ -310,6 +485,7 @@ class WebPage
|
||||
}
|
||||
echo "</script>\n";
|
||||
}
|
||||
$this->output_dict_entries();
|
||||
foreach($this->a_linked_stylesheets as $a_stylesheet)
|
||||
{
|
||||
if ($a_stylesheet['condition'] != "")
|
||||
@@ -332,28 +508,44 @@ class WebPage
|
||||
}
|
||||
echo "</style>\n";
|
||||
}
|
||||
if (class_exists('MetaModel') && MetaModel::GetConfig())
|
||||
{
|
||||
echo "<link rel=\"shortcut icon\" href=\"".utils::GetAbsoluteUrlAppRoot()."images/favicon.ico\" />\n";
|
||||
}
|
||||
echo "</head>\n";
|
||||
echo "<body>\n";
|
||||
echo $this->s_content;
|
||||
echo self::FilterXSS($this->s_content);
|
||||
if (trim($s_captured_output) != "")
|
||||
{
|
||||
echo "<div class=\"raw_output\">$s_captured_output</div>\n";
|
||||
echo "<div class=\"raw_output\">".self::FilterXSS($s_captured_output)."</div>\n";
|
||||
}
|
||||
echo '<div id="at_the_end">'.$this->s_deferred_content.'</div>';
|
||||
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\">");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -384,7 +576,29 @@ class WebPage
|
||||
{
|
||||
return $this->iNextId++;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the content-type (mime type) for the page's content
|
||||
* @param $sContentType string
|
||||
* @return void
|
||||
*/
|
||||
public function SetContentType($sContentType)
|
||||
{
|
||||
$this->sContentType = $sContentType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the content-disposition (mime type) for the page's content
|
||||
* @param $sDisposition string The disposition: 'inline' or 'attachment'
|
||||
* @param $sFileName string The original name of the file
|
||||
* @return void
|
||||
*/
|
||||
public function SetContentDisposition($sDisposition, $sFileName)
|
||||
{
|
||||
$this->sContentDisposition = $sDisposition;
|
||||
$this->sContentFileName = $sFileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the transactionId of the current form
|
||||
* @param $iTransactionId integer
|
||||
@@ -403,5 +617,427 @@ class WebPage
|
||||
{
|
||||
return $this->iTransactionId;
|
||||
}
|
||||
|
||||
public static function FilterXSS($sHTML)
|
||||
{
|
||||
return str_ireplace('<script', '<script', $sHTML);
|
||||
}
|
||||
|
||||
/**
|
||||
* What is the currently selected output format
|
||||
* @return string The selected output format: html, pdf...
|
||||
*/
|
||||
public function GetOutputFormat()
|
||||
{
|
||||
return $this->s_OutputFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the desired output format is possible or not
|
||||
* @param string $sOutputFormat The desired output format: html, pdf...
|
||||
* @return bool True if the format is Ok, false otherwise
|
||||
*/
|
||||
function IsOutputFormatAvailable($sOutputFormat)
|
||||
{
|
||||
$bResult = false;
|
||||
switch($sOutputFormat)
|
||||
{
|
||||
case 'html':
|
||||
$bResult = true; // Always supported
|
||||
break;
|
||||
|
||||
case 'pdf':
|
||||
$bResult = @is_readable(APPROOT.'lib/MPDF/mpdf.php');
|
||||
break;
|
||||
}
|
||||
return $bResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the value of a named output option for the given format
|
||||
* @param string $sFormat The format: html or pdf
|
||||
* @param string $sOptionName The name of the option
|
||||
* @return mixed false if the option was never set or the options's value
|
||||
*/
|
||||
public function GetOutputOption($sFormat, $sOptionName)
|
||||
{
|
||||
if (isset($this->a_OutputOptions[$sFormat][$sOptionName]))
|
||||
{
|
||||
return $this->a_OutputOptions[$sFormat][$sOptionName];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Sets a named output option for the given format
|
||||
* @param string $sFormat The format for which to set the option: html or pdf
|
||||
* @param string $sOptionName the name of the option
|
||||
* @param mixed $sValue The value of the option
|
||||
*/
|
||||
public function SetOutputOption($sFormat, $sOptionName, $sValue)
|
||||
{
|
||||
if (!isset($this->a_OutputOptions[$sFormat]))
|
||||
{
|
||||
$this->a_OutputOptions[$sFormat] = array($sOptionName => $sValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->a_OutputOptions[$sFormat][$sOptionName] = $sValue;
|
||||
}
|
||||
}
|
||||
|
||||
public function RenderPopupMenuItems($aActions, $aFavoriteActions = array())
|
||||
{
|
||||
$sPrevUrl = '';
|
||||
$sHtml = '';
|
||||
foreach ($aActions as $aAction)
|
||||
{
|
||||
$sClass = isset($aAction['class']) ? " class=\"{$aAction['class']}\"" : "";
|
||||
$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...
|
||||
{
|
||||
$sHtml .= "<li>{$aAction['label']}</li>";
|
||||
}
|
||||
$sPrevUrl = '';
|
||||
}
|
||||
else
|
||||
{
|
||||
$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)
|
||||
{
|
||||
$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');
|
||||
@@ -76,7 +77,7 @@ class WizardHelper
|
||||
if ( isset($aLinkedObject[$sLinkedAttCode]) && ($aLinkedObject[$sLinkedAttCode] !== null) )
|
||||
{
|
||||
$sLinkedAttDef = MetaModel::GetAttributeDef($sLinkedClass, $sLinkedAttCode);
|
||||
if (($sLinkedAttDef->IsExternalKey()) && ($aLinkedObject[$sLinkedAttCode] != '') && ($aLinkedObject[$sLinkedAttCode] != 0) )
|
||||
if (($sLinkedAttDef->IsExternalKey()) && ($aLinkedObject[$sLinkedAttCode] != '') && ($aLinkedObject[$sLinkedAttCode] > 0) )
|
||||
{
|
||||
// For external keys: load the target object so that external fields
|
||||
// get filled too
|
||||
@@ -98,7 +99,7 @@ class WizardHelper
|
||||
{
|
||||
if ($bReadUploadedFiles)
|
||||
{
|
||||
$oDocument = utils::ReadPostedDocument('file_'.$sAttCode);
|
||||
$oDocument = utils::ReadPostedDocument('attr_'.$sAttCode, 'fcontents');
|
||||
$oObj->Set($sAttCode, $oDocument);
|
||||
}
|
||||
else
|
||||
@@ -108,7 +109,7 @@ class WizardHelper
|
||||
$oObj->Set($sAttCode, $oDocument);
|
||||
}
|
||||
}
|
||||
else if (($oAttDef->IsExternalKey()) && (!empty($value)) )
|
||||
else if (($oAttDef->IsExternalKey()) && (!empty($value)) && ($value > 0) )
|
||||
{
|
||||
// For external keys: load the target object so that external fields
|
||||
// get filled too
|
||||
@@ -125,7 +126,7 @@ class WizardHelper
|
||||
{
|
||||
$oObj->Set(MetaModel::GetStateAttributeCode($this->m_aData['m_sClass']), $this->m_aData['m_sState']);
|
||||
}
|
||||
|
||||
$oObj->DoComputeValues();
|
||||
return $oObj;
|
||||
}
|
||||
|
||||
@@ -260,7 +261,7 @@ class WizardHelper
|
||||
foreach($aLinkObj as $sAttCode => $value)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($sLinkClass, $sAttCode);
|
||||
if (($oAttDef->IsExternalKey()) && ($value != '') )
|
||||
if (($oAttDef->IsExternalKey()) && ($value != '') && ($value > 0))
|
||||
{
|
||||
// For external keys: load the target object so that external fields
|
||||
// get filled too
|
||||
|
||||
456
application/xlsxwriter.class.php
Normal file
456
application/xlsxwriter.class.php
Normal file
@@ -0,0 +1,456 @@
|
||||
<?php
|
||||
/* @author Mark Jones
|
||||
* @license MIT License
|
||||
* */
|
||||
|
||||
if (!class_exists('ZipArchive')) { throw new Exception('ZipArchive not found'); }
|
||||
|
||||
Class XLSXWriter
|
||||
{
|
||||
//------------------------------------------------------------------
|
||||
protected $author ='Doc Author';
|
||||
protected $sheets_meta = array();
|
||||
protected $shared_strings = array();//unique set
|
||||
protected $shared_string_count = 0;//count of non-unique references to the unique set
|
||||
protected $temp_files = array();
|
||||
|
||||
public function __construct(){}
|
||||
public function setAuthor($author='') { $this->author=$author; }
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
if (!empty($this->temp_files)) {
|
||||
foreach($this->temp_files as $temp_file) {
|
||||
@unlink($temp_file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function tempFilename()
|
||||
{
|
||||
$filename = tempnam("/tmp", "xlsx_writer_");
|
||||
$this->temp_files[] = $filename;
|
||||
return $filename;
|
||||
}
|
||||
|
||||
public function writeToStdOut()
|
||||
{
|
||||
$temp_file = $this->tempFilename();
|
||||
self::writeToFile($temp_file);
|
||||
readfile($temp_file);
|
||||
}
|
||||
|
||||
public function writeToString()
|
||||
{
|
||||
$temp_file = $this->tempFilename();
|
||||
self::writeToFile($temp_file);
|
||||
$string = file_get_contents($temp_file);
|
||||
return $string;
|
||||
}
|
||||
|
||||
public function writeToFile($filename)
|
||||
{
|
||||
@unlink($filename);//if the zip already exists, overwrite it
|
||||
$zip = new ZipArchive();
|
||||
if (empty($this->sheets_meta)) { self::log("Error in ".__CLASS__."::".__FUNCTION__.", no worksheets defined."); return; }
|
||||
if (!$zip->open($filename, ZipArchive::CREATE)) { self::log("Error in ".__CLASS__."::".__FUNCTION__.", unable to create zip."); return; }
|
||||
|
||||
$zip->addEmptyDir("docProps/");
|
||||
$zip->addFromString("docProps/app.xml" , self::buildAppXML() );
|
||||
$zip->addFromString("docProps/core.xml", self::buildCoreXML());
|
||||
|
||||
$zip->addEmptyDir("_rels/");
|
||||
$zip->addFromString("_rels/.rels", self::buildRelationshipsXML());
|
||||
|
||||
$zip->addEmptyDir("xl/worksheets/");
|
||||
foreach($this->sheets_meta as $sheet_meta) {
|
||||
$zip->addFile($sheet_meta['filename'], "xl/worksheets/".$sheet_meta['xmlname'] );
|
||||
}
|
||||
if (!empty($this->shared_strings)) {
|
||||
$zip->addFile($this->writeSharedStringsXML(), "xl/sharedStrings.xml" ); //$zip->addFromString("xl/sharedStrings.xml", self::buildSharedStringsXML() );
|
||||
}
|
||||
$zip->addFromString("xl/workbook.xml" , self::buildWorkbookXML() );
|
||||
$zip->addFile($this->writeStylesXML(), "xl/styles.xml" ); //$zip->addFromString("xl/styles.xml" , self::buildStylesXML() );
|
||||
$zip->addFromString("[Content_Types].xml" , self::buildContentTypesXML() );
|
||||
|
||||
$zip->addEmptyDir("xl/_rels/");
|
||||
$zip->addFromString("xl/_rels/workbook.xml.rels", self::buildWorkbookRelsXML() );
|
||||
$zip->close();
|
||||
}
|
||||
|
||||
|
||||
public function writeSheet(array $data, $sheet_name='', array $header_types=array() )
|
||||
{
|
||||
$data = empty($data) ? array( array('') ) : $data;
|
||||
|
||||
$sheet_filename = $this->tempFilename();
|
||||
$sheet_default = 'Sheet'.(count($this->sheets_meta)+1);
|
||||
$sheet_name = !empty($sheet_name) ? $sheet_name : $sheet_default;
|
||||
$this->sheets_meta[] = array('filename'=>$sheet_filename, 'sheetname'=>$sheet_name ,'xmlname'=>strtolower($sheet_default).".xml" );
|
||||
|
||||
$header_offset = empty($header_types) ? 0 : 1;
|
||||
$row_count = count($data) + $header_offset;
|
||||
$column_count = count($data[self::array_first_key($data)]);
|
||||
$max_cell = self::xlsCell( $row_count-1, $column_count-1 );
|
||||
|
||||
$tabselected = count($this->sheets_meta)==1 ? 'true' : 'false';//only first sheet is selected
|
||||
$cell_formats_arr = empty($header_types) ? array_fill(0, $column_count, 'string') : array_values($header_types);
|
||||
$header_row = empty($header_types) ? array() : array_keys($header_types);
|
||||
|
||||
$fd = fopen($sheet_filename, "w+");
|
||||
if ($fd===false) { self::log("write failed in ".__CLASS__."::".__FUNCTION__."."); return; }
|
||||
|
||||
fwrite($fd,'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n");
|
||||
fwrite($fd,'<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">');
|
||||
fwrite($fd, '<sheetPr filterMode="false">');
|
||||
fwrite($fd, '<pageSetUpPr fitToPage="false"/>');
|
||||
fwrite($fd, '</sheetPr>');
|
||||
fwrite($fd, '<dimension ref="A1:'.$max_cell.'"/>');
|
||||
fwrite($fd, '<sheetViews>');
|
||||
fwrite($fd, '<sheetView colorId="64" defaultGridColor="true" rightToLeft="false" showFormulas="false" showGridLines="true" showOutlineSymbols="true" showRowColHeaders="true" showZeros="true" tabSelected="'.$tabselected.'" topLeftCell="A1" view="normal" windowProtection="false" workbookViewId="0" zoomScale="100" zoomScaleNormal="100" zoomScalePageLayoutView="100">');
|
||||
fwrite($fd, '<selection activeCell="A1" activeCellId="0" pane="topLeft" sqref="A1"/>');
|
||||
fwrite($fd, '</sheetView>');
|
||||
fwrite($fd, '</sheetViews>');
|
||||
fwrite($fd, '<cols>');
|
||||
fwrite($fd, '<col collapsed="false" hidden="false" max="1025" min="1" style="0" width="19"/>');
|
||||
fwrite($fd, '</cols>');
|
||||
fwrite($fd, '<sheetData>');
|
||||
if (!empty($header_row))
|
||||
{
|
||||
fwrite($fd, '<row collapsed="false" customFormat="false" customHeight="false" hidden="false" ht="12.1" outlineLevel="0" r="'.(1).'">');
|
||||
foreach($header_row as $k=>$v)
|
||||
{
|
||||
$this->writeCell($fd, 0, $k, $v, $cell_format='string');
|
||||
}
|
||||
fwrite($fd, '</row>');
|
||||
}
|
||||
foreach($data as $i=>$row)
|
||||
{
|
||||
fwrite($fd, '<row collapsed="false" customFormat="false" customHeight="false" hidden="false" ht="12.1" outlineLevel="0" r="'.($i+$header_offset+1).'">');
|
||||
foreach($row as $k=>$v)
|
||||
{
|
||||
$this->writeCell($fd, $i+$header_offset, $k, $v, $cell_formats_arr[$k]);
|
||||
}
|
||||
fwrite($fd, '</row>');
|
||||
}
|
||||
fwrite($fd, '</sheetData>');
|
||||
fwrite($fd, '<printOptions headings="false" gridLines="false" gridLinesSet="true" horizontalCentered="false" verticalCentered="false"/>');
|
||||
fwrite($fd, '<pageMargins left="0.5" right="0.5" top="1.0" bottom="1.0" header="0.5" footer="0.5"/>');
|
||||
fwrite($fd, '<pageSetup blackAndWhite="false" cellComments="none" copies="1" draft="false" firstPageNumber="1" fitToHeight="1" fitToWidth="1" horizontalDpi="300" orientation="portrait" pageOrder="downThenOver" paperSize="1" scale="100" useFirstPageNumber="true" usePrinterDefaults="false" verticalDpi="300"/>');
|
||||
fwrite($fd, '<headerFooter differentFirst="false" differentOddEven="false">');
|
||||
fwrite($fd, '<oddHeader>&C&"Times New Roman,Regular"&12&A</oddHeader>');
|
||||
fwrite($fd, '<oddFooter>&C&"Times New Roman,Regular"&12Page &P</oddFooter>');
|
||||
fwrite($fd, '</headerFooter>');
|
||||
fwrite($fd,'</worksheet>');
|
||||
fclose($fd);
|
||||
}
|
||||
|
||||
protected function writeCell($fd, $row_number, $column_number, $value, $cell_format)
|
||||
{
|
||||
static $styles = array('money'=>1,'dollar'=>1,'datetime'=>2,'date'=>3,'string'=>0);
|
||||
$cell = self::xlsCell($row_number, $column_number);
|
||||
$s = isset($styles[$cell_format]) ? $styles[$cell_format] : '0';
|
||||
|
||||
if (is_numeric($value)) {
|
||||
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'" t="n"><v>'.($value*1).'</v></c>');//int,float, etc
|
||||
} else if ($cell_format=='date') {
|
||||
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'" t="n"><v>'.intval(self::convert_date_time($value)).'</v></c>');
|
||||
} else if ($cell_format=='datetime') {
|
||||
if ($value === '') {
|
||||
fwrite($fd,'<c r="'.$cell.'" s="0"/>');
|
||||
} else {
|
||||
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'" t="n"><v>'.self::convert_date_time($value).'</v></c>');
|
||||
}
|
||||
} else if ($value==''){
|
||||
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'"/>');
|
||||
} else if ($value{0}=='='){
|
||||
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'" t="s"><f>'.self::xmlspecialchars($value).'</f></c>');
|
||||
} else if ($value!==''){
|
||||
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'" t="s"><v>'.self::xmlspecialchars($this->setSharedString($value)).'</v></c>');
|
||||
}
|
||||
}
|
||||
|
||||
protected function writeStylesXML()
|
||||
{
|
||||
$tempfile = $this->tempFilename();
|
||||
$fd = fopen($tempfile, "w+");
|
||||
if ($fd===false) { self::log("write failed in ".__CLASS__."::".__FUNCTION__."."); return; }
|
||||
fwrite($fd, '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n");
|
||||
fwrite($fd, '<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">');
|
||||
fwrite($fd, '<numFmts count="4">');
|
||||
fwrite($fd, '<numFmt formatCode="GENERAL" numFmtId="164"/>');
|
||||
fwrite($fd, '<numFmt formatCode="[$$-1009]#,##0.00;[RED]\-[$$-1009]#,##0.00" numFmtId="165"/>');
|
||||
fwrite($fd, '<numFmt formatCode="YYYY-MM-DD\ HH:MM:SS" numFmtId="166"/>');
|
||||
fwrite($fd, '<numFmt formatCode="YYYY-MM-DD" numFmtId="167"/>');
|
||||
fwrite($fd, '</numFmts>');
|
||||
fwrite($fd, '<fonts count="4">');
|
||||
fwrite($fd, '<font><name val="Arial"/><charset val="1"/><family val="2"/><sz val="10"/></font>');
|
||||
fwrite($fd, '<font><name val="Arial"/><family val="0"/><sz val="10"/></font>');
|
||||
fwrite($fd, '<font><name val="Arial"/><family val="0"/><sz val="10"/></font>');
|
||||
fwrite($fd, '<font><name val="Arial"/><family val="0"/><sz val="10"/></font>');
|
||||
fwrite($fd, '</fonts>');
|
||||
fwrite($fd, '<fills count="2"><fill><patternFill patternType="none"/></fill><fill><patternFill patternType="gray125"/></fill></fills>');
|
||||
fwrite($fd, '<borders count="1"><border diagonalDown="false" diagonalUp="false"><left/><right/><top/><bottom/><diagonal/></border></borders>');
|
||||
fwrite($fd, '<cellStyleXfs count="15">');
|
||||
fwrite($fd, '<xf applyAlignment="true" applyBorder="true" applyFont="true" applyProtection="true" borderId="0" fillId="0" fontId="0" numFmtId="164">');
|
||||
fwrite($fd, '<alignment horizontal="general" indent="0" shrinkToFit="false" textRotation="0" vertical="bottom" wrapText="false"/>');
|
||||
fwrite($fd, '<protection hidden="false" locked="true"/>');
|
||||
fwrite($fd, '</xf>');
|
||||
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="0"/>');
|
||||
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="0"/>');
|
||||
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="2" numFmtId="0"/>');
|
||||
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="2" numFmtId="0"/>');
|
||||
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
|
||||
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
|
||||
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
|
||||
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
|
||||
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
|
||||
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
|
||||
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
|
||||
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
|
||||
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
|
||||
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
|
||||
//fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="43"/>');
|
||||
//fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="41"/>');
|
||||
//fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="44"/>');
|
||||
//fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="42"/>');
|
||||
//fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="9"/>');
|
||||
fwrite($fd, '</cellStyleXfs>');
|
||||
fwrite($fd, '<cellXfs count="4">');
|
||||
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="164" xfId="0"/>');
|
||||
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="165" xfId="0"/>');
|
||||
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="166" xfId="0"/>');
|
||||
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="167" xfId="0"/>');
|
||||
fwrite($fd, '</cellXfs>');
|
||||
fwrite($fd, '<cellStyles count="1">');
|
||||
fwrite($fd, '<cellStyle builtinId="0" customBuiltin="false" name="Normal" xfId="0"/>');
|
||||
//fwrite($fd, '<cellStyle builtinId="3" customBuiltin="false" name="Comma" xfId="15"/>');
|
||||
//fwrite($fd, '<cellStyle builtinId="6" customBuiltin="false" name="Comma [0]" xfId="16"/>');
|
||||
//fwrite($fd, '<cellStyle builtinId="4" customBuiltin="false" name="Currency" xfId="17"/>');
|
||||
//fwrite($fd, '<cellStyle builtinId="7" customBuiltin="false" name="Currency [0]" xfId="18"/>');
|
||||
//fwrite($fd, '<cellStyle builtinId="5" customBuiltin="false" name="Percent" xfId="19"/>');
|
||||
fwrite($fd, '</cellStyles>');
|
||||
fwrite($fd, '</styleSheet>');
|
||||
fclose($fd);
|
||||
return $tempfile;
|
||||
}
|
||||
|
||||
protected function setSharedString($v)
|
||||
{
|
||||
// Strip control characters which Excel does not seem to like...
|
||||
$v = preg_replace('/[\x00-\x09\x0B\x0C\x0E-\x1F]/u', '', $v);
|
||||
if (isset($this->shared_strings[$v]))
|
||||
{
|
||||
$string_value = $this->shared_strings[$v];
|
||||
}
|
||||
else
|
||||
{
|
||||
$string_value = count($this->shared_strings);
|
||||
$this->shared_strings[$v] = $string_value;
|
||||
}
|
||||
$this->shared_string_count++;//non-unique count
|
||||
return $string_value;
|
||||
}
|
||||
|
||||
protected function writeSharedStringsXML()
|
||||
{
|
||||
$tempfile = $this->tempFilename();
|
||||
$fd = fopen($tempfile, "w+");
|
||||
if ($fd===false) { self::log("write failed in ".__CLASS__."::".__FUNCTION__."."); return; }
|
||||
|
||||
fwrite($fd,'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n");
|
||||
fwrite($fd,'<sst count="'.($this->shared_string_count).'" uniqueCount="'.count($this->shared_strings).'" xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">');
|
||||
foreach($this->shared_strings as $s=>$c)
|
||||
{
|
||||
fwrite($fd,'<si><t>'.self::xmlspecialchars($s).'</t></si>');
|
||||
}
|
||||
fwrite($fd, '</sst>');
|
||||
fclose($fd);
|
||||
return $tempfile;
|
||||
}
|
||||
|
||||
protected function buildAppXML()
|
||||
{
|
||||
$app_xml="";
|
||||
$app_xml.='<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n";
|
||||
$app_xml.='<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"><TotalTime>0</TotalTime></Properties>';
|
||||
return $app_xml;
|
||||
}
|
||||
|
||||
protected function buildCoreXML()
|
||||
{
|
||||
$core_xml="";
|
||||
$core_xml.='<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n";
|
||||
$core_xml.='<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">';
|
||||
$core_xml.='<dcterms:created xsi:type="dcterms:W3CDTF">'.date("Y-m-d\TH:i:s.00\Z").'</dcterms:created>';//$date_time = '2013-07-25T15:54:37.00Z';
|
||||
$core_xml.='<dc:creator>'.self::xmlspecialchars($this->author).'</dc:creator>';
|
||||
$core_xml.='<cp:revision>0</cp:revision>';
|
||||
$core_xml.='</cp:coreProperties>';
|
||||
return $core_xml;
|
||||
}
|
||||
|
||||
protected function buildRelationshipsXML()
|
||||
{
|
||||
$rels_xml="";
|
||||
$rels_xml.='<?xml version="1.0" encoding="UTF-8"?>'."\n";
|
||||
$rels_xml.='<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">';
|
||||
$rels_xml.='<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>';
|
||||
$rels_xml.='<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>';
|
||||
$rels_xml.='<Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>';
|
||||
$rels_xml.="\n";
|
||||
$rels_xml.='</Relationships>';
|
||||
return $rels_xml;
|
||||
}
|
||||
|
||||
protected function buildWorkbookXML()
|
||||
{
|
||||
$workbook_xml="";
|
||||
$workbook_xml.='<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n";
|
||||
$workbook_xml.='<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">';
|
||||
$workbook_xml.='<fileVersion appName="Calc"/><workbookPr backupFile="false" showObjects="all" date1904="false"/><workbookProtection/>';
|
||||
$workbook_xml.='<bookViews><workbookView activeTab="0" firstSheet="0" showHorizontalScroll="true" showSheetTabs="true" showVerticalScroll="true" tabRatio="212" windowHeight="8192" windowWidth="16384" xWindow="0" yWindow="0"/></bookViews>';
|
||||
$workbook_xml.='<sheets>';
|
||||
foreach($this->sheets_meta as $i=>$sheet_meta) {
|
||||
$workbook_xml.='<sheet name="'.self::xmlspecialchars($sheet_meta['sheetname']).'" sheetId="'.($i+1).'" state="visible" r:id="rId'.($i+2).'"/>';
|
||||
}
|
||||
$workbook_xml.='</sheets>';
|
||||
$workbook_xml.='<calcPr iterateCount="100" refMode="A1" iterate="false" iterateDelta="0.001"/></workbook>';
|
||||
return $workbook_xml;
|
||||
}
|
||||
|
||||
protected function buildWorkbookRelsXML()
|
||||
{
|
||||
$wkbkrels_xml="";
|
||||
$wkbkrels_xml.='<?xml version="1.0" encoding="UTF-8"?>'."\n";
|
||||
$wkbkrels_xml.='<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">';
|
||||
$wkbkrels_xml.='<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>';
|
||||
foreach($this->sheets_meta as $i=>$sheet_meta) {
|
||||
$wkbkrels_xml.='<Relationship Id="rId'.($i+2).'" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/'.($sheet_meta['xmlname']).'"/>';
|
||||
}
|
||||
if (!empty($this->shared_strings)) {
|
||||
$wkbkrels_xml.='<Relationship Id="rId'.(count($this->sheets_meta)+2).'" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="sharedStrings.xml"/>';
|
||||
}
|
||||
$wkbkrels_xml.="\n";
|
||||
$wkbkrels_xml.='</Relationships>';
|
||||
return $wkbkrels_xml;
|
||||
}
|
||||
|
||||
protected function buildContentTypesXML()
|
||||
{
|
||||
$content_types_xml="";
|
||||
$content_types_xml.='<?xml version="1.0" encoding="UTF-8"?>'."\n";
|
||||
$content_types_xml.='<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">';
|
||||
$content_types_xml.='<Override PartName="/_rels/.rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>';
|
||||
$content_types_xml.='<Override PartName="/xl/_rels/workbook.xml.rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>';
|
||||
foreach($this->sheets_meta as $i=>$sheet_meta) {
|
||||
$content_types_xml.='<Override PartName="/xl/worksheets/'.($sheet_meta['xmlname']).'" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>';
|
||||
}
|
||||
if (!empty($this->shared_strings)) {
|
||||
$content_types_xml.='<Override PartName="/xl/sharedStrings.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/>';
|
||||
}
|
||||
$content_types_xml.='<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>';
|
||||
$content_types_xml.='<Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>';
|
||||
$content_types_xml.='<Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>';
|
||||
$content_types_xml.='<Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>';
|
||||
$content_types_xml.="\n";
|
||||
$content_types_xml.='</Types>';
|
||||
return $content_types_xml;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
/*
|
||||
* @param $row_number int, zero based
|
||||
* @param $column_number int, zero based
|
||||
* @return Cell label/coordinates, ex: A1, C3, AA42
|
||||
* */
|
||||
public static function xlsCell($row_number, $column_number)
|
||||
{
|
||||
$n = $column_number;
|
||||
for($r = ""; $n >= 0; $n = intval($n / 26) - 1) {
|
||||
$r = chr($n%26 + 0x41) . $r;
|
||||
}
|
||||
return $r . ($row_number+1);
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
public static function log($string)
|
||||
{
|
||||
file_put_contents("php://stderr", date("Y-m-d H:i:s:").rtrim(is_array($string) ? json_encode($string) : $string)."\n");
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
public static function xmlspecialchars($val)
|
||||
{
|
||||
return str_replace("'", "'", htmlspecialchars($val));
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
public static function array_first_key(array $arr)
|
||||
{
|
||||
reset($arr);
|
||||
$first_key = key($arr);
|
||||
return $first_key;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
public static function convert_date_time($date_input) //thanks to Excel::Writer::XLSX::Worksheet.pm (perl)
|
||||
{
|
||||
$days = 0; # Number of days since epoch
|
||||
$seconds = 0; # Time expressed as fraction of 24h hours in seconds
|
||||
$year=$month=$day=0;
|
||||
$hour=$min =$sec=0;
|
||||
|
||||
$date_time = $date_input;
|
||||
if (preg_match("/(\d{4})\-(\d{2})\-(\d{2})/", $date_time, $matches))
|
||||
{
|
||||
list($junk,$year,$month,$day) = $matches;
|
||||
}
|
||||
if (preg_match("/(\d{2}):(\d{2}):(\d{2})/", $date_time, $matches))
|
||||
{
|
||||
list($junk,$hour,$min,$sec) = $matches;
|
||||
$seconds = ( $hour * 60 * 60 + $min * 60 + $sec ) / ( 24 * 60 * 60 );
|
||||
}
|
||||
|
||||
//using 1900 as epoch, not 1904, ignoring 1904 special case
|
||||
|
||||
# Special cases for Excel.
|
||||
if ("$year-$month-$day"=='1899-12-31') return $seconds ; # Excel 1900 epoch
|
||||
if ("$year-$month-$day"=='1900-01-00') return $seconds ; # Excel 1900 epoch
|
||||
if ("$year-$month-$day"=='1900-02-29') return 60 + $seconds ; # Excel false leapday
|
||||
|
||||
# We calculate the date by calculating the number of days since the epoch
|
||||
# and adjust for the number of leap days. We calculate the number of leap
|
||||
# days by normalising the year in relation to the epoch. Thus the year 2000
|
||||
# becomes 100 for 4 and 100 year leapdays and 400 for 400 year leapdays.
|
||||
$epoch = 1900;
|
||||
$offset = 0;
|
||||
$norm = 300;
|
||||
$range = $year - $epoch;
|
||||
|
||||
# Set month days and check for leap year.
|
||||
$leap = (($year % 400 == 0) || (($year % 4 == 0) && ($year % 100)) ) ? 1 : 0;
|
||||
$mdays = array( 31, ($leap ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 );
|
||||
|
||||
# Some boundary checks
|
||||
if($year < $epoch || $year > 9999) return 0;
|
||||
if($month < 1 || $month > 12) return 0;
|
||||
if($day < 1 || $day > $mdays[ $month - 1 ]) return 0;
|
||||
|
||||
# Accumulate the number of days since the epoch.
|
||||
$days = $day; # Add days for current month
|
||||
$days += array_sum( array_slice($mdays, 0, $month-1 ) ); # Add days for past months
|
||||
$days += $range * 365; # Add days for past years
|
||||
$days += intval( ( $range ) / 4 ); # Add leapdays
|
||||
$days -= intval( ( $range + $offset ) / 100 ); # Subtract 100 year leapdays
|
||||
$days += intval( ( $range + $offset + $norm ) / 400 ); # Add 400 year leapdays
|
||||
$days -= $leap; # Already counted above
|
||||
|
||||
# Adjust for Excel erroneously treating 1900 as a leap year.
|
||||
if ($days > 59) { $days++;}
|
||||
|
||||
return $days + $seconds;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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/>
|
||||
|
||||
|
||||
/**
|
||||
* 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,70 @@ 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)
|
||||
{
|
||||
// Get the unexpected output but do nothing with it
|
||||
$sTrash = $this->ob_get_clean_safe();
|
||||
|
||||
$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 = $this->ob_get_clean_safe();
|
||||
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())
|
||||
{
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
<?php
|
||||
|
||||
define('APPROOT', dirname(__FILE__).'/');
|
||||
define('APPCONF', APPROOT.'conf/');
|
||||
define('ITOP_DEFAULT_ENV', 'production');
|
||||
|
||||
if (function_exists('microtime'))
|
||||
{
|
||||
$fItopStarted = microtime(true);
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
|
||||
@@ -42,7 +43,7 @@ abstract class Action extends cmdbAbstractObject
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"reconc_keys" => array('name'),
|
||||
"db_table" => "priv_action",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "realclass",
|
||||
@@ -106,7 +107,7 @@ abstract class ActionNotification extends Action
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"reconc_keys" => array('name'),
|
||||
"db_table" => "priv_action_notification",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
@@ -139,7 +140,7 @@ class ActionEmail extends ActionNotification
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"reconc_keys" => array('name'),
|
||||
"db_table" => "priv_action_email",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
@@ -183,6 +184,7 @@ class ActionEmail extends ActionNotification
|
||||
try
|
||||
{
|
||||
$oSearch = DBObjectSearch::FromOQL($sOQL);
|
||||
$oSearch->AllowAllData();
|
||||
}
|
||||
catch (OQLException $e)
|
||||
{
|
||||
@@ -211,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);
|
||||
}
|
||||
@@ -274,26 +280,36 @@ class ActionEmail extends ActionNotification
|
||||
|
||||
protected function _DoExecute($oTrigger, $aContextArgs, &$oLog)
|
||||
{
|
||||
$this->m_iRecipients = 0;
|
||||
$this->m_aMailErrors = array();
|
||||
$bRes = false; // until we do succeed in sending the email
|
||||
|
||||
// Determine recicipients
|
||||
//
|
||||
$sTo = $this->FindRecipients('to', $aContextArgs);
|
||||
$sCC = $this->FindRecipients('cc', $aContextArgs);
|
||||
$sBCC = $this->FindRecipients('bcc', $aContextArgs);
|
||||
|
||||
$sFrom = $this->Get('from');
|
||||
$sReplyTo = $this->Get('reply_to');
|
||||
|
||||
$sSubject = MetaModel::ApplyParams($this->Get('subject'), $aContextArgs);
|
||||
$sBody = MetaModel::ApplyParams($this->Get('body'), $aContextArgs);
|
||||
$sPreviousUrlMaker = ApplicationContext::SetUrlMakerClass();
|
||||
try
|
||||
{
|
||||
$this->m_iRecipients = 0;
|
||||
$this->m_aMailErrors = array();
|
||||
$bRes = false; // until we do succeed in sending the email
|
||||
|
||||
// Determine recicipients
|
||||
//
|
||||
$sTo = $this->FindRecipients('to', $aContextArgs);
|
||||
$sCC = $this->FindRecipients('cc', $aContextArgs);
|
||||
$sBCC = $this->FindRecipients('bcc', $aContextArgs);
|
||||
|
||||
$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);
|
||||
|
||||
$oObj = $aContextArgs['this->object()'];
|
||||
$sMessageId = sprintf('iTop_%s_%d_%f@%s.openitop.org', get_class($oObj), $oObj->GetKey(), microtime(true /* get as float*/), MetaModel::GetEnvironmentId());
|
||||
$sReference = '<'.$sMessageId.'>';
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
ApplicationContext::SetUrlMakerClass($sPreviousUrlMaker);
|
||||
throw $e;
|
||||
}
|
||||
ApplicationContext::SetUrlMakerClass($sPreviousUrlMaker);
|
||||
|
||||
$oObj = $aContextArgs['this->object()'];
|
||||
$sServerIP = $_SERVER['SERVER_ADDR']; //gethostbyname(gethostname());
|
||||
$sReference = '<iTop/'.get_class($oObj).'/'.$oObj->GetKey().'@'.$sServerIP.'>';
|
||||
|
||||
if (!is_null($oLog))
|
||||
{
|
||||
// Note: we have to secure this because those values are calculated
|
||||
@@ -330,6 +346,7 @@ class ActionEmail extends ActionNotification
|
||||
$oEmail->SetRecipientTO($this->Get('test_recipient'));
|
||||
$oEmail->SetRecipientFrom($this->Get('test_recipient'));
|
||||
$oEmail->SetReferences($sReference);
|
||||
$oEmail->SetMessageId($sMessageId);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -341,6 +358,18 @@ class ActionEmail extends ActionNotification
|
||||
$oEmail->SetRecipientFrom($sFrom);
|
||||
$oEmail->SetRecipientReplyTo($sReplyTo);
|
||||
$oEmail->SetReferences($sReference);
|
||||
$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))
|
||||
@@ -379,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,20 +80,101 @@ 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())));
|
||||
MetaModel::Init_AddAttribute(new AttributeInteger("last_error_code", array("allowed_values"=>null, "sql"=>"last_error_code", "default_value"=>0, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("last_error", array("allowed_values"=>null, "sql"=>"last_error", "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeDateTime("last_attempt", array("allowed_values"=>null, "sql"=>"last_attempt", "default_value"=>"", "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
|
||||
{
|
||||
if ($this->Get('status') == 'running')
|
||||
{
|
||||
return self::ALREADY_RUNNING;
|
||||
}
|
||||
else
|
||||
{
|
||||
$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($iErrorCode = null)
|
||||
{
|
||||
$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($iErrorCode = null)
|
||||
{
|
||||
$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'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Override to notify people that a task cannot be performed
|
||||
*/
|
||||
protected function OnDefinitiveFailure()
|
||||
{
|
||||
}
|
||||
|
||||
protected function OnInsert()
|
||||
@@ -100,18 +182,92 @@ abstract class AsyncTask extends DBObject
|
||||
$this->Set('created', time());
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
$this->HandleError($e->getMessage(), $e->getCode());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Already done or being handled by another process... skip...
|
||||
$bRet = false;
|
||||
}
|
||||
return $bRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridable to extend the behavior in case of error (logging)
|
||||
*/
|
||||
protected function HandleError($sErrorMessage, $iErrorCode)
|
||||
{
|
||||
if ($this->Get('last_attempt') == '')
|
||||
{
|
||||
// First attempt
|
||||
$this->Set('remaining_retries', $this->GetMaxRetries($iErrorCode));
|
||||
}
|
||||
|
||||
$this->Set('last_error', $sErrorMessage);
|
||||
$this->Set('last_error_code', $iErrorCode); // Note: can be ZERO !!!
|
||||
$this->Set('last_attempt', time());
|
||||
|
||||
$iRemaining = $this->Get('remaining_retries');
|
||||
if ($iRemaining > 0)
|
||||
{
|
||||
$iRetryDelay = $this->GetRetryDelay($iErrorCode);
|
||||
IssueLog::Info('Failed to process async task #'.$this->GetKey().' - reason: '.$sErrorMessage.' - 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);
|
||||
}
|
||||
else
|
||||
{
|
||||
IssueLog::Error('Failed to process async task #'.$this->GetKey().' - reason: '.$sErrorMessage);
|
||||
|
||||
$this->Set('status', 'error');
|
||||
$this->Set('started', null);
|
||||
$this->Set('planned', null);
|
||||
$this->OnDefinitiveFailure();
|
||||
}
|
||||
$this->DBUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an exception (message and code)
|
||||
*/
|
||||
abstract public function DoProcess();
|
||||
|
||||
/**
|
||||
* Describes the error codes that DoProcess can return by the mean of exceptions
|
||||
*/
|
||||
static public function EnumErrorCodes()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -138,10 +294,10 @@ 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 AttributeText("body", array("allowed_values"=>null, "sql"=>"body", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeText("header", array("allowed_values"=>null, "sql"=>"header", "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())));
|
||||
|
||||
// Display lists
|
||||
// MetaModel::Init_SetZListItems('details', array('name', 'description', 'status', 'test_recipient', 'from', 'reply_to', 'to', 'cc', 'bcc', 'subject', 'body', 'importance', 'trigger_list')); // Attributes to be displayed for the complete details
|
||||
@@ -151,31 +307,42 @@ class AsyncSendEmail extends AsyncTask
|
||||
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
|
||||
}
|
||||
|
||||
static public function AddToQueue($sTo, $sSubject, $sBody, $aHeaders, $oLog)
|
||||
static public function AddToQueue(EMail $oEMail, $oLog)
|
||||
{
|
||||
$oNew = MetaModel::NewObject(__class__);
|
||||
if ($oLog)
|
||||
{
|
||||
$oNew->Set('event_id', $oLog->GetKey());
|
||||
}
|
||||
$oNew->Set('to', $sTo);
|
||||
$oNew->Set('subject', $sSubject);
|
||||
$oNew->Set('body', $sBody);
|
||||
$sHeaders = serialize($aHeaders);
|
||||
$oNew->Set('header', $sHeaders);
|
||||
$oNew->Set('to', $oEMail->GetRecipientTO(true /* string */));
|
||||
$oNew->Set('subject', $oEMail->GetSubject());
|
||||
|
||||
// $oNew->Set('version', 1);
|
||||
// $sMessage = serialize($oEMail);
|
||||
$oNew->Set('version', 2);
|
||||
$sMessage = $oEMail->SerializeV2();
|
||||
$oNew->Set('message', $sMessage);
|
||||
$oNew->DBInsert();
|
||||
}
|
||||
|
||||
public function DoProcess()
|
||||
{
|
||||
$sTo = $this->Get('to');
|
||||
$sSubject = $this->Get('subject');
|
||||
$sBody = $this->Get('body');
|
||||
$sHeaders = $this->Get('header');
|
||||
$aHeaders = unserialize($sHeaders);
|
||||
|
||||
$oEmail = new EMail($sTo, $sSubject, $sBody, $aHeaders);
|
||||
$iRes = $oEmail->Send($aIssues, true /* force synchro !!!!! */);
|
||||
$sMessage = $this->Get('message');
|
||||
$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)
|
||||
{
|
||||
case EMAIL_SEND_OK:
|
||||
@@ -189,4 +356,4 @@ class AsyncSendEmail extends AsyncTask
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
?>
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,8 +258,11 @@ class BulkChange
|
||||
protected $m_aReconcilKeys; // attcode (attcode = 'id' for the pkey)
|
||||
protected $m_sSynchroScope; // OQL - if specified, then the missing items will be reported
|
||||
protected $m_aOnDisappear; // array of attcode => value, values to be set when an object gets out of scope (ignored if no scope has been defined)
|
||||
protected $m_sDateFormat; // Date format specification, see 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)
|
||||
public function __construct($sClass, $aData, $aAttList, $aExtKeys, $aReconcilKeys, $sSynchroScope = null, $aOnDisappear = null, $sDateFormat = null, $bLocalize = false)
|
||||
{
|
||||
$this->m_sClass = $sClass;
|
||||
$this->m_aData = $aData;
|
||||
@@ -262,6 +271,9 @@ class BulkChange
|
||||
$this->m_aExtKeys = $aExtKeys;
|
||||
$this->m_sSynchroScope = $sSynchroScope;
|
||||
$this->m_aOnDisappear = $aOnDisappear;
|
||||
$this->m_sDateFormat = $sDateFormat;
|
||||
$this->m_bLocalizedValues = $bLocalize;
|
||||
$this->m_aExtKeysMappingCache = array();
|
||||
}
|
||||
|
||||
protected $m_bReportHtml = false;
|
||||
@@ -286,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]);
|
||||
}
|
||||
|
||||
@@ -329,6 +350,7 @@ class BulkChange
|
||||
{
|
||||
foreach ($aKeyConfig as $sForeignAttCode => $iCol)
|
||||
{
|
||||
// Default reporting
|
||||
$aResults[$iCol] = new CellStatus_Void($aRowData[$iCol]);
|
||||
}
|
||||
if ($oExtKey->IsNullAllowed())
|
||||
@@ -338,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', $iCount);
|
||||
$aResults[$sAttCode]= new CellStatus_Ambiguous($oTargetObj->Get($sAttCode), $iCount, $sOQL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -382,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
|
||||
@@ -399,29 +472,43 @@ class BulkChange
|
||||
if ($sAttCode == 'id') continue;
|
||||
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
|
||||
if ($oAttDef->IsLinkSet() && $oAttDef->IsIndirect())
|
||||
$aReasons = array();
|
||||
$iFlags = $oTargetObj->GetAttributeFlags($sAttCode, $aReasons);
|
||||
if ( (($iFlags & OPT_ATT_READONLY) == OPT_ATT_READONLY) && ( $oTargetObj->Get($sAttCode) != $aRowData[$iCol]) )
|
||||
{
|
||||
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-Readonly', $sAttCode, $oTargetObj->Get($sAttCode), $aRowData[$iCol]);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -439,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))
|
||||
{
|
||||
@@ -476,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;
|
||||
}
|
||||
@@ -540,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;
|
||||
}
|
||||
@@ -554,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;
|
||||
}
|
||||
|
||||
@@ -573,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;
|
||||
}
|
||||
|
||||
@@ -607,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;
|
||||
}
|
||||
|
||||
@@ -648,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;
|
||||
}
|
||||
|
||||
@@ -702,6 +791,35 @@ class BulkChange
|
||||
exit;
|
||||
}
|
||||
|
||||
$aResult = array();
|
||||
|
||||
if (!is_null($this->m_sDateFormat) && (strlen($this->m_sDateFormat) > 0))
|
||||
{
|
||||
// Translate dates from the source data
|
||||
//
|
||||
foreach ($this->m_aAttList as $sAttCode => $iCol)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
|
||||
if ($oAttDef instanceof AttributeDateTime)
|
||||
{
|
||||
foreach($this->m_aData as $iRow => $aRowData)
|
||||
{
|
||||
$sNewDate = utils::StringToTime($this->m_aData[$iRow][$iCol], $this->m_sDateFormat);
|
||||
if ($sNewDate !== false)
|
||||
{
|
||||
// Todo - improve the reporting
|
||||
$this->m_aData[$iRow][$iCol] = $sNewDate;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Leave the cell unchanged
|
||||
$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'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the results
|
||||
//
|
||||
@@ -709,97 +827,141 @@ class BulkChange
|
||||
{
|
||||
$aVisited = array();
|
||||
}
|
||||
$aResult = array();
|
||||
$iPreviousTimeLimit = ini_get('max_execution_time');
|
||||
$iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop');
|
||||
foreach($this->m_aData as $iRow => $aRowData)
|
||||
{
|
||||
$oReconciliationFilter = new CMDBSearchFilter($this->m_sClass);
|
||||
$bSkipQuery = false;
|
||||
foreach($this->m_aReconcilKeys as $sAttCode)
|
||||
set_time_limit($iLoopTimeLimit);
|
||||
if (isset($aResult[$iRow]["__STATUS__"]))
|
||||
{
|
||||
$valuecondition = null;
|
||||
if (array_key_exists($sAttCode, $this->m_aExtKeys))
|
||||
// An issue at the earlier steps - skip the rest
|
||||
continue;
|
||||
}
|
||||
try
|
||||
{
|
||||
$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';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Whatever happened, do report the reconciliation values
|
||||
catch (Exception $e)
|
||||
{
|
||||
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue(Dict::Format('UI:CSVReport-Row-Issue-Internal', get_class($e), $e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_null($this->m_sSynchroScope))
|
||||
{
|
||||
// Compute the delta between the scope and visited objects
|
||||
$oScopeSearch = DBObjectSearch::FromOQL($this->m_sSynchroScope);
|
||||
$oScopeSet = new DBObjectSet($oScopeSearch);
|
||||
while ($oObj = $oScopeSet->Fetch())
|
||||
{
|
||||
$iObj = $oObj->GetKey();
|
||||
if (!in_array($iObj, $aVisited))
|
||||
{
|
||||
set_time_limit($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
|
||||
//
|
||||
foreach($this->m_aData as $iRow => $aRowData)
|
||||
{
|
||||
foreach($this->m_aAttList as $iCol)
|
||||
{
|
||||
if (!array_key_exists($iCol, $aResult[$iRow]))
|
||||
@@ -824,22 +986,6 @@ class BulkChange
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_null($this->m_sSynchroScope))
|
||||
{
|
||||
// Compute the delta between the scope and visited objects
|
||||
$oScopeSearch = DBObjectSearch::FromOQL($this->m_sSynchroScope);
|
||||
$oScopeSet = new DBObjectSet($oScopeSearch);
|
||||
while ($oObj = $oScopeSet->Fetch())
|
||||
{
|
||||
$iObj = $oObj->GetKey();
|
||||
if (!in_array($iObj, $aVisited))
|
||||
{
|
||||
$iRow++;
|
||||
$this->UpdateMissingObject($aResult, $iRow, $oObj, $oChange);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $aResult;
|
||||
}
|
||||
|
||||
@@ -854,22 +1000,22 @@ 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)
|
||||
{
|
||||
$iMaxObjects = MetaModel::GetConfig()->GetMinDisplayLimit();
|
||||
$iMaxObjects = appUserPreferences::GetPref('default_page_size', MetaModel::GetConfig()->GetMinDisplayLimit());
|
||||
$oBulkChanges->SetLimit($iMaxObjects);
|
||||
}
|
||||
}
|
||||
@@ -878,7 +1024,7 @@ class BulkChange
|
||||
$aDetails = array();
|
||||
while ($oChange = $oBulkChanges->Fetch())
|
||||
{
|
||||
$sDate = '<a href="?step=10&changeid='.$oChange->GetKey().'&'.$oAppContext->GetForLink().'">'.$oChange->Get('date').'</a>';
|
||||
$sDate = '<a href="csvimport.php?step=10&changeid='.$oChange->GetKey().'&'.$oAppContext->GetForLink().'">'.$oChange->Get('date').'</a>';
|
||||
$sUser = $oChange->GetUserName();
|
||||
if (preg_match('/^(.*)\\(CSV\\)$/i', $oChange->Get('userinfo'), $aMatches))
|
||||
{
|
||||
@@ -922,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+')),
|
||||
@@ -942,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>');
|
||||
@@ -960,7 +1105,8 @@ EOF
|
||||
<<<EOF
|
||||
function OnTruncatedHistoryToggle(bShowAll)
|
||||
{
|
||||
$.get('../pages/ajax.render.php?{$sAppContext}', {operation: 'displayCSVHistory', showall: bShowAll}, function(data)
|
||||
$('#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);
|
||||
var table = $('#$sAjaxDivId .listResults');
|
||||
@@ -1032,10 +1178,19 @@ EOF
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
if ($oAttDef->IsExternalKey())
|
||||
{
|
||||
$oOldTarget = MetaModel::GetObject($oAttDef->GetTargetClass(), $oOperation->Get('oldvalue'));
|
||||
$oNewTarget = MetaModel::GetObject($oAttDef->GetTargetClass(), $oOperation->Get('newvalue'));
|
||||
$sOldValue = $oOldTarget->GetHyperlink();
|
||||
$sNewValue = $oNewTarget->GetHyperlink();
|
||||
$sOldValue = Dict::S('UI:UndefinedObject');
|
||||
if ($oOperation->Get('oldvalue') != 0)
|
||||
{
|
||||
$oOldTarget = MetaModel::GetObject($oAttDef->GetTargetClass(), $oOperation->Get('oldvalue'));
|
||||
$sOldValue = $oOldTarget->GetHyperlink();
|
||||
}
|
||||
|
||||
$sNewValue = Dict::S('UI:UndefinedObject');
|
||||
if ($oOperation->Get('newvalue') != 0)
|
||||
{
|
||||
$oNewTarget = MetaModel::GetObject($oAttDef->GetTargetClass(), $oOperation->Get('newvalue'));
|
||||
$sNewValue = $oNewTarget->GetHyperlink();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1112,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();
|
||||
@@ -51,7 +55,7 @@ class CMDBChangeOp extends DBObject
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("date", array("allowed_values"=>null, "extkey_attcode"=>"change", "target_attcode"=>"date")));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("userinfo", array("allowed_values"=>null, "extkey_attcode"=>"change", "target_attcode"=>"userinfo")));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("objclass", array("allowed_values"=>null, "sql"=>"objclass", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("objkey", array("allowed_values"=>null, "sql"=>"objkey", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeInteger("objkey", array("allowed_values"=>null, "sql"=>"objkey", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
|
||||
MetaModel::Init_SetZListItems('details', array('change', 'date', 'userinfo')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('change', 'date', 'userinfo')); // Attributes to be displayed for the complete details
|
||||
@@ -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
|
||||
@@ -200,9 +222,6 @@ class CMDBChangeOpSetAttributeScalar extends CMDBChangeOpSetAttribute
|
||||
*/
|
||||
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');
|
||||
@@ -212,66 +231,13 @@ class CMDBChangeOpSetAttributeScalar extends CMDBChangeOpSetAttribute
|
||||
$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();
|
||||
$sNewValue = $this->Get('newvalue');
|
||||
$sOldValue = $this->Get('oldvalue');
|
||||
if ( (($oAttDef->GetType() == 'String') || ($oAttDef->GetType() == 'Text')) &&
|
||||
(strlen($sNewValue) > strlen($sOldValue)) )
|
||||
{
|
||||
// Check if some text was not appended to the field
|
||||
if (substr($sNewValue,0, strlen($sOldValue)) == $sOldValue) // Text added at the end
|
||||
{
|
||||
$sDelta = substr($sNewValue, strlen($sOldValue));
|
||||
$sResult = Dict::Format('Change:Text_AppendedTo_AttName', $sDelta, $sAttName);
|
||||
}
|
||||
else if (substr($sNewValue, -strlen($sOldValue)) == $sOldValue) // Text added at the beginning
|
||||
{
|
||||
$sDelta = substr($sNewValue, 0, strlen($sNewValue) - strlen($sOldValue));
|
||||
$sResult = Dict::Format('Change:Text_AppendedTo_AttName', $sDelta, $sAttName);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strlen($sOldValue) == 0)
|
||||
{
|
||||
$sResult = Dict::Format('Change:AttName_SetTo', $sAttName, $sNewValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sResult = Dict::Format('Change:AttName_SetTo_NewValue_PreviousValue_OldValue', $sAttName, $sNewValue, $sOldValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif($bIsHtml && $oAttDef->IsExternalKey())
|
||||
{
|
||||
$sTargetClass = $oAttDef->GetTargetClass();
|
||||
$sFrom = MetaModel::GetHyperLink($sTargetClass, $sOldValue);
|
||||
$sTo = MetaModel::GetHyperLink($sTargetClass, $sNewValue);
|
||||
$sResult = "$sAttName set to $sTo (previous: $sFrom)";
|
||||
if (strlen($sFrom) == 0)
|
||||
{
|
||||
$sResult = Dict::Format('Change:AttName_SetTo', $sAttName, $sTo);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sResult = Dict::Format('Change:AttName_SetTo_NewValue_PreviousValue_OldValue', $sAttName, $sTo, $sFrom);
|
||||
}
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeBlob)
|
||||
{
|
||||
$sResult = "#@# Issue... found an attribute for which other type of tracking should be made";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strlen($sOldValue) == 0)
|
||||
{
|
||||
$sResult = Dict::Format('Change:AttName_SetTo', $sAttName, $sNewValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sResult = Dict::Format('Change:AttName_SetTo_NewValue_PreviousValue_OldValue', $sAttName, $sNewValue, $sOldValue);
|
||||
}
|
||||
}
|
||||
$sResult = $oAttDef->DescribeChangeAsHTML($sOldValue, $sNewValue);
|
||||
}
|
||||
return $sResult;
|
||||
}
|
||||
@@ -323,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";
|
||||
@@ -379,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;
|
||||
@@ -431,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);
|
||||
}
|
||||
@@ -486,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');
|
||||
@@ -546,12 +609,238 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Record an action made by a plug-in
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class CMDBChangeOpPlugin extends CMDBChangeOp
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_plugin",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
/* May be used later when implementing an extension mechanism that will allow the plug-ins to store some extra information and still degrades gracefully when the plug-in is desinstalled
|
||||
MetaModel::Init_AddAttribute(new AttributeString("extension_class", array("allowed_values"=>null, "sql"=>"extension_class", "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeInteger("extension_id", array("allowed_values"=>null, "sql"=>"extension_id", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
*/
|
||||
MetaModel::Init_InheritAttributes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Describe (as a text string) the modifications corresponding to this change
|
||||
*/
|
||||
public function GetDescription()
|
||||
{
|
||||
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
|
||||
*/
|
||||
|
||||
|
||||
@@ -76,6 +77,7 @@ require_once('cmdbchangeop.class.inc.php');
|
||||
// Romain: temporary moved into application.inc.php (see explanations there)
|
||||
//require_once('event.class.inc.php');
|
||||
|
||||
require_once('templatestring.class.inc.php');
|
||||
require_once('csvparser.class.inc.php');
|
||||
require_once('bulkchange.class.inc.php');
|
||||
|
||||
@@ -90,47 +92,174 @@ 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;
|
||||
|
||||
|
||||
private function RecordObjCreation(CMDBChange $oChange)
|
||||
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;
|
||||
}
|
||||
|
||||
//
|
||||
// Todo: simplify the APIs and do not pass the current change as an argument anymore
|
||||
// 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
|
||||
//
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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->IsLinkSet()) continue; // #@# temporary
|
||||
if ($oAttDef->IsExternalField()) continue;
|
||||
if ($oAttDef->IsLinkSet()) continue;
|
||||
if ($oAttDef->GetTrackingLevel() == ATTRIBUTE_TRACKING_NONE) continue;
|
||||
|
||||
if (array_key_exists($sAttCode, $aOrigValues))
|
||||
{
|
||||
$original = $aOrigValues[$sAttCode];
|
||||
}
|
||||
else
|
||||
{
|
||||
$original = null;
|
||||
}
|
||||
|
||||
if ($oAttDef instanceOf AttributeOneWayPassword)
|
||||
{
|
||||
// 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);
|
||||
|
||||
if (array_key_exists($sAttCode, $aOrigValues))
|
||||
{
|
||||
$original = $aOrigValues[$sAttCode];
|
||||
}
|
||||
else
|
||||
if (is_null($original))
|
||||
{
|
||||
$original = '';
|
||||
}
|
||||
@@ -141,16 +270,11 @@ 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);
|
||||
|
||||
if (array_key_exists($sAttCode, $aOrigValues))
|
||||
{
|
||||
$original = $aOrigValues[$sAttCode];
|
||||
}
|
||||
else
|
||||
if (is_null($original))
|
||||
{
|
||||
$original = '';
|
||||
}
|
||||
@@ -161,26 +285,46 @@ 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);
|
||||
|
||||
if (array_key_exists($sAttCode, $aOrigValues))
|
||||
{
|
||||
$original = $aOrigValues[$sAttCode];
|
||||
}
|
||||
else
|
||||
if (is_null($original))
|
||||
{
|
||||
$original = new ormDocument();
|
||||
}
|
||||
$oMyChangeOp->Set("prevdata", $original);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeStopWatch)
|
||||
{
|
||||
// Stop watches - record changes for sub items only (they are visible, the rest is not visible)
|
||||
//
|
||||
if (is_null($original))
|
||||
{
|
||||
$original = new OrmStopWatch();
|
||||
}
|
||||
foreach ($oAttDef->ListSubItems() as $sSubItemAttCode => $oSubItemAttDef)
|
||||
{
|
||||
$item_value = $oSubItemAttDef->GetValue($value);
|
||||
$item_original = $oSubItemAttDef->GetValue($original);
|
||||
|
||||
if ($item_value != $item_original)
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sSubItemAttCode);
|
||||
|
||||
$oMyChangeOp->Set("oldvalue", $item_original);
|
||||
$oMyChangeOp->Set("newvalue", $item_value);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeCaseLog)
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeCaseLog");
|
||||
$oMyChangeOp->Set("change", $oChange->GetKey());
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
@@ -188,49 +332,67 @@ 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);
|
||||
|
||||
if (array_key_exists($sAttCode, $aOrigValues))
|
||||
if (!is_null($original) && ($original instanceof ormCaseLog))
|
||||
{
|
||||
$original = $aOrigValues[$sAttCode];
|
||||
if ($original instanceof ormCaseLog)
|
||||
{
|
||||
$original = $original->GetText();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$original = null;
|
||||
$original = $original->GetText();
|
||||
}
|
||||
$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);
|
||||
|
||||
if (array_key_exists($sAttCode, $aOrigValues))
|
||||
{
|
||||
$sOriginalValue = $aOrigValues[$sAttCode];
|
||||
}
|
||||
else
|
||||
{
|
||||
$sOriginalValue = 'undefined';
|
||||
}
|
||||
$oMyChangeOp->Set("oldvalue", $sOriginalValue);
|
||||
$oMyChangeOp->Set("oldvalue", $original);
|
||||
$oMyChangeOp->Set("newvalue", $value);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
@@ -266,63 +428,51 @@ 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);
|
||||
|
||||
self::$m_oCurrChange = $oChange;
|
||||
$ret = $this->DBInsertTracked_Internal();
|
||||
self::$m_oCurrChange = null;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
public function DBInsertTrackedNoReload(CMDBChange $oChange, $bSkipStrongSecurity = null)
|
||||
{
|
||||
self::SetCurrentChange($oChange);
|
||||
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY);
|
||||
|
||||
self::$m_oCurrChange = $oChange;
|
||||
$ret = $this->DBInsertTracked_Internal(true);
|
||||
self::$m_oCurrChange = null;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* To Be Obsoleted: DO NOT rely on an overload of this method since
|
||||
* DBInsertTracked (resp. DBInsertTrackedNoReload) may call directly
|
||||
* DBInsert (resp. DBInsertNoReload) in future versions of iTop.
|
||||
* @param bool $bDoNotReload
|
||||
* @return integer Identifier of the created object
|
||||
*/
|
||||
protected function DBInsertTracked_Internal($bDoNotReload = false)
|
||||
{
|
||||
if ($bDoNotReload)
|
||||
{
|
||||
$ret = parent::DBInsertNoReload();
|
||||
$ret = $this->DBInsertNoReload();
|
||||
}
|
||||
else
|
||||
{
|
||||
$ret = parent::DBInsert();
|
||||
$ret = $this->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)
|
||||
{
|
||||
self::$m_oCurrChange = $oChange;
|
||||
self::SetCurrentChange($oChange);
|
||||
$this->DBCloneTracked_Internal($newKey);
|
||||
self::$m_oCurrChange = null;
|
||||
}
|
||||
|
||||
protected function DBCloneTracked_Internal($newKey = null)
|
||||
@@ -330,124 +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);
|
||||
|
||||
self::$m_oCurrChange = $oChange;
|
||||
$this->DBUpdateTracked_Internal();
|
||||
self::$m_oCurrChange = null;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
self::$m_oCurrChange = $oChange;
|
||||
$this->DBDeleteTracked_Internal($oDeletionPlan);
|
||||
self::$m_oCurrChange = null;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
self::$m_oCurrChange = $oChange;
|
||||
$this->BulkDeleteTracked_Internal($oFilter);
|
||||
self::$m_oCurrChange = null;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
self::$m_oCurrChange = $oChange;
|
||||
self::SetCurrentChange($oChange);
|
||||
$this->BulkUpdateTracked_Internal($oFilter, $aValues);
|
||||
self::$m_oCurrChange = null;
|
||||
}
|
||||
|
||||
protected static function BulkUpdateTracked_Internal(DBObjectSearch $oFilter, array $aValues)
|
||||
@@ -474,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;
|
||||
}
|
||||
@@ -498,24 +581,22 @@ class CMDBObjectSet extends DBObjectSet
|
||||
static public function FromScratch($sClass)
|
||||
{
|
||||
$oFilter = new CMDBSearchFilter($sClass);
|
||||
$oRetSet = new CMDBObjectSet($oFilter); // THE ONLY DIFF IS HERE
|
||||
// NOTE: THIS DOES NOT WORK IF m_bLoaded is private...
|
||||
// BUT IT THAT CASE YOU DO NOT GET ANY ERROR !!!!!
|
||||
$oFilter->AddConditionExpression(new FalseExpression());
|
||||
$oRetSet = new self($oFilter);
|
||||
// NOTE: THIS DOES NOT WORK IF m_bLoaded is private in the base class (and you will not get any error message)
|
||||
$oRetSet->m_bLoaded = true; // no DB load
|
||||
return $oRetSet;
|
||||
}
|
||||
|
||||
// create an object set ex nihilo
|
||||
// input = array of objects
|
||||
static public function FromArray($sClass, $aObjects)
|
||||
{
|
||||
$oFilter = new CMDBSearchFilter($sClass);
|
||||
$oRetSet = new CMDBObjectSet($oFilter); // THE ONLY DIFF IS HERE
|
||||
// NOTE: THIS DOES NOT WORK IF m_bLoaded is private...
|
||||
// BUT IT THAT CASE YOU DO NOT GET ANY ERROR !!!!!
|
||||
$oRetSet->m_bLoaded = true; // no DB load
|
||||
$oRetSet->AddObjectArray($aObjects);
|
||||
$oRetSet = self::FromScratch($sClass);
|
||||
$oRetSet->AddObjectArray($aObjects, $sClass);
|
||||
return $oRetSet;
|
||||
}
|
||||
|
||||
|
||||
static public function FromArrayAssoc($aClasses, $aObjects)
|
||||
{
|
||||
// In a perfect world, we should create a complete tree of DBObjectSearch,
|
||||
|
||||
@@ -1,36 +1,46 @@
|
||||
<?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');
|
||||
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'] = mysql_error();
|
||||
$aContext['mysql_errno'] = mysql_errno();
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -48,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 = '')
|
||||
{
|
||||
@@ -56,15 +66,41 @@ class CMDBSource
|
||||
self::$m_sDBUser = $sUser;
|
||||
self::$m_sDBPwd = $sPwd;
|
||||
self::$m_sDBName = $sSource;
|
||||
if (!self::$m_resDBLink = mysql_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($sServer, 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 (!mysql_select_db($sSource, self::$m_resDBLink))
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,7 +154,7 @@ class CMDBSource
|
||||
{
|
||||
// In case we don't have rights to enumerate the databases
|
||||
// Let's try to connect directly
|
||||
return @mysql_select_db($sSource, self::$m_resDBLink);
|
||||
return @((bool)self::$m_oMysqli->query("USE `$sSource`"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -131,7 +167,7 @@ class CMDBSource
|
||||
|
||||
public static function SelectDB($sSource)
|
||||
{
|
||||
if (!mysql_select_db($sSource, self::$m_resDBLink))
|
||||
if (!((bool)self::$m_oMysqli->query("USE `$sSource`")))
|
||||
{
|
||||
throw new MySQLException('Could not select DB', array('db_name'=>$sSource));
|
||||
}
|
||||
@@ -171,6 +207,30 @@ class CMDBSource
|
||||
return $res;
|
||||
}
|
||||
|
||||
public static function GetErrNo()
|
||||
{
|
||||
if (self::$m_oMysqli->errno != 0)
|
||||
{
|
||||
return self::$m_oMysqli->errno;
|
||||
}
|
||||
else
|
||||
{
|
||||
return self::$m_oMysqli->connect_errno;
|
||||
}
|
||||
}
|
||||
|
||||
public static function GetError()
|
||||
{
|
||||
if (self::$m_oMysqli->error != '')
|
||||
{
|
||||
return self::$m_oMysqli->error;
|
||||
}
|
||||
else
|
||||
{
|
||||
return self::$m_oMysqli->connect_error;
|
||||
}
|
||||
}
|
||||
|
||||
public static function DBHost() {return self::$m_sDBHost;}
|
||||
public static function DBUser() {return self::$m_sDBUser;}
|
||||
public static function DBPwd() {return self::$m_sDBPwd;}
|
||||
@@ -208,43 +268,50 @@ class CMDBSource
|
||||
// Quote if not a number or a numeric string
|
||||
if ($bAlways || is_string($value))
|
||||
{
|
||||
$value = $cQuoteStyle . mysql_real_escape_string($value, self::$m_resDBLink) . $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 = mysql_query($sSQLQuery, self::$m_resDBLink);
|
||||
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 = mysql_fetch_assoc($result);
|
||||
$oResult = self::Query($sSQL);
|
||||
$aRow = $oResult->fetch_assoc();
|
||||
$iNextInsertId = $aRow['Auto_increment'];
|
||||
return $iNextInsertId;
|
||||
}
|
||||
|
||||
public static function GetInsertId()
|
||||
{
|
||||
return mysql_insert_id(self::$m_resDBLink);
|
||||
$iRes = self::$m_oMysqli->insert_id;
|
||||
if (is_null($iRes))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return $iRes;
|
||||
}
|
||||
|
||||
public static function InsertInto($sSQLQuery)
|
||||
{
|
||||
if (self::Query($sSQLQuery))
|
||||
@@ -261,37 +328,59 @@ class CMDBSource
|
||||
|
||||
public static function QueryToScalar($sSql)
|
||||
{
|
||||
$result = mysql_query($sSql, self::$m_resDBLink);
|
||||
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 = mysql_fetch_array($result, MYSQL_BOTH))
|
||||
|
||||
if ($aRow = $oResult->fetch_array(MYSQLI_BOTH))
|
||||
{
|
||||
$res = $aRow[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
mysql_free_result($result);
|
||||
$oResult->free();
|
||||
throw new MySQLException('Found no result for query', array('query' => $sSql));
|
||||
}
|
||||
mysql_free_result($result);
|
||||
$oResult->free();
|
||||
return $res;
|
||||
}
|
||||
|
||||
public static function QueryToArray($sSql)
|
||||
{
|
||||
$aData = array();
|
||||
$result = mysql_query($sSql, self::$m_resDBLink);
|
||||
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 = mysql_fetch_array($result, MYSQL_BOTH))
|
||||
|
||||
while ($aRow = $oResult->fetch_array(MYSQLI_BOTH))
|
||||
{
|
||||
$aData[] = $aRow;
|
||||
}
|
||||
mysql_free_result($result);
|
||||
$oResult->free();
|
||||
return $aData;
|
||||
}
|
||||
|
||||
@@ -309,71 +398,94 @@ class CMDBSource
|
||||
public static function ExplainQuery($sSql)
|
||||
{
|
||||
$aData = array();
|
||||
$result = mysql_query("EXPLAIN $sSql", self::$m_resDBLink);
|
||||
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 = mysql_fetch_array($result, MYSQL_ASSOC))
|
||||
while ($aRow = $oResult->fetch_array(MYSQLI_ASSOC))
|
||||
{
|
||||
$aData[] = $aRow;
|
||||
}
|
||||
mysql_free_result($result);
|
||||
$oResult->free();
|
||||
return $aData;
|
||||
}
|
||||
|
||||
public static function TestQuery($sSql)
|
||||
{
|
||||
$result = mysql_query("EXPLAIN $sSql", self::$m_resDBLink);
|
||||
if (!$result)
|
||||
try
|
||||
{
|
||||
return mysql_error();
|
||||
$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));
|
||||
}
|
||||
|
||||
mysql_free_result($result);
|
||||
if (is_object($oResult))
|
||||
{
|
||||
$oResult->free();
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
public static function NbRows($result)
|
||||
public static function NbRows($oResult)
|
||||
{
|
||||
return mysql_num_rows($result);
|
||||
return $oResult->num_rows;
|
||||
}
|
||||
|
||||
public static function FetchArray($result)
|
||||
public static function AffectedRows()
|
||||
{
|
||||
return mysql_fetch_array($result, MYSQL_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 < mysql_num_fields($result) ; $i++)
|
||||
for ($i = 0; $i < (($___mysqli_tmp = $oResult->field_count) ? $___mysqli_tmp : 0) ; $i++)
|
||||
{
|
||||
$meta = mysql_fetch_field($result, $i);
|
||||
if (!$meta)
|
||||
{
|
||||
$meta = $oResult->fetch_field_direct($i);
|
||||
if (!$meta)
|
||||
{
|
||||
throw new MySQLException('mysql_fetch_field: No information available', array('query'=>$sSql, 'i'=>$i));
|
||||
}
|
||||
else
|
||||
{
|
||||
$aNames[] = $meta->name;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$aNames[] = $meta->name;
|
||||
}
|
||||
}
|
||||
return $aNames;
|
||||
}
|
||||
|
||||
public static function Seek($result, $iRow)
|
||||
public static function Seek($oResult, $iRow)
|
||||
{
|
||||
return mysql_data_seek($result, $iRow);
|
||||
return $oResult->data_seek($iRow);
|
||||
}
|
||||
|
||||
public static function FreeResult($result)
|
||||
public static function FreeResult($oResult)
|
||||
{
|
||||
return mysql_free_result($result);
|
||||
$oResult->free(); /* returns void */
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function IsTable($sTable)
|
||||
@@ -429,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)
|
||||
@@ -486,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()
|
||||
//{
|
||||
@@ -511,18 +643,25 @@ class CMDBSource
|
||||
public static function DumpTable($sTable)
|
||||
{
|
||||
$sSql = "SELECT * FROM `$sTable`";
|
||||
$result = mysql_query($sSql, self::$m_resDBLink);
|
||||
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 = mysql_fetch_array($result, MYSQL_ASSOC))
|
||||
while ($aRow = $oResult->fetch_array(MYSQLI_ASSOC))
|
||||
{
|
||||
$aRows[] = $aRow;
|
||||
}
|
||||
mysql_free_result($result);
|
||||
$oResult->free();
|
||||
return $aRows;
|
||||
}
|
||||
|
||||
@@ -552,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)
|
||||
{
|
||||
@@ -560,12 +699,12 @@ class CMDBSource
|
||||
}
|
||||
|
||||
$aRes = array();
|
||||
while ($aRow = mysql_fetch_array($result, MYSQL_NUM))
|
||||
while ($aRow = $oResult->fetch_array(MYSQLI_NUM))
|
||||
{
|
||||
// so far, only one column...
|
||||
$aRes[] = implode('/', $aRow);
|
||||
}
|
||||
mysql_free_result($result);
|
||||
$oResult->free();
|
||||
// so far, only one line...
|
||||
return implode(', ', $aRes);
|
||||
}
|
||||
@@ -578,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 (mysql_num_rows($result) == 0)
|
||||
if ($oResult->num_rows == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns one single row anytime
|
||||
$aRow = mysql_fetch_array($result, MYSQL_ASSOC);
|
||||
mysql_free_result($result);
|
||||
$aRow = $oResult->fetch_array(MYSQLI_ASSOC);
|
||||
$oResult->free();
|
||||
|
||||
if (!isset($aRow['Slave_IO_Running']))
|
||||
{
|
||||
@@ -614,7 +753,4 @@ class CMDBSource
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
}
|
||||
139
core/computing.inc.php
Normal file
139
core/computing.inc.php
Normal file
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2014 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/>
|
||||
|
||||
|
||||
/**
|
||||
* Any extension to compute things like a stop watch deadline or working hours
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Metric computing for stop watches
|
||||
*/
|
||||
interface iMetricComputer
|
||||
{
|
||||
public static function GetDescription();
|
||||
public function ComputeMetric($oObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Working time computing for stop watches
|
||||
*/
|
||||
interface iWorkingTimeComputer
|
||||
{
|
||||
public static function GetDescription();
|
||||
|
||||
/**
|
||||
* Get the date/time corresponding to a given delay in the future from the present
|
||||
* considering only the valid (open) hours for a specified object
|
||||
* @param $oObject DBObject The object for which to compute the deadline
|
||||
* @param $iDuration integer The duration (in seconds) in the future
|
||||
* @param $oStartDate DateTime The starting point for the computation
|
||||
* @return DateTime The date/time for the deadline
|
||||
*/
|
||||
public function GetDeadline($oObject, $iDuration, DateTime $oStartDate);
|
||||
|
||||
/**
|
||||
* Get duration (considering only open hours) elapsed bewteen two given DateTimes
|
||||
* @param $oObject DBObject The object for which to compute the duration
|
||||
* @param $oStartDate DateTime The starting point for the computation (default = now)
|
||||
* @param $oEndDate DateTime The ending point for the computation (default = now)
|
||||
* @return integer The duration (number of seconds) of open hours elapsed between the two dates
|
||||
*/
|
||||
public function GetOpenDuration($oObject, DateTime $oStartDate, DateTime $oEndDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation oof deadline computing: NO deadline
|
||||
*/
|
||||
class DefaultMetricComputer implements iMetricComputer
|
||||
{
|
||||
public static function GetDescription()
|
||||
{
|
||||
return "Null";
|
||||
}
|
||||
|
||||
public function ComputeMetric($oObject)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation of working time computing
|
||||
*/
|
||||
class DefaultWorkingTimeComputer implements iWorkingTimeComputer
|
||||
{
|
||||
public static function GetDescription()
|
||||
{
|
||||
return "24x7, no holidays";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the date/time corresponding to a given delay in the future from the present
|
||||
* considering only the valid (open) hours for a specified object
|
||||
* @param $oObject DBObject The object for which to compute the deadline
|
||||
* @param $iDuration integer The duration (in seconds) in the future
|
||||
* @param $oStartDate DateTime The starting point for the computation
|
||||
* @return DateTime The date/time for the deadline
|
||||
*/
|
||||
public function GetDeadline($oObject, $iDuration, DateTime $oStartDate)
|
||||
{
|
||||
if (class_exists('WorkingTimeRecorder'))
|
||||
{
|
||||
WorkingTimeRecorder::Trace(WorkingTimeRecorder::TRACE_DEBUG, __class__.'::'.__function__);
|
||||
}
|
||||
//echo "GetDeadline - default: ".$oStartDate->format('Y-m-d H:i:s')." + $iDuration<br/>\n";
|
||||
// Default implementation: 24x7, no holidays: to compute the deadline, just add
|
||||
// the specified duration to the given date/time
|
||||
$oResult = clone $oStartDate;
|
||||
$oResult->modify('+'.$iDuration.' seconds');
|
||||
if (class_exists('WorkingTimeRecorder'))
|
||||
{
|
||||
WorkingTimeRecorder::SetValues($oStartDate->format('U'), $oResult->format('U'), $iDuration, WorkingTimeRecorder::COMPUTED_END);
|
||||
}
|
||||
return $oResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get duration (considering only open hours) elapsed bewteen two given DateTimes
|
||||
* @param $oObject DBObject The object for which to compute the duration
|
||||
* @param $oStartDate DateTime The starting point for the computation (default = now)
|
||||
* @param $oEndDate DateTime The ending point for the computation (default = now)
|
||||
* @return integer The duration (number of seconds) of open hours elapsed between the two dates
|
||||
*/
|
||||
public function GetOpenDuration($oObject, DateTime $oStartDate, DateTime $oEndDate)
|
||||
{
|
||||
if (class_exists('WorkingTimeRecorder'))
|
||||
{
|
||||
WorkingTimeRecorder::Trace(WorkingTimeRecorder::TRACE_DEBUG, __class__.'::'.__function__);
|
||||
}
|
||||
//echo "GetOpenDuration - default: ".$oStartDate->format('Y-m-d H:i:s')." to ".$oEndDate->format('Y-m-d H:i:s')."<br/>\n";
|
||||
$iDuration = abs($oEndDate->format('U') - $oStartDate->format('U'));
|
||||
if (class_exists('WorkingTimeRecorder'))
|
||||
{
|
||||
WorkingTimeRecorder::SetValues($oStartDate->format('U'), $oEndDate->format('U'), $iDuration, WorkingTimeRecorder::COMPUTED_DURATION);
|
||||
}
|
||||
return $iDuration;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
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/>
|
||||
|
||||
|
||||
/**
|
||||
* 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
File diff suppressed because it is too large
Load Diff
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/>
|
||||
|
||||
|
||||
/**
|
||||
* 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,17 +107,22 @@ 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'];
|
||||
$oObject = $aData['to_reset'];
|
||||
$aExtKeyLabels = array();
|
||||
foreach ($aData['attributes'] as $sRemoteExtKey => $aRemoteAttDef)
|
||||
{
|
||||
$oObject->Set($sRemoteExtKey, 0);
|
||||
$oObject->Set($sRemoteExtKey, $aData['values'][$sRemoteExtKey]);
|
||||
$aExtKeyLabels[] = $aRemoteAttDef->GetLabel();
|
||||
}
|
||||
$this->m_aToUpdate[$sClass][$iId]['attributes_list'] = implode(', ', $aExtKeyLabels);
|
||||
@@ -130,9 +139,9 @@ class DeletionPlan
|
||||
$this->m_bFoundSecurityIssue = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
set_time_limit($iPreviousTimeLimit);
|
||||
}
|
||||
|
||||
public function GetIssues()
|
||||
@@ -264,7 +273,7 @@ class DeletionPlan
|
||||
}
|
||||
}
|
||||
|
||||
public function AddToUpdate($oObject, $oAttDef)
|
||||
public function AddToUpdate($oObject, $oAttDef, $value = 0)
|
||||
{
|
||||
$sClass = get_class($oObject);
|
||||
$iId = $oObject->GetKey();
|
||||
@@ -281,6 +290,7 @@ class DeletionPlan
|
||||
);
|
||||
}
|
||||
$this->m_aToUpdate[$sClass][$iId]['attributes'][$oAttDef->GetCode()] = $oAttDef;
|
||||
$this->m_aToUpdate[$sClass][$iId]['values'][$oAttDef->GetCode()] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -97,7 +97,7 @@ class Dict
|
||||
}
|
||||
|
||||
|
||||
public static function GetCurrentLanguage()
|
||||
public static function GetUserLanguage()
|
||||
{
|
||||
if (self::$m_sCurrentLanguage == null) // May happen when no user is logged in (i.e login screen, non authentifed page)
|
||||
{
|
||||
@@ -120,33 +120,36 @@ 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
|
||||
//
|
||||
if (!array_key_exists(self::GetCurrentLanguage(), self::$m_aData))
|
||||
if (!array_key_exists(self::GetUserLanguage(), self::$m_aData))
|
||||
{
|
||||
// It may happen, when something happens before the dictionnaries get loaded
|
||||
return $sStringCode;
|
||||
}
|
||||
$aCurrentDictionary = self::$m_aData[self::GetCurrentLanguage()];
|
||||
$aCurrentDictionary = self::$m_aData[self::GetUserLanguage()];
|
||||
if (array_key_exists($sStringCode, $aCurrentDictionary))
|
||||
{
|
||||
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...
|
||||
//
|
||||
@@ -220,6 +223,20 @@ class Dict
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone a string in every language (if it exists in that language)
|
||||
*/
|
||||
public static function CloneString($sSourceCode, $sDestCode)
|
||||
{
|
||||
foreach(self::$m_aLanguages as $sLanguageCode => $foo)
|
||||
{
|
||||
if (isset(self::$m_aData[$sLanguageCode][$sSourceCode]))
|
||||
{
|
||||
self::$m_aData[$sLanguageCode][$sDestCode] = self::$m_aData[$sLanguageCode][$sSourceCode];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function MakeStats($sLanguageCode, $sLanguageRef = 'EN US')
|
||||
{
|
||||
$aMissing = array(); // Strings missing for the target language
|
||||
|
||||
@@ -1,64 +1,150 @@
|
||||
<?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 mail (for notification, testing,... purposes)
|
||||
* #@# TODO - replace by a more sophisticated mean (and update the prototype)
|
||||
* 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');
|
||||
|
||||
Swift_Preferences::getInstance()->setCharset('UTF-8');
|
||||
|
||||
|
||||
define ('EMAIL_SEND_OK', 0);
|
||||
define ('EMAIL_SEND_PENDING', 1);
|
||||
define ('EMAIL_SEND_ERROR', 2);
|
||||
|
||||
|
||||
class EMail
|
||||
{
|
||||
protected $m_sBody;
|
||||
protected $m_sSubject;
|
||||
protected $m_sTo;
|
||||
protected $m_aHeaders; // array of key=>value
|
||||
// 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 __construct($sTo = '', $sSubject = '', $sBody = '', $aHeaders = array())
|
||||
public function LoadConfig($sConfigFile = ITOP_DEFAULT_CONFIG_FILE)
|
||||
{
|
||||
$this->m_sTo = $sTo;
|
||||
$this->m_sSubject = $sSubject;
|
||||
$this->m_sBody = $sBody;
|
||||
$this->m_aHeaders = $aHeaders;
|
||||
if (is_null(self::$m_oConfig))
|
||||
{
|
||||
self::$m_oConfig = new Config($sConfigFile);
|
||||
}
|
||||
}
|
||||
|
||||
// Errors management : not that simple because we need that function to be
|
||||
// executed in the background, while making sure that any issue would be reported clearly
|
||||
protected $m_aMailErrors; //array of strings explaining the issues
|
||||
protected $m_oMessage;
|
||||
|
||||
public function mail_error_handler($errno, $errstr, $errfile, $errline)
|
||||
public function __construct()
|
||||
{
|
||||
$sCleanMessage= str_replace("mail() [<a href='function.mail'>function.mail</a>]: ", "", $errstr);
|
||||
$this->m_aMailErrors[] = $sCleanMessage;
|
||||
$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
|
||||
{
|
||||
AsyncSendEmail::AddToQueue($this->m_sTo, $this->m_sSubject, $this->m_sBody, $this->m_aHeaders, $oLog);
|
||||
AsyncSendEmail::AddToQueue($this, $oLog);
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
@@ -71,34 +157,49 @@ class EMail
|
||||
|
||||
protected function SendSynchronous(&$aIssues, $oLog = null)
|
||||
{
|
||||
$sHeaders = 'MIME-Version: 1.0' . "\r\n";
|
||||
// ! the case is important for MS-Outlook
|
||||
$sHeaders .= 'Content-Type: text/html; charset=UTF-8' . "\r\n";
|
||||
$sHeaders .= 'Content-Transfer-Encoding: 8bit' . "\r\n";
|
||||
foreach ($this->m_aHeaders as $sKey => $sValue)
|
||||
$this->LoadConfig();
|
||||
|
||||
$sTransport = self::$m_oConfig->Get('email_transport');
|
||||
switch ($sTransport)
|
||||
{
|
||||
$sHeaders .= "$sKey: $sValue\r\n";
|
||||
case 'SMTP':
|
||||
$sHost = self::$m_oConfig->Get('email_transport_smtp.host');
|
||||
$sPort = self::$m_oConfig->Get('email_transport_smtp.port');
|
||||
$sEncryption = self::$m_oConfig->Get('email_transport_smtp.encryption');
|
||||
$sUserName = self::$m_oConfig->Get('email_transport_smtp.username');
|
||||
$sPassword = self::$m_oConfig->Get('email_transport_smtp.password');
|
||||
|
||||
$oTransport = Swift_SmtpTransport::newInstance($sHost, $sPort, $sEncryption);
|
||||
if (strlen($sUserName) > 0)
|
||||
{
|
||||
$oTransport->setUsername($sUserName);
|
||||
$oTransport->setPassword($sPassword);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'Null':
|
||||
$oTransport = Swift_NullTransport::newInstance();
|
||||
break;
|
||||
|
||||
case 'LogFile':
|
||||
$oTransport = Swift_LogFileTransport::newInstance();
|
||||
$oTransport->setLogFile(APPROOT.'log/mail.log');
|
||||
break;
|
||||
|
||||
case 'PHPMail':
|
||||
default:
|
||||
$oTransport = Swift_MailTransport::newInstance();
|
||||
}
|
||||
|
||||
// Under Windows (not yet proven for Linux/PHP) mail may issue a warning
|
||||
// that I could not mask (tried error_reporting(), etc.)
|
||||
$this->m_aMailErrors = array();
|
||||
set_error_handler(array($this, 'mail_error_handler'));
|
||||
$bRes = mail
|
||||
(
|
||||
$this->m_sTo,
|
||||
$this->m_sSubject,
|
||||
$this->m_sBody,
|
||||
$sHeaders
|
||||
);
|
||||
restore_error_handler();
|
||||
if (!$bRes && empty($this->m_aMailErrors))
|
||||
$oMailer = Swift_Mailer::newInstance($oTransport);
|
||||
|
||||
$aFailedRecipients = array();
|
||||
$iSent = $oMailer->send($this->m_oMessage, $aFailedRecipients);
|
||||
if ($iSent === 0)
|
||||
{
|
||||
$this->m_aMailErrors[] = 'Unknown reason';
|
||||
}
|
||||
if (count($this->m_aMailErrors) > 0)
|
||||
{
|
||||
$aIssues = $this->m_aMailErrors;
|
||||
// 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
|
||||
@@ -128,59 +229,251 @@ class EMail
|
||||
}
|
||||
}
|
||||
|
||||
protected function AddToHeader($sKey, $sValue)
|
||||
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)
|
||||
{
|
||||
$this->m_aHeaders[$sKey] = $sValue;
|
||||
$oHeaders = $this->m_oMessage->getHeaders();
|
||||
switch(strtolower($sKey))
|
||||
{
|
||||
default:
|
||||
$oHeaders->addTextHeader($sKey, $sValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
$oMsgId = $this->m_oMessage->getHeaders()->get('Message-ID');
|
||||
$oMsgId->SetId($sId);
|
||||
}
|
||||
|
||||
public function SetReferences($sReferences)
|
||||
{
|
||||
$this->AddToHeader('References', $sReferences);
|
||||
}
|
||||
|
||||
public function SetBody($sBody)
|
||||
public function SetBody($sBody, $sMimeType = 'text/html')
|
||||
{
|
||||
$this->m_sBody = $sBody;
|
||||
$this->m_aData['body'] = array('body' => $sBody, 'mimeType' => $sMimeType);
|
||||
$this->m_oMessage->setBody($sBody, $sMimeType);
|
||||
}
|
||||
|
||||
public function SetSubject($aSubject)
|
||||
public function AddPart($sText, $sMimeType = 'text/html')
|
||||
{
|
||||
$this->m_sSubject = $aSubject;
|
||||
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($sSubject)
|
||||
{
|
||||
$this->m_aData['subject'] = $sSubject;
|
||||
$this->m_oMessage->setSubject($sSubject);
|
||||
}
|
||||
|
||||
public function GetSubject()
|
||||
{
|
||||
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_sTo = $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();
|
||||
foreach ($aRes as $sEmail => $sName)
|
||||
{
|
||||
if (is_null($sName))
|
||||
{
|
||||
$aStrings[] = $sEmail;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sName = str_replace(array('<', '>'), '', $sName);
|
||||
$aStrings[] = "$sName <$sEmail>";
|
||||
}
|
||||
}
|
||||
return implode(', ', $aStrings);
|
||||
}
|
||||
else
|
||||
{
|
||||
return $aRes;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
public function SetRecipientFrom($sAddress, $sLabel = '')
|
||||
{
|
||||
$this->AddToHeader('From', $sAddress);
|
||||
|
||||
// This is required on Windows because otherwise I would get the error
|
||||
// "sendmail_from" not set in php.ini" even if it is correctly working
|
||||
// (apparently, once it worked the SMTP server won't claim anymore for it)
|
||||
ini_set("sendmail_from", $sAddress);
|
||||
$this->m_aData['from'] = array('address' => $sAddress, 'label' => $sLabel);
|
||||
if ($sLabel != '')
|
||||
{
|
||||
$this->m_oMessage->setFrom(array($sAddress => $sLabel));
|
||||
}
|
||||
else if (!empty($sAddress))
|
||||
{
|
||||
$this->m_oMessage->setFrom($sAddress);
|
||||
}
|
||||
}
|
||||
|
||||
public function SetRecipientReplyTo($sAddress)
|
||||
{
|
||||
$this->AddToHeader('Reply-To', $sAddress);
|
||||
$this->m_aData['reply_to'] = $sAddress;
|
||||
if (!empty($sAddress))
|
||||
{
|
||||
$this->m_oMessage->setReplyTo($sAddress);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Extension to SwiftMailer: "debug" transport that pretends messages have been sent,
|
||||
* but just log them to a file.
|
||||
*
|
||||
* @package Swift
|
||||
* @author Denis Flaven
|
||||
*/
|
||||
class Swift_Transport_LogFileTransport extends Swift_Transport_NullTransport
|
||||
{
|
||||
protected $sLogFile;
|
||||
|
||||
/**
|
||||
* Sends the given message.
|
||||
*
|
||||
* @param Swift_Mime_Message $message
|
||||
* @param string[] $failedRecipients An array of failures by-reference
|
||||
*
|
||||
* @return int The number of sent emails
|
||||
*/
|
||||
public function send(Swift_Mime_Message $message, &$failedRecipients = null)
|
||||
{
|
||||
$hFile = @fopen($this->sLogFile, 'a');
|
||||
if ($hFile)
|
||||
{
|
||||
$sTxt = "================== ".date('Y-m-d H:i:s')." ==================\n";
|
||||
$sTxt .= $message->toString()."\n";
|
||||
|
||||
@fwrite($hFile, $sTxt);
|
||||
@fclose($hFile);
|
||||
}
|
||||
|
||||
return parent::send($message, $failedRecipients);
|
||||
}
|
||||
|
||||
public function setLogFile($sFilename)
|
||||
{
|
||||
$this->sLogFile = $sFilename;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pretends messages have been sent, but just log them to a file.
|
||||
*
|
||||
* @package Swift
|
||||
* @author Denis Flaven
|
||||
*/
|
||||
class Swift_LogFileTransport extends Swift_Transport_LogFileTransport
|
||||
{
|
||||
/**
|
||||
* Create a new LogFileTransport.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
call_user_func_array(
|
||||
array($this, 'Swift_Transport_LogFileTransport::__construct'),
|
||||
Swift_DependencyContainer::getInstance()
|
||||
->createDependenciesFor('transport.null')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new LogFileTransport instance.
|
||||
*
|
||||
* @return Swift_LogFileTransport
|
||||
*/
|
||||
public static function newInstance()
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
@@ -90,7 +92,7 @@ class Event extends DBObject implements iDisplay
|
||||
|
||||
public static function GetUIPage()
|
||||
{
|
||||
return '../pages/UI.php';
|
||||
return 'UI.php';
|
||||
}
|
||||
|
||||
function DisplayDetails(WebPage $oPage, $bEditMode = false)
|
||||
@@ -103,9 +105,9 @@ class Event extends DBObject implements iDisplay
|
||||
$this->DisplayBareProperties($oPage, $bEditMode);
|
||||
}
|
||||
|
||||
function DisplayBareProperties(WebPage $oPage, $bEditMode = false)
|
||||
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();
|
||||
@@ -174,11 +182,12 @@ class EventNotificationEmail extends EventNotification
|
||||
MetaModel::Init_AddAttribute(new AttributeText("bcc", array("allowed_values"=>null, "sql"=>"bcc", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
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 AttributeText("body", array("allowed_values"=>null, "sql"=>"body", "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,19 +346,28 @@ 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();
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", array("targetclass"=>"User", "jointype"=> "", "allowed_values"=>null, "sql"=>"user_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("contact_name", array("allowed_values"=>null, "extkey_attcode"=>"user_id", "target_attcode"=>"contactid", "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("contact_email", array("allowed_values"=>null, "extkey_attcode"=>"user_id", "target_attcode"=>"email", "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", array("targetclass"=>"User", "jointype"=> "", "allowed_values"=>null, "sql"=>"user_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_SILENT, "depends_on"=>array())));
|
||||
$aZList = array('date', 'user_id');
|
||||
if (MetaModel::IsValidAttCode('Contact', 'name'))
|
||||
{
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("contact_name", array("allowed_values"=>null, "extkey_attcode"=>"user_id", "target_attcode"=>"contactid", "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
$aZList[] = 'contact_name';
|
||||
}
|
||||
if (MetaModel::IsValidAttCode('Contact', 'email'))
|
||||
{
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("contact_email", array("allowed_values"=>null, "extkey_attcode"=>"user_id", "target_attcode"=>"email", "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
$aZList[] = 'contact_email';
|
||||
}
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('date', 'user_id', 'contact_name', 'contact_email', 'userinfo', 'message')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('date', 'user_id', 'contact_name', 'contact_email', 'userinfo')); // Attributes to be displayed for a list
|
||||
MetaModel::Init_SetZListItems('details', array_merge($aZList, array('userinfo', 'message'))); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array_merge($aZList, array('userinfo'))); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
MetaModel::Init_SetZListItems('standard_search', array('date', 'user_id', 'contact_name', 'contact_email')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('standard_search', $aZList); // Criteria of the std search form
|
||||
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -36,11 +37,19 @@ abstract class Expression
|
||||
// recursive rendering (aArgs used as input by default, or used as output if bRetrofitParams set to True
|
||||
abstract public function Render(&$aArgs = null, $bRetrofitParams = false);
|
||||
|
||||
abstract public function ApplyParameters($aArgs);
|
||||
|
||||
// recursively builds an array of class => fieldname
|
||||
abstract public function ListRequiredFields();
|
||||
|
||||
abstract public function IsTrue();
|
||||
// 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
|
||||
abstract public function ListConstantFields();
|
||||
|
||||
public function RequiresField($sClass, $sFieldName)
|
||||
{
|
||||
// #@# todo - optimize : this is called quite often when building a single query !
|
||||
@@ -84,6 +93,21 @@ abstract class Expression
|
||||
{
|
||||
return new BinaryExpression($this, 'OR', $oExpr);
|
||||
}
|
||||
|
||||
abstract public function RenameParam($sOldName, $sNewName);
|
||||
|
||||
/**
|
||||
* Make the most relevant label, given the value of the expression
|
||||
*
|
||||
* @param DBObjectSearch oFilter The context in which this expression has been used
|
||||
* @param string sValue The value returned by the query, for this expression
|
||||
* @param string sDefault The default value if no relevant label could be computed
|
||||
* @return The label
|
||||
*/
|
||||
public function MakeValueLabel($oFilter, $sValue, $sDefault)
|
||||
{
|
||||
return $sDefault;
|
||||
}
|
||||
}
|
||||
|
||||
class SQLExpression extends Expression
|
||||
@@ -106,6 +130,10 @@ class SQLExpression extends Expression
|
||||
return $this->m_sSQL;
|
||||
}
|
||||
|
||||
public function ApplyParameters($aArgs)
|
||||
{
|
||||
}
|
||||
|
||||
public function GetUnresolvedFields($sAlias, &$aUnresolved)
|
||||
{
|
||||
}
|
||||
@@ -119,6 +147,20 @@ class SQLExpression extends Expression
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
public function CollectUsedParents(&$aTable)
|
||||
{
|
||||
}
|
||||
|
||||
public function ListConstantFields()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
public function RenameParam($sOldName, $sNewName)
|
||||
{
|
||||
// Do nothing, since there is nothing to rename
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -157,11 +199,11 @@ class BinaryExpression extends Expression
|
||||
// return true if we are certain that it will be true
|
||||
if ($this->m_sOperator == 'AND')
|
||||
{
|
||||
if ($this->m_oLeftExpr->IsTrue() && $this->m_oLeftExpr->IsTrue()) return true;
|
||||
if ($this->m_oLeftExpr->IsTrue() && $this->m_oRightExpr->IsTrue()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public function GetLeftExpr()
|
||||
{
|
||||
return $this->m_oLeftExpr;
|
||||
@@ -185,7 +227,27 @@ class BinaryExpression extends Expression
|
||||
$sRight = $this->GetRightExpr()->Render($aArgs, $bRetrofitParams);
|
||||
return "($sLeft $sOperator $sRight)";
|
||||
}
|
||||
|
||||
|
||||
public function ApplyParameters($aArgs)
|
||||
{
|
||||
if ($this->m_oLeftExpr instanceof VariableExpression)
|
||||
{
|
||||
$this->m_oLeftExpr = $this->m_oLeftExpr->GetAsScalar($aArgs);
|
||||
}
|
||||
else //if ($this->m_oLeftExpr instanceof Expression)
|
||||
{
|
||||
$this->m_oLeftExpr->ApplyParameters($aArgs);
|
||||
}
|
||||
if ($this->m_oRightExpr instanceof VariableExpression)
|
||||
{
|
||||
$this->m_oRightExpr = $this->m_oRightExpr->GetAsScalar($aArgs);
|
||||
}
|
||||
else //if ($this->m_oRightExpr instanceof Expression)
|
||||
{
|
||||
$this->m_oRightExpr->ApplyParameters($aArgs);
|
||||
}
|
||||
}
|
||||
|
||||
public function GetUnresolvedFields($sAlias, &$aUnresolved)
|
||||
{
|
||||
$this->GetLeftExpr()->GetUnresolvedFields($sAlias, $aUnresolved);
|
||||
@@ -205,6 +267,52 @@ 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>
|
||||
* Could be extended to support <field> = <function><constant_expression>
|
||||
*/
|
||||
public function ListConstantFields()
|
||||
{
|
||||
$aResult = array();
|
||||
if ($this->m_sOperator == '=')
|
||||
{
|
||||
if (($this->m_oLeftExpr instanceof FieldExpression) && ($this->m_oRightExpr instanceof ScalarExpression))
|
||||
{
|
||||
$aResult[$this->m_oLeftExpr->GetParent()][$this->m_oLeftExpr->GetName()] = $this->m_oRightExpr;
|
||||
}
|
||||
else if (($this->m_oRightExpr instanceof FieldExpression) && ($this->m_oLeftExpr instanceof ScalarExpression))
|
||||
{
|
||||
$aResult[$this->m_oRightExpr->GetParent()][$this->m_oRightExpr->GetName()] = $this->m_oLeftExpr;
|
||||
}
|
||||
else if (($this->m_oLeftExpr instanceof FieldExpression) && ($this->m_oRightExpr instanceof VariableExpression))
|
||||
{
|
||||
$aResult[$this->m_oLeftExpr->GetParent()][$this->m_oLeftExpr->GetName()] = $this->m_oRightExpr;
|
||||
}
|
||||
else if (($this->m_oRightExpr instanceof FieldExpression) && ($this->m_oLeftExpr instanceof VariableExpression))
|
||||
{
|
||||
$aResult[$this->m_oRightExpr->GetParent()][$this->m_oRightExpr->GetName()] = $this->m_oLeftExpr;
|
||||
}
|
||||
}
|
||||
else if ($this->m_sOperator == 'AND')
|
||||
{
|
||||
// Strictly, this should be done only for the AND operator
|
||||
$aResult = array_merge_recursive($this->m_oRightExpr->ListConstantFields(), $this->m_oLeftExpr->ListConstantFields());
|
||||
}
|
||||
return $aResult;
|
||||
}
|
||||
|
||||
public function RenameParam($sOldName, $sNewName)
|
||||
{
|
||||
$this->GetLeftExpr()->RenameParam($sOldName, $sNewName);
|
||||
$this->GetRightExpr()->RenameParam($sOldName, $sNewName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -231,18 +339,13 @@ class UnaryExpression extends Expression
|
||||
// recursive rendering
|
||||
public function Render(&$aArgs = null, $bRetrofitParams = false)
|
||||
{
|
||||
if ($bRetrofitParams)
|
||||
{
|
||||
$iParamIndex = count($aArgs) + 1; // 1-based indexation
|
||||
$aArgs['param'.$iParamIndex] = $this->m_value;
|
||||
return ':param'.$iParamIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
return CMDBSource::Quote($this->m_value);
|
||||
}
|
||||
return CMDBSource::Quote($this->m_value);
|
||||
}
|
||||
|
||||
public function ApplyParameters($aArgs)
|
||||
{
|
||||
}
|
||||
|
||||
public function GetUnresolvedFields($sAlias, &$aUnresolved)
|
||||
{
|
||||
}
|
||||
@@ -256,18 +359,52 @@ class UnaryExpression extends Expression
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
public function CollectUsedParents(&$aTable)
|
||||
{
|
||||
}
|
||||
|
||||
public function ListConstantFields()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
public function RenameParam($sOldName, $sNewName)
|
||||
{
|
||||
// Do nothing
|
||||
// really ? what about :param{$iParamIndex} ??
|
||||
}
|
||||
}
|
||||
|
||||
class ScalarExpression extends UnaryExpression
|
||||
{
|
||||
public function __construct($value)
|
||||
{
|
||||
if (!is_scalar($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)));
|
||||
}
|
||||
parent::__construct($value);
|
||||
}
|
||||
|
||||
// recursive rendering
|
||||
public function Render(&$aArgs = null, $bRetrofitParams = false)
|
||||
{
|
||||
if (is_null($this->m_value))
|
||||
{
|
||||
$sRet = 'NULL';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sRet = CMDBSource::Quote($this->m_value);
|
||||
}
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
public function GetAsScalar($aArgs)
|
||||
{
|
||||
return clone $this;
|
||||
}
|
||||
}
|
||||
|
||||
class TrueExpression extends ScalarExpression
|
||||
@@ -333,6 +470,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)
|
||||
@@ -340,6 +482,12 @@ class FieldExpression extends UnaryExpression
|
||||
// Add a reference to the field
|
||||
$aUnresolved[$this->m_sName] = $this;
|
||||
}
|
||||
elseif ($sAlias == '')
|
||||
{
|
||||
// An empty alias means "any alias"
|
||||
// In such a case, the results are indexed differently
|
||||
$aUnresolved[$this->m_sParent][$this->m_sName] = $this;
|
||||
}
|
||||
}
|
||||
|
||||
public function Translate($aTranslationData, $bMatchAll = true, $bMarkFieldsAsResolved = true)
|
||||
@@ -374,6 +522,52 @@ class FieldExpression extends UnaryExpression
|
||||
}
|
||||
return $oRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the most relevant label, given the value of the expression
|
||||
*
|
||||
* @param DBObjectSearch oFilter The context in which this expression has been used
|
||||
* @param string sValue The value returned by the query, for this expression
|
||||
* @param string sDefault The default value if no relevant label could be computed
|
||||
* @return The label
|
||||
*/
|
||||
public function MakeValueLabel($oFilter, $sValue, $sDefault)
|
||||
{
|
||||
$sAttCode = $this->GetName();
|
||||
$sParentAlias = $this->GetParent();
|
||||
|
||||
$aSelectedClasses = $oFilter->GetSelectedClasses();
|
||||
$sClass = $aSelectedClasses[$sParentAlias];
|
||||
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
// Set a default value for the general case
|
||||
$sRes = $oAttDef->GetAsHtml($sValue);
|
||||
|
||||
// Exceptions...
|
||||
if ($oAttDef->IsExternalKey())
|
||||
{
|
||||
$sObjClass = $oAttDef->GetTargetClass();
|
||||
$iObjKey = (int)$sValue;
|
||||
if ($iObjKey > 0)
|
||||
{
|
||||
$oObject = MetaModel::GetObject($sObjClass, $iObjKey);
|
||||
$sRes = $oObject->GetHyperlink();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Undefined
|
||||
$sRes = DBObject::MakeHyperLink($sObjClass, 0);
|
||||
}
|
||||
}
|
||||
elseif ($oAttDef->IsExternalField())
|
||||
{
|
||||
if (is_null($sValue))
|
||||
{
|
||||
$sRes = Dict::S('UI:UndefinedObject');
|
||||
}
|
||||
}
|
||||
return $sRes;
|
||||
}
|
||||
}
|
||||
|
||||
// Has been resolved into an SQL expression
|
||||
@@ -419,16 +613,70 @@ class VariableExpression extends UnaryExpression
|
||||
{
|
||||
return CMDBSource::Quote($aArgs[$this->m_sName]);
|
||||
}
|
||||
elseif ($bRetrofitParams)
|
||||
elseif (($iPos = strpos($this->m_sName, '->')) !== false)
|
||||
{
|
||||
//$aArgs[$this->m_sName] = null;
|
||||
$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)));
|
||||
}
|
||||
}
|
||||
|
||||
public function RenameParam($sOldName, $sNewName)
|
||||
{
|
||||
if ($this->m_sName == $sOldName)
|
||||
{
|
||||
$this->m_sName = $sNewName;
|
||||
}
|
||||
}
|
||||
|
||||
public function GetAsScalar($aArgs)
|
||||
{
|
||||
$value = null;
|
||||
if (array_key_exists($this->m_sName, $aArgs))
|
||||
{
|
||||
$value = $aArgs[$this->m_sName];
|
||||
}
|
||||
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)));
|
||||
}
|
||||
return new ScalarExpression($value);
|
||||
}
|
||||
}
|
||||
|
||||
// Temporary, until we implement functions and expression casting!
|
||||
@@ -474,6 +722,22 @@ class ListExpression extends Expression
|
||||
return '('.implode(', ', $aRes).')';
|
||||
}
|
||||
|
||||
public function ApplyParameters($aArgs)
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($this->m_aExpressions as $idx => $oExpr)
|
||||
{
|
||||
if ($oExpr instanceof VariableExpression)
|
||||
{
|
||||
$this->m_aExpressions[$idx] = $oExpr->GetAsScalar();
|
||||
}
|
||||
else
|
||||
{
|
||||
$oExpr->ApplyParameters($aArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function GetUnresolvedFields($sAlias, &$aUnresolved)
|
||||
{
|
||||
foreach ($this->m_aExpressions as $oExpr)
|
||||
@@ -501,6 +765,33 @@ class ListExpression extends Expression
|
||||
}
|
||||
return $aRes;
|
||||
}
|
||||
|
||||
public function CollectUsedParents(&$aTable)
|
||||
{
|
||||
foreach ($this->m_aExpressions as $oExpr)
|
||||
{
|
||||
$oExpr->CollectUsedParents($aTable);
|
||||
}
|
||||
}
|
||||
|
||||
public function ListConstantFields()
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($this->m_aExpressions as $oExpr)
|
||||
{
|
||||
$aRes = array_merge($aRes, $oExpr->ListConstantFields());
|
||||
}
|
||||
return $aRes;
|
||||
}
|
||||
|
||||
public function RenameParam($sOldName, $sNewName)
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($this->m_aExpressions as $key => $oExpr)
|
||||
{
|
||||
$this->m_aExpressions[$key] = $oExpr->RenameParam($sOldName, $sNewName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -542,6 +833,22 @@ class FunctionExpression extends Expression
|
||||
return $this->m_sVerb.'('.implode(', ', $aRes).')';
|
||||
}
|
||||
|
||||
public function ApplyParameters($aArgs)
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($this->m_aArgs as $idx => $oExpr)
|
||||
{
|
||||
if ($oExpr instanceof VariableExpression)
|
||||
{
|
||||
$this->m_aArgs[$idx] = $oExpr->GetAsScalar($aArgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oExpr->ApplyParameters($aArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function GetUnresolvedFields($sAlias, &$aUnresolved)
|
||||
{
|
||||
foreach ($this->m_aArgs as $oExpr)
|
||||
@@ -569,6 +876,103 @@ class FunctionExpression extends Expression
|
||||
}
|
||||
return $aRes;
|
||||
}
|
||||
|
||||
public function CollectUsedParents(&$aTable)
|
||||
{
|
||||
foreach ($this->m_aArgs as $oExpr)
|
||||
{
|
||||
$oExpr->CollectUsedParents($aTable);
|
||||
}
|
||||
}
|
||||
|
||||
public function ListConstantFields()
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($this->m_aArgs as $oExpr)
|
||||
{
|
||||
$aRes = array_merge($aRes, $oExpr->ListConstantFields());
|
||||
}
|
||||
return $aRes;
|
||||
}
|
||||
|
||||
public function RenameParam($sOldName, $sNewName)
|
||||
{
|
||||
foreach ($this->m_aArgs as $key => $oExpr)
|
||||
{
|
||||
$this->m_aArgs[$key] = $oExpr->RenameParam($sOldName, $sNewName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the most relevant label, given the value of the expression
|
||||
*
|
||||
* @param DBObjectSearch oFilter The context in which this expression has been used
|
||||
* @param string sValue The value returned by the query, for this expression
|
||||
* @param string sDefault The default value if no relevant label could be computed
|
||||
* @return The label
|
||||
*/
|
||||
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'")
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
class IntervalExpression extends Expression
|
||||
@@ -604,6 +1008,18 @@ class IntervalExpression extends Expression
|
||||
return 'INTERVAL '.$this->m_oValue->Render($aArgs, $bRetrofitParams).' '.$this->m_sUnit;
|
||||
}
|
||||
|
||||
public function ApplyParameters($aArgs)
|
||||
{
|
||||
if ($this->m_oValue instanceof VariableExpression)
|
||||
{
|
||||
$this->m_oValue = $this->m_oValue->GetAsScalar($aArgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->m_oValue->ApplyParameters($aArgs);
|
||||
}
|
||||
}
|
||||
|
||||
public function GetUnresolvedFields($sAlias, &$aUnresolved)
|
||||
{
|
||||
$this->m_oValue->GetUnresolvedFields($sAlias, $aUnresolved);
|
||||
@@ -618,6 +1034,20 @@ class IntervalExpression extends Expression
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
public function CollectUsedParents(&$aTable)
|
||||
{
|
||||
}
|
||||
|
||||
public function ListConstantFields()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
public function RenameParam($sOldName, $sNewName)
|
||||
{
|
||||
$this->m_oValue->RenameParam($sOldName, $sNewName);
|
||||
}
|
||||
}
|
||||
|
||||
class CharConcatExpression extends Expression
|
||||
@@ -653,6 +1083,22 @@ class CharConcatExpression extends Expression
|
||||
return "CAST(CONCAT(".implode(', ', $aRes).") AS CHAR)";
|
||||
}
|
||||
|
||||
public function ApplyParameters($aArgs)
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($this->m_aExpressions as $idx => $oExpr)
|
||||
{
|
||||
if ($oExpr instanceof VariableExpression)
|
||||
{
|
||||
$this->m_aExpressions[$idx] = $oExpr->GetAsScalar();
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->m_aExpressions->ApplyParameters($aArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function GetUnresolvedFields($sAlias, &$aUnresolved)
|
||||
{
|
||||
foreach ($this->m_aExpressions as $oExpr)
|
||||
@@ -680,6 +1126,32 @@ class CharConcatExpression extends Expression
|
||||
}
|
||||
return $aRes;
|
||||
}
|
||||
|
||||
public function CollectUsedParents(&$aTable)
|
||||
{
|
||||
foreach ($this->m_aExpressions as $oExpr)
|
||||
{
|
||||
$oExpr->CollectUsedParents($aTable);
|
||||
}
|
||||
}
|
||||
|
||||
public function ListConstantFields()
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($this->m_aExpressions as $oExpr)
|
||||
{
|
||||
$aRes = array_merge($aRes, $oExpr->ListConstantFields());
|
||||
}
|
||||
return $aRes;
|
||||
}
|
||||
|
||||
public function RenameParam($sOldName, $sNewName)
|
||||
{
|
||||
foreach ($this->m_aExpressions as $key => $oExpr)
|
||||
{
|
||||
$this->m_aExpressions[$key] = $oExpr->RenameParam($sOldName, $sNewName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -706,6 +1178,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -713,13 +1195,22 @@ class QueryBuilderExpressions
|
||||
{
|
||||
protected $m_oConditionExpr;
|
||||
protected $m_aSelectExpr;
|
||||
protected $m_aGroupByExpr;
|
||||
protected $m_aJoinFields;
|
||||
protected $m_aClassIds;
|
||||
|
||||
public function __construct($aSelect, $oCondition)
|
||||
public function __construct($oSearch, $aGroupByExpr = null)
|
||||
{
|
||||
$this->m_oConditionExpr = $oCondition;
|
||||
$this->m_aSelectExpr = $aSelect;
|
||||
$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()
|
||||
@@ -727,6 +1218,11 @@ class QueryBuilderExpressions
|
||||
return $this->m_aSelectExpr;
|
||||
}
|
||||
|
||||
public function GetGroupBy()
|
||||
{
|
||||
return $this->m_aGroupByExpr;
|
||||
}
|
||||
|
||||
public function GetCondition()
|
||||
{
|
||||
return $this->m_oConditionExpr;
|
||||
@@ -753,6 +1249,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);
|
||||
@@ -760,6 +1270,13 @@ class QueryBuilderExpressions
|
||||
{
|
||||
$oExpr->GetUnresolvedFields($sAlias, $aUnresolved);
|
||||
}
|
||||
if ($this->m_aGroupByExpr)
|
||||
{
|
||||
foreach($this->m_aGroupByExpr as $sColAlias => $oExpr)
|
||||
{
|
||||
$oExpr->GetUnresolvedFields($sAlias, $aUnresolved);
|
||||
}
|
||||
}
|
||||
foreach($this->m_aJoinFields as $oExpression)
|
||||
{
|
||||
$oExpression->GetUnresolvedFields($sAlias, $aUnresolved);
|
||||
@@ -773,10 +1290,42 @@ class QueryBuilderExpressions
|
||||
{
|
||||
$this->m_aSelectExpr[$sColAlias] = $oExpr->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
|
||||
}
|
||||
if ($this->m_aGroupByExpr)
|
||||
{
|
||||
foreach($this->m_aGroupByExpr as $sColAlias => $oExpr)
|
||||
{
|
||||
$this->m_aGroupByExpr[$sColAlias] = $oExpr->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
|
||||
}
|
||||
}
|
||||
foreach($this->m_aJoinFields as $index => $oExpression)
|
||||
{
|
||||
$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)
|
||||
{
|
||||
$this->m_oConditionExpr->RenameParam($sOldName, $sNewName);
|
||||
foreach($this->m_aSelectExpr as $sColAlias => $oExpr)
|
||||
{
|
||||
$this->m_aSelectExpr[$sColAlias] = $oExpr->RenameParam($sOldName, $sNewName);
|
||||
}
|
||||
if ($this->m_aGroupByExpr)
|
||||
{
|
||||
foreach($this->m_aGroupByExpr as $sColAlias => $oExpr)
|
||||
{
|
||||
$this->m_aGroupByExpr[$sColAlias] = $oExpr->RenameParam($sOldName, $sNewName);
|
||||
}
|
||||
}
|
||||
foreach($this->m_aJoinFields as $index => $oExpression)
|
||||
{
|
||||
$this->m_aJoinFields[$index] = $oExpression->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
|
||||
*/
|
||||
|
||||
|
||||
@@ -50,21 +51,6 @@ abstract class FilterDefinition
|
||||
$this->ConsistencyCheck();
|
||||
}
|
||||
|
||||
public function OverloadParams($aParams)
|
||||
{
|
||||
foreach ($aParams as $sParam => $value)
|
||||
{
|
||||
if (!array_key_exists($sParam, $this->m_aParams))
|
||||
{
|
||||
throw new CoreException("Unknown attribute definition parameter '$sParam', please select a value in {".implode(", ", $this->m_aParams)."}");
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->m_aParams[$sParam] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// to be overloaded
|
||||
static protected function ListExpectedParams()
|
||||
{
|
||||
@@ -170,12 +156,13 @@ class FilterFromAttribute extends FilterDefinition
|
||||
return array_merge(parent::ListExpectedParams(), array("refattribute"));
|
||||
}
|
||||
|
||||
public function __construct($oRefAttribute, $aParam = array())
|
||||
public function __construct($oRefAttribute, $sSuffix = '')
|
||||
{
|
||||
// In this very specific case, the code is the one of the attribute
|
||||
// (this to get a very very simple syntax upon declaration)
|
||||
$aParam = array();
|
||||
$aParam["refattribute"] = $oRefAttribute;
|
||||
parent::__construct($oRefAttribute->GetCode(), $aParam);
|
||||
parent::__construct($oRefAttribute->GetCode().$sSuffix, $aParam);
|
||||
}
|
||||
|
||||
public function GetType() {return "Basic";}
|
||||
|
||||
@@ -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)
|
||||
@@ -191,15 +383,15 @@ class ExecutionKPI
|
||||
return $output[1] * 1024;
|
||||
}
|
||||
}
|
||||
|
||||
static public function memory_get_peak_usage($bRealUsage = false)
|
||||
{
|
||||
if (function_exists('memory_get_peak_usage'))
|
||||
{
|
||||
return memory_get_peak_usage($bRealUsage);
|
||||
}
|
||||
// PHP > 5.2.1 - this verb depends on a compilation option
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
@@ -59,8 +59,11 @@ class FileLog
|
||||
$hLogFile = @fopen($this->m_sFile, 'a');
|
||||
if ($hLogFile !== false)
|
||||
{
|
||||
flock($hLogFile, LOCK_EX);
|
||||
$sDate = date('Y-m-d H:i:s');
|
||||
fwrite($hLogFile, "$sDate | $sText\n");
|
||||
fflush($hLogFile);
|
||||
flock($hLogFile, LOCK_UN);
|
||||
fclose($hLogFile);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
32
core/metamodelmodifier.inc.php
Normal file
32
core/metamodelmodifier.inc.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?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/>
|
||||
|
||||
|
||||
/**
|
||||
* Any extension to hook the initialization of the metamodel
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
interface iOnClassInitialization
|
||||
{
|
||||
public function OnAfterClassInitialization($sClass);
|
||||
}
|
||||
|
||||
?>
|
||||
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
|
||||
|
||||
239
core/mutex.class.inc.php
Normal file
239
core/mutex.class.inc.php
Normal file
@@ -0,0 +1,239 @@
|
||||
<?php
|
||||
// Copyright (C) 2013-2016 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* 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-2016 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: names are server-wide!!! So let's make the name specific to this iTop instance
|
||||
$oConfig = utils::GetConfig(); // Will return an empty config when called during the setup
|
||||
$sDBName = $oConfig->GetDBName();
|
||||
$sDBSubname = $oConfig->GetDBSubname();
|
||||
$this->sName = 'itop.'.$sName;
|
||||
if (substr($sName, -strlen($sDBName.$sDBSubname)) != $sDBName.$sDBSubname)
|
||||
{
|
||||
// If the name supplied already ends with the expected suffix
|
||||
// don't add it twice, since the setup may try to detect an already
|
||||
// running cron job by its mutex, without knowing if the config already exists or not
|
||||
$this->sName .= $sDBName.$sDBSubname;
|
||||
}
|
||||
|
||||
$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)
|
||||
$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]++;
|
||||
}
|
||||
if (($res !== '1') && ($res !== '0'))
|
||||
{
|
||||
$sMsg = 'GET_LOCK('.$this->sName.', 0) returned: '.var_export($res, true).'. Expected values are: 0, 1 or null';
|
||||
IssueLog::Error($sMsg);
|
||||
throw new Exception($sMsg);
|
||||
}
|
||||
return ($res !== '0');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the mutex is locked WITHOUT TRYING TO ACQUIRE IT
|
||||
* @returns bool True if the mutex is in use, false otherwise
|
||||
*/
|
||||
public function IsLocked()
|
||||
{
|
||||
if ($this->bLocked)
|
||||
{
|
||||
return true; // Already acquired
|
||||
}
|
||||
if (self::$aAcquiredLocks[$this->sName] > 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
$res = $this->QueryToScalar("SELECT IS_FREE_LOCK('".$this->sName."')"); // IS_FREE_LOCK detects some error cases that IS_USED_LOCK do not detect
|
||||
if (is_null($res))
|
||||
{
|
||||
$sMsg = "MySQL Error, IS_FREE_LOCK('".$this->sName."') returned null. Error (".mysqli_errno($this->hDBLink).") = '".mysqli_error($this->hDBLink)."'";
|
||||
IssueLog::Error($sMsg);
|
||||
throw new Exception($sMsg);
|
||||
}
|
||||
else if ($res == '1')
|
||||
{
|
||||
// Lock is free
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
4
core/oql/build.bash
Executable file
4
core/oql/build.bash
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
php /usr/share/php/PHP/LexerGenerator/cli.php oql-lexer.plex
|
||||
php /usr/share/php/PHP/ParserGenerator/cli.php oql-parser.y
|
||||
|
||||
@@ -3,5 +3,6 @@
|
||||
# since they are used solely for constructing other files during the build process
|
||||
#
|
||||
build.cmd
|
||||
build.bash
|
||||
oql-lexer.plex
|
||||
oql-parser.y
|
||||
oql-parser.y
|
||||
|
||||
@@ -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
|
||||
@@ -104,155 +105,169 @@ class OQLLexerRaw
|
||||
if ($this->count >= strlen($this->data)) {
|
||||
return false; // end of input
|
||||
}
|
||||
do {
|
||||
$rules = array(
|
||||
'/^[ \t\n\r]+/',
|
||||
'/^SELECT/',
|
||||
'/^FROM/',
|
||||
'/^AS/',
|
||||
'/^WHERE/',
|
||||
'/^JOIN/',
|
||||
'/^ON/',
|
||||
'/^\//',
|
||||
'/^\\*/',
|
||||
'/^\\+/',
|
||||
'/^-/',
|
||||
'/^AND/',
|
||||
'/^OR/',
|
||||
'/^,/',
|
||||
'/^\\(/',
|
||||
'/^\\)/',
|
||||
'/^REGEXP/',
|
||||
'/^=/',
|
||||
'/^!=/',
|
||||
'/^>/',
|
||||
'/^</',
|
||||
'/^>=/',
|
||||
'/^<=/',
|
||||
'/^LIKE/',
|
||||
'/^NOT LIKE/',
|
||||
'/^IN/',
|
||||
'/^NOT IN/',
|
||||
'/^INTERVAL/',
|
||||
'/^IF/',
|
||||
'/^ELT/',
|
||||
'/^COALESCE/',
|
||||
'/^ISNULL/',
|
||||
'/^CONCAT/',
|
||||
'/^SUBSTR/',
|
||||
'/^TRIM/',
|
||||
'/^DATE/',
|
||||
'/^DATE_FORMAT/',
|
||||
'/^CURRENT_DATE/',
|
||||
'/^NOW/',
|
||||
'/^TIME/',
|
||||
'/^TO_DAYS/',
|
||||
'/^FROM_DAYS/',
|
||||
'/^YEAR/',
|
||||
'/^MONTH/',
|
||||
'/^DAY/',
|
||||
'/^HOUR/',
|
||||
'/^MINUTE/',
|
||||
'/^SECOND/',
|
||||
'/^DATE_ADD/',
|
||||
'/^DATE_SUB/',
|
||||
'/^ROUND/',
|
||||
'/^FLOOR/',
|
||||
'/^INET_ATON/',
|
||||
'/^INET_NTOA/',
|
||||
'/^[0-9]+|0x[0-9a-fA-F]+/',
|
||||
'/^\"([^\\\\\"]|\\\\\"|\\\\\\\\)*\"|'.chr(94).chr(39).'([^\\\\'.chr(39).']|\\\\'.chr(39).'|\\\\\\\\)*'.chr(39).'/',
|
||||
'/^([_a-zA-Z][_a-zA-Z0-9]*|`[^`]+`)/',
|
||||
'/^:([_a-zA-Z][_a-zA-Z0-9]*->[_a-zA-Z][_a-zA-Z0-9]*|[_a-zA-Z][_a-zA-Z0-9]*)/',
|
||||
'/^\\./',
|
||||
);
|
||||
$match = false;
|
||||
foreach ($rules as $index => $rule) {
|
||||
if (preg_match($rule, substr($this->data, $this->count), $yymatches)) {
|
||||
if ($match) {
|
||||
if (strlen($yymatches[0]) > strlen($match[0][0])) {
|
||||
$match = array($yymatches, $index); // matches, token
|
||||
}
|
||||
} else {
|
||||
$match = array($yymatches, $index);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$match) {
|
||||
throw new Exception('Unexpected input at line' . $this->line .
|
||||
': ' . $this->data[$this->count]);
|
||||
}
|
||||
$this->token = $match[1];
|
||||
$this->value = $match[0][0];
|
||||
$yysubmatches = $match[0];
|
||||
array_shift($yysubmatches);
|
||||
if (!$yysubmatches) {
|
||||
$yysubmatches = array();
|
||||
}
|
||||
$r = $this->{'yy_r1_' . $this->token}($yysubmatches);
|
||||
if ($r === null) {
|
||||
$this->count += strlen($this->value);
|
||||
$this->line += substr_count($this->value, "\n");
|
||||
// accept this token
|
||||
return true;
|
||||
} elseif ($r === true) {
|
||||
// we have changed state
|
||||
// process this token in the new state
|
||||
return $this->yylex();
|
||||
} elseif ($r === false) {
|
||||
$this->count += strlen($this->value);
|
||||
$this->line += substr_count($this->value, "\n");
|
||||
if ($this->count >= strlen($this->data)) {
|
||||
return false; // end of input
|
||||
}
|
||||
// skip this token
|
||||
continue;
|
||||
} else {
|
||||
$yy_yymore_patterns = array_slice($rules, $this->token, true);
|
||||
// yymore is needed
|
||||
do {
|
||||
if (!isset($yy_yymore_patterns[$this->token])) {
|
||||
throw new Exception('cannot do yymore for the last token');
|
||||
}
|
||||
$match = false;
|
||||
foreach ($yy_yymore_patterns[$this->token] as $index => $rule) {
|
||||
if (preg_match('/' . $rule . '/',
|
||||
substr($this->data, $this->count), $yymatches)) {
|
||||
$yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
|
||||
if ($match) {
|
||||
if (strlen($yymatches[0]) > strlen($match[0][0])) {
|
||||
$match = array($yymatches, $index); // matches, token
|
||||
}
|
||||
} else {
|
||||
$match = array($yymatches, $index);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$match) {
|
||||
throw new Exception('Unexpected input at line' . $this->line .
|
||||
': ' . $this->data[$this->count]);
|
||||
}
|
||||
$this->token = $match[1];
|
||||
$this->value = $match[0][0];
|
||||
$yysubmatches = $match[0];
|
||||
array_shift($yysubmatches);
|
||||
if (!$yysubmatches) {
|
||||
$yysubmatches = array();
|
||||
}
|
||||
$this->line = substr_count($this->value, "\n");
|
||||
$r = $this->{'yy_r1_' . $this->token}();
|
||||
} while ($r !== null || !$r);
|
||||
if ($r === true) {
|
||||
// we have changed state
|
||||
// process this token in the new state
|
||||
return $this->yylex();
|
||||
} else {
|
||||
// accept
|
||||
$this->count += strlen($this->value);
|
||||
$this->line += substr_count($this->value, "\n");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
do {
|
||||
$rules = array(
|
||||
'/\G[ \t\n\r]+/ ',
|
||||
'/\GSELECT/ ',
|
||||
'/\GFROM/ ',
|
||||
'/\GAS/ ',
|
||||
'/\GWHERE/ ',
|
||||
'/\GJOIN/ ',
|
||||
'/\GON/ ',
|
||||
'/\G\// ',
|
||||
'/\G\\*/ ',
|
||||
'/\G\\+/ ',
|
||||
'/\G-/ ',
|
||||
'/\GAND/ ',
|
||||
'/\GOR/ ',
|
||||
'/\G\\|/ ',
|
||||
'/\G&/ ',
|
||||
'/\G\\^/ ',
|
||||
'/\G<</ ',
|
||||
'/\G>>/ ',
|
||||
'/\G,/ ',
|
||||
'/\G\\(/ ',
|
||||
'/\G\\)/ ',
|
||||
'/\GREGEXP/ ',
|
||||
'/\G=/ ',
|
||||
'/\G!=/ ',
|
||||
'/\G>/ ',
|
||||
'/\G</ ',
|
||||
'/\G>=/ ',
|
||||
'/\G<=/ ',
|
||||
'/\GLIKE/ ',
|
||||
'/\GNOT LIKE/ ',
|
||||
'/\GIN/ ',
|
||||
'/\GNOT IN/ ',
|
||||
'/\GINTERVAL/ ',
|
||||
'/\GIF/ ',
|
||||
'/\GELT/ ',
|
||||
'/\GCOALESCE/ ',
|
||||
'/\GISNULL/ ',
|
||||
'/\GCONCAT/ ',
|
||||
'/\GSUBSTR/ ',
|
||||
'/\GTRIM/ ',
|
||||
'/\GDATE/ ',
|
||||
'/\GDATE_FORMAT/ ',
|
||||
'/\GCURRENT_DATE/ ',
|
||||
'/\GNOW/ ',
|
||||
'/\GTIME/ ',
|
||||
'/\GTO_DAYS/ ',
|
||||
'/\GFROM_DAYS/ ',
|
||||
'/\GYEAR/ ',
|
||||
'/\GMONTH/ ',
|
||||
'/\GDAY/ ',
|
||||
'/\GHOUR/ ',
|
||||
'/\GMINUTE/ ',
|
||||
'/\GSECOND/ ',
|
||||
'/\GDATE_ADD/ ',
|
||||
'/\GDATE_SUB/ ',
|
||||
'/\GROUND/ ',
|
||||
'/\GFLOOR/ ',
|
||||
'/\GINET_ATON/ ',
|
||||
'/\GINET_NTOA/ ',
|
||||
'/\GBELOW/ ',
|
||||
'/\GBELOW STRICT/ ',
|
||||
'/\GNOT BELOW/ ',
|
||||
'/\GNOT BELOW STRICT/ ',
|
||||
'/\GABOVE/ ',
|
||||
'/\GABOVE STRICT/ ',
|
||||
'/\GNOT ABOVE/ ',
|
||||
'/\GNOT ABOVE STRICT/ ',
|
||||
'/\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]*)/ ',
|
||||
'/\G\\./ ',
|
||||
);
|
||||
$match = false;
|
||||
foreach ($rules as $index => $rule) {
|
||||
if (preg_match($rule, substr($this->data, $this->count), $yymatches)) {
|
||||
if ($match) {
|
||||
if (strlen($yymatches[0]) > strlen($match[0][0])) {
|
||||
$match = array($yymatches, $index); // matches, token
|
||||
}
|
||||
} else {
|
||||
$match = array($yymatches, $index);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$match) {
|
||||
throw new Exception('Unexpected input at line ' . $this->line .
|
||||
': ' . $this->data[$this->count]);
|
||||
}
|
||||
$this->token = $match[1];
|
||||
$this->value = $match[0][0];
|
||||
$yysubmatches = $match[0];
|
||||
array_shift($yysubmatches);
|
||||
if (!$yysubmatches) {
|
||||
$yysubmatches = array();
|
||||
}
|
||||
$r = $this->{'yy_r1_' . $this->token}($yysubmatches);
|
||||
if ($r === null) {
|
||||
$this->count += strlen($this->value);
|
||||
$this->line += substr_count($this->value, "\n");
|
||||
// accept this token
|
||||
return true;
|
||||
} elseif ($r === true) {
|
||||
// we have changed state
|
||||
// process this token in the new state
|
||||
return $this->yylex();
|
||||
} elseif ($r === false) {
|
||||
$this->count += strlen($this->value);
|
||||
$this->line += substr_count($this->value, "\n");
|
||||
if ($this->count >= strlen($this->data)) {
|
||||
return false; // end of input
|
||||
}
|
||||
// skip this token
|
||||
continue;
|
||||
} else {
|
||||
$yy_yymore_patterns = array_slice($rules, $this->token, true);
|
||||
// yymore is needed
|
||||
do {
|
||||
if (!isset($yy_yymore_patterns[$this->token])) {
|
||||
throw new Exception('cannot do yymore for the last token');
|
||||
}
|
||||
$match = false;
|
||||
foreach ($yy_yymore_patterns[$this->token] as $index => $rule) {
|
||||
if (preg_match('/' . $rule . '/',
|
||||
$this->data, $yymatches, null, $this->count)) {
|
||||
$yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
|
||||
if ($match) {
|
||||
if (strlen($yymatches[0]) > strlen($match[0][0])) {
|
||||
$match = array($yymatches, $index); // matches, token
|
||||
}
|
||||
} else {
|
||||
$match = array($yymatches, $index);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$match) {
|
||||
throw new Exception('Unexpected input at line ' . $this->line .
|
||||
': ' . $this->data[$this->count]);
|
||||
}
|
||||
$this->token = $match[1];
|
||||
$this->value = $match[0][0];
|
||||
$yysubmatches = $match[0];
|
||||
array_shift($yysubmatches);
|
||||
if (!$yysubmatches) {
|
||||
$yysubmatches = array();
|
||||
}
|
||||
$this->line = substr_count($this->value, "\n");
|
||||
$r = $this->{'yy_r1_' . $this->token}();
|
||||
} while ($r !== null || !$r);
|
||||
if ($r === true) {
|
||||
// we have changed state
|
||||
// process this token in the new state
|
||||
return $this->yylex();
|
||||
} else {
|
||||
// accept
|
||||
$this->count += strlen($this->value);
|
||||
$this->line += substr_count($this->value, "\n");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} while (true);
|
||||
|
||||
} // end function
|
||||
@@ -325,229 +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::NUMVAL;
|
||||
$this->token = OQLParser::F_DATE_SUB;
|
||||
}
|
||||
function yy_r1_55($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::STRVAL;
|
||||
$this->token = OQLParser::F_ROUND;
|
||||
}
|
||||
function yy_r1_56($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NAME;
|
||||
$this->token = OQLParser::F_FLOOR;
|
||||
}
|
||||
function yy_r1_57($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::VARNAME;
|
||||
$this->token = OQLParser::F_INET_ATON;
|
||||
}
|
||||
function yy_r1_58($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_INET_NTOA;
|
||||
}
|
||||
function yy_r1_59($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::BELOW;
|
||||
}
|
||||
function yy_r1_60($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::BELOW_STRICT;
|
||||
}
|
||||
function yy_r1_61($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NOT_BELOW;
|
||||
}
|
||||
function yy_r1_62($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NOT_BELOW_STRICT;
|
||||
}
|
||||
function yy_r1_63($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::ABOVE;
|
||||
}
|
||||
function yy_r1_64($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::ABOVE_STRICT;
|
||||
}
|
||||
function yy_r1_65($yy_subpatterns)
|
||||
{
|
||||
|
||||
$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 = "!="
|
||||
@@ -132,7 +138,34 @@ f_round = "ROUND"
|
||||
f_floor = "FLOOR"
|
||||
f_inet_aton = "INET_ATON"
|
||||
f_inet_ntoa = "INET_NTOA"
|
||||
numval = /[0-9]+|0x[0-9a-fA-F]+/
|
||||
below = "BELOW"
|
||||
below_strict = "BELOW STRICT"
|
||||
not_below = "NOT BELOW"
|
||||
not_below_strict = "NOT BELOW STRICT"
|
||||
above = "ABOVE"
|
||||
above_strict = "ABOVE STRICT"
|
||||
not_above = "NOT ABOVE"
|
||||
not_above_strict = "NOT ABOVE STRICT"
|
||||
//
|
||||
// WARNING: there seems to be a bug in the Lexer about matching the longest pattern
|
||||
// when there are alternates in the regexp.
|
||||
//
|
||||
// For instance:
|
||||
// numval = /[0-9]+|0x[0-9a-fA-F]+/
|
||||
// Does not work: SELECT Toto WHERE name = 'Text0xCTest' => Fails because 0xC is recongnized as a numval (inside the string) instead of a strval !!
|
||||
//
|
||||
// Inserting a ^ after the alternate (see comment at the top of this file) does not work either
|
||||
// numval = /[0-9]+|'.chr(94).'0x[0-9a-fA-F]+/
|
||||
// SELECT Toto WHERE name = 'Text0xCTest' => works but
|
||||
// SELECT Toto WHERE id = 0xC => does not work, 'xC' is found as a name (apparently 0 is recognized as a numval and the remaining is a name !)
|
||||
//
|
||||
// numval = /([0-9]+|0x[0-9a-fA-F]+)/
|
||||
// Does not work either, the hexadecimal numbers are not matched properly
|
||||
// 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]*)/
|
||||
@@ -179,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;
|
||||
}
|
||||
@@ -302,6 +350,33 @@ f_inet_aton {
|
||||
f_inet_ntoa {
|
||||
$this->token = OQLParser::F_INET_NTOA;
|
||||
}
|
||||
below {
|
||||
$this->token = OQLParser::BELOW;
|
||||
}
|
||||
below_strict {
|
||||
$this->token = OQLParser::BELOW_STRICT;
|
||||
}
|
||||
not_below {
|
||||
$this->token = OQLParser::NOT_BELOW;
|
||||
}
|
||||
not_below_strict {
|
||||
$this->token = OQLParser::NOT_BELOW_STRICT;
|
||||
}
|
||||
above {
|
||||
$this->token = OQLParser::ABOVE;
|
||||
}
|
||||
above_strict {
|
||||
$this->token = OQLParser::ABOVE_STRICT;
|
||||
}
|
||||
not_above {
|
||||
$this->token = OQLParser::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
@@ -78,6 +78,14 @@ join_item(A) ::= JOIN class_name(X) ON join_condition(C).
|
||||
}
|
||||
|
||||
join_condition(A) ::= field_id(X) EQ field_id(Y). { A = new BinaryOqlExpression(X, '=', Y); }
|
||||
join_condition(A) ::= field_id(X) BELOW field_id(Y). { A = new BinaryOqlExpression(X, 'BELOW', Y); }
|
||||
join_condition(A) ::= field_id(X) BELOW_STRICT field_id(Y). { A = new BinaryOqlExpression(X, 'BELOW_STRICT', Y); }
|
||||
join_condition(A) ::= field_id(X) NOT_BELOW field_id(Y). { A = new BinaryOqlExpression(X, 'NOT_BELOW', Y); }
|
||||
join_condition(A) ::= field_id(X) NOT_BELOW_STRICT field_id(Y). { A = new BinaryOqlExpression(X, 'NOT_BELOW_STRICT', Y); }
|
||||
join_condition(A) ::= field_id(X) ABOVE field_id(Y). { A = new BinaryOqlExpression(X, 'ABOVE', Y); }
|
||||
join_condition(A) ::= field_id(X) ABOVE_STRICT field_id(Y). { A = new BinaryOqlExpression(X, 'ABOVE_STRICT', Y); }
|
||||
join_condition(A) ::= field_id(X) NOT_ABOVE field_id(Y). { A = new BinaryOqlExpression(X, 'NOT_ABOVE', Y); }
|
||||
join_condition(A) ::= field_id(X) NOT_ABOVE_STRICT field_id(Y). { A = new BinaryOqlExpression(X, 'NOT_ABOVE_STRICT', Y); }
|
||||
|
||||
condition(A) ::= expression_prio4(X). { A = X; }
|
||||
|
||||
@@ -156,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;}
|
||||
@@ -182,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
|
||||
*/
|
||||
|
||||
|
||||
@@ -50,7 +50,13 @@ class OQLException extends CoreException
|
||||
parent::__construct($sMessage, 0);
|
||||
}
|
||||
|
||||
public function getHtmlDesc($sHighlightHtmlBegin = '<b>', $sHighlightHtmlEnd = '</b>')
|
||||
public function GetUserFriendlyDescription()
|
||||
{
|
||||
// Todo - translate all errors!
|
||||
return $this->getMessage();
|
||||
}
|
||||
|
||||
public function getHtmlDesc($sHighlightHtmlBegin = '<span style="font-weight: bolder">', $sHighlightHtmlEnd = '</span>')
|
||||
{
|
||||
$sRet = htmlentities($this->m_MyIssue.", found '".$this->m_sUnexpected."' in: ", ENT_QUOTES, 'UTF-8');
|
||||
$sRet .= htmlentities(substr($this->m_sInput, 0, $this->m_iCol), ENT_QUOTES, 'UTF-8');
|
||||
@@ -59,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)
|
||||
{
|
||||
@@ -71,7 +80,27 @@ class OQLException extends CoreException
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
static protected function FindClosestString($sInput, $aDictionary)
|
||||
public function GetIssue()
|
||||
{
|
||||
return $this->m_MyIssue;
|
||||
}
|
||||
|
||||
public function GetSuggestions()
|
||||
{
|
||||
return $this->m_aExpecting;
|
||||
}
|
||||
|
||||
public function GetWrongWord()
|
||||
{
|
||||
return $this->m_sUnexpected;
|
||||
}
|
||||
|
||||
public function GetColumn()
|
||||
{
|
||||
return $this->m_iCol;
|
||||
}
|
||||
|
||||
static public function FindClosestString($sInput, $aDictionary)
|
||||
{
|
||||
// no shortest distance found, yet
|
||||
$fShortest = -1;
|
||||
@@ -88,7 +117,7 @@ class OQLException extends CoreException
|
||||
return $sSuggestion;
|
||||
}
|
||||
|
||||
if ($fShortest < 0 || ($fDist < 4 && $fDist <= $fShortest))
|
||||
if (($fDist <= 3) && ($fShortest < 0 || $fDist <= $fShortest))
|
||||
{
|
||||
// set the closest match, and shortest distance
|
||||
$sRet = $sSuggestion;
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
|
||||
@@ -31,6 +32,28 @@ class OqlNormalizeException extends OQLException
|
||||
parent::__construct($sIssue, $sInput, 0, $oName->GetPos(), $oName->GetValue(), $aExpecting);
|
||||
}
|
||||
}
|
||||
class UnknownClassOqlException extends OqlNormalizeException
|
||||
{
|
||||
public function __construct($sInput, OqlName $oName, $aExpecting = null)
|
||||
{
|
||||
parent::__construct('Unknown class', $sInput, $oName, $aExpecting);
|
||||
}
|
||||
|
||||
public function GetUserFriendlyDescription()
|
||||
{
|
||||
$sWrongClass = $this->GetWrongWord();
|
||||
$sSuggest = self::FindClosestString($sWrongClass, $this->GetSuggestions());
|
||||
|
||||
if ($sSuggest != '')
|
||||
{
|
||||
return Dict::Format('UI:OQL:UnknownClassAndFix', $sWrongClass, $sSuggest);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Dict::Format('UI:OQL:UnknownClassNoFix', $sWrongClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class OqlInterpreterException extends OQLException
|
||||
{
|
||||
|
||||
@@ -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,12 +54,34 @@ 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;
|
||||
protected $m_oClassAlias;
|
||||
protected $m_oLeftField;
|
||||
protected $m_oRightField;
|
||||
protected $m_sOperator;
|
||||
|
||||
protected $m_oNextJoinspec;
|
||||
|
||||
@@ -68,6 +91,8 @@ class OqlJoinSpec
|
||||
$this->m_oClassAlias = $oClassAlias;
|
||||
$this->m_oLeftField = $oExpression->GetLeftExpr();
|
||||
$this->m_oRightField = $oExpression->GetRightExpr();
|
||||
$this->m_oRightField = $oExpression->GetRightExpr();
|
||||
$this->m_sOperator = $oExpression->GetOperator();
|
||||
}
|
||||
|
||||
public function GetClass()
|
||||
@@ -96,17 +121,44 @@ class OqlJoinSpec
|
||||
{
|
||||
return $this->m_oRightField;
|
||||
}
|
||||
public function GetOperator()
|
||||
{
|
||||
return $this->m_sOperator;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -132,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
|
||||
@@ -206,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,31 +1,35 @@
|
||||
<?php
|
||||
// Copyright (C) 2011 Combodo SARL
|
||||
// Copyright (C) 2010-2015 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-2015 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
class ormCaseLog {
|
||||
protected $m_sLog;
|
||||
protected $m_aIndex;
|
||||
protected $m_bModified;
|
||||
|
||||
/**
|
||||
* Initializes the log with the first (initial) entry
|
||||
@@ -36,6 +40,7 @@ class ormCaseLog {
|
||||
{
|
||||
$this->m_sLog = $sLog;
|
||||
$this->m_aIndex = $aIndex;
|
||||
$this->m_bModified = false;
|
||||
}
|
||||
|
||||
public function GetText()
|
||||
@@ -43,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;
|
||||
@@ -53,13 +132,103 @@ class ormCaseLog {
|
||||
return $this->m_sLog;
|
||||
}
|
||||
|
||||
public function ClearModifiedFlag()
|
||||
{
|
||||
$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
|
||||
$iPos = 0;
|
||||
for($index=count($this->m_aIndex)-1 ; $index >= 0 ; $index--)
|
||||
$aIndex = $this->m_aIndex;
|
||||
if (($bEditMode) && (count($aIndex) > 0) && $this->m_bModified)
|
||||
{
|
||||
if ($index < count($this->m_aIndex) - CASELOG_VISIBLE_ITEMS)
|
||||
// Don't display the first element, that is still considered as editable
|
||||
$iPos = $aIndex[0]['separator_length'] + $aIndex[0]['text_length'];
|
||||
array_shift($aIndex);
|
||||
}
|
||||
for($index=count($aIndex)-1 ; $index >= 0 ; $index--)
|
||||
{
|
||||
if ($index < count($aIndex) - CASELOG_VISIBLE_ITEMS)
|
||||
{
|
||||
$sOpen = '';
|
||||
$sDisplay = 'style="display:none;"';
|
||||
@@ -69,17 +238,39 @@ class ormCaseLog {
|
||||
$sOpen = ' open';
|
||||
$sDisplay = '';
|
||||
}
|
||||
$iPos += $this->m_aIndex[$index]['separator_length'];
|
||||
$sTextEntry = substr($this->m_sLog, $iPos, $this->m_aIndex[$index]['text_length']);
|
||||
$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'));
|
||||
if (!is_null($aTransfoHandler))
|
||||
{
|
||||
$sTextEntry = call_user_func($aTransfoHandler, $sTextEntry);
|
||||
}
|
||||
$iPos += $this->m_aIndex[$index]['text_length'];
|
||||
$iPos += $aIndex[$index]['text_length'];
|
||||
|
||||
$sEntry = '<div class="caselog_header'.$sOpen.'">';
|
||||
$sEntry .= sprintf(Dict::S('UI:CaseLog:Header_Date_UserName'), $this->m_aIndex[$index]['date']->format(Dict::S('UI:CaseLog:DateFormat')), $this->m_aIndex[$index]['user_name']);
|
||||
// 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'), $sDate, $aIndex[$index]['user_name']);
|
||||
$sEntry .= '</div>';
|
||||
$sEntry .= '<div class="caselog_entry"'.$sDisplay.'>';
|
||||
$sEntry .= $sTextEntry;
|
||||
@@ -128,23 +319,141 @@ class ormCaseLog {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new entry to the log and updates the internal index
|
||||
* Add a new entry to the log or merge the given text into the currently modified entry
|
||||
* and updates the internal index
|
||||
* @param $sText string The text of the new entry
|
||||
*/
|
||||
public function AddLogEntry($sText)
|
||||
public function AddLogEntry($sText, $sOnBehalfOf = '')
|
||||
{
|
||||
$bMergeEntries = false;
|
||||
$sDate = date(Dict::S('UI:CaseLog:DateFormat'));
|
||||
$sSeparator = sprintf(CASELOG_SEPARATOR, $sDate, UserRights::GetUserFriendlyName(), UserRights::GetUserId());
|
||||
if ($sOnBehalfOf == '')
|
||||
{
|
||||
$sOnBehalfOf = UserRights::GetUserFriendlyName();
|
||||
$iUserId = UserRights::GetUserId();
|
||||
}
|
||||
else
|
||||
{
|
||||
$iUserId = null;
|
||||
}
|
||||
if ($this->m_bModified)
|
||||
{
|
||||
$aLatestEntry = end($this->m_aIndex);
|
||||
if ($aLatestEntry['user_name'] != $sOnBehalfOf)
|
||||
{
|
||||
$bMergeEntries = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
$bMergeEntries = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($bMergeEntries)
|
||||
{
|
||||
$aLatestEntry = end($this->m_aIndex);
|
||||
$this->m_sLog = substr($this->m_sLog, $aLatestEntry['separator_length']);
|
||||
$sSeparator = sprintf(CASELOG_SEPARATOR, $sDate, $sOnBehalfOf, $iUserId);
|
||||
$iSepLength = strlen($sSeparator);
|
||||
$iTextlength = strlen($sText."\n");
|
||||
$this->m_sLog = $sSeparator.$sText.$this->m_sLog; // Latest entry printed first
|
||||
$this->m_aIndex[] = array(
|
||||
'user_name' => $sOnBehalfOf,
|
||||
'user_id' => $iUserId,
|
||||
'date' => time(),
|
||||
'text_length' => $aLatestEntry['text_length'] + $iTextlength,
|
||||
'separator_length' => $iSepLength,
|
||||
);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
$sSeparator = sprintf(CASELOG_SEPARATOR, $sDate, $sOnBehalfOf, $iUserId);
|
||||
$iSepLength = strlen($sSeparator);
|
||||
$iTextlength = strlen($sText);
|
||||
$this->m_sLog = $sSeparator.$sText.$this->m_sLog; // Latest entry printed first
|
||||
$this->m_aIndex[] = array(
|
||||
'user_name' => $sOnBehalfOf,
|
||||
'user_id' => $iUserId,
|
||||
'date' => time(),
|
||||
'text_length' => $iTextlength,
|
||||
'separator_length' => $iSepLength,
|
||||
);
|
||||
}
|
||||
$this->m_bModified = true;
|
||||
}
|
||||
|
||||
|
||||
public function AddLogEntryFromJSON($oJson, $bCheckUserId = true)
|
||||
{
|
||||
$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);
|
||||
}
|
||||
if ($bCheckUserId && ($oJson->user_id != 0))
|
||||
{
|
||||
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 = $oJson->user_id;
|
||||
$sOnBehalfOf = $oJson->user_login;
|
||||
}
|
||||
}
|
||||
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' => UserRights::GetUserFriendlyName(),
|
||||
'user_id' => UserRights::GetUserId(),
|
||||
'date' => new DateTime(),
|
||||
'user_name' => $sOnBehalfOf,
|
||||
'user_id' => $iUserId,
|
||||
'date' => $iDate,
|
||||
'text_length' => $iTextlength,
|
||||
'separator_length' => $iSepLength,
|
||||
);
|
||||
|
||||
$this->m_bModified = true;
|
||||
}
|
||||
|
||||
|
||||
public function GetModifiedEntry()
|
||||
{
|
||||
$sModifiedEntry = '';
|
||||
if ($this->m_bModified)
|
||||
{
|
||||
$sModifiedEntry = $this->GetLatestEntry();
|
||||
}
|
||||
return $sModifiedEntry;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -153,8 +462,7 @@ class ormCaseLog {
|
||||
*/
|
||||
public function GetLatestEntry()
|
||||
{
|
||||
$iLast = count($this->m_aIndex) - 1;
|
||||
$aLastEntry = $this->m_aIndex[$iLast];
|
||||
$aLastEntry = end($this->m_aIndex);
|
||||
$sRes = substr($this->m_sLog, $aLastEntry['separator_length'], $aLastEntry['text_length']);
|
||||
return $sRes;
|
||||
}
|
||||
@@ -165,8 +473,9 @@ class ormCaseLog {
|
||||
*/
|
||||
public function GetLatestEntryIndex()
|
||||
{
|
||||
$iLast = count($this->m_aIndex) - 1;
|
||||
$aKeys = array_keys($this->m_aIndex);
|
||||
$iLast = end($aKeys); // Strict standards: the parameter passed to 'end' must be a variable since it is passed by reference
|
||||
return $iLast;
|
||||
}
|
||||
}
|
||||
?>
|
||||
?>
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
|
||||
@@ -105,7 +106,7 @@ class ormDocument
|
||||
*/
|
||||
public function GetDisplayLink($sClass, $Id, $sAttCode)
|
||||
{
|
||||
return "<a href=\"../pages/ajax.render.php?operation=display_document&class=$sClass&id=$Id&field=$sAttCode\" target=\"_blank\" >".$this->GetFileName()."</a>\n";
|
||||
return "<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=display_document&class=$sClass&id=$Id&field=$sAttCode\" target=\"_blank\" >".$this->GetFileName()."</a>\n";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -114,7 +115,34 @@ class ormDocument
|
||||
*/
|
||||
public function GetDownloadLink($sClass, $Id, $sAttCode)
|
||||
{
|
||||
return "<a href=\"../pages/ajax.render.php?operation=download_document&class=$sClass&id=$Id&field=$sAttCode\">".$this->GetFileName()."</a>\n";
|
||||
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
|
||||
*/
|
||||
|
||||
|
||||
579
core/ormstopwatch.class.inc.php
Normal file
579
core/ormstopwatch.class.inc.php
Normal file
@@ -0,0 +1,579 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2014 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
require_once('backgroundprocess.inc.php');
|
||||
|
||||
/**
|
||||
* ormStopWatch
|
||||
* encapsulate the behavior of a stop watch that will be stored as an attribute of class AttributeStopWatch
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* ormStopWatch
|
||||
* encapsulate the behavior of a stop watch that will be stored as an attribute of class AttributeStopWatch
|
||||
*
|
||||
* @package itopORM
|
||||
*/
|
||||
class ormStopWatch
|
||||
{
|
||||
protected $iTimeSpent; // seconds
|
||||
protected $iStarted; // unix time (seconds)
|
||||
protected $iLastStart; // unix time (seconds)
|
||||
protected $iStopped; // unix time (seconds)
|
||||
protected $aThresholds;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct($iTimeSpent = 0, $iStarted = null, $iLastStart = null, $iStopped = null)
|
||||
{
|
||||
$this->iTimeSpent = (int) $iTimeSpent;
|
||||
$this->iStarted = $iStarted;
|
||||
$this->iLastStart = $iLastStart;
|
||||
$this->iStopped = $iStopped;
|
||||
|
||||
$this->aThresholds = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Necessary for the triggers
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return (string) $this->iTimeSpent;
|
||||
}
|
||||
|
||||
public function DefineThreshold($iPercent, $tDeadline = null, $bPassed = false, $bTriggered = false, $iOverrun = null, $aHighlightDef = null)
|
||||
{
|
||||
$this->aThresholds[$iPercent] = array(
|
||||
'deadline' => $tDeadline, // unix time (seconds)
|
||||
'triggered' => $bTriggered,
|
||||
'overrun' => $iOverrun,
|
||||
'highlight' => $aHighlightDef, // array('code' => string, 'persistent' => boolean)
|
||||
);
|
||||
}
|
||||
|
||||
public function MarkThresholdAsTriggered($iPercent)
|
||||
{
|
||||
$this->aThresholds[$iPercent]['triggered'] = true;
|
||||
}
|
||||
|
||||
public function GetTimeSpent()
|
||||
{
|
||||
return $this->iTimeSpent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the working elapsed time since the start of the stop watch
|
||||
* even if it is currently running
|
||||
* @param oAttDef AttributeDefinition Attribute hosting the stop watch
|
||||
* @param oObject Hosting object (used for query parameters)
|
||||
*/
|
||||
public function GetElapsedTime($oAttDef, $oObject)
|
||||
{
|
||||
if (is_null($this->iLastStart))
|
||||
{
|
||||
return $this->GetTimeSpent();
|
||||
}
|
||||
else
|
||||
{
|
||||
$iElapsed = $this->ComputeDuration($oObject, $oAttDef, $this->iLastStart, time());
|
||||
return $this->iTimeSpent + $iElapsed;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function GetStartDate()
|
||||
{
|
||||
return $this->iStarted;
|
||||
}
|
||||
|
||||
public function GetLastStartDate()
|
||||
{
|
||||
return $this->iLastStart;
|
||||
}
|
||||
|
||||
public function GetStopDate()
|
||||
{
|
||||
return $this->iStopped;
|
||||
}
|
||||
|
||||
public function GetThresholdDate($iPercent)
|
||||
{
|
||||
if (array_key_exists($iPercent, $this->aThresholds))
|
||||
{
|
||||
return $this->aThresholds[$iPercent]['deadline'];
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function GetOverrun($iPercent)
|
||||
{
|
||||
if (array_key_exists($iPercent, $this->aThresholds))
|
||||
{
|
||||
return $this->aThresholds[$iPercent]['overrun'];
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public function IsThresholdPassed($iPercent)
|
||||
{
|
||||
$bRet = false;
|
||||
if (array_key_exists($iPercent, $this->aThresholds))
|
||||
{
|
||||
$aThresholdData = $this->aThresholds[$iPercent];
|
||||
if (!is_null($aThresholdData['deadline']) && ($aThresholdData['deadline'] <= time()))
|
||||
{
|
||||
$bRet = true;
|
||||
}
|
||||
if (isset($aThresholdData['overrun']) && ($aThresholdData['overrun'] > 0))
|
||||
{
|
||||
$bRet = true;
|
||||
}
|
||||
}
|
||||
return $bRet;
|
||||
}
|
||||
public function IsThresholdTriggered($iPercent)
|
||||
{
|
||||
if (array_key_exists($iPercent, $this->aThresholds))
|
||||
{
|
||||
return $this->aThresholds[$iPercent]['triggered'];
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function GetHighlightCode()
|
||||
{
|
||||
$sCode = '';
|
||||
// Process the thresholds in ascending order
|
||||
$aPercents = array();
|
||||
foreach($this->aThresholds as $iPercent => $aDefs)
|
||||
{
|
||||
$aPercents[] = $iPercent;
|
||||
}
|
||||
sort($aPercents, SORT_NUMERIC);
|
||||
foreach($aPercents as $iPercent)
|
||||
{
|
||||
$aDefs = $this->aThresholds[$iPercent];
|
||||
if (array_key_exists('highlight', $aDefs) && is_array($aDefs['highlight']) && $this->IsThresholdPassed($iPercent))
|
||||
{
|
||||
// If persistant or SW running...
|
||||
if (($aDefs['highlight']['persistent'] == true) || (($aDefs['highlight']['persistent'] == false) && !is_null($this->iLastStart)))
|
||||
{
|
||||
$sCode = $aDefs['highlight']['code'];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $sCode;
|
||||
}
|
||||
|
||||
public function GetAsHTML($oAttDef, $oHostObject = null)
|
||||
{
|
||||
$aProperties = array();
|
||||
|
||||
$aProperties['States'] = implode(', ', $oAttDef->GetStates());
|
||||
|
||||
if (is_null($this->iLastStart))
|
||||
{
|
||||
if (is_null($this->iStarted))
|
||||
{
|
||||
$aProperties['Elapsed'] = 'never started';
|
||||
}
|
||||
else
|
||||
{
|
||||
$aProperties['Elapsed'] = $this->iTimeSpent.' s';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$aProperties['Elapsed'] = 'running <img src="../images/indicator.gif">';
|
||||
}
|
||||
|
||||
$aProperties['Started'] = $oAttDef->SecondsToDate($this->iStarted);
|
||||
$aProperties['LastStart'] = $oAttDef->SecondsToDate($this->iLastStart);
|
||||
$aProperties['Stopped'] = $oAttDef->SecondsToDate($this->iStopped);
|
||||
|
||||
foreach ($this->aThresholds as $iPercent => $aThresholdData)
|
||||
{
|
||||
$sThresholdDesc = $oAttDef->SecondsToDate($aThresholdData['deadline']);
|
||||
if ($aThresholdData['triggered'])
|
||||
{
|
||||
$sThresholdDesc .= " <b>TRIGGERED</b>";
|
||||
}
|
||||
if ($aThresholdData['overrun'])
|
||||
{
|
||||
$sThresholdDesc .= " Overrun:".(int) $aThresholdData['overrun']." sec.";
|
||||
}
|
||||
$aProperties[$iPercent.'%'] = $sThresholdDesc;
|
||||
}
|
||||
$sRes = "<TABLE>";
|
||||
$sRes .= "<TBODY>";
|
||||
foreach ($aProperties as $sProperty => $sValue)
|
||||
{
|
||||
$sRes .= "<TR>";
|
||||
$sCell = str_replace("\n", "<br>\n", $sValue);
|
||||
$sRes .= "<TD class=\"label\">$sProperty</TD><TD>$sCell</TD>";
|
||||
$sRes .= "</TR>";
|
||||
}
|
||||
$sRes .= "</TBODY>";
|
||||
$sRes .= "</TABLE>";
|
||||
return $sRes;
|
||||
}
|
||||
|
||||
protected function ComputeGoal($oObject, $oAttDef)
|
||||
{
|
||||
$sMetricComputer = $oAttDef->Get('goal_computing');
|
||||
$oComputer = new $sMetricComputer();
|
||||
$aCallSpec = array($oComputer, 'ComputeMetric');
|
||||
if (!is_callable($aCallSpec))
|
||||
{
|
||||
throw new CoreException("Unknown class/verb '$sMetricComputer/ComputeMetric'");
|
||||
}
|
||||
$iRet = call_user_func($aCallSpec, $oObject);
|
||||
return $iRet;
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
//throw new CoreException("Pas de constructeur pour $sWorkingTimeComputer!");
|
||||
}
|
||||
$oComputer = new $sWorkingTimeComputer();
|
||||
$aCallSpec = array($oComputer, 'GetDeadline');
|
||||
if (!is_callable($aCallSpec))
|
||||
{
|
||||
throw new CoreException("Unknown class/verb '$sWorkingTimeComputer/GetDeadline'");
|
||||
}
|
||||
// GetDeadline($oObject, $iDuration, DateTime $oStartDate)
|
||||
$oStartDate = new DateTime('@'.$iStartTime); // setTimestamp not available in PHP 5.2
|
||||
$oDeadline = call_user_func($aCallSpec, $oObject, $iDurationSec, $oStartDate);
|
||||
$iRet = $oDeadline->format('U');
|
||||
return $iRet;
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
throw new CoreException("Unknown class/verb '$sWorkingTimeComputer/GetOpenDuration'");
|
||||
}
|
||||
// GetOpenDuration($oObject, DateTime $oStartDate, DateTime $oEndDate)
|
||||
$oStartDate = new DateTime('@'.$iStartTime); // setTimestamp not available in PHP 5.2
|
||||
$oEndDate = new DateTime('@'.$iEndTime);
|
||||
$iRet = call_user_func($aCallSpec, $oObject, $oStartDate, $oEndDate);
|
||||
return $iRet;
|
||||
}
|
||||
|
||||
public function Reset($oObject, $oAttDef)
|
||||
{
|
||||
$this->iTimeSpent = 0;
|
||||
$this->iStopped = null;
|
||||
$this->iStarted = null;
|
||||
|
||||
foreach ($this->aThresholds as $iPercent => &$aThresholdData)
|
||||
{
|
||||
$aThresholdData['triggered'] = false;
|
||||
$aThresholdData['overrun'] = null;
|
||||
}
|
||||
|
||||
if (!is_null($this->iLastStart))
|
||||
{
|
||||
// Currently running... starting again from now!
|
||||
$this->iStarted = time();
|
||||
$this->iLastStart = time();
|
||||
$this->ComputeDeadlines($oObject, $oAttDef);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start or continue
|
||||
* It is the responsibility of the caller to compute the deadlines
|
||||
* (to avoid computing twice for the same result)
|
||||
*/
|
||||
public function Start($oObject, $oAttDef, $iNow = null)
|
||||
{
|
||||
if (!is_null($this->iLastStart))
|
||||
{
|
||||
// Already started
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_null($iNow))
|
||||
{
|
||||
$iNow = time();
|
||||
}
|
||||
|
||||
if (is_null($this->iStarted))
|
||||
{
|
||||
$this->iStarted = $iNow;
|
||||
}
|
||||
$this->iLastStart = $iNow;
|
||||
$this->iStopped = null;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute or recompute the goal and threshold deadlines
|
||||
*/
|
||||
public function ComputeDeadlines($oObject, $oAttDef)
|
||||
{
|
||||
if (is_null($this->iLastStart))
|
||||
{
|
||||
// Currently stopped - do nothing
|
||||
return false;
|
||||
}
|
||||
|
||||
$iDurationGoal = $this->ComputeGoal($oObject, $oAttDef);
|
||||
$iComputationRefTime = time();
|
||||
foreach ($this->aThresholds as $iPercent => &$aThresholdData)
|
||||
{
|
||||
if (is_null($iDurationGoal))
|
||||
{
|
||||
// No limit: leave null thresholds
|
||||
$aThresholdData['deadline'] = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
$iThresholdDuration = round($iPercent * $iDurationGoal / 100);
|
||||
|
||||
if (class_exists('WorkingTimeRecorder'))
|
||||
{
|
||||
$sClass = get_class($oObject);
|
||||
$sAttCode = $oAttDef->GetCode();
|
||||
WorkingTimeRecorder::Start($oObject, $iComputationRefTime, "ormStopWatch-Deadline-$iPercent-$sAttCode", 'Core:ExplainWTC:StopWatch-Deadline', array("Class:$sClass/Attribute:$sAttCode", $iPercent));
|
||||
}
|
||||
$aThresholdData['deadline'] = $this->ComputeDeadline($oObject, $oAttDef, $this->iLastStart, $iThresholdDuration - $this->iTimeSpent);
|
||||
// OR $aThresholdData['deadline'] = $this->ComputeDeadline($oObject, $oAttDef, $this->iStarted, $iThresholdDuration);
|
||||
|
||||
if (class_exists('WorkingTimeRecorder'))
|
||||
{
|
||||
WorkingTimeRecorder::End();
|
||||
}
|
||||
}
|
||||
if (is_null($aThresholdData['deadline']) || ($aThresholdData['deadline'] > time()))
|
||||
{
|
||||
// The threshold is in the future, reset
|
||||
$aThresholdData['triggered'] = false;
|
||||
$aThresholdData['overrun'] = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The new threshold is in the past
|
||||
// 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!!!
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop counting if not already done
|
||||
*/
|
||||
public function Stop($oObject, $oAttDef)
|
||||
{
|
||||
if (is_null($this->iLastStart))
|
||||
{
|
||||
// Already stopped
|
||||
return false;
|
||||
}
|
||||
|
||||
if (class_exists('WorkingTimeRecorder'))
|
||||
{
|
||||
$sClass = get_class($oObject);
|
||||
$sAttCode = $oAttDef->GetCode();
|
||||
WorkingTimeRecorder::Start($oObject, time(), "ormStopWatch-TimeSpent-$sAttCode", 'Core:ExplainWTC:StopWatch-TimeSpent', array("Class:$sClass/Attribute:$sAttCode"), true /*cumulative*/);
|
||||
}
|
||||
$iElapsed = $this->ComputeDuration($oObject, $oAttDef, $this->iLastStart, time());
|
||||
$this->iTimeSpent = $this->iTimeSpent + $iElapsed;
|
||||
if (class_exists('WorkingTimeRecorder'))
|
||||
{
|
||||
WorkingTimeRecorder::End();
|
||||
}
|
||||
|
||||
foreach ($this->aThresholds as $iPercent => &$aThresholdData)
|
||||
{
|
||||
if (!is_null($aThresholdData['deadline']) && (time() > $aThresholdData['deadline']))
|
||||
{
|
||||
if ($aThresholdData['overrun'] > 0)
|
||||
{
|
||||
// Accumulate from last start
|
||||
$aThresholdData['overrun'] += $iElapsed;
|
||||
}
|
||||
else
|
||||
{
|
||||
// First stop after the deadline has been passed
|
||||
$iOverrun = $this->ComputeDuration($oObject, $oAttDef, $aThresholdData['deadline'], time());
|
||||
$aThresholdData['overrun'] = $iOverrun;
|
||||
}
|
||||
}
|
||||
$aThresholdData['deadline'] = null;
|
||||
}
|
||||
|
||||
$this->iLastStart = null;
|
||||
$this->iStopped = time();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* CheckStopWatchThresholds
|
||||
* Implements the automatic actions
|
||||
*
|
||||
* @package itopORM
|
||||
*/
|
||||
class CheckStopWatchThresholds implements iBackgroundProcess
|
||||
{
|
||||
public function GetPeriodicity()
|
||||
{
|
||||
return 10; // seconds
|
||||
}
|
||||
|
||||
public function Process($iTimeLimit)
|
||||
{
|
||||
$aList = array();
|
||||
foreach (MetaModel::GetClasses() as $sClass)
|
||||
{
|
||||
foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
|
||||
{
|
||||
if ($oAttDef instanceof AttributeStopWatch)
|
||||
{
|
||||
foreach ($oAttDef->ListThresholds() as $iThreshold => $aThresholdData)
|
||||
{
|
||||
$iPercent = $aThresholdData['percent']; // could be different than the index !
|
||||
|
||||
$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);
|
||||
$oSet = new DBObjectSet($oFilter);
|
||||
while ((time() < $iTimeLimit) && ($oObj = $oSet->Fetch()))
|
||||
{
|
||||
$sClass = get_class($oObj);
|
||||
|
||||
$aList[] = $sClass.'::'.$oObj->GetKey().' '.$sAttCode.' '.$iThreshold;
|
||||
|
||||
// Execute planned actions
|
||||
//
|
||||
foreach ($aThresholdData['actions'] as $aActionData)
|
||||
{
|
||||
$sVerb = $aActionData['verb'];
|
||||
$aParams = $aActionData['params'];
|
||||
$aValues = array();
|
||||
foreach($aParams as $def)
|
||||
{
|
||||
if (is_string($def))
|
||||
{
|
||||
// Old method (pre-2.1.0) non typed parameters
|
||||
$aValues[] = $def;
|
||||
}
|
||||
else // if(is_array($def))
|
||||
{
|
||||
$sParamType = array_key_exists('type', $def) ? $def['type'] : 'string';
|
||||
switch($sParamType)
|
||||
{
|
||||
case 'int':
|
||||
$value = (int)$def['value'];
|
||||
break;
|
||||
|
||||
case 'float':
|
||||
$value = (float)$def['value'];
|
||||
break;
|
||||
|
||||
case 'bool':
|
||||
$value = (bool)$def['value'];
|
||||
break;
|
||||
|
||||
case 'reference':
|
||||
$value = ${$def['value']};
|
||||
break;
|
||||
|
||||
case 'string':
|
||||
default:
|
||||
$value = (string)$def['value'];
|
||||
}
|
||||
$aValues[] = $value;
|
||||
}
|
||||
}
|
||||
$aCallSpec = array($oObj, $sVerb);
|
||||
call_user_func_array($aCallSpec, $aValues);
|
||||
}
|
||||
|
||||
// Mark the threshold as "triggered"
|
||||
//
|
||||
$oSW = $oObj->Get($sAttCode);
|
||||
$oSW->MarkThresholdAsTriggered($iThreshold);
|
||||
$oObj->Set($sAttCode, $oSW);
|
||||
|
||||
if($oObj->IsModified())
|
||||
{
|
||||
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));
|
||||
$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 = $oTriggerSet->Fetch())
|
||||
{
|
||||
$oTrigger->DoActivate($oObj->ToArgs('this'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$iProcessed = count($aList);
|
||||
return "Triggered $iProcessed threshold(s):".implode(", ", $aList);
|
||||
}
|
||||
}
|
||||
74
core/querybuildercontext.class.inc.php
Normal file
74
core/querybuildercontext.class.inc.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?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/>
|
||||
|
||||
/**
|
||||
* Associated with the metamodel -> MakeQuery/MakeQuerySingleTable
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
class QueryBuilderContext
|
||||
{
|
||||
protected $m_oRootFilter;
|
||||
protected $m_aClassAliases;
|
||||
protected $m_aTableAliases;
|
||||
protected $m_aModifierProperties;
|
||||
|
||||
public $m_oQBExpressions;
|
||||
|
||||
public function __construct($oFilter, $aModifierProperties, $aGroupByExpr = null)
|
||||
{
|
||||
$this->m_oRootFilter = $oFilter;
|
||||
$this->m_oQBExpressions = new QueryBuilderExpressions($oFilter, $aGroupByExpr);
|
||||
|
||||
$this->m_aClassAliases = $oFilter->GetJoinedClasses();
|
||||
$this->m_aTableAliases = array();
|
||||
|
||||
$this->m_aModifierProperties = $aModifierProperties;
|
||||
}
|
||||
|
||||
public function GetRootFilter()
|
||||
{
|
||||
return $this->m_oRootFilter;
|
||||
}
|
||||
|
||||
public function GenerateTableAlias($sNewName, $sRealName)
|
||||
{
|
||||
return MetaModel::GenerateUniqueAlias($this->m_aTableAliases, $sNewName, $sRealName);
|
||||
}
|
||||
|
||||
public function GenerateClassAlias($sNewName, $sRealName)
|
||||
{
|
||||
return MetaModel::GenerateUniqueAlias($this->m_aClassAliases, $sNewName, $sRealName);
|
||||
}
|
||||
|
||||
public function GetModifierProperties($sPluginClass)
|
||||
{
|
||||
if (array_key_exists($sPluginClass, $this->m_aModifierProperties))
|
||||
{
|
||||
return $this->m_aModifierProperties[$sPluginClass];
|
||||
}
|
||||
else
|
||||
{
|
||||
return array();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
34
core/querymodifier.class.inc.php
Normal file
34
core/querymodifier.class.inc.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?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/>
|
||||
|
||||
|
||||
/**
|
||||
* Interface iQueryModifier
|
||||
* Defines the API to tweak queries (e.g. translate data on the fly)
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
interface iQueryModifier
|
||||
{
|
||||
public function __construct();
|
||||
|
||||
public function GetFieldExpression(QueryBuilderContext &$oBuild, $sClass, $sAttCode, $sColId, Expression $oFieldSQLExp, SQLQuery &$oSelect);
|
||||
}
|
||||
?>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user